使用 /proc/*/stat 进行分析

Posted

技术标签:

【中文标题】使用 /proc/*/stat 进行分析【英文标题】:Using /proc/*/stat for profiling 【发布时间】:2017-02-14 19:07:48 【问题描述】:

在 Linux 上,进程(主线程)的最后一个程序计数器值显示在 /proc/$PID/stat 中。这似乎是一种非常简单易行的方法来进行一些采样分析,而无需以任何方式检测程序。

不过,我想知道这在采样质量方面是否有任何警告。我假设每当进程用完它的时间片时更新这个值,这应该在程序代码中以完全随机的间隔发生,并且在超过时间片长度的情况下采集的样本应该是一致的根据程序实际花费的时间随机分布。但这只是一个假设,我意识到它在很多方面都可能是错误的。

有人知道吗?

【问题讨论】:

【参考方案1】:

为什么不试试像perf (https://perf.wiki.kernel.org/index.php/Main_Page) 这样的现代内置Linux 工具?

它具有可调节频率的record 模式(-F100 用于 100 Hz),具有许多事件,例如,在软件事件 task-clock 上不使用硬件性能计数器(使用 Ctrl-C 停止 perf或者在右边加sleep 10采样10秒):

 perf record -p $PID -e task-clock -o perf.output.file

Perf 适用于所有线程,无需任何检测(重新编译或代码编辑),并且不会干扰程序执行(仅对定时器中断稍作修改)。 (还有一些使用-g 选项支持堆栈跟踪采样。)

可以使用perf report 离线解析输出(只有这个命令会尝试解析二进制和共享库)

 perf report -i perf.output.file

或使用perf script -i perf.output.file 转换为原始 PC (EIP) 样本。

PS:/proc/$pid/stat 文件中的 EIP 指针在官方 linux 手册页 5 proc http://man7.org/linux/man-pages/man5/proc.5.html 中提到为 kstkeip - “当前 EIP(指令指针)。”它在fs/proc/array.c:do_task_stateip = KSTK_EIP(task); 阅读,但我不确定它在何时何地被填满。它可以写在任务切换(taskslice 结束时非自愿和任务执行sched_yield 之类的事情时自愿)或阻塞系统调用上,因此它可能不是作为采样源的最佳选择。

【讨论】:

我确实自己发现了perf record 并使用它取得了一些成功(如迈克回答的 cmets 中所述),我还构建了一个简单的 C 程序,它使用 libunwind 非常快速从正在运行的进程中获取完整的堆栈跟踪。话虽如此,但我不太明白您基于什么得出/proc/*/stat 信息不理想的结论。 我不知道/proc/*/stat 的“ip”字段何时更新;阻止系统调用可能存在很高的系统偏差。它的时间分辨率也受到限制(每秒大约 100 次更新),默认情况下 perf 可能允许频率高达 2-4 kHz 的事件(适用于短时间运行的程序)。您在 libunwind、ptrace 中使用了哪种访问方式? 我想可能存在这样的偏见,但就我们目前所知,我看不出有什么特别表明存在,或者我错过了什么?是的,我确实在 libunwind 中使用了 ptrace 访问权限。 fs/proc/array.c:do_task_stat() 只是打印出eip = KSTK_EIP(task),用KSTK_EIP(task) (task_pt_regs(task)->ip) 读取。它是任务的thread_info堆栈上的struct pt_regs,这是有关用户线程的内核模式信息。当线程在 CPU 上运行时,没有人会更新内存中的一些内核内容,CPU 只是获取 EIP、下一个 EIP、下一个 EIP,而不会将真正的 EIP 写入 RAM。我认为,该字段由系统调用和/或中断上的某些 entry.s 或switch_to - lxr.free-electrons.com/source/arch/x86/include/asm/… 更新【参考方案2】:

如果它可以工作,它就会有 prof 的缺点,而 gprof 应该可以弥补这些缺点。然后 gprof 有自己的shortcomings,这导致了许多更现代的分析器。我们中的一些人认为this 是最有效的,它可以使用像 pstacklsstack 这样简单的工具来完成。

【讨论】:

哦,我非常同意使用调试器进行手动采样通常是最好的,但我的问题是我正在尝试使用 LLVM-JITted 代码分析软实时程序,这会导致 gdb需要大约 1 秒的时间来附加它(以读取 JIT 生成的符号表),这打破了实时约束。因此,我试图找到不同的方法来对其进行采样。 :) @Dolda2000: 想法: 1) 你能在调试器下运行整个事情,所以预先支付符号表的成本吗? 2)你能完全运行它,而不是等待外部事件吗? - 如果那里隐藏着任何浪费,这应该暴露它。 3) 根据我的经验,在我打断它之后需要多少时间 并不重要。我可以花一整天的时间来研究它。重要的是中断本身发生的时间对于程序来说是不可预测的,因此会看到浪费,达到需要时间的程度。 除此之外,我只想说我会对这个问题的实际答案感兴趣,因为这将帮助我确定来自@987654323 的 EIP 值何时以及在何种情况下@ 可能有用。但是是的,至于 1) 和 3),那么问题是我中断程序、打印堆栈跟踪并恢复它的时间也需要大约一秒钟(重新调用 gdb 的好处是我可以给它-batch -ex 选项)。如果有办法解决这个问题,那也将非常有趣。至于 2),我需要“在生产中”运行程序才能使结果有意义。 这样做的问题是,我不仅干扰了程序,还干扰了它的用户,我不想这样做。 其实我后来发现perf record,它也做基于PC的采样,并用它来优化两个特定的算法函数,使程序整体速度提高了1.5倍-2。我并不反对堆栈通常更有用,但它表明基于 PC 的采样也可以有用。

以上是关于使用 /proc/*/stat 进行分析的主要内容,如果未能解决你的问题,请参考以下文章

Linux下进程信息的深入分析

R语言使用pROC包的plot.roc函数对单变量进行ROC分析并可视化ROC曲线

linux进程隐藏 hook readdir函数 挂载覆盖/proc/pid 目

linux /proc/meminfo 文件分析(转载)

docker stats 命令源码分析

R语言使用pROC包的的plot.roc函数对单变量进行ROC分析并可视化ROC曲线寻找最佳阈值(thresholdcutoff)在可视化曲线中添加最佳阈值点