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 thread__put(thread); 369 repipe: 370 perf_event__repipe(tool, event, sample, machine); 371 return 0; 372 } 373 374 static int perf_inject__sched_process_exit(struct perf_tool *tool, 375 union perf_event *event __maybe_unused, 376 struct perf_sample *sample, 377 struct perf_evsel *evsel __maybe_unused, 378 struct machine *machine __maybe_unused) 379 { 380 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 381 struct event_entry *ent; 382 383 list_for_each_entry(ent, &inject->samples, node) { 384 if (sample->tid == ent->tid) { 385 list_del_init(&ent->node); 386 free(ent); 387 break; 388 } 389 } 390 391 return 0; 392 } 393 394 static int perf_inject__sched_switch(struct perf_tool *tool, 395 union perf_event *event, 396 struct perf_sample *sample, 397 struct perf_evsel *evsel, 398 struct machine *machine) 399 { 400 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 401 struct event_entry *ent; 402 403 perf_inject__sched_process_exit(tool, event, sample, evsel, machine); 404 405 ent = malloc(event->header.size + sizeof(struct event_entry)); 406 if (ent == NULL) { 407 color_fprintf(stderr, PERF_COLOR_RED, 408 "Not enough memory to process sched switch event!"); 409 return -1; 410 } 411 412 ent->tid = sample->tid; 413 memcpy(&ent->event, event, event->header.size); 414 list_add(&ent->node, &inject->samples); 415 return 0; 416 } 417 418 static int perf_inject__sched_stat(struct perf_tool *tool, 419 union perf_event *event __maybe_unused, 420 struct perf_sample *sample, 421 struct perf_evsel *evsel, 422 struct machine *machine) 423 { 424 struct event_entry *ent; 425 union perf_event *event_sw; 426 struct perf_sample sample_sw; 427 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 428 u32 pid = perf_evsel__intval(evsel, sample, "pid"); 429 430 list_for_each_entry(ent, &inject->samples, node) { 431 if (pid == ent->tid) 432 goto found; 433 } 434 435 return 0; 436 found: 437 event_sw = &ent->event[0]; 438 perf_evsel__parse_sample(evsel, event_sw, &sample_sw); 439 440 sample_sw.period = sample->period; 441 sample_sw.time = sample->time; 442 perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, 443 evsel->attr.read_format, &sample_sw, 444 false); 445 build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); 446 return perf_event__repipe(tool, event_sw, &sample_sw, machine); 447 } 448 449 static void sig_handler(int sig __maybe_unused) 450 { 451 session_done = 1; 452 } 453 454 static int perf_evsel__check_stype(struct perf_evsel *evsel, 455 u64 sample_type, const char *sample_msg) 456 { 457 struct perf_event_attr *attr = &evsel->attr; 458 const char *name = perf_evsel__name(evsel); 459 460 if (!(attr->sample_type & sample_type)) { 461 pr_err("Samples for %s event do not have %s attribute set.", 462 name, sample_msg); 463 return -EINVAL; 464 } 465 466 return 0; 467 } 468 469 static int __cmd_inject(struct perf_inject *inject) 470 { 471 int ret = -EINVAL; 472 struct perf_session *session = inject->session; 473 struct perf_data_file *file_out = &inject->output; 474 int fd = perf_data_file__fd(file_out); 475 u64 output_data_offset; 476 477 signal(SIGINT, sig_handler); 478 479 if (inject->build_ids || inject->sched_stat || 480 inject->itrace_synth_opts.set) { 481 inject->tool.mmap = perf_event__repipe_mmap; 482 inject->tool.mmap2 = perf_event__repipe_mmap2; 483 inject->tool.fork = perf_event__repipe_fork; 484 inject->tool.tracing_data = perf_event__repipe_tracing_data; 485 } 486 487 output_data_offset = session->header.data_offset; 488 489 if (inject->build_ids) { 490 inject->tool.sample = perf_event__inject_buildid; 491 } else if (inject->sched_stat) { 492 struct perf_evsel *evsel; 493 494 evlist__for_each(session->evlist, evsel) { 495 const char *name = perf_evsel__name(evsel); 496 497 if (!strcmp(name, "sched:sched_switch")) { 498 if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) 499 return -EINVAL; 500 501 evsel->handler = perf_inject__sched_switch; 502 } else if (!strcmp(name, "sched:sched_process_exit")) 503 evsel->handler = perf_inject__sched_process_exit; 504 else if (!strncmp(name, "sched:sched_stat_", 17)) 505 evsel->handler = perf_inject__sched_stat; 506 } 507 } else if (inject->itrace_synth_opts.set) { 508 session->itrace_synth_opts = &inject->itrace_synth_opts; 509 inject->itrace_synth_opts.inject = true; 510 inject->tool.comm = perf_event__repipe_comm; 511 inject->tool.exit = perf_event__repipe_exit; 512 inject->tool.id_index = perf_event__repipe_id_index; 513 inject->tool.auxtrace_info = perf_event__process_auxtrace_info; 514 inject->tool.auxtrace = perf_event__process_auxtrace; 515 inject->tool.ordered_events = true; 516 inject->tool.ordering_requires_timestamps = true; 517 /* Allow space in the header for new attributes */ 518 output_data_offset = 4096; 519 } 520 521 if (!inject->itrace_synth_opts.set) 522 auxtrace_index__free(&session->auxtrace_index); 523 524 if (!file_out->is_pipe) 525 lseek(fd, output_data_offset, SEEK_SET); 526 527 ret = perf_session__process_events(session); 528 529 if (!file_out->is_pipe) { 530 if (inject->build_ids) { 531 perf_header__set_feat(&session->header, 532 HEADER_BUILD_ID); 533 if (inject->have_auxtrace) 534 dsos__hit_all(session); 535 } 536 /* 537 * The AUX areas have been removed and replaced with 538 * synthesized hardware events, so clear the feature flag. 539 */ 540 if (inject->itrace_synth_opts.set) 541 perf_header__clear_feat(&session->header, 542 HEADER_AUXTRACE); 543 session->header.data_offset = output_data_offset; 544 session->header.data_size = inject->bytes_written; 545 perf_session__write_header(session, session->evlist, fd, true); 546 } 547 548 return ret; 549 } 550 551 int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) 552 { 553 struct perf_inject inject = { 554 .tool = { 555 .sample = perf_event__repipe_sample, 556 .mmap = perf_event__repipe, 557 .mmap2 = perf_event__repipe, 558 .comm = perf_event__repipe, 559 .fork = perf_event__repipe, 560 .exit = perf_event__repipe, 561 .lost = perf_event__repipe, 562 .aux = perf_event__repipe, 563 .itrace_start = perf_event__repipe, 564 .read = perf_event__repipe_sample, 565 .throttle = perf_event__repipe, 566 .unthrottle = perf_event__repipe, 567 .attr = perf_event__repipe_attr, 568 .tracing_data = perf_event__repipe_op2_synth, 569 .auxtrace_info = perf_event__repipe_op2_synth, 570 .auxtrace = perf_event__repipe_auxtrace, 571 .auxtrace_error = perf_event__repipe_op2_synth, 572 .finished_round = perf_event__repipe_oe_synth, 573 .build_id = perf_event__repipe_op2_synth, 574 .id_index = perf_event__repipe_op2_synth, 575 }, 576 .input_name = "-", 577 .samples = LIST_HEAD_INIT(inject.samples), 578 .output = { 579 .path = "-", 580 .mode = PERF_DATA_MODE_WRITE, 581 }, 582 }; 583 struct perf_data_file file = { 584 .mode = PERF_DATA_MODE_READ, 585 }; 586 int ret; 587 588 const struct option options[] = { 589 OPT_BOOLEAN('b', "build-ids", &inject.build_ids, 590 "Inject build-ids into the output stream"), 591 OPT_STRING('i', "input", &inject.input_name, "file", 592 "input file name"), 593 OPT_STRING('o', "output", &inject.output.path, "file", 594 "output file name"), 595 OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, 596 "Merge sched-stat and sched-switch for getting events " 597 "where and how long tasks slept"), 598 OPT_INCR('v', "verbose", &verbose, 599 "be more verbose (show build ids, etc)"), 600 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", 601 "kallsyms pathname"), 602 OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), 603 OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts, 604 NULL, "opts", "Instruction Tracing options", 605 itrace_parse_synth_opts), 606 OPT_END() 607 }; 608 const char * const inject_usage[] = { 609 "perf inject [<options>]", 610 NULL 611 }; 612 613 argc = parse_options(argc, argv, options, inject_usage, 0); 614 615 /* 616 * Any (unrecognized) arguments left? 617 */ 618 if (argc) 619 usage_with_options(inject_usage, options); 620 621 if (perf_data_file__open(&inject.output)) { 622 perror("failed to create output file"); 623 return -1; 624 } 625 626 inject.tool.ordered_events = inject.sched_stat; 627 628 file.path = inject.input_name; 629 inject.session = perf_session__new(&file, true, &inject.tool); 630 if (inject.session == NULL) 631 return -1; 632 633 if (symbol__init(&inject.session->header.env) < 0) 634 return -1; 635 636 ret = __cmd_inject(&inject); 637 638 perf_session__delete(inject.session); 639 640 return ret; 641 } 642