确定进程正在花费时间等待的系统调用或子系统

时间:2019-02-08 16:43:32

标签: linux performance blocking perf systemtap

我正在寻找学习哪个syscalls 哪个子系统的方法,这些过程或线程花费时间等待,即被阻塞并且没有计划在CPU上运行。

特别是如果我有一些未知的过程,或者我们所知道的只是“很慢”的过程,我希望能够学习类似的东西:

  • “它在sys_write()上花费了80%的时间在/ some / file的fd 13上”
  • “它花费大量时间等待从网络套接字读取()”
  • “它正在epoll_wait()中休眠,用于fds [4,5,6]上的活动,这些fds是[file / boo],[socket 10.1.1.:42]、[notifyfd等等]”

换句话说当我的程序不在CPU上运行时,它在做什么?

perf很难回答 ,因为它似乎无法记录从sys_enter到sys_exit的持续时间。否则,请跟踪事件的持续时间。大概是由于其采样性质。

我知道使用Brendan Gregg的off-cpu work进行的Linux 4.6及更高版本的eBPF的一些实验工作可能会有所帮助。但是在运行和支持4.6内核的悲惨世界中,这是一个难得的珍贵独角兽。

现实世界中有哪些选择?

ftrace,systemtap等在这里提供任何见解吗?

1 个答案:

答案 0 :(得分:3)

您可以使用strace。首先,您可能需要对每种类型的系统调用的费用进行简要概述。您可以通过运行strace -c获得此摘要。例如,可能的输出如下:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 90.07    0.000263          26        10           getdents
  3.42    0.000010           0      1572           read
  3.42    0.000010           0       762         1 stat
  3.08    0.000009           0      1574         6 open
  0.00    0.000000           0        11           write
  0.00    0.000000           0      1569           close
  0.00    0.000000           0        48           fstat

% time值是相对于整个内核时间,而不是相对于整个执行时间(内核+用户)。此摘要告诉您最昂贵的系统调用是什么。但是,如果需要确定哪些特定的系统调用实例最昂贵,以及将哪些参数传递给它们,则可以运行strace -i -T-i选项显示执行系统调用的指令的指令地址,而-T选项显示在系统调用中花费的时间。输出可能看起来像这样:

[00007f97f1b37367] open("myfile", O_RDONLY|O_CLOEXEC) = 3 <0.000020>
[00007f97f1b372f4] fstat(3, {st_mode=S_IFREG|0644, st_size=159776, ...}) = 0 <0.000018>
[00007f97f1b374ba] mmap(NULL, 159776, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f97f1d19000 <0.000019>
[00007f97f1b37467] close(3)             = 0 <0.000018>

第一列显示指令地址,第二列显示带有参数的系统调用,第三列显示返回的值,最后一列显示在该系统调用中花费的时间。该列表是根据系统调用的动态发生而排序的。您可以使用grep-e选项过滤此输出。指令地址可以帮助您找到这些系统调用在源代码中的位置。例如,如果一长串的系统调用具有相同的地址,则很有可能在包含系统调用的代码中的某个地方出现循环。如果可执行二进制文件不是PIE,则动态地址与objdump所示的静态地址相同。但是即使使用PIE,动态地址的相对顺序也相同。我不知道是否有一种简单的方法可以将这些系统调用映射到源代码行。

如果要查找诸如“它花费了80%的时间在/ some / file的fd 13上的sys_write()中”的信息,则需要编写一个脚本,该脚本首先提取所有{{ 1}}调用和相应的文件名参数,然后总结open参数等于某个值的所有sys_write调用的时间。