关于perf_event_open怎么监控多事件的思考

之前在用c语言构造轻量级perf工具的时候,总是监控单个type.

  29 enum perf_type_id {                                                                   
  30         PERF_TYPE_HARDWARE                      = 0,                                          
  31         PERF_TYPE_SOFTWARE                      = 1,                                       
  32         PERF_TYPE_TRACEPOINT                    = 2,                                       
  33         PERF_TYPE_HW_CACHE                      = 3,                                          
  34         PERF_TYPE_RAW                           = 4,                                         
  35         PERF_TYPE_BREAKPOINT                    = 5,                                        
  36                                                                                            
  37         PERF_TYPE_MAX,                          /* non-ABI */                              
  38 }; 

在配置perf_event_attr时候,指定type时,只能配置一个tpye, 例如如下代码:

 58     struct perf_event_attr sw_event;
 75     memset(&sw_event,0,sizeof(struct perf_event_attr));
 76     sw_event.size=sizeof(struct perf_event_attr);
 77     sw_event.type = PERF_TYPE_SOFTWARE;  //监控软件
 79     sw_event.sample_type=PERF_SAMPLE_IP|PERF_SAMPLE_CALLCHAIN;
 80     sw_event.config=PERF_COUNT_SW_CPU_CLOCK;  //监控软件cpu时钟产生的时间。
 81     sw_event.config= 0;
 82     sw_event.sample_id_all = 1;
 83     sw_event.mmap = 1;
 84     sw_event.comm = 1;

但是如果此时我想监控PERF_TYPE_HARDWARE该怎么办?

首先按照上面的例子,配置一个新的perf_event_attr

 58     struct perf_event_attr hard_event;
 70     memset(&hard_event, 0, sizeof(struct perf_event_attr));
 72
 73     hard_event.type = PERF_TYPE_HARDWARE;
 74     hard_event.size = sizeof(struct perf_event_attr);
 75     hard_event.disabled = 1;
 76     hard_event.config = PERF_COUNT_HW_CPU_CYCLES; // 同样监控cpu cycles事件,但是此时是硬件方式监控
 77     hard_event.exclude_kernel = 1;             
 78     hard_event.exclude_hv = 1;                   

其次,我们怎么才能获取到该两个perf_event_attr采集的数据呢?

我们知道,perf的数据采集和用户态之间传输是通过句柄联系在一起的,而数据的获取可以通过read或者mmap的方式。但是怎么同时获取呢,

下面我们需要做的就是同时创建两个采样的句柄fd1和fd2。

 92     fd1 = perf_event_open(&hard_event, pid, -1, -1, 0);
 93     if (!is_fd_vaild(fd1))
 94     {
 95         fprintf(stderr, "Error opening leader %llx
", hw_event.config);
 96         exit(EXIT_FAILURE);
 97     }
 98     ioctl(fd1, PERF_EVENT_IOC_ID, &id1);
 99
100     fd2 = perf_event_open(&sf_event, pid, -1, fd1, 0); 
101     if (!is_fd_vaild(fd2))
102     {
103         fprintf(stderr, "Error opening leader member %llx
", _event.config);
104         exit(EXIT_FAILURE);
105     }
106     ioctl(fd2, PERF_EVENT_IOC_ID, &id2);

其中后续采集的数据如果需要区分是哪个句柄采集获取的,可以通过id1和id2表示区别开来,到此处,感觉可以了,但是其实还原因不够,笔者在调试的时候

认为这样就没有问题了,后面才发现,自己打错特错。

因为是多tpye采样,此时的采样并不是单个,而是以group的形式进行,所以必须在设置read_format的时候,指定格式为group的形式。

hardw_event.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
sw_event.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;

此处的设置,是采到数据的关键,后面句柄的启动,只需要ENABLE其中一个即可。

108     ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
109     ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
110     do_someting();
111     ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);

后面的数据就不写出来了,已经很简单了。