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 static void sig_handler(int sig __maybe_unused) 325 { 326 session_done = 1; 327 } 328 329 static int perf_evsel__check_stype(struct perf_evsel *evsel, 330 u64 sample_type, const char *sample_msg) 331 { 332 struct perf_event_attr *attr = &evsel->attr; 333 const char *name = perf_evsel__name(evsel); 334 335 if (!(attr->sample_type & sample_type)) { 336 pr_err("Samples for %s event do not have %s attribute set.", 337 name, sample_msg); 338 return -EINVAL; 339 } 340 341 return 0; 342 } 343 344 static int __cmd_inject(struct perf_inject *inject) 345 { 346 struct perf_session *session; 347 int ret = -EINVAL; 348 349 signal(SIGINT, sig_handler); 350 351 if (inject->build_ids || inject->sched_stat) { 352 inject->tool.mmap = perf_event__repipe_mmap; 353 inject->tool.mmap2 = perf_event__repipe_mmap2; 354 inject->tool.fork = perf_event__repipe_fork; 355 inject->tool.tracing_data = perf_event__repipe_tracing_data; 356 } 357 358 session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); 359 if (session == NULL) 360 return -ENOMEM; 361 362 if (inject->build_ids) { 363 inject->tool.sample = perf_event__inject_buildid; 364 } else if (inject->sched_stat) { 365 struct perf_evsel *evsel; 366 367 inject->tool.ordered_samples = true; 368 369 list_for_each_entry(evsel, &session->evlist->entries, node) { 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.func = perf_inject__sched_switch; 377 } else if (!strcmp(name, "sched:sched_process_exit")) 378 evsel->handler.func = perf_inject__sched_process_exit; 379 else if (!strncmp(name, "sched:sched_stat_", 17)) 380 evsel->handler.func = perf_inject__sched_stat; 381 } 382 } 383 384 if (!inject->pipe_output) 385 lseek(inject->output, session->header.data_offset, SEEK_SET); 386 387 ret = perf_session__process_events(session, &inject->tool); 388 389 if (!inject->pipe_output) { 390 session->header.data_size = inject->bytes_written; 391 perf_session__write_header(session, session->evlist, inject->output, true); 392 } 393 394 perf_session__delete(session); 395 396 return ret; 397 } 398 399 int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) 400 { 401 struct perf_inject inject = { 402 .tool = { 403 .sample = perf_event__repipe_sample, 404 .mmap = perf_event__repipe, 405 .mmap2 = perf_event__repipe, 406 .comm = perf_event__repipe, 407 .fork = perf_event__repipe, 408 .exit = perf_event__repipe, 409 .lost = perf_event__repipe, 410 .read = perf_event__repipe_sample, 411 .throttle = perf_event__repipe, 412 .unthrottle = perf_event__repipe, 413 .attr = perf_event__repipe_attr, 414 .tracing_data = perf_event__repipe_op2_synth, 415 .finished_round = perf_event__repipe_op2_synth, 416 .build_id = perf_event__repipe_op2_synth, 417 }, 418 .input_name = "-", 419 .samples = LIST_HEAD_INIT(inject.samples), 420 }; 421 const char *output_name = "-"; 422 const struct option options[] = { 423 OPT_BOOLEAN('b', "build-ids", &inject.build_ids, 424 "Inject build-ids into the output stream"), 425 OPT_STRING('i', "input", &inject.input_name, "file", 426 "input file name"), 427 OPT_STRING('o', "output", &output_name, "file", 428 "output file name"), 429 OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, 430 "Merge sched-stat and sched-switch for getting events " 431 "where and how long tasks slept"), 432 OPT_INCR('v', "verbose", &verbose, 433 "be more verbose (show build ids, etc)"), 434 OPT_END() 435 }; 436 const char * const inject_usage[] = { 437 "perf inject [<options>]", 438 NULL 439 }; 440 441 argc = parse_options(argc, argv, options, inject_usage, 0); 442 443 /* 444 * Any (unrecognized) arguments left? 445 */ 446 if (argc) 447 usage_with_options(inject_usage, options); 448 449 if (!strcmp(output_name, "-")) { 450 inject.pipe_output = 1; 451 inject.output = STDOUT_FILENO; 452 } else { 453 inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, 454 S_IRUSR | S_IWUSR); 455 if (inject.output < 0) { 456 perror("failed to create output file"); 457 return -1; 458 } 459 } 460 461 if (symbol__init() < 0) 462 return -1; 463 464 return __cmd_inject(&inject); 465 } 466