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 20#include "util/parse-options.h" 21 22#include <linux/list.h> 23 24struct perf_inject { 25 struct perf_tool tool; 26 struct perf_session *session; 27 bool build_ids; 28 bool sched_stat; 29 const char *input_name; 30 struct perf_data_file output; 31 u64 bytes_written; 32 struct list_head samples; 33}; 34 35struct event_entry { 36 struct list_head node; 37 u32 tid; 38 union perf_event event[0]; 39}; 40 41static int perf_event__repipe_synth(struct perf_tool *tool, 42 union perf_event *event) 43{ 44 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 45 ssize_t size; 46 47 size = perf_data_file__write(&inject->output, event, 48 event->header.size); 49 if (size < 0) 50 return -errno; 51 52 inject->bytes_written += size; 53 return 0; 54} 55 56static int perf_event__repipe_oe_synth(struct perf_tool *tool, 57 union perf_event *event, 58 struct ordered_events *oe __maybe_unused) 59{ 60 return perf_event__repipe_synth(tool, event); 61} 62 63static int perf_event__repipe_op2_synth(struct perf_tool *tool, 64 union perf_event *event, 65 struct perf_session *session 66 __maybe_unused) 67{ 68 return perf_event__repipe_synth(tool, event); 69} 70 71static int perf_event__repipe_attr(struct perf_tool *tool, 72 union perf_event *event, 73 struct perf_evlist **pevlist) 74{ 75 struct perf_inject *inject = container_of(tool, struct perf_inject, 76 tool); 77 int ret; 78 79 ret = perf_event__process_attr(tool, event, pevlist); 80 if (ret) 81 return ret; 82 83 if (!inject->output.is_pipe) 84 return 0; 85 86 return perf_event__repipe_synth(tool, event); 87} 88 89static int perf_event__repipe(struct perf_tool *tool, 90 union perf_event *event, 91 struct perf_sample *sample __maybe_unused, 92 struct machine *machine __maybe_unused) 93{ 94 return perf_event__repipe_synth(tool, event); 95} 96 97typedef int (*inject_handler)(struct perf_tool *tool, 98 union perf_event *event, 99 struct perf_sample *sample, 100 struct perf_evsel *evsel, 101 struct machine *machine); 102 103static int perf_event__repipe_sample(struct perf_tool *tool, 104 union perf_event *event, 105 struct perf_sample *sample, 106 struct perf_evsel *evsel, 107 struct machine *machine) 108{ 109 if (evsel->handler) { 110 inject_handler f = evsel->handler; 111 return f(tool, event, sample, evsel, machine); 112 } 113 114 build_id__mark_dso_hit(tool, event, sample, evsel, machine); 115 116 return perf_event__repipe_synth(tool, event); 117} 118 119static int perf_event__repipe_mmap(struct perf_tool *tool, 120 union perf_event *event, 121 struct perf_sample *sample, 122 struct machine *machine) 123{ 124 int err; 125 126 err = perf_event__process_mmap(tool, event, sample, machine); 127 perf_event__repipe(tool, event, sample, machine); 128 129 return err; 130} 131 132static int perf_event__repipe_mmap2(struct perf_tool *tool, 133 union perf_event *event, 134 struct perf_sample *sample, 135 struct machine *machine) 136{ 137 int err; 138 139 err = perf_event__process_mmap2(tool, event, sample, machine); 140 perf_event__repipe(tool, event, sample, machine); 141 142 return err; 143} 144 145static int perf_event__repipe_fork(struct perf_tool *tool, 146 union perf_event *event, 147 struct perf_sample *sample, 148 struct machine *machine) 149{ 150 int err; 151 152 err = perf_event__process_fork(tool, event, sample, machine); 153 perf_event__repipe(tool, event, sample, machine); 154 155 return err; 156} 157 158static int perf_event__repipe_tracing_data(struct perf_tool *tool, 159 union perf_event *event, 160 struct perf_session *session) 161{ 162 int err; 163 164 perf_event__repipe_synth(tool, event); 165 err = perf_event__process_tracing_data(tool, event, session); 166 167 return err; 168} 169 170static int dso__read_build_id(struct dso *dso) 171{ 172 if (dso->has_build_id) 173 return 0; 174 175 if (filename__read_build_id(dso->long_name, dso->build_id, 176 sizeof(dso->build_id)) > 0) { 177 dso->has_build_id = true; 178 return 0; 179 } 180 181 return -1; 182} 183 184static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool, 185 struct machine *machine) 186{ 187 u16 misc = PERF_RECORD_MISC_USER; 188 int err; 189 190 if (dso__read_build_id(dso) < 0) { 191 pr_debug("no build_id found for %s\n", dso->long_name); 192 return -1; 193 } 194 195 if (dso->kernel) 196 misc = PERF_RECORD_MISC_KERNEL; 197 198 err = perf_event__synthesize_build_id(tool, dso, misc, perf_event__repipe, 199 machine); 200 if (err) { 201 pr_err("Can't synthesize build_id event for %s\n", dso->long_name); 202 return -1; 203 } 204 205 return 0; 206} 207 208static int perf_event__inject_buildid(struct perf_tool *tool, 209 union perf_event *event, 210 struct perf_sample *sample, 211 struct perf_evsel *evsel __maybe_unused, 212 struct machine *machine) 213{ 214 struct addr_location al; 215 struct thread *thread; 216 u8 cpumode; 217 218 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 219 220 thread = machine__findnew_thread(machine, sample->pid, sample->tid); 221 if (thread == NULL) { 222 pr_err("problem processing %d event, skipping it.\n", 223 event->header.type); 224 goto repipe; 225 } 226 227 thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); 228 229 if (al.map != NULL) { 230 if (!al.map->dso->hit) { 231 al.map->dso->hit = 1; 232 if (map__load(al.map, NULL) >= 0) { 233 dso__inject_build_id(al.map->dso, tool, machine); 234 /* 235 * If this fails, too bad, let the other side 236 * account this as unresolved. 237 */ 238 } else { 239#ifdef HAVE_LIBELF_SUPPORT 240 pr_warning("no symbols found in %s, maybe " 241 "install a debug package?\n", 242 al.map->dso->long_name); 243#endif 244 } 245 } 246 } 247 248repipe: 249 perf_event__repipe(tool, event, sample, machine); 250 return 0; 251} 252 253static int perf_inject__sched_process_exit(struct perf_tool *tool, 254 union perf_event *event __maybe_unused, 255 struct perf_sample *sample, 256 struct perf_evsel *evsel __maybe_unused, 257 struct machine *machine __maybe_unused) 258{ 259 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 260 struct event_entry *ent; 261 262 list_for_each_entry(ent, &inject->samples, node) { 263 if (sample->tid == ent->tid) { 264 list_del_init(&ent->node); 265 free(ent); 266 break; 267 } 268 } 269 270 return 0; 271} 272 273static int perf_inject__sched_switch(struct perf_tool *tool, 274 union perf_event *event, 275 struct perf_sample *sample, 276 struct perf_evsel *evsel, 277 struct machine *machine) 278{ 279 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 280 struct event_entry *ent; 281 282 perf_inject__sched_process_exit(tool, event, sample, evsel, machine); 283 284 ent = malloc(event->header.size + sizeof(struct event_entry)); 285 if (ent == NULL) { 286 color_fprintf(stderr, PERF_COLOR_RED, 287 "Not enough memory to process sched switch event!"); 288 return -1; 289 } 290 291 ent->tid = sample->tid; 292 memcpy(&ent->event, event, event->header.size); 293 list_add(&ent->node, &inject->samples); 294 return 0; 295} 296 297static int perf_inject__sched_stat(struct perf_tool *tool, 298 union perf_event *event __maybe_unused, 299 struct perf_sample *sample, 300 struct perf_evsel *evsel, 301 struct machine *machine) 302{ 303 struct event_entry *ent; 304 union perf_event *event_sw; 305 struct perf_sample sample_sw; 306 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 307 u32 pid = perf_evsel__intval(evsel, sample, "pid"); 308 309 list_for_each_entry(ent, &inject->samples, node) { 310 if (pid == ent->tid) 311 goto found; 312 } 313 314 return 0; 315found: 316 event_sw = &ent->event[0]; 317 perf_evsel__parse_sample(evsel, event_sw, &sample_sw); 318 319 sample_sw.period = sample->period; 320 sample_sw.time = sample->time; 321 perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, 322 evsel->attr.read_format, &sample_sw, 323 false); 324 build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); 325 return perf_event__repipe(tool, event_sw, &sample_sw, machine); 326} 327 328static void sig_handler(int sig __maybe_unused) 329{ 330 session_done = 1; 331} 332 333static int perf_evsel__check_stype(struct perf_evsel *evsel, 334 u64 sample_type, const char *sample_msg) 335{ 336 struct perf_event_attr *attr = &evsel->attr; 337 const char *name = perf_evsel__name(evsel); 338 339 if (!(attr->sample_type & sample_type)) { 340 pr_err("Samples for %s event do not have %s attribute set.", 341 name, sample_msg); 342 return -EINVAL; 343 } 344 345 return 0; 346} 347 348static int __cmd_inject(struct perf_inject *inject) 349{ 350 int ret = -EINVAL; 351 struct perf_session *session = inject->session; 352 struct perf_data_file *file_out = &inject->output; 353 int fd = perf_data_file__fd(file_out); 354 355 signal(SIGINT, sig_handler); 356 357 if (inject->build_ids || inject->sched_stat) { 358 inject->tool.mmap = perf_event__repipe_mmap; 359 inject->tool.mmap2 = perf_event__repipe_mmap2; 360 inject->tool.fork = perf_event__repipe_fork; 361 inject->tool.tracing_data = perf_event__repipe_tracing_data; 362 } 363 364 if (inject->build_ids) { 365 inject->tool.sample = perf_event__inject_buildid; 366 } else if (inject->sched_stat) { 367 struct perf_evsel *evsel; 368 369 evlist__for_each(session->evlist, evsel) { 370 const char *name = perf_evsel__name(evsel); 371 372 if (!strcmp(name, "sched:sched_switch")) { 373 if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) 374 return -EINVAL; 375 376 evsel->handler = perf_inject__sched_switch; 377 } else if (!strcmp(name, "sched:sched_process_exit")) 378 evsel->handler = perf_inject__sched_process_exit; 379 else if (!strncmp(name, "sched:sched_stat_", 17)) 380 evsel->handler = perf_inject__sched_stat; 381 } 382 } 383 384 if (!file_out->is_pipe) 385 lseek(fd, session->header.data_offset, SEEK_SET); 386 387 ret = perf_session__process_events(session); 388 389 if (!file_out->is_pipe) { 390 if (inject->build_ids) 391 perf_header__set_feat(&session->header, 392 HEADER_BUILD_ID); 393 session->header.data_size = inject->bytes_written; 394 perf_session__write_header(session, session->evlist, fd, true); 395 } 396 397 return ret; 398} 399 400int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) 401{ 402 struct perf_inject inject = { 403 .tool = { 404 .sample = perf_event__repipe_sample, 405 .mmap = perf_event__repipe, 406 .mmap2 = perf_event__repipe, 407 .comm = perf_event__repipe, 408 .fork = perf_event__repipe, 409 .exit = perf_event__repipe, 410 .lost = perf_event__repipe, 411 .read = perf_event__repipe_sample, 412 .throttle = perf_event__repipe, 413 .unthrottle = perf_event__repipe, 414 .attr = perf_event__repipe_attr, 415 .tracing_data = perf_event__repipe_op2_synth, 416 .finished_round = perf_event__repipe_oe_synth, 417 .build_id = perf_event__repipe_op2_synth, 418 .id_index = perf_event__repipe_op2_synth, 419 }, 420 .input_name = "-", 421 .samples = LIST_HEAD_INIT(inject.samples), 422 .output = { 423 .path = "-", 424 .mode = PERF_DATA_MODE_WRITE, 425 }, 426 }; 427 struct perf_data_file file = { 428 .mode = PERF_DATA_MODE_READ, 429 }; 430 int ret; 431 432 const struct option options[] = { 433 OPT_BOOLEAN('b', "build-ids", &inject.build_ids, 434 "Inject build-ids into the output stream"), 435 OPT_STRING('i', "input", &inject.input_name, "file", 436 "input file name"), 437 OPT_STRING('o', "output", &inject.output.path, "file", 438 "output file name"), 439 OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, 440 "Merge sched-stat and sched-switch for getting events " 441 "where and how long tasks slept"), 442 OPT_INCR('v', "verbose", &verbose, 443 "be more verbose (show build ids, etc)"), 444 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", 445 "kallsyms pathname"), 446 OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), 447 OPT_END() 448 }; 449 const char * const inject_usage[] = { 450 "perf inject [<options>]", 451 NULL 452 }; 453 454 argc = parse_options(argc, argv, options, inject_usage, 0); 455 456 /* 457 * Any (unrecognized) arguments left? 458 */ 459 if (argc) 460 usage_with_options(inject_usage, options); 461 462 if (perf_data_file__open(&inject.output)) { 463 perror("failed to create output file"); 464 return -1; 465 } 466 467 inject.tool.ordered_events = inject.sched_stat; 468 469 file.path = inject.input_name; 470 inject.session = perf_session__new(&file, true, &inject.tool); 471 if (inject.session == NULL) 472 return -1; 473 474 if (symbol__init(&inject.session->header.env) < 0) 475 return -1; 476 477 ret = __cmd_inject(&inject); 478 479 perf_session__delete(inject.session); 480 481 return ret; 482} 483