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 24 struct perf_inject { 25 struct perf_tool tool; 26 bool build_ids; 27 bool sched_stat; 28 const char *input_name; 29 int pipe_output, 30 output; 31 u64 bytes_written; 32 struct list_head samples; 33 }; 34 35 struct event_entry { 36 struct list_head node; 37 u32 tid; 38 union perf_event event[0]; 39 }; 40 41 static 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 uint32_t size; 46 void *buf = event; 47 48 size = event->header.size; 49 50 while (size) { 51 int ret = write(inject->output, buf, size); 52 if (ret < 0) 53 return -errno; 54 55 size -= ret; 56 buf += ret; 57 inject->bytes_written += ret; 58 } 59 60 return 0; 61 } 62 63 static 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 71 static 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->pipe_output) 84 return 0; 85 86 return perf_event__repipe_synth(tool, event); 87 } 88 89 static 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 97 typedef 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 103 static 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 119 static 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 132 static 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 145 static 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 158 static 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 170 static 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 184 static 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 208 static 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->pid); 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, machine, cpumode, MAP__FUNCTION, 228 sample->ip, &al); 229 230 if (al.map != NULL) { 231 if (!al.map->dso->hit) { 232 al.map->dso->hit = 1; 233 if (map__load(al.map, NULL) >= 0) { 234 dso__inject_build_id(al.map->dso, tool, machine); 235 /* 236 * If this fails, too bad, let the other side 237 * account this as unresolved. 238 */ 239 } else { 240 #ifdef HAVE_LIBELF_SUPPORT 241 pr_warning("no symbols found in %s, maybe " 242 "install a debug package?\n", 243 al.map->dso->long_name); 244 #endif 245 } 246 } 247 } 248 249 repipe: 250 perf_event__repipe(tool, event, sample, machine); 251 return 0; 252 } 253 254 static int perf_inject__sched_process_exit(struct perf_tool *tool, 255 union perf_event *event __maybe_unused, 256 struct perf_sample *sample, 257 struct perf_evsel *evsel __maybe_unused, 258 struct machine *machine __maybe_unused) 259 { 260 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 261 struct event_entry *ent; 262 263 list_for_each_entry(ent, &inject->samples, node) { 264 if (sample->tid == ent->tid) { 265 list_del_init(&ent->node); 266 free(ent); 267 break; 268 } 269 } 270 271 return 0; 272 } 273 274 static int perf_inject__sched_switch(struct perf_tool *tool, 275 union perf_event *event, 276 struct perf_sample *sample, 277 struct perf_evsel *evsel, 278 struct machine *machine) 279 { 280 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 281 struct event_entry *ent; 282 283 perf_inject__sched_process_exit(tool, event, sample, evsel, machine); 284 285 ent = malloc(event->header.size + sizeof(struct event_entry)); 286 if (ent == NULL) { 287 color_fprintf(stderr, PERF_COLOR_RED, 288 "Not enough memory to process sched switch event!"); 289 return -1; 290 } 291 292 ent->tid = sample->tid; 293 memcpy(&ent->event, event, event->header.size); 294 list_add(&ent->node, &inject->samples); 295 return 0; 296 } 297 298 static int perf_inject__sched_stat(struct perf_tool *tool, 299 union perf_event *event __maybe_unused, 300 struct perf_sample *sample, 301 struct perf_evsel *evsel, 302 struct machine *machine) 303 { 304 struct event_entry *ent; 305 union perf_event *event_sw; 306 struct perf_sample sample_sw; 307 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 308 u32 pid = perf_evsel__intval(evsel, sample, "pid"); 309 310 list_for_each_entry(ent, &inject->samples, node) { 311 if (pid == ent->tid) 312 goto found; 313 } 314 315 return 0; 316 found: 317 event_sw = &ent->event[0]; 318 perf_evsel__parse_sample(evsel, event_sw, &sample_sw); 319 320 sample_sw.period = sample->period; 321 sample_sw.time = sample->time; 322 perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, 323 evsel->attr.sample_regs_user, 324 evsel->attr.read_format, &sample_sw, 325 false); 326 build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); 327 return perf_event__repipe(tool, event_sw, &sample_sw, machine); 328 } 329 330 static void sig_handler(int sig __maybe_unused) 331 { 332 session_done = 1; 333 } 334 335 static int perf_evsel__check_stype(struct perf_evsel *evsel, 336 u64 sample_type, const char *sample_msg) 337 { 338 struct perf_event_attr *attr = &evsel->attr; 339 const char *name = perf_evsel__name(evsel); 340 341 if (!(attr->sample_type & sample_type)) { 342 pr_err("Samples for %s event do not have %s attribute set.", 343 name, sample_msg); 344 return -EINVAL; 345 } 346 347 return 0; 348 } 349 350 static int __cmd_inject(struct perf_inject *inject) 351 { 352 struct perf_session *session; 353 int ret = -EINVAL; 354 struct perf_data_file file = { 355 .path = inject->input_name, 356 .mode = PERF_DATA_MODE_READ, 357 }; 358 359 signal(SIGINT, sig_handler); 360 361 if (inject->build_ids || inject->sched_stat) { 362 inject->tool.mmap = perf_event__repipe_mmap; 363 inject->tool.mmap2 = perf_event__repipe_mmap2; 364 inject->tool.fork = perf_event__repipe_fork; 365 inject->tool.tracing_data = perf_event__repipe_tracing_data; 366 } 367 368 session = perf_session__new(&file, true, &inject->tool); 369 if (session == NULL) 370 return -ENOMEM; 371 372 if (inject->build_ids) { 373 inject->tool.sample = perf_event__inject_buildid; 374 } else if (inject->sched_stat) { 375 struct perf_evsel *evsel; 376 377 inject->tool.ordered_samples = true; 378 379 list_for_each_entry(evsel, &session->evlist->entries, node) { 380 const char *name = perf_evsel__name(evsel); 381 382 if (!strcmp(name, "sched:sched_switch")) { 383 if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) 384 return -EINVAL; 385 386 evsel->handler = perf_inject__sched_switch; 387 } else if (!strcmp(name, "sched:sched_process_exit")) 388 evsel->handler = perf_inject__sched_process_exit; 389 else if (!strncmp(name, "sched:sched_stat_", 17)) 390 evsel->handler = perf_inject__sched_stat; 391 } 392 } 393 394 if (!inject->pipe_output) 395 lseek(inject->output, session->header.data_offset, SEEK_SET); 396 397 ret = perf_session__process_events(session, &inject->tool); 398 399 if (!inject->pipe_output) { 400 session->header.data_size = inject->bytes_written; 401 perf_session__write_header(session, session->evlist, inject->output, true); 402 } 403 404 perf_session__delete(session); 405 406 return ret; 407 } 408 409 int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) 410 { 411 struct perf_inject inject = { 412 .tool = { 413 .sample = perf_event__repipe_sample, 414 .mmap = perf_event__repipe, 415 .mmap2 = perf_event__repipe, 416 .comm = perf_event__repipe, 417 .fork = perf_event__repipe, 418 .exit = perf_event__repipe, 419 .lost = perf_event__repipe, 420 .read = perf_event__repipe_sample, 421 .throttle = perf_event__repipe, 422 .unthrottle = perf_event__repipe, 423 .attr = perf_event__repipe_attr, 424 .tracing_data = perf_event__repipe_op2_synth, 425 .finished_round = perf_event__repipe_op2_synth, 426 .build_id = perf_event__repipe_op2_synth, 427 }, 428 .input_name = "-", 429 .samples = LIST_HEAD_INIT(inject.samples), 430 }; 431 const char *output_name = "-"; 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", &output_name, "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_END() 445 }; 446 const char * const inject_usage[] = { 447 "perf inject [<options>]", 448 NULL 449 }; 450 451 argc = parse_options(argc, argv, options, inject_usage, 0); 452 453 /* 454 * Any (unrecognized) arguments left? 455 */ 456 if (argc) 457 usage_with_options(inject_usage, options); 458 459 if (!strcmp(output_name, "-")) { 460 inject.pipe_output = 1; 461 inject.output = STDOUT_FILENO; 462 } else { 463 inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, 464 S_IRUSR | S_IWUSR); 465 if (inject.output < 0) { 466 perror("failed to create output file"); 467 return -1; 468 } 469 } 470 471 if (symbol__init() < 0) 472 return -1; 473 474 return __cmd_inject(&inject); 475 } 476