]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - tools/perf/builtin-inject.c
perf tools: Hit all build ids when AUX area tracing
[mirror_ubuntu-bionic-kernel.git] / tools / perf / builtin-inject.c
1 /*
2 * builtin-inject.c
3 *
4 * Builtin inject command: Examine the live mode (stdin) event stream
5 * and repipe it to stdout while optionally injecting additional
6 * events into it.
7 */
8 #include "builtin.h"
9
10 #include "perf.h"
11 #include "util/color.h"
12 #include "util/evlist.h"
13 #include "util/evsel.h"
14 #include "util/session.h"
15 #include "util/tool.h"
16 #include "util/debug.h"
17 #include "util/build-id.h"
18 #include "util/data.h"
19 #include "util/auxtrace.h"
20
21 #include "util/parse-options.h"
22
23 #include <linux/list.h>
24
25 struct perf_inject {
26 struct perf_tool tool;
27 struct perf_session *session;
28 bool build_ids;
29 bool sched_stat;
30 bool have_auxtrace;
31 const char *input_name;
32 struct perf_data_file output;
33 u64 bytes_written;
34 struct list_head samples;
35 struct itrace_synth_opts itrace_synth_opts;
36 };
37
38 struct event_entry {
39 struct list_head node;
40 u32 tid;
41 union perf_event event[0];
42 };
43
44 static int output_bytes(struct perf_inject *inject, void *buf, size_t sz)
45 {
46 ssize_t size;
47
48 size = perf_data_file__write(&inject->output, buf, sz);
49 if (size < 0)
50 return -errno;
51
52 inject->bytes_written += size;
53 return 0;
54 }
55
56 static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
57 {
58 char buf[4096];
59 ssize_t ssz;
60 int ret;
61
62 while (size > 0) {
63 ssz = read(fd, buf, min(size, (off_t)sizeof(buf)));
64 if (ssz < 0)
65 return -errno;
66 ret = output_bytes(inject, buf, ssz);
67 if (ret)
68 return ret;
69 size -= ssz;
70 }
71
72 return 0;
73 }
74
75 static int perf_event__repipe_synth(struct perf_tool *tool,
76 union perf_event *event)
77 {
78 struct perf_inject *inject = container_of(tool, struct perf_inject,
79 tool);
80
81 return output_bytes(inject, event, event->header.size);
82 }
83
84 static int perf_event__repipe_oe_synth(struct perf_tool *tool,
85 union perf_event *event,
86 struct ordered_events *oe __maybe_unused)
87 {
88 return perf_event__repipe_synth(tool, event);
89 }
90
91 static int perf_event__repipe_op2_synth(struct perf_tool *tool,
92 union perf_event *event,
93 struct perf_session *session
94 __maybe_unused)
95 {
96 return perf_event__repipe_synth(tool, event);
97 }
98
99 static int perf_event__repipe_attr(struct perf_tool *tool,
100 union perf_event *event,
101 struct perf_evlist **pevlist)
102 {
103 struct perf_inject *inject = container_of(tool, struct perf_inject,
104 tool);
105 int ret;
106
107 ret = perf_event__process_attr(tool, event, pevlist);
108 if (ret)
109 return ret;
110
111 if (!inject->output.is_pipe)
112 return 0;
113
114 return perf_event__repipe_synth(tool, event);
115 }
116
117 static s64 perf_event__repipe_auxtrace(struct perf_tool *tool,
118 union perf_event *event,
119 struct perf_session *session
120 __maybe_unused)
121 {
122 struct perf_inject *inject = container_of(tool, struct perf_inject,
123 tool);
124 int ret;
125
126 inject->have_auxtrace = true;
127
128 if (!inject->output.is_pipe) {
129 off_t offset;
130
131 offset = lseek(inject->output.fd, 0, SEEK_CUR);
132 if (offset == -1)
133 return -errno;
134 ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
135 event, offset);
136 if (ret < 0)
137 return ret;
138 }
139
140 if (perf_data_file__is_pipe(session->file) || !session->one_mmap) {
141 ret = output_bytes(inject, event, event->header.size);
142 if (ret < 0)
143 return ret;
144 ret = copy_bytes(inject, perf_data_file__fd(session->file),
145 event->auxtrace.size);
146 } else {
147 ret = output_bytes(inject, event,
148 event->header.size + event->auxtrace.size);
149 }
150 if (ret < 0)
151 return ret;
152
153 return event->auxtrace.size;
154 }
155
156 static int perf_event__repipe(struct perf_tool *tool,
157 union perf_event *event,
158 struct perf_sample *sample __maybe_unused,
159 struct machine *machine __maybe_unused)
160 {
161 return perf_event__repipe_synth(tool, event);
162 }
163
164 typedef int (*inject_handler)(struct perf_tool *tool,
165 union perf_event *event,
166 struct perf_sample *sample,
167 struct perf_evsel *evsel,
168 struct machine *machine);
169
170 static int perf_event__repipe_sample(struct perf_tool *tool,
171 union perf_event *event,
172 struct perf_sample *sample,
173 struct perf_evsel *evsel,
174 struct machine *machine)
175 {
176 if (evsel->handler) {
177 inject_handler f = evsel->handler;
178 return f(tool, event, sample, evsel, machine);
179 }
180
181 build_id__mark_dso_hit(tool, event, sample, evsel, machine);
182
183 return perf_event__repipe_synth(tool, event);
184 }
185
186 static int perf_event__repipe_mmap(struct perf_tool *tool,
187 union perf_event *event,
188 struct perf_sample *sample,
189 struct machine *machine)
190 {
191 int err;
192
193 err = perf_event__process_mmap(tool, event, sample, machine);
194 perf_event__repipe(tool, event, sample, machine);
195
196 return err;
197 }
198
199 static int perf_event__repipe_mmap2(struct perf_tool *tool,
200 union perf_event *event,
201 struct perf_sample *sample,
202 struct machine *machine)
203 {
204 int err;
205
206 err = perf_event__process_mmap2(tool, event, sample, machine);
207 perf_event__repipe(tool, event, sample, machine);
208
209 return err;
210 }
211
212 static int perf_event__repipe_fork(struct perf_tool *tool,
213 union perf_event *event,
214 struct perf_sample *sample,
215 struct machine *machine)
216 {
217 int err;
218
219 err = perf_event__process_fork(tool, event, sample, machine);
220 perf_event__repipe(tool, event, sample, machine);
221
222 return err;
223 }
224
225 static int perf_event__repipe_comm(struct perf_tool *tool,
226 union perf_event *event,
227 struct perf_sample *sample,
228 struct machine *machine)
229 {
230 int err;
231
232 err = perf_event__process_comm(tool, event, sample, machine);
233 perf_event__repipe(tool, event, sample, machine);
234
235 return err;
236 }
237
238 static int perf_event__repipe_exit(struct perf_tool *tool,
239 union perf_event *event,
240 struct perf_sample *sample,
241 struct machine *machine)
242 {
243 int err;
244
245 err = perf_event__process_exit(tool, event, sample, machine);
246 perf_event__repipe(tool, event, sample, machine);
247
248 return err;
249 }
250
251 static int perf_event__repipe_tracing_data(struct perf_tool *tool,
252 union perf_event *event,
253 struct perf_session *session)
254 {
255 int err;
256
257 perf_event__repipe_synth(tool, event);
258 err = perf_event__process_tracing_data(tool, event, session);
259
260 return err;
261 }
262
263 static int perf_event__repipe_id_index(struct perf_tool *tool,
264 union perf_event *event,
265 struct perf_session *session)
266 {
267 int err;
268
269 perf_event__repipe_synth(tool, event);
270 err = perf_event__process_id_index(tool, event, session);
271
272 return err;
273 }
274
275 static int dso__read_build_id(struct dso *dso)
276 {
277 if (dso->has_build_id)
278 return 0;
279
280 if (filename__read_build_id(dso->long_name, dso->build_id,
281 sizeof(dso->build_id)) > 0) {
282 dso->has_build_id = true;
283 return 0;
284 }
285
286 return -1;
287 }
288
289 static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool,
290 struct machine *machine)
291 {
292 u16 misc = PERF_RECORD_MISC_USER;
293 int err;
294
295 if (dso__read_build_id(dso) < 0) {
296 pr_debug("no build_id found for %s\n", dso->long_name);
297 return -1;
298 }
299
300 if (dso->kernel)
301 misc = PERF_RECORD_MISC_KERNEL;
302
303 err = perf_event__synthesize_build_id(tool, dso, misc, perf_event__repipe,
304 machine);
305 if (err) {
306 pr_err("Can't synthesize build_id event for %s\n", dso->long_name);
307 return -1;
308 }
309
310 return 0;
311 }
312
313 static int perf_event__inject_buildid(struct perf_tool *tool,
314 union perf_event *event,
315 struct perf_sample *sample,
316 struct perf_evsel *evsel __maybe_unused,
317 struct machine *machine)
318 {
319 struct addr_location al;
320 struct thread *thread;
321 u8 cpumode;
322
323 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
324
325 thread = machine__findnew_thread(machine, sample->pid, sample->tid);
326 if (thread == NULL) {
327 pr_err("problem processing %d event, skipping it.\n",
328 event->header.type);
329 goto repipe;
330 }
331
332 thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
333
334 if (al.map != NULL) {
335 if (!al.map->dso->hit) {
336 al.map->dso->hit = 1;
337 if (map__load(al.map, NULL) >= 0) {
338 dso__inject_build_id(al.map->dso, tool, machine);
339 /*
340 * If this fails, too bad, let the other side
341 * account this as unresolved.
342 */
343 } else {
344 #ifdef HAVE_LIBELF_SUPPORT
345 pr_warning("no symbols found in %s, maybe "
346 "install a debug package?\n",
347 al.map->dso->long_name);
348 #endif
349 }
350 }
351 }
352
353 repipe:
354 perf_event__repipe(tool, event, sample, machine);
355 return 0;
356 }
357
358 static int perf_inject__sched_process_exit(struct perf_tool *tool,
359 union perf_event *event __maybe_unused,
360 struct perf_sample *sample,
361 struct perf_evsel *evsel __maybe_unused,
362 struct machine *machine __maybe_unused)
363 {
364 struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
365 struct event_entry *ent;
366
367 list_for_each_entry(ent, &inject->samples, node) {
368 if (sample->tid == ent->tid) {
369 list_del_init(&ent->node);
370 free(ent);
371 break;
372 }
373 }
374
375 return 0;
376 }
377
378 static int perf_inject__sched_switch(struct perf_tool *tool,
379 union perf_event *event,
380 struct perf_sample *sample,
381 struct perf_evsel *evsel,
382 struct machine *machine)
383 {
384 struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
385 struct event_entry *ent;
386
387 perf_inject__sched_process_exit(tool, event, sample, evsel, machine);
388
389 ent = malloc(event->header.size + sizeof(struct event_entry));
390 if (ent == NULL) {
391 color_fprintf(stderr, PERF_COLOR_RED,
392 "Not enough memory to process sched switch event!");
393 return -1;
394 }
395
396 ent->tid = sample->tid;
397 memcpy(&ent->event, event, event->header.size);
398 list_add(&ent->node, &inject->samples);
399 return 0;
400 }
401
402 static int perf_inject__sched_stat(struct perf_tool *tool,
403 union perf_event *event __maybe_unused,
404 struct perf_sample *sample,
405 struct perf_evsel *evsel,
406 struct machine *machine)
407 {
408 struct event_entry *ent;
409 union perf_event *event_sw;
410 struct perf_sample sample_sw;
411 struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
412 u32 pid = perf_evsel__intval(evsel, sample, "pid");
413
414 list_for_each_entry(ent, &inject->samples, node) {
415 if (pid == ent->tid)
416 goto found;
417 }
418
419 return 0;
420 found:
421 event_sw = &ent->event[0];
422 perf_evsel__parse_sample(evsel, event_sw, &sample_sw);
423
424 sample_sw.period = sample->period;
425 sample_sw.time = sample->time;
426 perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
427 evsel->attr.read_format, &sample_sw,
428 false);
429 build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
430 return perf_event__repipe(tool, event_sw, &sample_sw, machine);
431 }
432
433 static void sig_handler(int sig __maybe_unused)
434 {
435 session_done = 1;
436 }
437
438 static int perf_evsel__check_stype(struct perf_evsel *evsel,
439 u64 sample_type, const char *sample_msg)
440 {
441 struct perf_event_attr *attr = &evsel->attr;
442 const char *name = perf_evsel__name(evsel);
443
444 if (!(attr->sample_type & sample_type)) {
445 pr_err("Samples for %s event do not have %s attribute set.",
446 name, sample_msg);
447 return -EINVAL;
448 }
449
450 return 0;
451 }
452
453 static int __cmd_inject(struct perf_inject *inject)
454 {
455 int ret = -EINVAL;
456 struct perf_session *session = inject->session;
457 struct perf_data_file *file_out = &inject->output;
458 int fd = perf_data_file__fd(file_out);
459 u64 output_data_offset;
460
461 signal(SIGINT, sig_handler);
462
463 if (inject->build_ids || inject->sched_stat ||
464 inject->itrace_synth_opts.set) {
465 inject->tool.mmap = perf_event__repipe_mmap;
466 inject->tool.mmap2 = perf_event__repipe_mmap2;
467 inject->tool.fork = perf_event__repipe_fork;
468 inject->tool.tracing_data = perf_event__repipe_tracing_data;
469 }
470
471 output_data_offset = session->header.data_offset;
472
473 if (inject->build_ids) {
474 inject->tool.sample = perf_event__inject_buildid;
475 } else if (inject->sched_stat) {
476 struct perf_evsel *evsel;
477
478 evlist__for_each(session->evlist, evsel) {
479 const char *name = perf_evsel__name(evsel);
480
481 if (!strcmp(name, "sched:sched_switch")) {
482 if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
483 return -EINVAL;
484
485 evsel->handler = perf_inject__sched_switch;
486 } else if (!strcmp(name, "sched:sched_process_exit"))
487 evsel->handler = perf_inject__sched_process_exit;
488 else if (!strncmp(name, "sched:sched_stat_", 17))
489 evsel->handler = perf_inject__sched_stat;
490 }
491 } else if (inject->itrace_synth_opts.set) {
492 session->itrace_synth_opts = &inject->itrace_synth_opts;
493 inject->itrace_synth_opts.inject = true;
494 inject->tool.comm = perf_event__repipe_comm;
495 inject->tool.exit = perf_event__repipe_exit;
496 inject->tool.id_index = perf_event__repipe_id_index;
497 inject->tool.auxtrace_info = perf_event__process_auxtrace_info;
498 inject->tool.auxtrace = perf_event__process_auxtrace;
499 inject->tool.ordered_events = true;
500 inject->tool.ordering_requires_timestamps = true;
501 /* Allow space in the header for new attributes */
502 output_data_offset = 4096;
503 }
504
505 if (!inject->itrace_synth_opts.set)
506 auxtrace_index__free(&session->auxtrace_index);
507
508 if (!file_out->is_pipe)
509 lseek(fd, output_data_offset, SEEK_SET);
510
511 ret = perf_session__process_events(session);
512
513 if (!file_out->is_pipe) {
514 if (inject->build_ids) {
515 perf_header__set_feat(&session->header,
516 HEADER_BUILD_ID);
517 if (inject->have_auxtrace)
518 dsos__hit_all(session);
519 }
520 /*
521 * The AUX areas have been removed and replaced with
522 * synthesized hardware events, so clear the feature flag.
523 */
524 if (inject->itrace_synth_opts.set)
525 perf_header__clear_feat(&session->header,
526 HEADER_AUXTRACE);
527 session->header.data_offset = output_data_offset;
528 session->header.data_size = inject->bytes_written;
529 perf_session__write_header(session, session->evlist, fd, true);
530 }
531
532 return ret;
533 }
534
535 int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
536 {
537 struct perf_inject inject = {
538 .tool = {
539 .sample = perf_event__repipe_sample,
540 .mmap = perf_event__repipe,
541 .mmap2 = perf_event__repipe,
542 .comm = perf_event__repipe,
543 .fork = perf_event__repipe,
544 .exit = perf_event__repipe,
545 .lost = perf_event__repipe,
546 .read = perf_event__repipe_sample,
547 .throttle = perf_event__repipe,
548 .unthrottle = perf_event__repipe,
549 .attr = perf_event__repipe_attr,
550 .tracing_data = perf_event__repipe_op2_synth,
551 .auxtrace_info = perf_event__repipe_op2_synth,
552 .auxtrace = perf_event__repipe_auxtrace,
553 .auxtrace_error = perf_event__repipe_op2_synth,
554 .finished_round = perf_event__repipe_oe_synth,
555 .build_id = perf_event__repipe_op2_synth,
556 .id_index = perf_event__repipe_op2_synth,
557 },
558 .input_name = "-",
559 .samples = LIST_HEAD_INIT(inject.samples),
560 .output = {
561 .path = "-",
562 .mode = PERF_DATA_MODE_WRITE,
563 },
564 };
565 struct perf_data_file file = {
566 .mode = PERF_DATA_MODE_READ,
567 };
568 int ret;
569
570 const struct option options[] = {
571 OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
572 "Inject build-ids into the output stream"),
573 OPT_STRING('i', "input", &inject.input_name, "file",
574 "input file name"),
575 OPT_STRING('o', "output", &inject.output.path, "file",
576 "output file name"),
577 OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
578 "Merge sched-stat and sched-switch for getting events "
579 "where and how long tasks slept"),
580 OPT_INCR('v', "verbose", &verbose,
581 "be more verbose (show build ids, etc)"),
582 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
583 "kallsyms pathname"),
584 OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
585 OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts,
586 NULL, "opts", "Instruction Tracing options",
587 itrace_parse_synth_opts),
588 OPT_END()
589 };
590 const char * const inject_usage[] = {
591 "perf inject [<options>]",
592 NULL
593 };
594
595 argc = parse_options(argc, argv, options, inject_usage, 0);
596
597 /*
598 * Any (unrecognized) arguments left?
599 */
600 if (argc)
601 usage_with_options(inject_usage, options);
602
603 if (perf_data_file__open(&inject.output)) {
604 perror("failed to create output file");
605 return -1;
606 }
607
608 inject.tool.ordered_events = inject.sched_stat;
609
610 file.path = inject.input_name;
611 inject.session = perf_session__new(&file, true, &inject.tool);
612 if (inject.session == NULL)
613 return -1;
614
615 if (symbol__init(&inject.session->header.env) < 0)
616 return -1;
617
618 ret = __cmd_inject(&inject);
619
620 perf_session__delete(inject.session);
621
622 return ret;
623 }