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