每秒将“perf stat”输出保存到文件中 完全未经测试的示例

问题描述

我想获得 perf 输出并对其进行分析。我用过

while (true) {
      system("sudo perf kvm stat -e r10f4 -a sleep 1 2>&1 | sed '1,3d' | sed '2,$d' > 'system.log' 2>&1");
      sleep(0.5);
    }

上面的代码经常使用perf,成本很高。相反,我正在运行 perf stat,如:perf kvm stat -a -I 1000 > system.log 2>&1

这个命令会不断地将数据写入“system.log”,但我只需要一秒钟的数据。

我想知道如何让新数据每秒覆盖旧数据。 有人知道我的想法是否可行吗?或者其他可以解决我的问题的方法

解决方法

你的意思是让 perf 继续覆盖,而不是追加,所以你只有最后一秒?

一种方法是让您自己的程序管道 perf stat -I1000 进入自身(例如,通过 C stdio popen 或使用 POSIX pipe / dup2 / fork/exec)。然后,您将实现选择将行写入文件的位置和方式的逻辑。您可以在每次写入之前查找输出文件的开头,而不是正常写入它们。或者 pwrite 而不是 write 始终写入文件位置 0。您可以附加一些空格以填充到固定宽度,以确保较长的行不会在文件中留下一些字符你没有覆盖。 (或 ftruncate 行比前一行短的写入后的输出文件)。


或:使用 O_APPEND 重定向 perf stat -I 1000 >> file.log,并定期截断长度。

为追加而打开的文件将自动写入当前结尾的任何位置,因此您可以让 perf ... -I 1000 运行并每隔一秒或每 5 秒左右截断文件。因此,您最多需要通读 5 行内容才能找到您真正想要的最后一行。如果使用 system 通过 shell 运行它,请使用 >>。或者,如果使用 open(O_APPEND) / dup2,请使用 fork/execve

要在 sleep 循环中进行实际截断,truncate() by path,or ftruncate() by open fd。理想情况下,您应该在即将写入新行之前 截断右侧,因此大多数情况下都会有一行。但是,除非您进行额外的系统调用,例如 fstatinotify,否则您将不知道什么时候会出现另一个系统调用,尽管航位推算并假设 sleep 睡眠时间最短也可以。


或者:重定向perf stat -I 1000没有附加),并从父进程中寻找它的fd,以创建与通过进程/线程在写之前寻找的管道相同的效果。

Why does forking my process cause the file to be read infinitely 表明具有共享相同打开文件描述的文件描述符的父/子进程可以通过 lseek 影响彼此的文件位置。

这仅在您自己使用 open / dup2 进行重定向时才有效,如果您通过 system 将其留给 shell,则无效。您需要自己的描述符来引用相同的打开文件描述

不确定您是否可以在子进程打开的同一个 fd 上玩 lseek 之类的技巧;在这种情况下,避免 O_APPEND 可以让您在不截断的情况下将写入位置设置为 0,因此下一次写入将覆盖内容。但这仅适用于 open / dup2 / fork/exec 并且父进程中的 fd 的文件位置与子进程中的 fd 相关联。>

完全未经测试的示例

即使使用正确的标头,这实际上也可能无法编译,但希望能说明这个想法。

  // must *not* use O_APPEND,otherwise file position is ignored for write.
 int fd = open("system.log",O_WRONLY|O_CREAT|O_TRUNC,0666);

 if ((pid = fork()) != 0){
     // parent
     while(1) {
         sleep(1);
         lseek(fd,SEEK_SET);    // reset the position at which perf will do its next write
     }
 } else {
     // child
     dup2(fd,1);          // redirect stdout to the open file
      // TODO: look up best practices for fork/exec,and error checking
     execlp("sudo","perf","kvm","stat","-er10f4","-a","sleep","1",NULL);
     perror("execlp sudo perf failed");
 }

注意完全没有对返回值进行错误检查。这更像是伪代码,尽管它是用(希望如此)有效的 C 语法编写的。


顺便说一句,您可以将该管道减少到一个 sed 命令,只需 sed -n 4p 来打印第 4 行而不是其他行。但是 perf ... stat -I 1000 不会浪费行,因此无需以这种方式过滤行号。