1 | #include <errno.h> |
2 | #include <fcntl.h> |
3 | #include <inttypes.h> |
4 | #include <linux/kernel.h> |
5 | #include <linux/types.h> |
6 | #include <perf/cpumap.h> |
7 | #include <sys/types.h> |
8 | #include <sys/stat.h> |
9 | #include <unistd.h> |
10 | #include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */ |
11 | #include <linux/perf_event.h> |
12 | #include <linux/zalloc.h> |
13 | #include "cpumap.h" |
14 | #include "dso.h" |
15 | #include "event.h" |
16 | #include "debug.h" |
17 | #include "hist.h" |
18 | #include "machine.h" |
19 | #include "sort.h" |
20 | #include "string2.h" |
21 | #include "strlist.h" |
22 | #include "thread.h" |
23 | #include "thread_map.h" |
24 | #include "time-utils.h" |
25 | #include <linux/ctype.h> |
26 | #include "map.h" |
27 | #include "util/namespaces.h" |
28 | #include "symbol.h" |
29 | #include "symbol/kallsyms.h" |
30 | #include "asm/bug.h" |
31 | #include "stat.h" |
32 | #include "session.h" |
33 | #include "bpf-event.h" |
34 | #include "print_binary.h" |
35 | #include "tool.h" |
36 | #include "util.h" |
37 | |
38 | static const char *perf_event__names[] = { |
39 | [0] = "TOTAL" , |
40 | [PERF_RECORD_MMAP] = "MMAP" , |
41 | [PERF_RECORD_MMAP2] = "MMAP2" , |
42 | [PERF_RECORD_LOST] = "LOST" , |
43 | [PERF_RECORD_COMM] = "COMM" , |
44 | [PERF_RECORD_EXIT] = "EXIT" , |
45 | [PERF_RECORD_THROTTLE] = "THROTTLE" , |
46 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE" , |
47 | [PERF_RECORD_FORK] = "FORK" , |
48 | [PERF_RECORD_READ] = "READ" , |
49 | [PERF_RECORD_SAMPLE] = "SAMPLE" , |
50 | [PERF_RECORD_AUX] = "AUX" , |
51 | [PERF_RECORD_ITRACE_START] = "ITRACE_START" , |
52 | [PERF_RECORD_LOST_SAMPLES] = "LOST_SAMPLES" , |
53 | [PERF_RECORD_SWITCH] = "SWITCH" , |
54 | [PERF_RECORD_SWITCH_CPU_WIDE] = "SWITCH_CPU_WIDE" , |
55 | [PERF_RECORD_NAMESPACES] = "NAMESPACES" , |
56 | [PERF_RECORD_KSYMBOL] = "KSYMBOL" , |
57 | [PERF_RECORD_BPF_EVENT] = "BPF_EVENT" , |
58 | [PERF_RECORD_CGROUP] = "CGROUP" , |
59 | [PERF_RECORD_TEXT_POKE] = "TEXT_POKE" , |
60 | [PERF_RECORD_AUX_OUTPUT_HW_ID] = "AUX_OUTPUT_HW_ID" , |
61 | [PERF_RECORD_HEADER_ATTR] = "ATTR" , |
62 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE" , |
63 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA" , |
64 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID" , |
65 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND" , |
66 | [PERF_RECORD_ID_INDEX] = "ID_INDEX" , |
67 | [PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO" , |
68 | [PERF_RECORD_AUXTRACE] = "AUXTRACE" , |
69 | [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR" , |
70 | [PERF_RECORD_THREAD_MAP] = "THREAD_MAP" , |
71 | [PERF_RECORD_CPU_MAP] = "CPU_MAP" , |
72 | [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG" , |
73 | [PERF_RECORD_STAT] = "STAT" , |
74 | [PERF_RECORD_STAT_ROUND] = "STAT_ROUND" , |
75 | [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE" , |
76 | [PERF_RECORD_TIME_CONV] = "TIME_CONV" , |
77 | [PERF_RECORD_HEADER_FEATURE] = "FEATURE" , |
78 | [PERF_RECORD_COMPRESSED] = "COMPRESSED" , |
79 | [PERF_RECORD_FINISHED_INIT] = "FINISHED_INIT" , |
80 | }; |
81 | |
82 | const char *perf_event__name(unsigned int id) |
83 | { |
84 | if (id >= ARRAY_SIZE(perf_event__names)) |
85 | return "INVALID" ; |
86 | if (!perf_event__names[id]) |
87 | return "UNKNOWN" ; |
88 | return perf_event__names[id]; |
89 | } |
90 | |
91 | struct process_symbol_args { |
92 | const char *name; |
93 | u64 start; |
94 | }; |
95 | |
96 | static int find_func_symbol_cb(void *arg, const char *name, char type, |
97 | u64 start) |
98 | { |
99 | struct process_symbol_args *args = arg; |
100 | |
101 | /* |
102 | * Must be a function or at least an alias, as in PARISC64, where "_text" is |
103 | * an 'A' to the same address as "_stext". |
104 | */ |
105 | if (!(kallsyms__is_function(type) || |
106 | type == 'A') || strcmp(name, args->name)) |
107 | return 0; |
108 | |
109 | args->start = start; |
110 | return 1; |
111 | } |
112 | |
113 | static int find_any_symbol_cb(void *arg, const char *name, |
114 | char type __maybe_unused, u64 start) |
115 | { |
116 | struct process_symbol_args *args = arg; |
117 | |
118 | if (strcmp(name, args->name)) |
119 | return 0; |
120 | |
121 | args->start = start; |
122 | return 1; |
123 | } |
124 | |
125 | int kallsyms__get_function_start(const char *kallsyms_filename, |
126 | const char *symbol_name, u64 *addr) |
127 | { |
128 | struct process_symbol_args args = { .name = symbol_name, }; |
129 | |
130 | if (kallsyms__parse(kallsyms_filename, &args, find_func_symbol_cb) <= 0) |
131 | return -1; |
132 | |
133 | *addr = args.start; |
134 | return 0; |
135 | } |
136 | |
137 | int kallsyms__get_symbol_start(const char *kallsyms_filename, |
138 | const char *symbol_name, u64 *addr) |
139 | { |
140 | struct process_symbol_args args = { .name = symbol_name, }; |
141 | |
142 | if (kallsyms__parse(kallsyms_filename, &args, find_any_symbol_cb) <= 0) |
143 | return -1; |
144 | |
145 | *addr = args.start; |
146 | return 0; |
147 | } |
148 | |
149 | void perf_event__read_stat_config(struct perf_stat_config *config, |
150 | struct perf_record_stat_config *event) |
151 | { |
152 | unsigned i; |
153 | |
154 | for (i = 0; i < event->nr; i++) { |
155 | |
156 | switch (event->data[i].tag) { |
157 | #define CASE(__term, __val) \ |
158 | case PERF_STAT_CONFIG_TERM__##__term: \ |
159 | config->__val = event->data[i].val; \ |
160 | break; |
161 | |
162 | CASE(AGGR_MODE, aggr_mode) |
163 | CASE(SCALE, scale) |
164 | CASE(INTERVAL, interval) |
165 | CASE(AGGR_LEVEL, aggr_level) |
166 | #undef CASE |
167 | default: |
168 | pr_warning("unknown stat config term %" PRI_lu64 "\n" , |
169 | event->data[i].tag); |
170 | } |
171 | } |
172 | } |
173 | |
174 | size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) |
175 | { |
176 | const char *s; |
177 | |
178 | if (event->header.misc & PERF_RECORD_MISC_COMM_EXEC) |
179 | s = " exec" ; |
180 | else |
181 | s = "" ; |
182 | |
183 | return fprintf(fp, "%s: %s:%d/%d\n" , s, event->comm.comm, event->comm.pid, event->comm.tid); |
184 | } |
185 | |
186 | size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp) |
187 | { |
188 | size_t ret = 0; |
189 | struct perf_ns_link_info *ns_link_info; |
190 | u32 nr_namespaces, idx; |
191 | |
192 | ns_link_info = event->namespaces.link_info; |
193 | nr_namespaces = event->namespaces.nr_namespaces; |
194 | |
195 | ret += fprintf(fp, " %d/%d - nr_namespaces: %u\n\t\t[" , |
196 | event->namespaces.pid, |
197 | event->namespaces.tid, |
198 | nr_namespaces); |
199 | |
200 | for (idx = 0; idx < nr_namespaces; idx++) { |
201 | if (idx && (idx % 4 == 0)) |
202 | ret += fprintf(fp, "\n\t\t " ); |
203 | |
204 | ret += fprintf(fp, "%u/%s: %" PRIu64 "/%#" PRIx64 "%s" , idx, |
205 | perf_ns__name(idx), (u64)ns_link_info[idx].dev, |
206 | (u64)ns_link_info[idx].ino, |
207 | ((idx + 1) != nr_namespaces) ? ", " : "]\n" ); |
208 | } |
209 | |
210 | return ret; |
211 | } |
212 | |
213 | size_t perf_event__fprintf_cgroup(union perf_event *event, FILE *fp) |
214 | { |
215 | return fprintf(fp, " cgroup: %" PRI_lu64 " %s\n" , |
216 | event->cgroup.id, event->cgroup.path); |
217 | } |
218 | |
219 | int perf_event__process_comm(struct perf_tool *tool __maybe_unused, |
220 | union perf_event *event, |
221 | struct perf_sample *sample, |
222 | struct machine *machine) |
223 | { |
224 | return machine__process_comm_event(machine, event, sample); |
225 | } |
226 | |
227 | int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, |
228 | union perf_event *event, |
229 | struct perf_sample *sample, |
230 | struct machine *machine) |
231 | { |
232 | return machine__process_namespaces_event(machine, event, sample); |
233 | } |
234 | |
235 | int perf_event__process_cgroup(struct perf_tool *tool __maybe_unused, |
236 | union perf_event *event, |
237 | struct perf_sample *sample, |
238 | struct machine *machine) |
239 | { |
240 | return machine__process_cgroup_event(machine, event, sample); |
241 | } |
242 | |
243 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
244 | union perf_event *event, |
245 | struct perf_sample *sample, |
246 | struct machine *machine) |
247 | { |
248 | return machine__process_lost_event(machine, event, sample); |
249 | } |
250 | |
251 | int perf_event__process_aux(struct perf_tool *tool __maybe_unused, |
252 | union perf_event *event, |
253 | struct perf_sample *sample __maybe_unused, |
254 | struct machine *machine) |
255 | { |
256 | return machine__process_aux_event(machine, event); |
257 | } |
258 | |
259 | int perf_event__process_itrace_start(struct perf_tool *tool __maybe_unused, |
260 | union perf_event *event, |
261 | struct perf_sample *sample __maybe_unused, |
262 | struct machine *machine) |
263 | { |
264 | return machine__process_itrace_start_event(machine, event); |
265 | } |
266 | |
267 | int perf_event__process_aux_output_hw_id(struct perf_tool *tool __maybe_unused, |
268 | union perf_event *event, |
269 | struct perf_sample *sample __maybe_unused, |
270 | struct machine *machine) |
271 | { |
272 | return machine__process_aux_output_hw_id_event(machine, event); |
273 | } |
274 | |
275 | int perf_event__process_lost_samples(struct perf_tool *tool __maybe_unused, |
276 | union perf_event *event, |
277 | struct perf_sample *sample, |
278 | struct machine *machine) |
279 | { |
280 | return machine__process_lost_samples_event(machine, event, sample); |
281 | } |
282 | |
283 | int perf_event__process_switch(struct perf_tool *tool __maybe_unused, |
284 | union perf_event *event, |
285 | struct perf_sample *sample __maybe_unused, |
286 | struct machine *machine) |
287 | { |
288 | return machine__process_switch_event(machine, event); |
289 | } |
290 | |
291 | int perf_event__process_ksymbol(struct perf_tool *tool __maybe_unused, |
292 | union perf_event *event, |
293 | struct perf_sample *sample __maybe_unused, |
294 | struct machine *machine) |
295 | { |
296 | return machine__process_ksymbol(machine, event, sample); |
297 | } |
298 | |
299 | int perf_event__process_bpf(struct perf_tool *tool __maybe_unused, |
300 | union perf_event *event, |
301 | struct perf_sample *sample, |
302 | struct machine *machine) |
303 | { |
304 | return machine__process_bpf(machine, event, sample); |
305 | } |
306 | |
307 | int perf_event__process_text_poke(struct perf_tool *tool __maybe_unused, |
308 | union perf_event *event, |
309 | struct perf_sample *sample, |
310 | struct machine *machine) |
311 | { |
312 | return machine__process_text_poke(machine, event, sample); |
313 | } |
314 | |
315 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
316 | { |
317 | return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 "]: %c %s\n" , |
318 | event->mmap.pid, event->mmap.tid, event->mmap.start, |
319 | event->mmap.len, event->mmap.pgoff, |
320 | (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', |
321 | event->mmap.filename); |
322 | } |
323 | |
324 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) |
325 | { |
326 | if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { |
327 | char sbuild_id[SBUILD_ID_SIZE]; |
328 | struct build_id bid; |
329 | |
330 | build_id__init(bid: &bid, data: event->mmap2.build_id, |
331 | size: event->mmap2.build_id_size); |
332 | build_id__sprintf(build_id: &bid, bf: sbuild_id); |
333 | |
334 | return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 |
335 | " <%s>]: %c%c%c%c %s\n" , |
336 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, |
337 | event->mmap2.len, event->mmap2.pgoff, sbuild_id, |
338 | (event->mmap2.prot & PROT_READ) ? 'r' : '-', |
339 | (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', |
340 | (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', |
341 | (event->mmap2.flags & MAP_SHARED) ? 's' : 'p', |
342 | event->mmap2.filename); |
343 | } else { |
344 | return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 |
345 | " %02x:%02x %" PRI_lu64" %" PRI_lu64"]: %c%c%c%c %s\n" , |
346 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, |
347 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, |
348 | event->mmap2.min, event->mmap2.ino, |
349 | event->mmap2.ino_generation, |
350 | (event->mmap2.prot & PROT_READ) ? 'r' : '-', |
351 | (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', |
352 | (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', |
353 | (event->mmap2.flags & MAP_SHARED) ? 's' : 'p', |
354 | event->mmap2.filename); |
355 | } |
356 | } |
357 | |
358 | size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp) |
359 | { |
360 | struct perf_thread_map *threads = thread_map__new_event(event: &event->thread_map); |
361 | size_t ret; |
362 | |
363 | ret = fprintf(fp, " nr: " ); |
364 | |
365 | if (threads) |
366 | ret += thread_map__fprintf(threads, fp); |
367 | else |
368 | ret += fprintf(fp, "failed to get threads from event\n" ); |
369 | |
370 | perf_thread_map__put(threads); |
371 | return ret; |
372 | } |
373 | |
374 | size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp) |
375 | { |
376 | struct perf_cpu_map *cpus = cpu_map__new_data(data: &event->cpu_map.data); |
377 | size_t ret; |
378 | |
379 | ret = fprintf(fp, ": " ); |
380 | |
381 | if (cpus) |
382 | ret += cpu_map__fprintf(cpus, fp); |
383 | else |
384 | ret += fprintf(fp, "failed to get cpumap from event\n" ); |
385 | |
386 | perf_cpu_map__put(cpus); |
387 | return ret; |
388 | } |
389 | |
390 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, |
391 | union perf_event *event, |
392 | struct perf_sample *sample, |
393 | struct machine *machine) |
394 | { |
395 | return machine__process_mmap_event(machine, event, sample); |
396 | } |
397 | |
398 | int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, |
399 | union perf_event *event, |
400 | struct perf_sample *sample, |
401 | struct machine *machine) |
402 | { |
403 | return machine__process_mmap2_event(machine, event, sample); |
404 | } |
405 | |
406 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) |
407 | { |
408 | return fprintf(fp, "(%d:%d):(%d:%d)\n" , |
409 | event->fork.pid, event->fork.tid, |
410 | event->fork.ppid, event->fork.ptid); |
411 | } |
412 | |
413 | int perf_event__process_fork(struct perf_tool *tool __maybe_unused, |
414 | union perf_event *event, |
415 | struct perf_sample *sample, |
416 | struct machine *machine) |
417 | { |
418 | return machine__process_fork_event(machine, event, sample); |
419 | } |
420 | |
421 | int perf_event__process_exit(struct perf_tool *tool __maybe_unused, |
422 | union perf_event *event, |
423 | struct perf_sample *sample, |
424 | struct machine *machine) |
425 | { |
426 | return machine__process_exit_event(machine, event, sample); |
427 | } |
428 | |
429 | size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp) |
430 | { |
431 | return fprintf(fp, " offset: %#" PRI_lx64" size: %#" PRI_lx64" flags: %#" PRI_lx64" [%s%s%s]\n" , |
432 | event->aux.aux_offset, event->aux.aux_size, |
433 | event->aux.flags, |
434 | event->aux.flags & PERF_AUX_FLAG_TRUNCATED ? "T" : "" , |
435 | event->aux.flags & PERF_AUX_FLAG_OVERWRITE ? "O" : "" , |
436 | event->aux.flags & PERF_AUX_FLAG_PARTIAL ? "P" : "" ); |
437 | } |
438 | |
439 | size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp) |
440 | { |
441 | return fprintf(fp, " pid: %u tid: %u\n" , |
442 | event->itrace_start.pid, event->itrace_start.tid); |
443 | } |
444 | |
445 | size_t perf_event__fprintf_aux_output_hw_id(union perf_event *event, FILE *fp) |
446 | { |
447 | return fprintf(fp, " hw_id: %#" PRI_lx64"\n" , |
448 | event->aux_output_hw_id.hw_id); |
449 | } |
450 | |
451 | size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp) |
452 | { |
453 | bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT; |
454 | const char *in_out = !out ? "IN " : |
455 | !(event->header.misc & PERF_RECORD_MISC_SWITCH_OUT_PREEMPT) ? |
456 | "OUT " : "OUT preempt" ; |
457 | |
458 | if (event->header.type == PERF_RECORD_SWITCH) |
459 | return fprintf(fp, " %s\n" , in_out); |
460 | |
461 | return fprintf(fp, " %s %s pid/tid: %5d/%-5d\n" , |
462 | in_out, out ? "next" : "prev" , |
463 | event->context_switch.next_prev_pid, |
464 | event->context_switch.next_prev_tid); |
465 | } |
466 | |
467 | static size_t perf_event__fprintf_lost(union perf_event *event, FILE *fp) |
468 | { |
469 | return fprintf(fp, " lost %" PRI_lu64 "\n" , event->lost.lost); |
470 | } |
471 | |
472 | size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp) |
473 | { |
474 | return fprintf(fp, " addr %" PRI_lx64 " len %u type %u flags 0x%x name %s\n" , |
475 | event->ksymbol.addr, event->ksymbol.len, |
476 | event->ksymbol.ksym_type, |
477 | event->ksymbol.flags, event->ksymbol.name); |
478 | } |
479 | |
480 | size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp) |
481 | { |
482 | return fprintf(fp, " type %u, flags %u, id %u\n" , |
483 | event->bpf.type, event->bpf.flags, event->bpf.id); |
484 | } |
485 | |
486 | static int text_poke_printer(enum binary_printer_ops op, unsigned int val, |
487 | void *, FILE *fp) |
488 | { |
489 | bool old = *(bool *)extra; |
490 | |
491 | switch ((int)op) { |
492 | case BINARY_PRINT_LINE_BEGIN: |
493 | return fprintf(fp, " %s bytes:" , old ? "Old" : "New" ); |
494 | case BINARY_PRINT_NUM_DATA: |
495 | return fprintf(fp, " %02x" , val); |
496 | case BINARY_PRINT_LINE_END: |
497 | return fprintf(fp, "\n" ); |
498 | default: |
499 | return 0; |
500 | } |
501 | } |
502 | |
503 | size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *machine, FILE *fp) |
504 | { |
505 | struct perf_record_text_poke_event *tp = &event->text_poke; |
506 | size_t ret; |
507 | bool old; |
508 | |
509 | ret = fprintf(fp, " %" PRI_lx64 " " , tp->addr); |
510 | if (machine) { |
511 | struct addr_location al; |
512 | |
513 | addr_location__init(al: &al); |
514 | al.map = maps__find(maps: machine__kernel_maps(machine), addr: tp->addr); |
515 | if (al.map && map__load(map: al.map) >= 0) { |
516 | al.addr = map__map_ip(map: al.map, ip_or_rip: tp->addr); |
517 | al.sym = map__find_symbol(map: al.map, addr: al.addr); |
518 | if (al.sym) |
519 | ret += symbol__fprintf_symname_offs(al.sym, &al, fp); |
520 | } |
521 | addr_location__exit(al: &al); |
522 | } |
523 | ret += fprintf(fp, " old len %u new len %u\n" , tp->old_len, tp->new_len); |
524 | old = true; |
525 | ret += binary__fprintf(tp->bytes, tp->old_len, 16, text_poke_printer, |
526 | &old, fp); |
527 | old = false; |
528 | ret += binary__fprintf(tp->bytes + tp->old_len, tp->new_len, 16, |
529 | text_poke_printer, &old, fp); |
530 | return ret; |
531 | } |
532 | |
533 | size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FILE *fp) |
534 | { |
535 | size_t ret = fprintf(fp, "PERF_RECORD_%s" , |
536 | perf_event__name(id: event->header.type)); |
537 | |
538 | switch (event->header.type) { |
539 | case PERF_RECORD_COMM: |
540 | ret += perf_event__fprintf_comm(event, fp); |
541 | break; |
542 | case PERF_RECORD_FORK: |
543 | case PERF_RECORD_EXIT: |
544 | ret += perf_event__fprintf_task(event, fp); |
545 | break; |
546 | case PERF_RECORD_MMAP: |
547 | ret += perf_event__fprintf_mmap(event, fp); |
548 | break; |
549 | case PERF_RECORD_NAMESPACES: |
550 | ret += perf_event__fprintf_namespaces(event, fp); |
551 | break; |
552 | case PERF_RECORD_CGROUP: |
553 | ret += perf_event__fprintf_cgroup(event, fp); |
554 | break; |
555 | case PERF_RECORD_MMAP2: |
556 | ret += perf_event__fprintf_mmap2(event, fp); |
557 | break; |
558 | case PERF_RECORD_AUX: |
559 | ret += perf_event__fprintf_aux(event, fp); |
560 | break; |
561 | case PERF_RECORD_ITRACE_START: |
562 | ret += perf_event__fprintf_itrace_start(event, fp); |
563 | break; |
564 | case PERF_RECORD_SWITCH: |
565 | case PERF_RECORD_SWITCH_CPU_WIDE: |
566 | ret += perf_event__fprintf_switch(event, fp); |
567 | break; |
568 | case PERF_RECORD_LOST: |
569 | ret += perf_event__fprintf_lost(event, fp); |
570 | break; |
571 | case PERF_RECORD_KSYMBOL: |
572 | ret += perf_event__fprintf_ksymbol(event, fp); |
573 | break; |
574 | case PERF_RECORD_BPF_EVENT: |
575 | ret += perf_event__fprintf_bpf(event, fp); |
576 | break; |
577 | case PERF_RECORD_TEXT_POKE: |
578 | ret += perf_event__fprintf_text_poke(event, machine, fp); |
579 | break; |
580 | case PERF_RECORD_AUX_OUTPUT_HW_ID: |
581 | ret += perf_event__fprintf_aux_output_hw_id(event, fp); |
582 | break; |
583 | default: |
584 | ret += fprintf(fp, "\n" ); |
585 | } |
586 | |
587 | return ret; |
588 | } |
589 | |
590 | int perf_event__process(struct perf_tool *tool __maybe_unused, |
591 | union perf_event *event, |
592 | struct perf_sample *sample, |
593 | struct machine *machine) |
594 | { |
595 | return machine__process_event(machine, event, sample); |
596 | } |
597 | |
598 | struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, |
599 | struct addr_location *al) |
600 | { |
601 | struct maps *maps = thread__maps(thread); |
602 | struct machine *machine = maps__machine(maps); |
603 | bool load_map = false; |
604 | |
605 | maps__zput(al->maps); |
606 | map__zput(al->map); |
607 | thread__zput(al->thread); |
608 | al->thread = thread__get(thread); |
609 | |
610 | al->addr = addr; |
611 | al->cpumode = cpumode; |
612 | al->filtered = 0; |
613 | |
614 | if (machine == NULL) |
615 | return NULL; |
616 | |
617 | if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { |
618 | al->level = 'k'; |
619 | maps = machine__kernel_maps(machine); |
620 | load_map = !symbol_conf.lazy_load_kernel_maps; |
621 | } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { |
622 | al->level = '.'; |
623 | } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { |
624 | al->level = 'g'; |
625 | maps = machine__kernel_maps(machine); |
626 | load_map = !symbol_conf.lazy_load_kernel_maps; |
627 | } else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { |
628 | al->level = 'u'; |
629 | } else { |
630 | al->level = 'H'; |
631 | |
632 | if ((cpumode == PERF_RECORD_MISC_GUEST_USER || |
633 | cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && |
634 | !perf_guest) |
635 | al->filtered |= (1 << HIST_FILTER__GUEST); |
636 | if ((cpumode == PERF_RECORD_MISC_USER || |
637 | cpumode == PERF_RECORD_MISC_KERNEL) && |
638 | !perf_host) |
639 | al->filtered |= (1 << HIST_FILTER__HOST); |
640 | |
641 | return NULL; |
642 | } |
643 | al->maps = maps__get(maps); |
644 | al->map = maps__find(maps, addr: al->addr); |
645 | if (al->map != NULL) { |
646 | /* |
647 | * Kernel maps might be changed when loading symbols so loading |
648 | * must be done prior to using kernel maps. |
649 | */ |
650 | if (load_map) |
651 | map__load(map: al->map); |
652 | al->addr = map__map_ip(map: al->map, ip_or_rip: al->addr); |
653 | } |
654 | |
655 | return al->map; |
656 | } |
657 | |
658 | /* |
659 | * For branch stacks or branch samples, the sample cpumode might not be correct |
660 | * because it applies only to the sample 'ip' and not necessary to 'addr' or |
661 | * branch stack addresses. If possible, use a fallback to deal with those cases. |
662 | */ |
663 | struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr, |
664 | struct addr_location *al) |
665 | { |
666 | struct map *map = thread__find_map(thread, cpumode, addr, al); |
667 | struct machine *machine = maps__machine(maps: thread__maps(thread)); |
668 | u8 addr_cpumode = machine__addr_cpumode(machine, cpumode, addr); |
669 | |
670 | if (map || addr_cpumode == cpumode) |
671 | return map; |
672 | |
673 | return thread__find_map(thread, cpumode: addr_cpumode, addr, al); |
674 | } |
675 | |
676 | struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode, |
677 | u64 addr, struct addr_location *al) |
678 | { |
679 | al->sym = NULL; |
680 | if (thread__find_map(thread, cpumode, addr, al)) |
681 | al->sym = map__find_symbol(map: al->map, addr: al->addr); |
682 | return al->sym; |
683 | } |
684 | |
685 | struct symbol *thread__find_symbol_fb(struct thread *thread, u8 cpumode, |
686 | u64 addr, struct addr_location *al) |
687 | { |
688 | al->sym = NULL; |
689 | if (thread__find_map_fb(thread, cpumode, addr, al)) |
690 | al->sym = map__find_symbol(map: al->map, addr: al->addr); |
691 | return al->sym; |
692 | } |
693 | |
694 | static bool check_address_range(struct intlist *addr_list, int addr_range, |
695 | unsigned long addr) |
696 | { |
697 | struct int_node *pos; |
698 | |
699 | intlist__for_each_entry(pos, addr_list) { |
700 | if (addr >= pos->i && addr < pos->i + addr_range) |
701 | return true; |
702 | } |
703 | |
704 | return false; |
705 | } |
706 | |
707 | /* |
708 | * Callers need to drop the reference to al->thread, obtained in |
709 | * machine__findnew_thread() |
710 | */ |
711 | int machine__resolve(struct machine *machine, struct addr_location *al, |
712 | struct perf_sample *sample) |
713 | { |
714 | struct thread *thread; |
715 | struct dso *dso; |
716 | |
717 | if (symbol_conf.guest_code && !machine__is_host(machine)) |
718 | thread = machine__findnew_guest_code(machine, pid: sample->pid); |
719 | else |
720 | thread = machine__findnew_thread(machine, pid: sample->pid, tid: sample->tid); |
721 | if (thread == NULL) |
722 | return -1; |
723 | |
724 | dump_printf(fmt: " ... thread: %s:%d\n" , thread__comm_str(thread), thread__tid(thread)); |
725 | thread__find_map(thread, cpumode: sample->cpumode, addr: sample->ip, al); |
726 | dso = al->map ? map__dso(map: al->map) : NULL; |
727 | dump_printf(fmt: " ...... dso: %s\n" , |
728 | dso |
729 | ? dso->long_name |
730 | : (al->level == 'H' ? "[hypervisor]" : "<not found>" )); |
731 | |
732 | if (thread__is_filtered(thread)) |
733 | al->filtered |= (1 << HIST_FILTER__THREAD); |
734 | |
735 | thread__put(thread); |
736 | thread = NULL; |
737 | |
738 | al->sym = NULL; |
739 | al->cpu = sample->cpu; |
740 | al->socket = -1; |
741 | al->srcline = NULL; |
742 | |
743 | if (al->cpu >= 0) { |
744 | struct perf_env *env = machine->env; |
745 | |
746 | if (env && env->cpu) |
747 | al->socket = env->cpu[al->cpu].socket_id; |
748 | } |
749 | |
750 | if (al->map) { |
751 | if (symbol_conf.dso_list && |
752 | (!dso || !(strlist__has_entry(slist: symbol_conf.dso_list, |
753 | entry: dso->short_name) || |
754 | (dso->short_name != dso->long_name && |
755 | strlist__has_entry(slist: symbol_conf.dso_list, |
756 | entry: dso->long_name))))) { |
757 | al->filtered |= (1 << HIST_FILTER__DSO); |
758 | } |
759 | |
760 | al->sym = map__find_symbol(map: al->map, addr: al->addr); |
761 | } else if (symbol_conf.dso_list) { |
762 | al->filtered |= (1 << HIST_FILTER__DSO); |
763 | } |
764 | |
765 | if (symbol_conf.sym_list) { |
766 | int ret = 0; |
767 | char al_addr_str[32]; |
768 | size_t sz = sizeof(al_addr_str); |
769 | |
770 | if (al->sym) { |
771 | ret = strlist__has_entry(slist: symbol_conf.sym_list, |
772 | entry: al->sym->name); |
773 | } |
774 | if (!ret && al->sym) { |
775 | snprintf(al_addr_str, sz, "0x%" PRIx64, |
776 | map__unmap_ip(al->map, al->sym->start)); |
777 | ret = strlist__has_entry(slist: symbol_conf.sym_list, |
778 | entry: al_addr_str); |
779 | } |
780 | if (!ret && symbol_conf.addr_list && al->map) { |
781 | unsigned long addr = map__unmap_ip(map: al->map, ip_or_rip: al->addr); |
782 | |
783 | ret = intlist__has_entry(ilist: symbol_conf.addr_list, i: addr); |
784 | if (!ret && symbol_conf.addr_range) { |
785 | ret = check_address_range(addr_list: symbol_conf.addr_list, |
786 | addr_range: symbol_conf.addr_range, |
787 | addr); |
788 | } |
789 | } |
790 | |
791 | if (!ret) |
792 | al->filtered |= (1 << HIST_FILTER__SYMBOL); |
793 | } |
794 | |
795 | return 0; |
796 | } |
797 | |
798 | bool is_bts_event(struct perf_event_attr *attr) |
799 | { |
800 | return attr->type == PERF_TYPE_HARDWARE && |
801 | (attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && |
802 | attr->sample_period == 1; |
803 | } |
804 | |
805 | bool sample_addr_correlates_sym(struct perf_event_attr *attr) |
806 | { |
807 | if (attr->type == PERF_TYPE_SOFTWARE && |
808 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS || |
809 | attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN || |
810 | attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)) |
811 | return true; |
812 | |
813 | if (is_bts_event(attr)) |
814 | return true; |
815 | |
816 | return false; |
817 | } |
818 | |
819 | void thread__resolve(struct thread *thread, struct addr_location *al, |
820 | struct perf_sample *sample) |
821 | { |
822 | thread__find_map_fb(thread, cpumode: sample->cpumode, addr: sample->addr, al); |
823 | |
824 | al->cpu = sample->cpu; |
825 | al->sym = NULL; |
826 | |
827 | if (al->map) |
828 | al->sym = map__find_symbol(map: al->map, addr: al->addr); |
829 | } |
830 | |