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 } 624