问题描述
我想获得 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。理想情况下,您应该在即将写入新行之前 截断右侧,因此大多数情况下都会有一行。但是,除非您进行额外的系统调用,例如 fstat
或 inotify
,否则您将不知道什么时候会出现另一个系统调用,尽管航位推算并假设 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
不会浪费行,因此无需以这种方式过滤行号。