diff options
Diffstat (limited to 'tools/testing/selftests/bpf/trace_helpers.c')
-rw-r--r-- | tools/testing/selftests/bpf/trace_helpers.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c new file mode 100644 index 000000000000..ad025bd75f1c --- /dev/null +++ b/tools/testing/selftests/bpf/trace_helpers.c | |||
@@ -0,0 +1,180 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <assert.h> | ||
6 | #include <errno.h> | ||
7 | #include <poll.h> | ||
8 | #include <unistd.h> | ||
9 | #include <linux/perf_event.h> | ||
10 | #include <sys/mman.h> | ||
11 | #include "trace_helpers.h" | ||
12 | |||
13 | #define MAX_SYMS 300000 | ||
14 | static struct ksym syms[MAX_SYMS]; | ||
15 | static int sym_cnt; | ||
16 | |||
17 | static int ksym_cmp(const void *p1, const void *p2) | ||
18 | { | ||
19 | return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; | ||
20 | } | ||
21 | |||
22 | int load_kallsyms(void) | ||
23 | { | ||
24 | FILE *f = fopen("/proc/kallsyms", "r"); | ||
25 | char func[256], buf[256]; | ||
26 | char symbol; | ||
27 | void *addr; | ||
28 | int i = 0; | ||
29 | |||
30 | if (!f) | ||
31 | return -ENOENT; | ||
32 | |||
33 | while (!feof(f)) { | ||
34 | if (!fgets(buf, sizeof(buf), f)) | ||
35 | break; | ||
36 | if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) | ||
37 | break; | ||
38 | if (!addr) | ||
39 | continue; | ||
40 | syms[i].addr = (long) addr; | ||
41 | syms[i].name = strdup(func); | ||
42 | i++; | ||
43 | } | ||
44 | sym_cnt = i; | ||
45 | qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | struct ksym *ksym_search(long key) | ||
50 | { | ||
51 | int start = 0, end = sym_cnt; | ||
52 | int result; | ||
53 | |||
54 | while (start < end) { | ||
55 | size_t mid = start + (end - start) / 2; | ||
56 | |||
57 | result = key - syms[mid].addr; | ||
58 | if (result < 0) | ||
59 | end = mid; | ||
60 | else if (result > 0) | ||
61 | start = mid + 1; | ||
62 | else | ||
63 | return &syms[mid]; | ||
64 | } | ||
65 | |||
66 | if (start >= 1 && syms[start - 1].addr < key && | ||
67 | key < syms[start].addr) | ||
68 | /* valid ksym */ | ||
69 | return &syms[start - 1]; | ||
70 | |||
71 | /* out of range. return _stext */ | ||
72 | return &syms[0]; | ||
73 | } | ||
74 | |||
75 | static int page_size; | ||
76 | static int page_cnt = 8; | ||
77 | static volatile struct perf_event_mmap_page *header; | ||
78 | |||
79 | int perf_event_mmap(int fd) | ||
80 | { | ||
81 | void *base; | ||
82 | int mmap_size; | ||
83 | |||
84 | page_size = getpagesize(); | ||
85 | mmap_size = page_size * (page_cnt + 1); | ||
86 | |||
87 | base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
88 | if (base == MAP_FAILED) { | ||
89 | printf("mmap err\n"); | ||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | header = base; | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int perf_event_poll(int fd) | ||
98 | { | ||
99 | struct pollfd pfd = { .fd = fd, .events = POLLIN }; | ||
100 | |||
101 | return poll(&pfd, 1, 1000); | ||
102 | } | ||
103 | |||
104 | struct perf_event_sample { | ||
105 | struct perf_event_header header; | ||
106 | __u32 size; | ||
107 | char data[]; | ||
108 | }; | ||
109 | |||
110 | static int perf_event_read(perf_event_print_fn fn) | ||
111 | { | ||
112 | __u64 data_tail = header->data_tail; | ||
113 | __u64 data_head = header->data_head; | ||
114 | __u64 buffer_size = page_cnt * page_size; | ||
115 | void *base, *begin, *end; | ||
116 | char buf[256]; | ||
117 | int ret; | ||
118 | |||
119 | asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */ | ||
120 | if (data_head == data_tail) | ||
121 | return PERF_EVENT_CONT; | ||
122 | |||
123 | base = ((char *)header) + page_size; | ||
124 | |||
125 | begin = base + data_tail % buffer_size; | ||
126 | end = base + data_head % buffer_size; | ||
127 | |||
128 | while (begin != end) { | ||
129 | struct perf_event_sample *e; | ||
130 | |||
131 | e = begin; | ||
132 | if (begin + e->header.size > base + buffer_size) { | ||
133 | long len = base + buffer_size - begin; | ||
134 | |||
135 | assert(len < e->header.size); | ||
136 | memcpy(buf, begin, len); | ||
137 | memcpy(buf + len, base, e->header.size - len); | ||
138 | e = (void *) buf; | ||
139 | begin = base + e->header.size - len; | ||
140 | } else if (begin + e->header.size == base + buffer_size) { | ||
141 | begin = base; | ||
142 | } else { | ||
143 | begin += e->header.size; | ||
144 | } | ||
145 | |||
146 | if (e->header.type == PERF_RECORD_SAMPLE) { | ||
147 | ret = fn(e->data, e->size); | ||
148 | if (ret != PERF_EVENT_CONT) | ||
149 | return ret; | ||
150 | } else if (e->header.type == PERF_RECORD_LOST) { | ||
151 | struct { | ||
152 | struct perf_event_header header; | ||
153 | __u64 id; | ||
154 | __u64 lost; | ||
155 | } *lost = (void *) e; | ||
156 | printf("lost %lld events\n", lost->lost); | ||
157 | } else { | ||
158 | printf("unknown event type=%d size=%d\n", | ||
159 | e->header.type, e->header.size); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | __sync_synchronize(); /* smp_mb() */ | ||
164 | header->data_tail = data_head; | ||
165 | return PERF_EVENT_CONT; | ||
166 | } | ||
167 | |||
168 | int perf_event_poller(int fd, perf_event_print_fn output_fn) | ||
169 | { | ||
170 | int ret; | ||
171 | |||
172 | for (;;) { | ||
173 | perf_event_poll(fd); | ||
174 | ret = perf_event_read(output_fn); | ||
175 | if (ret != PERF_EVENT_CONT) | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | return PERF_EVENT_DONE; | ||
180 | } | ||