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