aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/tests')
-rw-r--r--tools/perf/tests/builtin-test.c4
-rw-r--r--tools/perf/tests/code-reading.c509
-rw-r--r--tools/perf/tests/tests.h1
3 files changed, 514 insertions, 0 deletions
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index b7b4049fabbb..f5af19244a05 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -100,6 +100,10 @@ static struct test {
100 }, 100 },
101#endif 101#endif
102 { 102 {
103 .desc = "Test object code reading",
104 .func = test__code_reading,
105 },
106 {
103 .func = NULL, 107 .func = NULL,
104 }, 108 },
105}; 109};
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
new file mode 100644
index 000000000000..28bee6285f5d
--- /dev/null
+++ b/tools/perf/tests/code-reading.c
@@ -0,0 +1,509 @@
1#include <sys/types.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <stdio.h>
5#include <inttypes.h>
6#include <ctype.h>
7#include <string.h>
8
9#include "parse-events.h"
10#include "evlist.h"
11#include "evsel.h"
12#include "thread_map.h"
13#include "cpumap.h"
14#include "machine.h"
15#include "event.h"
16#include "thread.h"
17
18#include "tests.h"
19
20#define BUFSZ 1024
21#define READLEN 128
22
23static unsigned int hex(char c)
24{
25 if (c >= '0' && c <= '9')
26 return c - '0';
27 if (c >= 'a' && c <= 'f')
28 return c - 'a' + 10;
29 return c - 'A' + 10;
30}
31
32static void read_objdump_line(const char *line, size_t line_len, void **buf,
33 size_t *len)
34{
35 const char *p;
36 size_t i;
37
38 /* Skip to a colon */
39 p = strchr(line, ':');
40 if (!p)
41 return;
42 i = p + 1 - line;
43
44 /* Read bytes */
45 while (*len) {
46 char c1, c2;
47
48 /* Skip spaces */
49 for (; i < line_len; i++) {
50 if (!isspace(line[i]))
51 break;
52 }
53 /* Get 2 hex digits */
54 if (i >= line_len || !isxdigit(line[i]))
55 break;
56 c1 = line[i++];
57 if (i >= line_len || !isxdigit(line[i]))
58 break;
59 c2 = line[i++];
60 /* Followed by a space */
61 if (i < line_len && line[i] && !isspace(line[i]))
62 break;
63 /* Store byte */
64 *(unsigned char *)*buf = (hex(c1) << 4) | hex(c2);
65 *buf += 1;
66 *len -= 1;
67 }
68}
69
70static int read_objdump_output(FILE *f, void **buf, size_t *len)
71{
72 char *line = NULL;
73 size_t line_len;
74 ssize_t ret;
75 int err = 0;
76
77 while (1) {
78 ret = getline(&line, &line_len, f);
79 if (feof(f))
80 break;
81 if (ret < 0) {
82 pr_debug("getline failed\n");
83 err = -1;
84 break;
85 }
86 read_objdump_line(line, ret, buf, len);
87 }
88
89 free(line);
90
91 return err;
92}
93
94static int read_via_objdump(const char *filename, u64 addr, void *buf,
95 size_t len)
96{
97 char cmd[PATH_MAX * 2];
98 const char *fmt;
99 FILE *f;
100 int ret;
101
102 fmt = "%s -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s";
103 ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len,
104 filename);
105 if (ret <= 0 || (size_t)ret >= sizeof(cmd))
106 return -1;
107
108 pr_debug("Objdump command is: %s\n", cmd);
109
110 f = popen(cmd, "r");
111 if (!f) {
112 pr_debug("popen failed\n");
113 return -1;
114 }
115
116 ret = read_objdump_output(f, &buf, &len);
117 if (len) {
118 pr_debug("objdump read too few bytes\n");
119 if (!ret)
120 ret = len;
121 }
122
123 pclose(f);
124
125 return ret;
126}
127
128static int read_object_code(u64 addr, size_t len, u8 cpumode,
129 struct thread *thread, struct machine *machine)
130{
131 struct addr_location al;
132 unsigned char buf1[BUFSZ];
133 unsigned char buf2[BUFSZ];
134 size_t ret_len;
135 u64 objdump_addr;
136 int ret;
137
138 pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr);
139
140 thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr,
141 &al);
142 if (!al.map || !al.map->dso) {
143 pr_debug("thread__find_addr_map failed\n");
144 return -1;
145 }
146
147 pr_debug("File is: %s\n", al.map->dso->long_name);
148
149 if (al.map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
150 pr_debug("Unexpected kernel address - skipping\n");
151 return 0;
152 }
153
154 pr_debug("On file address is: %#"PRIx64"\n", al.addr);
155
156 if (len > BUFSZ)
157 len = BUFSZ;
158
159 /* Do not go off the map */
160 if (addr + len > al.map->end)
161 len = al.map->end - addr;
162
163 /* Read the object code using perf */
164 ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1,
165 len);
166 if (ret_len != len) {
167 pr_debug("dso__data_read_offset failed\n");
168 return -1;
169 }
170
171 /*
172 * Converting addresses for use by objdump requires more information.
173 * map__load() does that. See map__rip_2objdump() for details.
174 */
175 if (map__load(al.map, NULL))
176 return -1;
177
178 /* Read the object code using objdump */
179 objdump_addr = map__rip_2objdump(al.map, al.addr);
180 ret = read_via_objdump(al.map->dso->long_name, objdump_addr, buf2, len);
181 if (ret > 0) {
182 /*
183 * The kernel maps are inaccurate - assume objdump is right in
184 * that case.
185 */
186 if (cpumode == PERF_RECORD_MISC_KERNEL ||
187 cpumode == PERF_RECORD_MISC_GUEST_KERNEL) {
188 len -= ret;
189 if (len)
190 pr_debug("Reducing len to %zu\n", len);
191 else
192 return -1;
193 }
194 }
195 if (ret < 0) {
196 pr_debug("read_via_objdump failed\n");
197 return -1;
198 }
199
200 /* The results should be identical */
201 if (memcmp(buf1, buf2, len)) {
202 pr_debug("Bytes read differ from those read by objdump\n");
203 return -1;
204 }
205 pr_debug("Bytes read match those read by objdump\n");
206
207 return 0;
208}
209
210static int process_sample_event(struct machine *machine,
211 struct perf_evlist *evlist,
212 union perf_event *event)
213{
214 struct perf_sample sample;
215 struct thread *thread;
216 u8 cpumode;
217
218 if (perf_evlist__parse_sample(evlist, event, &sample)) {
219 pr_debug("perf_evlist__parse_sample failed\n");
220 return -1;
221 }
222
223 thread = machine__findnew_thread(machine, sample.pid);
224 if (!thread) {
225 pr_debug("machine__findnew_thread failed\n");
226 return -1;
227 }
228
229 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
230
231 return read_object_code(sample.ip, READLEN, cpumode, thread, machine);
232}
233
234static int process_event(struct machine *machine, struct perf_evlist *evlist,
235 union perf_event *event)
236{
237 if (event->header.type == PERF_RECORD_SAMPLE)
238 return process_sample_event(machine, evlist, event);
239
240 if (event->header.type < PERF_RECORD_MAX)
241 return machine__process_event(machine, event);
242
243 return 0;
244}
245
246static int process_events(struct machine *machine, struct perf_evlist *evlist)
247{
248 union perf_event *event;
249 int i, ret;
250
251 for (i = 0; i < evlist->nr_mmaps; i++) {
252 while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
253 ret = process_event(machine, evlist, event);
254 if (ret < 0)
255 return ret;
256 }
257 }
258 return 0;
259}
260
261static int comp(const void *a, const void *b)
262{
263 return *(int *)a - *(int *)b;
264}
265
266static void do_sort_something(void)
267{
268 size_t sz = 40960;
269 int buf[sz], i;
270
271 for (i = 0; i < (int)sz; i++)
272 buf[i] = sz - i - 1;
273
274 qsort(buf, sz, sizeof(int), comp);
275
276 for (i = 0; i < (int)sz; i++) {
277 if (buf[i] != i) {
278 pr_debug("qsort failed\n");
279 break;
280 }
281 }
282}
283
284static void sort_something(void)
285{
286 int i;
287
288 for (i = 0; i < 10; i++)
289 do_sort_something();
290}
291
292static void syscall_something(void)
293{
294 int pipefd[2];
295 int i;
296
297 for (i = 0; i < 1000; i++) {
298 if (pipe(pipefd) < 0) {
299 pr_debug("pipe failed\n");
300 break;
301 }
302 close(pipefd[1]);
303 close(pipefd[0]);
304 }
305}
306
307static void fs_something(void)
308{
309 const char *test_file_name = "temp-perf-code-reading-test-file--";
310 FILE *f;
311 int i;
312
313 for (i = 0; i < 1000; i++) {
314 f = fopen(test_file_name, "w+");
315 if (f) {
316 fclose(f);
317 unlink(test_file_name);
318 }
319 }
320}
321
322static void do_something(void)
323{
324 fs_something();
325
326 sort_something();
327
328 syscall_something();
329}
330
331enum {
332 TEST_CODE_READING_OK,
333 TEST_CODE_READING_NO_VMLINUX,
334 TEST_CODE_READING_NO_ACCESS,
335};
336
337static int do_test_code_reading(void)
338{
339 struct machines machines;
340 struct machine *machine;
341 struct thread *thread;
342 struct perf_record_opts opts = {
343 .mmap_pages = UINT_MAX,
344 .user_freq = UINT_MAX,
345 .user_interval = ULLONG_MAX,
346 .freq = 4000,
347 .target = {
348 .uses_mmap = true,
349 },
350 };
351 struct thread_map *threads = NULL;
352 struct cpu_map *cpus = NULL;
353 struct perf_evlist *evlist = NULL;
354 struct perf_evsel *evsel = NULL;
355 int err = -1, ret;
356 pid_t pid;
357 struct map *map;
358 bool have_vmlinux, excl_kernel = false;
359
360 pid = getpid();
361
362 machines__init(&machines);
363 machine = &machines.host;
364
365 ret = machine__create_kernel_maps(machine);
366 if (ret < 0) {
367 pr_debug("machine__create_kernel_maps failed\n");
368 goto out_err;
369 }
370
371 /* Load kernel map */
372 map = machine->vmlinux_maps[MAP__FUNCTION];
373 ret = map__load(map, NULL);
374 if (ret < 0) {
375 pr_debug("map__load failed\n");
376 goto out_err;
377 }
378 have_vmlinux = map->dso->symtab_type == DSO_BINARY_TYPE__VMLINUX;
379 /* No point getting kernel events if there is no vmlinux */
380 if (!have_vmlinux)
381 excl_kernel = true;
382
383 threads = thread_map__new_by_tid(pid);
384 if (!threads) {
385 pr_debug("thread_map__new_by_tid failed\n");
386 goto out_err;
387 }
388
389 ret = perf_event__synthesize_thread_map(NULL, threads,
390 perf_event__process, machine);
391 if (ret < 0) {
392 pr_debug("perf_event__synthesize_thread_map failed\n");
393 goto out_err;
394 }
395
396 thread = machine__findnew_thread(machine, pid);
397 if (!thread) {
398 pr_debug("machine__findnew_thread failed\n");
399 goto out_err;
400 }
401
402 cpus = cpu_map__new(NULL);
403 if (!cpus) {
404 pr_debug("cpu_map__new failed\n");
405 goto out_err;
406 }
407
408 while (1) {
409 const char *str;
410
411 evlist = perf_evlist__new();
412 if (!evlist) {
413 pr_debug("perf_evlist__new failed\n");
414 goto out_err;
415 }
416
417 perf_evlist__set_maps(evlist, cpus, threads);
418
419 if (excl_kernel)
420 str = "cycles:u";
421 else
422 str = "cycles";
423 pr_debug("Parsing event '%s'\n", str);
424 ret = parse_events(evlist, str);
425 if (ret < 0) {
426 pr_debug("parse_events failed\n");
427 goto out_err;
428 }
429
430 perf_evlist__config(evlist, &opts);
431
432 evsel = perf_evlist__first(evlist);
433
434 evsel->attr.comm = 1;
435 evsel->attr.disabled = 1;
436 evsel->attr.enable_on_exec = 0;
437
438 ret = perf_evlist__open(evlist);
439 if (ret < 0) {
440 if (!excl_kernel) {
441 excl_kernel = true;
442 perf_evlist__delete(evlist);
443 evlist = NULL;
444 continue;
445 }
446 pr_debug("perf_evlist__open failed\n");
447 goto out_err;
448 }
449 break;
450 }
451
452 ret = perf_evlist__mmap(evlist, UINT_MAX, false);
453 if (ret < 0) {
454 pr_debug("perf_evlist__mmap failed\n");
455 goto out_err;
456 }
457
458 perf_evlist__enable(evlist);
459
460 do_something();
461
462 perf_evlist__disable(evlist);
463
464 ret = process_events(machine, evlist);
465 if (ret < 0)
466 goto out_err;
467
468 if (!have_vmlinux)
469 err = TEST_CODE_READING_NO_VMLINUX;
470 else if (excl_kernel)
471 err = TEST_CODE_READING_NO_ACCESS;
472 else
473 err = TEST_CODE_READING_OK;
474out_err:
475 if (evlist) {
476 perf_evlist__munmap(evlist);
477 perf_evlist__close(evlist);
478 perf_evlist__delete(evlist);
479 }
480 if (cpus)
481 cpu_map__delete(cpus);
482 if (threads)
483 thread_map__delete(threads);
484 machines__destroy_kernel_maps(&machines);
485 machine__delete_threads(machine);
486 machines__exit(&machines);
487
488 return err;
489}
490
491int test__code_reading(void)
492{
493 int ret;
494
495 ret = do_test_code_reading();
496
497 switch (ret) {
498 case TEST_CODE_READING_OK:
499 return 0;
500 case TEST_CODE_READING_NO_VMLINUX:
501 fprintf(stderr, " (no vmlinux)");
502 return 0;
503 case TEST_CODE_READING_NO_ACCESS:
504 fprintf(stderr, " (no access)");
505 return 0;
506 default:
507 return -1;
508 };
509}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index d22202aa16e9..c748f532b20f 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -36,5 +36,6 @@ int test__bp_signal_overflow(void);
36int test__task_exit(void); 36int test__task_exit(void);
37int test__sw_clock_freq(void); 37int test__sw_clock_freq(void);
38int test__perf_time_to_tsc(void); 38int test__perf_time_to_tsc(void);
39int test__code_reading(void);
39 40
40#endif /* TESTS_H */ 41#endif /* TESTS_H */