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_fork(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_fork(tool, event, sample, machine); 134 perf_event__repipe(tool, event, sample, machine); 135 136 return err; 137 } 138 139 static int perf_event__repipe_tracing_data(struct perf_tool *tool, 140 union perf_event *event, 141 struct perf_session *session) 142 { 143 int err; 144 145 perf_event__repipe_synth(tool, event); 146 err = perf_event__process_tracing_data(tool, event, session); 147 148 return err; 149 } 150 151 static int dso__read_build_id(struct dso *self) 152 { 153 if (self->has_build_id) 154 return 0; 155 156 if (filename__read_build_id(self->long_name, self->build_id, 157 sizeof(self->build_id)) > 0) { 158 self->has_build_id = true; 159 return 0; 160 } 161 162 return -1; 163 } 164 165 static int dso__inject_build_id(struct dso *self, struct perf_tool *tool, 166 struct machine *machine) 167 { 168 u16 misc = PERF_RECORD_MISC_USER; 169 int err; 170 171 if (dso__read_build_id(self) < 0) { 172 pr_debug("no build_id found for %s\n", self->long_name); 173 return -1; 174 } 175 176 if (self->kernel) 177 misc = PERF_RECORD_MISC_KERNEL; 178 179 err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe, 180 machine); 181 if (err) { 182 pr_err("Can't synthesize build_id event for %s\n", self->long_name); 183 return -1; 184 } 185 186 return 0; 187 } 188 189 static int perf_event__inject_buildid(struct perf_tool *tool, 190 union perf_event *event, 191 struct perf_sample *sample, 192 struct perf_evsel *evsel __maybe_unused, 193 struct machine *machine) 194 { 195 struct addr_location al; 196 struct thread *thread; 197 u8 cpumode; 198 199 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 200 201 thread = machine__findnew_thread(machine, sample->pid, sample->pid); 202 if (thread == NULL) { 203 pr_err("problem processing %d event, skipping it.\n", 204 event->header.type); 205 goto repipe; 206 } 207 208 thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, 209 sample->ip, &al); 210 211 if (al.map != NULL) { 212 if (!al.map->dso->hit) { 213 al.map->dso->hit = 1; 214 if (map__load(al.map, NULL) >= 0) { 215 dso__inject_build_id(al.map->dso, tool, machine); 216 /* 217 * If this fails, too bad, let the other side 218 * account this as unresolved. 219 */ 220 } else { 221 #ifdef LIBELF_SUPPORT 222 pr_warning("no symbols found in %s, maybe " 223 "install a debug package?\n", 224 al.map->dso->long_name); 225 #endif 226 } 227 } 228 } 229 230 repipe: 231 perf_event__repipe(tool, event, sample, machine); 232 return 0; 233 } 234 235 static int perf_inject__sched_process_exit(struct perf_tool *tool, 236 union perf_event *event __maybe_unused, 237 struct perf_sample *sample, 238 struct perf_evsel *evsel __maybe_unused, 239 struct machine *machine __maybe_unused) 240 { 241 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 242 struct event_entry *ent; 243 244 list_for_each_entry(ent, &inject->samples, node) { 245 if (sample->tid == ent->tid) { 246 list_del_init(&ent->node); 247 free(ent); 248 break; 249 } 250 } 251 252 return 0; 253 } 254 255 static int perf_inject__sched_switch(struct perf_tool *tool, 256 union perf_event *event, 257 struct perf_sample *sample, 258 struct perf_evsel *evsel, 259 struct machine *machine) 260 { 261 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 262 struct event_entry *ent; 263 264 perf_inject__sched_process_exit(tool, event, sample, evsel, machine); 265 266 ent = malloc(event->header.size + sizeof(struct event_entry)); 267 if (ent == NULL) { 268 color_fprintf(stderr, PERF_COLOR_RED, 269 "Not enough memory to process sched switch event!"); 270 return -1; 271 } 272 273 ent->tid = sample->tid; 274 memcpy(&ent->event, event, event->header.size); 275 list_add(&ent->node, &inject->samples); 276 return 0; 277 } 278 279 static int perf_inject__sched_stat(struct perf_tool *tool, 280 union perf_event *event __maybe_unused, 281 struct perf_sample *sample, 282 struct perf_evsel *evsel, 283 struct machine *machine) 284 { 285 struct event_entry *ent; 286 union perf_event *event_sw; 287 struct perf_sample sample_sw; 288 struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 289 u32 pid = perf_evsel__intval(evsel, sample, "pid"); 290 291 list_for_each_entry(ent, &inject->samples, node) { 292 if (pid == ent->tid) 293 goto found; 294 } 295 296 return 0; 297 found: 298 event_sw = &ent->event[0]; 299 perf_evsel__parse_sample(evsel, event_sw, &sample_sw); 300 301 sample_sw.period = sample->period; 302 sample_sw.time = sample->time; 303 perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, 304 evsel->attr.sample_regs_user, 305 evsel->attr.read_format, &sample_sw, 306 false); 307 build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); 308 return perf_event__repipe(tool, event_sw, &sample_sw, machine); 309 } 310 311 extern volatile int session_done; 312 313 static void sig_handler(int sig __maybe_unused) 314 { 315 session_done = 1; 316 } 317 318 static int perf_evsel__check_stype(struct perf_evsel *evsel, 319 u64 sample_type, const char *sample_msg) 320 { 321 struct perf_event_attr *attr = &evsel->attr; 322 const char *name = perf_evsel__name(evsel); 323 324 if (!(attr->sample_type & sample_type)) { 325 pr_err("Samples for %s event do not have %s attribute set.", 326 name, sample_msg); 327 return -EINVAL; 328 } 329 330 return 0; 331 } 332 333 static int __cmd_inject(struct perf_inject *inject) 334 { 335 struct perf_session *session; 336 int ret = -EINVAL; 337 338 signal(SIGINT, sig_handler); 339 340 if (inject->build_ids || inject->sched_stat) { 341 inject->tool.mmap = perf_event__repipe_mmap; 342 inject->tool.fork = perf_event__repipe_fork; 343 inject->tool.tracing_data = perf_event__repipe_tracing_data; 344 } 345 346 session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); 347 if (session == NULL) 348 return -ENOMEM; 349 350 if (inject->build_ids) { 351 inject->tool.sample = perf_event__inject_buildid; 352 } else if (inject->sched_stat) { 353 struct perf_evsel *evsel; 354 355 inject->tool.ordered_samples = true; 356 357 list_for_each_entry(evsel, &session->evlist->entries, node) { 358 const char *name = perf_evsel__name(evsel); 359 360 if (!strcmp(name, "sched:sched_switch")) { 361 if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) 362 return -EINVAL; 363 364 evsel->handler.func = perf_inject__sched_switch; 365 } else if (!strcmp(name, "sched:sched_process_exit")) 366 evsel->handler.func = perf_inject__sched_process_exit; 367 else if (!strncmp(name, "sched:sched_stat_", 17)) 368 evsel->handler.func = perf_inject__sched_stat; 369 } 370 } 371 372 if (!inject->pipe_output) 373 lseek(inject->output, session->header.data_offset, SEEK_SET); 374 375 ret = perf_session__process_events(session, &inject->tool); 376 377 if (!inject->pipe_output) { 378 session->header.data_size = inject->bytes_written; 379 perf_session__write_header(session, session->evlist, inject->output, true); 380 } 381 382 perf_session__delete(session); 383 384 return ret; 385 } 386 387 int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) 388 { 389 struct perf_inject inject = { 390 .tool = { 391 .sample = perf_event__repipe_sample, 392 .mmap = perf_event__repipe, 393 .comm = perf_event__repipe, 394 .fork = perf_event__repipe, 395 .exit = perf_event__repipe, 396 .lost = perf_event__repipe, 397 .read = perf_event__repipe_sample, 398 .throttle = perf_event__repipe, 399 .unthrottle = perf_event__repipe, 400 .attr = perf_event__repipe_attr, 401 .tracing_data = perf_event__repipe_op2_synth, 402 .finished_round = perf_event__repipe_op2_synth, 403 .build_id = perf_event__repipe_op2_synth, 404 }, 405 .input_name = "-", 406 .samples = LIST_HEAD_INIT(inject.samples), 407 }; 408 const char *output_name = "-"; 409 const struct option options[] = { 410 OPT_BOOLEAN('b', "build-ids", &inject.build_ids, 411 "Inject build-ids into the output stream"), 412 OPT_STRING('i', "input", &inject.input_name, "file", 413 "input file name"), 414 OPT_STRING('o', "output", &output_name, "file", 415 "output file name"), 416 OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, 417 "Merge sched-stat and sched-switch for getting events " 418 "where and how long tasks slept"), 419 OPT_INCR('v', "verbose", &verbose, 420 "be more verbose (show build ids, etc)"), 421 OPT_END() 422 }; 423 const char * const inject_usage[] = { 424 "perf inject [<options>]", 425 NULL 426 }; 427 428 argc = parse_options(argc, argv, options, inject_usage, 0); 429 430 /* 431 * Any (unrecognized) arguments left? 432 */ 433 if (argc) 434 usage_with_options(inject_usage, options); 435 436 if (!strcmp(output_name, "-")) { 437 inject.pipe_output = 1; 438 inject.output = STDOUT_FILENO; 439 } else { 440 inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, 441 S_IRUSR | S_IWUSR); 442 if (inject.output < 0) { 443 perror("failed to create output file"); 444 return -1; 445 } 446 } 447 448 if (symbol__init() < 0) 449 return -1; 450 451 return __cmd_inject(&inject); 452 } 453