]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
a43783ae | 2 | #include <errno.h> |
877a7a11 | 3 | #include <linux/kernel.h> |
d944c4ee | 4 | #include <linux/types.h> |
fd20e811 | 5 | #include <inttypes.h> |
b55ae0a9 AH |
6 | #include <stdlib.h> |
7 | #include <unistd.h> | |
8 | #include <stdio.h> | |
b55ae0a9 | 9 | #include <string.h> |
391e4206 | 10 | #include <sys/param.h> |
b55ae0a9 AH |
11 | |
12 | #include "parse-events.h" | |
13 | #include "evlist.h" | |
14 | #include "evsel.h" | |
15 | #include "thread_map.h" | |
16 | #include "cpumap.h" | |
17 | #include "machine.h" | |
18 | #include "event.h" | |
19 | #include "thread.h" | |
20 | ||
21 | #include "tests.h" | |
22 | ||
3d689ed6 ACM |
23 | #include "sane_ctype.h" |
24 | ||
b55ae0a9 AH |
25 | #define BUFSZ 1024 |
26 | #define READLEN 128 | |
27 | ||
7a77bc2c AH |
28 | struct state { |
29 | u64 done[1024]; | |
30 | size_t done_cnt; | |
31 | }; | |
32 | ||
b55ae0a9 AH |
33 | static unsigned int hex(char c) |
34 | { | |
35 | if (c >= '0' && c <= '9') | |
36 | return c - '0'; | |
37 | if (c >= 'a' && c <= 'f') | |
38 | return c - 'a' + 10; | |
39 | return c - 'A' + 10; | |
40 | } | |
41 | ||
b2d0dbf0 JS |
42 | static size_t read_objdump_chunk(const char **line, unsigned char **buf, |
43 | size_t *buf_len) | |
b55ae0a9 | 44 | { |
b2d0dbf0 JS |
45 | size_t bytes_read = 0; |
46 | unsigned char *chunk_start = *buf; | |
b55ae0a9 AH |
47 | |
48 | /* Read bytes */ | |
b2d0dbf0 | 49 | while (*buf_len > 0) { |
b55ae0a9 AH |
50 | char c1, c2; |
51 | ||
b55ae0a9 | 52 | /* Get 2 hex digits */ |
b2d0dbf0 JS |
53 | c1 = *(*line)++; |
54 | if (!isxdigit(c1)) | |
b55ae0a9 | 55 | break; |
b2d0dbf0 JS |
56 | c2 = *(*line)++; |
57 | if (!isxdigit(c2)) | |
b55ae0a9 | 58 | break; |
b2d0dbf0 JS |
59 | |
60 | /* Store byte and advance buf */ | |
61 | **buf = (hex(c1) << 4) | hex(c2); | |
62 | (*buf)++; | |
63 | (*buf_len)--; | |
64 | bytes_read++; | |
65 | ||
66 | /* End of chunk? */ | |
67 | if (isspace(**line)) | |
b55ae0a9 | 68 | break; |
b55ae0a9 | 69 | } |
b2d0dbf0 JS |
70 | |
71 | /* | |
72 | * objdump will display raw insn as LE if code endian | |
73 | * is LE and bytes_per_chunk > 1. In that case reverse | |
74 | * the chunk we just read. | |
75 | * | |
76 | * see disassemble_bytes() at binutils/objdump.c for details | |
77 | * how objdump chooses display endian) | |
78 | */ | |
79 | if (bytes_read > 1 && !bigendian()) { | |
80 | unsigned char *chunk_end = chunk_start + bytes_read - 1; | |
81 | unsigned char tmp; | |
82 | ||
83 | while (chunk_start < chunk_end) { | |
84 | tmp = *chunk_start; | |
85 | *chunk_start = *chunk_end; | |
86 | *chunk_end = tmp; | |
87 | chunk_start++; | |
88 | chunk_end--; | |
89 | } | |
90 | } | |
91 | ||
92 | return bytes_read; | |
93 | } | |
94 | ||
95 | static size_t read_objdump_line(const char *line, unsigned char *buf, | |
96 | size_t buf_len) | |
97 | { | |
98 | const char *p; | |
99 | size_t ret, bytes_read = 0; | |
100 | ||
101 | /* Skip to a colon */ | |
102 | p = strchr(line, ':'); | |
103 | if (!p) | |
104 | return 0; | |
105 | p++; | |
106 | ||
107 | /* Skip initial spaces */ | |
108 | while (*p) { | |
109 | if (!isspace(*p)) | |
110 | break; | |
111 | p++; | |
112 | } | |
113 | ||
114 | do { | |
115 | ret = read_objdump_chunk(&p, &buf, &buf_len); | |
116 | bytes_read += ret; | |
117 | p++; | |
118 | } while (ret > 0); | |
119 | ||
729a7ed1 | 120 | /* return number of successfully read bytes */ |
b2d0dbf0 | 121 | return bytes_read; |
b55ae0a9 AH |
122 | } |
123 | ||
729a7ed1 | 124 | static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) |
b55ae0a9 AH |
125 | { |
126 | char *line = NULL; | |
729a7ed1 | 127 | size_t line_len, off_last = 0; |
b55ae0a9 AH |
128 | ssize_t ret; |
129 | int err = 0; | |
edfdb7ea | 130 | u64 addr, last_addr = start_addr; |
729a7ed1 JS |
131 | |
132 | while (off_last < *len) { | |
133 | size_t off, read_bytes, written_bytes; | |
134 | unsigned char tmp[BUFSZ]; | |
b55ae0a9 | 135 | |
b55ae0a9 AH |
136 | ret = getline(&line, &line_len, f); |
137 | if (feof(f)) | |
138 | break; | |
139 | if (ret < 0) { | |
140 | pr_debug("getline failed\n"); | |
141 | err = -1; | |
142 | break; | |
143 | } | |
729a7ed1 JS |
144 | |
145 | /* read objdump data into temporary buffer */ | |
b2d0dbf0 | 146 | read_bytes = read_objdump_line(line, tmp, sizeof(tmp)); |
729a7ed1 JS |
147 | if (!read_bytes) |
148 | continue; | |
149 | ||
150 | if (sscanf(line, "%"PRIx64, &addr) != 1) | |
151 | continue; | |
edfdb7ea JS |
152 | if (addr < last_addr) { |
153 | pr_debug("addr going backwards, read beyond section?\n"); | |
154 | break; | |
155 | } | |
156 | last_addr = addr; | |
729a7ed1 JS |
157 | |
158 | /* copy it from temporary buffer to 'buf' according | |
159 | * to address on current objdump line */ | |
160 | off = addr - start_addr; | |
161 | if (off >= *len) | |
162 | break; | |
163 | written_bytes = MIN(read_bytes, *len - off); | |
164 | memcpy(buf + off, tmp, written_bytes); | |
165 | off_last = off + written_bytes; | |
b55ae0a9 AH |
166 | } |
167 | ||
729a7ed1 JS |
168 | /* len returns number of bytes that could not be read */ |
169 | *len -= off_last; | |
170 | ||
b55ae0a9 AH |
171 | free(line); |
172 | ||
173 | return err; | |
174 | } | |
175 | ||
176 | static int read_via_objdump(const char *filename, u64 addr, void *buf, | |
177 | size_t len) | |
178 | { | |
179 | char cmd[PATH_MAX * 2]; | |
180 | const char *fmt; | |
181 | FILE *f; | |
182 | int ret; | |
183 | ||
06f679c1 | 184 | fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s"; |
b55ae0a9 AH |
185 | ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len, |
186 | filename); | |
187 | if (ret <= 0 || (size_t)ret >= sizeof(cmd)) | |
188 | return -1; | |
189 | ||
190 | pr_debug("Objdump command is: %s\n", cmd); | |
191 | ||
7a77bc2c AH |
192 | /* Ignore objdump errors */ |
193 | strcat(cmd, " 2>/dev/null"); | |
194 | ||
b55ae0a9 AH |
195 | f = popen(cmd, "r"); |
196 | if (!f) { | |
197 | pr_debug("popen failed\n"); | |
198 | return -1; | |
199 | } | |
200 | ||
729a7ed1 | 201 | ret = read_objdump_output(f, buf, &len, addr); |
b55ae0a9 | 202 | if (len) { |
b2d0dbf0 | 203 | pr_debug("objdump read too few bytes: %zd\n", len); |
b55ae0a9 AH |
204 | if (!ret) |
205 | ret = len; | |
206 | } | |
207 | ||
208 | pclose(f); | |
209 | ||
210 | return ret; | |
211 | } | |
212 | ||
fd405cf6 JS |
213 | static void dump_buf(unsigned char *buf, size_t len) |
214 | { | |
215 | size_t i; | |
216 | ||
217 | for (i = 0; i < len; i++) { | |
218 | pr_debug("0x%02x ", buf[i]); | |
219 | if (i % 16 == 15) | |
220 | pr_debug("\n"); | |
221 | } | |
222 | pr_debug("\n"); | |
223 | } | |
224 | ||
b55ae0a9 | 225 | static int read_object_code(u64 addr, size_t len, u8 cpumode, |
29f9e521 | 226 | struct thread *thread, struct state *state) |
b55ae0a9 AH |
227 | { |
228 | struct addr_location al; | |
229 | unsigned char buf1[BUFSZ]; | |
230 | unsigned char buf2[BUFSZ]; | |
231 | size_t ret_len; | |
232 | u64 objdump_addr; | |
94df1040 NK |
233 | const char *objdump_name; |
234 | char decomp_name[KMOD_DECOMP_LEN]; | |
b55ae0a9 AH |
235 | int ret; |
236 | ||
237 | pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); | |
238 | ||
bb871a9c | 239 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, addr, &al); |
b55ae0a9 | 240 | if (!al.map || !al.map->dso) { |
9a805d86 RB |
241 | if (cpumode == PERF_RECORD_MISC_HYPERVISOR) { |
242 | pr_debug("Hypervisor address can not be resolved - skipping\n"); | |
243 | return 0; | |
244 | } | |
245 | ||
b55ae0a9 AH |
246 | pr_debug("thread__find_addr_map failed\n"); |
247 | return -1; | |
248 | } | |
249 | ||
250 | pr_debug("File is: %s\n", al.map->dso->long_name); | |
251 | ||
7a77bc2c AH |
252 | if (al.map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
253 | !dso__is_kcore(al.map->dso)) { | |
b55ae0a9 AH |
254 | pr_debug("Unexpected kernel address - skipping\n"); |
255 | return 0; | |
256 | } | |
257 | ||
258 | pr_debug("On file address is: %#"PRIx64"\n", al.addr); | |
259 | ||
260 | if (len > BUFSZ) | |
261 | len = BUFSZ; | |
262 | ||
263 | /* Do not go off the map */ | |
264 | if (addr + len > al.map->end) | |
265 | len = al.map->end - addr; | |
266 | ||
267 | /* Read the object code using perf */ | |
29f9e521 ACM |
268 | ret_len = dso__data_read_offset(al.map->dso, thread->mg->machine, |
269 | al.addr, buf1, len); | |
b55ae0a9 AH |
270 | if (ret_len != len) { |
271 | pr_debug("dso__data_read_offset failed\n"); | |
272 | return -1; | |
273 | } | |
274 | ||
275 | /* | |
276 | * Converting addresses for use by objdump requires more information. | |
277 | * map__load() does that. See map__rip_2objdump() for details. | |
278 | */ | |
be39db9f | 279 | if (map__load(al.map)) |
b55ae0a9 AH |
280 | return -1; |
281 | ||
7a77bc2c AH |
282 | /* objdump struggles with kcore - try each map only once */ |
283 | if (dso__is_kcore(al.map->dso)) { | |
284 | size_t d; | |
285 | ||
286 | for (d = 0; d < state->done_cnt; d++) { | |
287 | if (state->done[d] == al.map->start) { | |
288 | pr_debug("kcore map tested already"); | |
289 | pr_debug(" - skipping\n"); | |
290 | return 0; | |
291 | } | |
292 | } | |
293 | if (state->done_cnt >= ARRAY_SIZE(state->done)) { | |
294 | pr_debug("Too many kcore maps - skipping\n"); | |
295 | return 0; | |
296 | } | |
297 | state->done[state->done_cnt++] = al.map->start; | |
298 | } | |
299 | ||
94df1040 NK |
300 | objdump_name = al.map->dso->long_name; |
301 | if (dso__needs_decompress(al.map->dso)) { | |
302 | if (dso__decompress_kmodule_path(al.map->dso, objdump_name, | |
303 | decomp_name, | |
304 | sizeof(decomp_name)) < 0) { | |
305 | pr_debug("decompression failed\n"); | |
306 | return -1; | |
307 | } | |
308 | ||
309 | objdump_name = decomp_name; | |
310 | } | |
311 | ||
b55ae0a9 AH |
312 | /* Read the object code using objdump */ |
313 | objdump_addr = map__rip_2objdump(al.map, al.addr); | |
94df1040 NK |
314 | ret = read_via_objdump(objdump_name, objdump_addr, buf2, len); |
315 | ||
316 | if (dso__needs_decompress(al.map->dso)) | |
317 | unlink(objdump_name); | |
318 | ||
b55ae0a9 AH |
319 | if (ret > 0) { |
320 | /* | |
321 | * The kernel maps are inaccurate - assume objdump is right in | |
322 | * that case. | |
323 | */ | |
324 | if (cpumode == PERF_RECORD_MISC_KERNEL || | |
325 | cpumode == PERF_RECORD_MISC_GUEST_KERNEL) { | |
326 | len -= ret; | |
7a77bc2c | 327 | if (len) { |
b55ae0a9 | 328 | pr_debug("Reducing len to %zu\n", len); |
7a77bc2c AH |
329 | } else if (dso__is_kcore(al.map->dso)) { |
330 | /* | |
331 | * objdump cannot handle very large segments | |
332 | * that may be found in kcore. | |
333 | */ | |
334 | pr_debug("objdump failed for kcore"); | |
335 | pr_debug(" - skipping\n"); | |
336 | return 0; | |
337 | } else { | |
b55ae0a9 | 338 | return -1; |
7a77bc2c | 339 | } |
b55ae0a9 AH |
340 | } |
341 | } | |
342 | if (ret < 0) { | |
343 | pr_debug("read_via_objdump failed\n"); | |
344 | return -1; | |
345 | } | |
346 | ||
347 | /* The results should be identical */ | |
348 | if (memcmp(buf1, buf2, len)) { | |
349 | pr_debug("Bytes read differ from those read by objdump\n"); | |
fd405cf6 JS |
350 | pr_debug("buf1 (dso):\n"); |
351 | dump_buf(buf1, len); | |
352 | pr_debug("buf2 (objdump):\n"); | |
353 | dump_buf(buf2, len); | |
b55ae0a9 AH |
354 | return -1; |
355 | } | |
356 | pr_debug("Bytes read match those read by objdump\n"); | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | static int process_sample_event(struct machine *machine, | |
362 | struct perf_evlist *evlist, | |
7a77bc2c | 363 | union perf_event *event, struct state *state) |
b55ae0a9 AH |
364 | { |
365 | struct perf_sample sample; | |
366 | struct thread *thread; | |
b91fc39f | 367 | int ret; |
b55ae0a9 AH |
368 | |
369 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | |
370 | pr_debug("perf_evlist__parse_sample failed\n"); | |
371 | return -1; | |
372 | } | |
373 | ||
13ce34df | 374 | thread = machine__findnew_thread(machine, sample.pid, sample.tid); |
b55ae0a9 AH |
375 | if (!thread) { |
376 | pr_debug("machine__findnew_thread failed\n"); | |
377 | return -1; | |
378 | } | |
379 | ||
473398a2 | 380 | ret = read_object_code(sample.ip, READLEN, sample.cpumode, thread, state); |
b91fc39f ACM |
381 | thread__put(thread); |
382 | return ret; | |
b55ae0a9 AH |
383 | } |
384 | ||
385 | static int process_event(struct machine *machine, struct perf_evlist *evlist, | |
7a77bc2c | 386 | union perf_event *event, struct state *state) |
b55ae0a9 AH |
387 | { |
388 | if (event->header.type == PERF_RECORD_SAMPLE) | |
7a77bc2c | 389 | return process_sample_event(machine, evlist, event, state); |
b55ae0a9 | 390 | |
48095b72 AH |
391 | if (event->header.type == PERF_RECORD_THROTTLE || |
392 | event->header.type == PERF_RECORD_UNTHROTTLE) | |
393 | return 0; | |
394 | ||
395 | if (event->header.type < PERF_RECORD_MAX) { | |
396 | int ret; | |
397 | ||
398 | ret = machine__process_event(machine, event, NULL); | |
399 | if (ret < 0) | |
400 | pr_debug("machine__process_event failed, event type %u\n", | |
401 | event->header.type); | |
402 | return ret; | |
403 | } | |
b55ae0a9 AH |
404 | |
405 | return 0; | |
406 | } | |
407 | ||
7a77bc2c AH |
408 | static int process_events(struct machine *machine, struct perf_evlist *evlist, |
409 | struct state *state) | |
b55ae0a9 AH |
410 | { |
411 | union perf_event *event; | |
412 | int i, ret; | |
413 | ||
414 | for (i = 0; i < evlist->nr_mmaps; i++) { | |
415 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | |
7a77bc2c | 416 | ret = process_event(machine, evlist, event, state); |
8e50d384 | 417 | perf_evlist__mmap_consume(evlist, i); |
b55ae0a9 AH |
418 | if (ret < 0) |
419 | return ret; | |
420 | } | |
421 | } | |
422 | return 0; | |
423 | } | |
424 | ||
425 | static int comp(const void *a, const void *b) | |
426 | { | |
427 | return *(int *)a - *(int *)b; | |
428 | } | |
429 | ||
430 | static void do_sort_something(void) | |
431 | { | |
309b5185 | 432 | int buf[40960], i; |
b55ae0a9 | 433 | |
309b5185 DA |
434 | for (i = 0; i < (int)ARRAY_SIZE(buf); i++) |
435 | buf[i] = ARRAY_SIZE(buf) - i - 1; | |
b55ae0a9 | 436 | |
309b5185 | 437 | qsort(buf, ARRAY_SIZE(buf), sizeof(int), comp); |
b55ae0a9 | 438 | |
309b5185 | 439 | for (i = 0; i < (int)ARRAY_SIZE(buf); i++) { |
b55ae0a9 AH |
440 | if (buf[i] != i) { |
441 | pr_debug("qsort failed\n"); | |
442 | break; | |
443 | } | |
444 | } | |
445 | } | |
446 | ||
447 | static void sort_something(void) | |
448 | { | |
449 | int i; | |
450 | ||
451 | for (i = 0; i < 10; i++) | |
452 | do_sort_something(); | |
453 | } | |
454 | ||
455 | static void syscall_something(void) | |
456 | { | |
457 | int pipefd[2]; | |
458 | int i; | |
459 | ||
460 | for (i = 0; i < 1000; i++) { | |
461 | if (pipe(pipefd) < 0) { | |
462 | pr_debug("pipe failed\n"); | |
463 | break; | |
464 | } | |
465 | close(pipefd[1]); | |
466 | close(pipefd[0]); | |
467 | } | |
468 | } | |
469 | ||
470 | static void fs_something(void) | |
471 | { | |
472 | const char *test_file_name = "temp-perf-code-reading-test-file--"; | |
473 | FILE *f; | |
474 | int i; | |
475 | ||
476 | for (i = 0; i < 1000; i++) { | |
477 | f = fopen(test_file_name, "w+"); | |
478 | if (f) { | |
479 | fclose(f); | |
480 | unlink(test_file_name); | |
481 | } | |
482 | } | |
483 | } | |
484 | ||
485 | static void do_something(void) | |
486 | { | |
487 | fs_something(); | |
488 | ||
489 | sort_something(); | |
490 | ||
491 | syscall_something(); | |
492 | } | |
493 | ||
494 | enum { | |
495 | TEST_CODE_READING_OK, | |
496 | TEST_CODE_READING_NO_VMLINUX, | |
7a77bc2c | 497 | TEST_CODE_READING_NO_KCORE, |
b55ae0a9 | 498 | TEST_CODE_READING_NO_ACCESS, |
7a77bc2c | 499 | TEST_CODE_READING_NO_KERNEL_OBJ, |
b55ae0a9 AH |
500 | }; |
501 | ||
7a77bc2c | 502 | static int do_test_code_reading(bool try_kcore) |
b55ae0a9 | 503 | { |
b55ae0a9 AH |
504 | struct machine *machine; |
505 | struct thread *thread; | |
b4006796 | 506 | struct record_opts opts = { |
b55ae0a9 AH |
507 | .mmap_pages = UINT_MAX, |
508 | .user_freq = UINT_MAX, | |
509 | .user_interval = ULLONG_MAX, | |
5243ba76 | 510 | .freq = 500, |
b55ae0a9 AH |
511 | .target = { |
512 | .uses_mmap = true, | |
513 | }, | |
514 | }; | |
7a77bc2c AH |
515 | struct state state = { |
516 | .done_cnt = 0, | |
517 | }; | |
b55ae0a9 AH |
518 | struct thread_map *threads = NULL; |
519 | struct cpu_map *cpus = NULL; | |
520 | struct perf_evlist *evlist = NULL; | |
521 | struct perf_evsel *evsel = NULL; | |
522 | int err = -1, ret; | |
523 | pid_t pid; | |
524 | struct map *map; | |
7a77bc2c | 525 | bool have_vmlinux, have_kcore, excl_kernel = false; |
b55ae0a9 AH |
526 | |
527 | pid = getpid(); | |
528 | ||
0fd4008e | 529 | machine = machine__new_host(); |
b55ae0a9 AH |
530 | |
531 | ret = machine__create_kernel_maps(machine); | |
532 | if (ret < 0) { | |
533 | pr_debug("machine__create_kernel_maps failed\n"); | |
534 | goto out_err; | |
535 | } | |
536 | ||
7a77bc2c AH |
537 | /* Force the use of kallsyms instead of vmlinux to try kcore */ |
538 | if (try_kcore) | |
539 | symbol_conf.kallsyms_name = "/proc/kallsyms"; | |
540 | ||
b55ae0a9 | 541 | /* Load kernel map */ |
a5e813c6 | 542 | map = machine__kernel_map(machine); |
be39db9f | 543 | ret = map__load(map); |
b55ae0a9 AH |
544 | if (ret < 0) { |
545 | pr_debug("map__load failed\n"); | |
546 | goto out_err; | |
547 | } | |
7a77bc2c AH |
548 | have_vmlinux = dso__is_vmlinux(map->dso); |
549 | have_kcore = dso__is_kcore(map->dso); | |
550 | ||
551 | /* 2nd time through we just try kcore */ | |
552 | if (try_kcore && !have_kcore) | |
553 | return TEST_CODE_READING_NO_KCORE; | |
554 | ||
555 | /* No point getting kernel events if there is no kernel object */ | |
556 | if (!have_vmlinux && !have_kcore) | |
b55ae0a9 AH |
557 | excl_kernel = true; |
558 | ||
559 | threads = thread_map__new_by_tid(pid); | |
560 | if (!threads) { | |
561 | pr_debug("thread_map__new_by_tid failed\n"); | |
562 | goto out_err; | |
563 | } | |
564 | ||
565 | ret = perf_event__synthesize_thread_map(NULL, threads, | |
9d9cad76 | 566 | perf_event__process, machine, false, 500); |
b55ae0a9 AH |
567 | if (ret < 0) { |
568 | pr_debug("perf_event__synthesize_thread_map failed\n"); | |
569 | goto out_err; | |
570 | } | |
571 | ||
314add6b | 572 | thread = machine__findnew_thread(machine, pid, pid); |
b55ae0a9 AH |
573 | if (!thread) { |
574 | pr_debug("machine__findnew_thread failed\n"); | |
b91fc39f | 575 | goto out_put; |
b55ae0a9 AH |
576 | } |
577 | ||
578 | cpus = cpu_map__new(NULL); | |
579 | if (!cpus) { | |
580 | pr_debug("cpu_map__new failed\n"); | |
b91fc39f | 581 | goto out_put; |
b55ae0a9 AH |
582 | } |
583 | ||
584 | while (1) { | |
585 | const char *str; | |
586 | ||
587 | evlist = perf_evlist__new(); | |
588 | if (!evlist) { | |
589 | pr_debug("perf_evlist__new failed\n"); | |
b91fc39f | 590 | goto out_put; |
b55ae0a9 AH |
591 | } |
592 | ||
593 | perf_evlist__set_maps(evlist, cpus, threads); | |
594 | ||
595 | if (excl_kernel) | |
596 | str = "cycles:u"; | |
597 | else | |
598 | str = "cycles"; | |
599 | pr_debug("Parsing event '%s'\n", str); | |
b39b8393 | 600 | ret = parse_events(evlist, str, NULL); |
b55ae0a9 AH |
601 | if (ret < 0) { |
602 | pr_debug("parse_events failed\n"); | |
b91fc39f | 603 | goto out_put; |
b55ae0a9 AH |
604 | } |
605 | ||
e68ae9cf | 606 | perf_evlist__config(evlist, &opts, NULL); |
b55ae0a9 AH |
607 | |
608 | evsel = perf_evlist__first(evlist); | |
609 | ||
610 | evsel->attr.comm = 1; | |
611 | evsel->attr.disabled = 1; | |
612 | evsel->attr.enable_on_exec = 0; | |
613 | ||
614 | ret = perf_evlist__open(evlist); | |
615 | if (ret < 0) { | |
616 | if (!excl_kernel) { | |
617 | excl_kernel = true; | |
7320b1b3 JO |
618 | /* |
619 | * Both cpus and threads are now owned by evlist | |
620 | * and will be freed by following perf_evlist__set_maps | |
621 | * call. Getting refference to keep them alive. | |
622 | */ | |
623 | cpu_map__get(cpus); | |
624 | thread_map__get(threads); | |
ae450a7d | 625 | perf_evlist__set_maps(evlist, NULL, NULL); |
b55ae0a9 AH |
626 | perf_evlist__delete(evlist); |
627 | evlist = NULL; | |
628 | continue; | |
629 | } | |
6880bbf9 | 630 | |
bb963e16 | 631 | if (verbose > 0) { |
6880bbf9 ACM |
632 | char errbuf[512]; |
633 | perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); | |
634 | pr_debug("perf_evlist__open() failed!\n%s\n", errbuf); | |
635 | } | |
636 | ||
b91fc39f | 637 | goto out_put; |
b55ae0a9 AH |
638 | } |
639 | break; | |
640 | } | |
641 | ||
642 | ret = perf_evlist__mmap(evlist, UINT_MAX, false); | |
643 | if (ret < 0) { | |
644 | pr_debug("perf_evlist__mmap failed\n"); | |
b91fc39f | 645 | goto out_put; |
b55ae0a9 AH |
646 | } |
647 | ||
648 | perf_evlist__enable(evlist); | |
649 | ||
650 | do_something(); | |
651 | ||
652 | perf_evlist__disable(evlist); | |
653 | ||
7a77bc2c | 654 | ret = process_events(machine, evlist, &state); |
b55ae0a9 | 655 | if (ret < 0) |
b91fc39f | 656 | goto out_put; |
b55ae0a9 | 657 | |
7a77bc2c AH |
658 | if (!have_vmlinux && !have_kcore && !try_kcore) |
659 | err = TEST_CODE_READING_NO_KERNEL_OBJ; | |
660 | else if (!have_vmlinux && !try_kcore) | |
b55ae0a9 AH |
661 | err = TEST_CODE_READING_NO_VMLINUX; |
662 | else if (excl_kernel) | |
663 | err = TEST_CODE_READING_NO_ACCESS; | |
664 | else | |
665 | err = TEST_CODE_READING_OK; | |
b91fc39f ACM |
666 | out_put: |
667 | thread__put(thread); | |
b55ae0a9 | 668 | out_err: |
b91fc39f | 669 | |
b55ae0a9 | 670 | if (evlist) { |
b55ae0a9 | 671 | perf_evlist__delete(evlist); |
03ad9747 | 672 | } else { |
f30a79b0 | 673 | cpu_map__put(cpus); |
186fbb74 | 674 | thread_map__put(threads); |
03ad9747 | 675 | } |
b55ae0a9 | 676 | machine__delete_threads(machine); |
0fd4008e | 677 | machine__delete(machine); |
b55ae0a9 AH |
678 | |
679 | return err; | |
680 | } | |
681 | ||
81f17c90 | 682 | int test__code_reading(struct test *test __maybe_unused, int subtest __maybe_unused) |
b55ae0a9 AH |
683 | { |
684 | int ret; | |
685 | ||
7a77bc2c AH |
686 | ret = do_test_code_reading(false); |
687 | if (!ret) | |
688 | ret = do_test_code_reading(true); | |
b55ae0a9 AH |
689 | |
690 | switch (ret) { | |
691 | case TEST_CODE_READING_OK: | |
692 | return 0; | |
693 | case TEST_CODE_READING_NO_VMLINUX: | |
597bdeb4 | 694 | pr_debug("no vmlinux\n"); |
b55ae0a9 | 695 | return 0; |
7a77bc2c | 696 | case TEST_CODE_READING_NO_KCORE: |
597bdeb4 | 697 | pr_debug("no kcore\n"); |
7a77bc2c | 698 | return 0; |
b55ae0a9 | 699 | case TEST_CODE_READING_NO_ACCESS: |
597bdeb4 | 700 | pr_debug("no access\n"); |
b55ae0a9 | 701 | return 0; |
7a77bc2c | 702 | case TEST_CODE_READING_NO_KERNEL_OBJ: |
597bdeb4 | 703 | pr_debug("no kernel obj\n"); |
7a77bc2c | 704 | return 0; |
b55ae0a9 AH |
705 | default: |
706 | return -1; | |
707 | }; | |
708 | } |