如何计算进程 ID 的已执行指令数,包括所有未来的子线程
How to count number of executed instructions of a process id including all future child threads
前段时间,我问了下面的问题,@M-Iduoad 好心地提供了一个解决方案 pgrep
来捕获所有子 PID,并在 perf stat 中与 -p 一起使用。效果很好!
但是,我遇到的一个问题是多线程应用程序,以及何时生成新线程。由于我不是算命先生(太糟糕了!),我不知道 tid
新生成的线程,因此我无法将它们添加到 perf stat
的 -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
不是必需的,如果有其他东西可以工作的话:
帮助我动态计算每个线程的“指令、周期、任务时钟、cpu-时钟、cpu-迁移、上下文切换、缓存未命中”的任何其他潜在解决方案给定的 PID(包括新生成的线程)是可以接受的,无论是使用 perf
还是其他任何东西!
perf record -s
和 perf report -T
的组合应该可以为您提供所需的信息。
为了演示,请使用以下示例代码,使用具有 well-defined 指令计数的线程:
#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 record
作为前缀命令。它将跟随所有派生的进程和线程。
$ perf record -s -e instructions -c 1000000000 ./a.out
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.003 MB perf.data (5 samples) ]
为了更好地显示统计数据:
$ perf report -T
[... snip ...]
# PID TID instructions:u
270682 270683 500003888
270682 270684 2000001866
270682 270685 4000002177
perf record
的参数有点棘手。 -s
用相当精确的数字写入单独的记录 - 它们不依赖于指令样本(每 1000000000 条指令生成)。然而,perf report
,即使 -T
在找不到单个样本时也会失败。所以你需要设置一个至少触发一次的指令样本计数-c
(或频率)。任何示例都可以,它不需要每个线程一个示例。
或者,您可以查看 perf.data
中的原始记录。然后你实际上可以告诉perf record
不要收集任何个样本。
$ 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
前段时间,我问了下面的问题pgrep
来捕获所有子 PID,并在 perf stat 中与 -p 一起使用。效果很好!
但是,我遇到的一个问题是多线程应用程序,以及何时生成新线程。由于我不是算命先生(太糟糕了!),我不知道 tid
新生成的线程,因此我无法将它们添加到 perf stat
的 -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
不是必需的,如果有其他东西可以工作的话:
帮助我动态计算每个线程的“指令、周期、任务时钟、cpu-时钟、cpu-迁移、上下文切换、缓存未命中”的任何其他潜在解决方案给定的 PID(包括新生成的线程)是可以接受的,无论是使用 perf
还是其他任何东西!
perf record -s
和 perf report -T
的组合应该可以为您提供所需的信息。
为了演示,请使用以下示例代码,使用具有 well-defined 指令计数的线程:
#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 record
作为前缀命令。它将跟随所有派生的进程和线程。
$ perf record -s -e instructions -c 1000000000 ./a.out
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.003 MB perf.data (5 samples) ]
为了更好地显示统计数据:
$ perf report -T
[... snip ...]
# PID TID instructions:u
270682 270683 500003888
270682 270684 2000001866
270682 270685 4000002177
perf record
的参数有点棘手。 -s
用相当精确的数字写入单独的记录 - 它们不依赖于指令样本(每 1000000000 条指令生成)。然而,perf report
,即使 -T
在找不到单个样本时也会失败。所以你需要设置一个至少触发一次的指令样本计数-c
(或频率)。任何示例都可以,它不需要每个线程一个示例。
或者,您可以查看 perf.data
中的原始记录。然后你实际上可以告诉perf record
不要收集任何个样本。
$ 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