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

标签 c linux perf

我的目标是编写一些代码以在某个时间间隔记录所有 CPU 的当前调用堆栈。本质上,我想做与 perf record 相同的事情,但我自己使用 perf_event_open

根据联机帮助页,我似乎需要使用 PERF_SAMPLE_CALLCHAIN 示例类型并使用 mmap 读取结果。也就是说,联机帮助页非常简洁,一些示例代码现在可以发挥很大作用。

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

最佳答案

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

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

definition of perf_event_open

如果观察系统调用的参数,您会发现第一个参数是struct perf_event_attr * 类型。这是接受系统调用属性的参数。这是您需要修改以记录调用链的内容。示例代码可能是这样的(请记住,您可以按照自己的方式调整结构 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 by Andi Kleen

如果您开始阅读系统调用的源代码,您会看到一个函数 perf_event_alloc正在被调用。此函数将使用 perf record 设置缓冲区以获取调用链。

函数get_callchain_buffers负责设置调用链缓冲区。

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

编辑#1:

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

enter image description here

当您调用 perf record 时,映射环形缓冲区的过程将从第一个函数开始 - 即 __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 次(默认为 cycles),内核会将此事件的样本记录到此缓冲区中.这意味着 perf 计数器将设置为 1000,因此它会在 0 时溢出,您将获得中断并最终记录样本。

关于c - 用 `perf record -g` 模拟 `perf_event_open`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49160450/

相关文章:

c++ - 如何将源代码附加到 Eclipse 中的预构建静态 C 库

c# - 如何从 C#.net 客户端列出 linux 运行进程?

linux - 如何使用 bacula 编写备份脚本?

linux - 多核多处理器中的cpu负载

PERF_TYPE_HARDWARE 和 PERF_TYPE_HW_CACHE 并发监控

intel - perf_event_open 和 PERF_COUNT_HW_INSTRUCTIONS

c - 32x32 乘加优化

c - 需要为 2D 元组数组和未知大小的 2D 指针数组分配内存?

performance - Perf overcounting simple CPU-bound loop : mysterious kernel work?

c - 内核在将状态设置为 TCP_SYN_RECV 后如何继续进行三次握手