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