繁体   English   中英

使用`perf_event_open`模拟`perf record -g`

[英]Emulate `perf record -g` with `perf_event_open`

我的目标是编写一些代码,以某个时间间隔记录所有CPU的当前调用堆栈。 基本上我想和perf record一样,但我自己也使用perf_event_open

根据联机帮助页,我似乎需要使用PERF_SAMPLE_CALLCHAIN样本类型并使用mmap读取结果。 也就是说,该手册页非常简洁,一些示例代码现在还有很长的路要走。

有人能指出我正确的方向吗?

了解这一点的最佳方法是阅读Linux内核源代码,并了解如何自己模拟perf record -g

正确识别后, perf events记录将从系统调用perf_event_open 这就是我们可以开始的地方,

perf_event_open的定义

如果观察系统调用的参数,您将看到第一个参数是struct perf_event_attr *类型。 这是接受系统调用属性的参数。 这是您需要修改以记录调用链。 示例代码可能是这样的(请记住,您可以按照您想要的方式调整其他参数和struct perf_event_attr的成员):

     int buf_size_shift = 8;

     static unsigned perf_mmap_size(int buf_size_shift)
     {
       return ((1U << buf_size_shift) + 1) * sysconf(_SC_PAGESIZE);
     }


     int main(int argc, char **argv)
     {

       struct perf_event_attr pe;
       long long count;
       int fd;

       memset(&pe, 0, sizeof(struct perf_event_attr));
       pe.type = PERF_TYPE_HARDWARE;
       pe.sample_type = PERF_SAMPLE_CALLCHAIN; /* this is what allows you to obtain callchains */

       pe.size = sizeof(struct perf_event_attr);
       pe.config = PERF_COUNT_HW_INSTRUCTIONS;
       pe.disabled = 1;
       pe.exclude_kernel = 1;
       pe.sample_period = 1000;
       pe.exclude_hv = 1;

       fd = perf_event_open(&pe, 0, -1, -1, 0); 
       if (fd == -1) {
          fprintf(stderr, "Error opening leader %llx\n", pe.config);
          exit(EXIT_FAILURE);
       }

       /* associate a buffer with the file */
       struct perf_event_mmap_page *mpage;
       mpage = mmap(NULL,  perf_mmap_size(buf_size_shift),
        PROT_READ|PROT_WRITE, MAP_SHARED,
       fd, 0);
       if (mpage == (struct perf_event_mmap_page *)-1L) {
        close(fd);
        return -1;
       }

       ioctl(fd, PERF_EVENT_IOC_RESET, 0);
       ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);

       printf("Measuring instruction count for this printf\n");

       ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
       read(fd, &count, sizeof(long long));

       printf("Used %lld instructions\n", count);

       close(fd);
     }

注意:可以在下面看到理解所有这些perf事件处理的简单方法 -

安迪克莱恩的PMU-TOOLS

如果您开始阅读系统调用的源代码,您将看到正在调用函数perf_event_alloc 此功能除其他外,将设置缓冲区以使用perf record获取调用链。

函数get_callchain_buffers负责设置callchain缓冲区。

perf_event_open通过采样/计数机制工作,如果与您正在分析的事件相对应的性能监视计数器溢出,则所有与事件相关的信息将被内核收集并存储到环形缓冲区中。 可以通过mmap(2)准备和访问此环形缓冲区。

编辑#1:

描述在执行perf record时使用mmap的流程图如下图所示。

在此输入图像描述

当你调用perf record时,mmaping环形缓冲区的过程将从第一个函数开始 - 这是__cmd_record ,这会调用record__open ,然后调用record__mmap ,然后调用record__mmap_evlist ,然后调用perf_evlist__mmap_ex ,然后是perf_evlist__mmap_per_cpu ,最后在perf_evlist__mmap_per_evsel中结束,就每个事件做一个mmap 而言 ,它正在完成大部分繁重工作。

编辑#2:

是的,你是对的。 当您将采样周期设置为1000时,这意味着每1000次事件发生(默认情况下为周期 ),内核会将此事件的样本记录到此缓冲区中。 这意味着perf计数器将设置为1000,因此它会在0处溢出,并且您将获得中断并最终记录样本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM