Linux 如何计算进程id(包括所有未来子线程)的已执行指令数

Linux 如何计算进程id(包括所有未来子线程)的已执行指令数,linux,performance,profiling,performance-testing,perf,Linux,Performance,Profiling,Performance Testing,Perf,几天前,我问了下面的问题,@M-Iduoad好心地提供了一个带有pgrep的解决方案来捕获所有子PID,并在perf stat中与-p一起使用。它工作得很好 然而,我遇到的一个问题是多线程应用程序,以及在生成新线程时。由于我不是算命师(太糟糕了!),我不知道新生成的线程的tid,因此我无法将它们添加到perf stat的s-p或-t参数中 例如,假设我有一个多线程nodejs服务器(部署为Kubernetes之上的容器),具有以下pstree: root@node2:/home/m# pstre

几天前,我问了下面的问题,@M-Iduoad好心地提供了一个带有
pgrep
的解决方案来捕获所有子PID,并在perf stat中与-p一起使用。它工作得很好

然而,我遇到的一个问题是多线程应用程序,以及在生成新线程时。由于我不是算命师(太糟糕了!),我不知道新生成的线程的
tid
,因此我无法将它们添加到
perf stat
的s-p或-t参数中

例如,假设我有一个多线程nodejs服务器(部署为Kubernetes之上的容器),具有以下
pstree

root@node2:/home/m# pstree -p 4037791
node(4037791)─┬─sh(4037824)───node(4037825)─┬─{node}(4037826)
              │                             ├─{node}(4037827)
              │                             ├─{node}(4037828)
              │                             ├─{node}(4037829)
              │                             ├─{node}(4037830)
              │                             └─{node}(4037831)
              ├─{node}(4037805)
              ├─{node}(4037806)
              ├─{node}(4037807)
              ├─{node}(4037808)
              ├─{node}(4037809)
              ├─{node}(4037810)
              ├─{node}(4037811)
              ├─{node}(4037812)
              ├─{node}(4037813)
              └─{node}(4037814) 
当然,我可以使用以下
perf stat
命令来监视其线程:

perf stat --per-thread -e instructions,cycles,task-clock,cpu-clock,cpu-migrations,context-switches,cache-misses,duration_time -p $(pgrep --ns 4037791 | paste -s -d ",")
它适用于单线程nodejs应用程序。但对于多线程服务,一旦收到请求,
pstree
输出将如下所示:

root@node2:/home/m# pstree -p 4037791
node(4037791)─┬─sh(4037824)───node(4037825)─┬─{node}(4037826)
              │                             ├─{node}(4037827)
              │                             ├─{node}(4037828)
              │                             ├─{node}(4037829)
              │                             ├─{node}(4037830)
              │                             ├─{node}(4037831)
              │                             ├─{node}(1047898)
              │                             ├─{node}(1047899)
              │                             ├─{node}(1047900)
              │                             ├─{node}(1047901)
              │                             ├─{node}(1047902)
              │                             ├─{node}(1047903)
              │                             ├─{node}(1047904)
              │                             ├─{node}(1047905)
              │                             ├─{node}(1047906)
              │                             ├─{node}(1047907)
              │                             ├─{node}(1047908)
              │                             ├─{node}(1047909)
              │                             ├─{node}(1047910)
              │                             ├─{node}(1047911)
              │                             ├─{node}(1047913)
              │                             ├─{node}(1047914)
              │                             ├─{node}(1047919)
              │                             ├─{node}(1047920)
              │                             ├─{node}(1047921)
              │                             └─{node}(1047922)
              ├─{node}(4037805)
              ├─{node}(4037806)
              ├─{node}(4037807)
              ├─{node}(4037808)
              ├─{node}(4037809)
              ├─{node}(4037810)
              ├─{node}(4037811)
              ├─{node}(4037812)
              ├─{node}(4037813)
              └─{node}(4037814)
因此,我以前的
perf stat
命令不会捕获新生成线程的统计信息。我的意思是,它可能会捕获累积的指令,但它肯定不会以“每线程”的格式显示

是否有任何方法可以在perf stat中使用
--per thread
,并在多线程应用程序中捕获新生成的线程的状态?它似乎只与
-p
-t
一起工作,以遵循
perf
启动时已经存在的一组固定线程,而不会遵循新线程


有一个类似的例子,但我使用的是
perf stat
。而且,这似乎并没有按线程分隔记录的配置文件,因此它只相当于
perf stat node…
,除非有一种方法可以处理记录的数据,以便在事后按线程将其分隔开


perf
如果还有其他可行的方法,则不需要:
无论使用
perf
还是其他任何方法,任何其他可能的解决方案都可以帮助我动态计算给定PID的每个线程(包括新生成的线程)的“指令、周期、任务时钟、cpu时钟、cpu迁移、上下文切换、缓存未命中数”

性能记录-s和性能报告-T的组合应能提供您所需的信息

为了演示,请使用具有定义良好的指令计数的线程执行以下示例代码:

#include <cstdint>
#include <thread>

void work(int64_t count) {
    for (int64_t i = 0; i < count; i++);
}

int main() {
    std::thread first(work, 100000000ll);
    std::thread second(work, 400000000ll);
    std::thread third(work, 800000000ll);
    first.join();
    second.join();
    third.join();
}
要很好地显示统计信息,请执行以下操作:

$ perf report -T
[... snip ...]
#    PID     TID  instructions:u
  270682  270683       500003888
  270682  270684      2000001866
  270682  270685      4000002177
perf record
的参数有点棘手
-s
使用相当精确的数字写入单独的记录-它们不依赖于指令样本(每100000000条指令生成一次)。但是,
perf report
,即使使用
-T
也会在找不到单个样本时失败。因此,您需要设置至少触发一次的指令样本计数
-c
(或频率)。任何样本都可以,每个线程不需要样本

或者,您可以查看
perf.data
中的原始记录。然后您可以告诉
perf record
不要采集任何样本

但是你需要过滤掉相关的记录,可能还有其他的记录需要总结

$ perf script -D | grep PERF_RECORD_READ | grep -v " 0$"
# Annotation by me                              PID    TID 
213962455637481 0x760 [0x40]: PERF_RECORD_READ: 270887 270888 instructions:u 500003881
213963194850657 0x890 [0x40]: PERF_RECORD_READ: 270887 270889 instructions:u 2000001874
213964190418415 0x9c0 [0x40]: PERF_RECORD_READ: 270887 270890 instructions:u 4000002175

perf stat
不带
-p
跟踪所有线程。e、 g.
perf stat节点foo.js
,与链接的
perf记录中的问题相同。您的问题是让perf在使用
-p
而不是作为
perf
@PeterCordes的子线程运行命令时执行什么,我尝试了不使用-p和使用--per线程,但它仍然不会报告perf启动后新分叉/衍生的线程:-(…换句话说,inherit模式似乎关闭了
perf stat ffmpeg-i foo.mkv bar。mkv
适合我。我看到
190958.96毫秒任务时钟(在我的4c8t Skylake系统上使用了
7.027 CPU),占用了27.17秒的挂钟时间。(按
q
提前停止后。)对于HW事件,其他计数反映了FFmpeg、libx264和音频编码启动的所有线程的计数。好吧,我很担心;您链接的性能记录实际上并不等同于您想要的性能记录。因此,仅用于
perf record
并不是这与您的问题之间的唯一区别。您是否尝试-s
取消性能记录以包括每个线程的统计信息,然后使用
性能报告-T
读取性能数据。您将在底部得到一个包含线程及其统计信息的表。每个线程的硬件计数器是否分别虚拟化?例如,是否可能有一个线程生成9999
内存指令失效。拆分加载de>事件,但另一个线程实际触发事件并在该线程中仅使用1个未对齐负载获取样本?(在大多数情况下,这不应该是一个实际问题,特别是对于像
指令
这样的非常频繁的事件,我只是好奇它是如何在后台工作的;无论是每个线程的样本记录,还是在同一进程的线程之间的上下文切换上保存/恢复的硬件计数器。)我相信整个事件状态与任务紧密相连,并在上下文切换期间进行切换(请参阅)。您也可以长时间地对事件进行计数,甚至不产生溢出中断,Linux应该确保您只从所选的pid获取计数。@Zulan是否也使用了-p?我的意思是提供正在运行的进程的pid?@Zulan
perf report-T
似乎不使用-p!但是,梳理您的第二个第一个解决方案给了我最好的结果:
perf-record-s-e-c 100000000-p
…解析:
perf-script-D | grep perf_-record|u READ | grep-v“0$”| grep
是的,我认为
-p
不可能遵循新生成的子进程。我假设您可以影响
$ perf record -s -e instructions -n ./a.out             
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.003 MB perf.data ]
$ perf script -D | grep PERF_RECORD_READ | grep -v " 0$"
# Annotation by me                              PID    TID 
213962455637481 0x760 [0x40]: PERF_RECORD_READ: 270887 270888 instructions:u 500003881
213963194850657 0x890 [0x40]: PERF_RECORD_READ: 270887 270889 instructions:u 2000001874
213964190418415 0x9c0 [0x40]: PERF_RECORD_READ: 270887 270890 instructions:u 4000002175