之前在用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);
后面的数据就不写出来了,已经很简单了。