1 /* This program is free software; you can redistribute it and/or
2 * modify it under the terms of version 2 of the GNU General Public
3 * License as published by the Free Software Foundation.
12 #include <linux/perf_event.h>
13 #include <linux/bpf.h>
16 #include <sys/syscall.h>
17 #include <sys/ioctl.h>
29 volatile struct perf_event_mmap_page
*header
;
31 typedef void (*print_fn
)(void *data
, int size
);
33 static int perf_event_mmap(int fd
)
38 page_size
= getpagesize();
39 mmap_size
= page_size
* (page_cnt
+ 1);
41 base
= mmap(NULL
, mmap_size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
42 if (base
== MAP_FAILED
) {
51 static int perf_event_poll(int fd
)
53 struct pollfd pfd
= { .fd
= fd
, .events
= POLLIN
};
55 return poll(&pfd
, 1, 1000);
58 struct perf_event_sample
{
59 struct perf_event_header header
;
64 static void perf_event_read(print_fn fn
)
66 __u64 data_tail
= header
->data_tail
;
67 __u64 data_head
= header
->data_head
;
68 __u64 buffer_size
= page_cnt
* page_size
;
69 void *base
, *begin
, *end
;
72 asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
73 if (data_head
== data_tail
)
76 base
= ((char *)header
) + page_size
;
78 begin
= base
+ data_tail
% buffer_size
;
79 end
= base
+ data_head
% buffer_size
;
81 while (begin
!= end
) {
82 struct perf_event_sample
*e
;
85 if (begin
+ e
->header
.size
> base
+ buffer_size
) {
86 long len
= base
+ buffer_size
- begin
;
88 assert(len
< e
->header
.size
);
89 memcpy(buf
, begin
, len
);
90 memcpy(buf
+ len
, base
, e
->header
.size
- len
);
92 begin
= base
+ e
->header
.size
- len
;
93 } else if (begin
+ e
->header
.size
== base
+ buffer_size
) {
96 begin
+= e
->header
.size
;
99 if (e
->header
.type
== PERF_RECORD_SAMPLE
) {
100 fn(e
->data
, e
->size
);
101 } else if (e
->header
.type
== PERF_RECORD_LOST
) {
103 struct perf_event_header header
;
106 } *lost
= (void *) e
;
107 printf("lost %lld events\n", lost
->lost
);
109 printf("unknown event type=%d size=%d\n",
110 e
->header
.type
, e
->header
.size
);
114 __sync_synchronize(); /* smp_mb() */
115 header
->data_tail
= data_head
;
118 static __u64
time_get_ns(void)
122 clock_gettime(CLOCK_MONOTONIC
, &ts
);
123 return ts
.tv_sec
* 1000000000ull + ts
.tv_nsec
;
126 static __u64 start_time
;
128 #define MAX_CNT 100000ll
130 static void print_bpf_output(void *data
, int size
)
138 if (e
->cookie
!= 0x12345678) {
139 printf("BUG pid %llx cookie %llx sized %d\n",
140 e
->pid
, e
->cookie
, size
);
146 if (cnt
== MAX_CNT
) {
147 printf("recv %lld events per sec\n",
148 MAX_CNT
* 1000000000ll / (time_get_ns() - start_time
));
153 static void test_bpf_perf_event(void)
155 struct perf_event_attr attr
= {
156 .sample_type
= PERF_SAMPLE_RAW
,
157 .type
= PERF_TYPE_SOFTWARE
,
158 .config
= PERF_COUNT_SW_BPF_OUTPUT
,
162 pmu_fd
= sys_perf_event_open(&attr
, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0);
165 assert(bpf_map_update_elem(map_fd
[0], &key
, &pmu_fd
, BPF_ANY
) == 0);
166 ioctl(pmu_fd
, PERF_EVENT_IOC_ENABLE
, 0);
169 int main(int argc
, char **argv
)
174 snprintf(filename
, sizeof(filename
), "%s_kern.o", argv
[0]);
176 if (load_bpf_file(filename
)) {
177 printf("%s", bpf_log_buf
);
181 test_bpf_perf_event();
183 if (perf_event_mmap(pmu_fd
) < 0)
186 f
= popen("taskset 1 dd if=/dev/zero of=/dev/null", "r");
189 start_time
= time_get_ns();
191 perf_event_poll(pmu_fd
);
192 perf_event_read(print_bpf_output
);