4 * Builtin inject command: Examine the live mode (stdin) event stream
5 * and repipe it to stdout while optionally injecting additional
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"
20 #include "util/parse-options.h"
22 #include <linux/list.h>
25 struct perf_tool tool
;
26 struct perf_session
*session
;
29 const char *input_name
;
30 struct perf_data_file output
;
32 struct list_head samples
;
36 struct list_head node
;
38 union perf_event event
[0];
41 static int perf_event__repipe_synth(struct perf_tool
*tool
,
42 union perf_event
*event
)
44 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
, tool
);
47 size
= perf_data_file__write(&inject
->output
, event
,
52 inject
->bytes_written
+= size
;
56 static int perf_event__repipe_oe_synth(struct perf_tool
*tool
,
57 union perf_event
*event
,
58 struct ordered_events
*oe __maybe_unused
)
60 return perf_event__repipe_synth(tool
, event
);
63 static int perf_event__repipe_op2_synth(struct perf_tool
*tool
,
64 union perf_event
*event
,
65 struct perf_session
*session
68 return perf_event__repipe_synth(tool
, event
);
71 static int perf_event__repipe_attr(struct perf_tool
*tool
,
72 union perf_event
*event
,
73 struct perf_evlist
**pevlist
)
75 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
,
79 ret
= perf_event__process_attr(tool
, event
, pevlist
);
83 if (!inject
->output
.is_pipe
)
86 return perf_event__repipe_synth(tool
, event
);
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
)
94 return perf_event__repipe_synth(tool
, event
);
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
);
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
)
109 if (evsel
->handler
) {
110 inject_handler f
= evsel
->handler
;
111 return f(tool
, event
, sample
, evsel
, machine
);
114 build_id__mark_dso_hit(tool
, event
, sample
, evsel
, machine
);
116 return perf_event__repipe_synth(tool
, event
);
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
)
126 err
= perf_event__process_mmap(tool
, event
, sample
, machine
);
127 perf_event__repipe(tool
, event
, sample
, machine
);
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
)
139 err
= perf_event__process_mmap2(tool
, event
, sample
, machine
);
140 perf_event__repipe(tool
, event
, sample
, machine
);
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
)
152 err
= perf_event__process_fork(tool
, event
, sample
, machine
);
153 perf_event__repipe(tool
, event
, sample
, machine
);
158 static int perf_event__repipe_tracing_data(struct perf_tool
*tool
,
159 union perf_event
*event
,
160 struct perf_session
*session
)
164 perf_event__repipe_synth(tool
, event
);
165 err
= perf_event__process_tracing_data(tool
, event
, session
);
170 static int dso__read_build_id(struct dso
*dso
)
172 if (dso
->has_build_id
)
175 if (filename__read_build_id(dso
->long_name
, dso
->build_id
,
176 sizeof(dso
->build_id
)) > 0) {
177 dso
->has_build_id
= true;
184 static int dso__inject_build_id(struct dso
*dso
, struct perf_tool
*tool
,
185 struct machine
*machine
)
187 u16 misc
= PERF_RECORD_MISC_USER
;
190 if (dso__read_build_id(dso
) < 0) {
191 pr_debug("no build_id found for %s\n", dso
->long_name
);
196 misc
= PERF_RECORD_MISC_KERNEL
;
198 err
= perf_event__synthesize_build_id(tool
, dso
, misc
, perf_event__repipe
,
201 pr_err("Can't synthesize build_id event for %s\n", dso
->long_name
);
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
)
214 struct addr_location al
;
215 struct thread
*thread
;
218 cpumode
= event
->header
.misc
& PERF_RECORD_MISC_CPUMODE_MASK
;
220 thread
= machine__findnew_thread(machine
, sample
->pid
, sample
->tid
);
221 if (thread
== NULL
) {
222 pr_err("problem processing %d event, skipping it.\n",
227 thread__find_addr_map(thread
, cpumode
, MAP__FUNCTION
, sample
->ip
, &al
);
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
);
235 * If this fails, too bad, let the other side
236 * account this as unresolved.
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
);
249 perf_event__repipe(tool
, event
, sample
, machine
);
253 static 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
)
259 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
, tool
);
260 struct event_entry
*ent
;
262 list_for_each_entry(ent
, &inject
->samples
, node
) {
263 if (sample
->tid
== ent
->tid
) {
264 list_del_init(&ent
->node
);
273 static 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
)
279 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
, tool
);
280 struct event_entry
*ent
;
282 perf_inject__sched_process_exit(tool
, event
, sample
, evsel
, machine
);
284 ent
= malloc(event
->header
.size
+ sizeof(struct event_entry
));
286 color_fprintf(stderr
, PERF_COLOR_RED
,
287 "Not enough memory to process sched switch event!");
291 ent
->tid
= sample
->tid
;
292 memcpy(&ent
->event
, event
, event
->header
.size
);
293 list_add(&ent
->node
, &inject
->samples
);
297 static 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
)
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");
309 list_for_each_entry(ent
, &inject
->samples
, node
) {
316 event_sw
= &ent
->event
[0];
317 perf_evsel__parse_sample(evsel
, event_sw
, &sample_sw
);
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
,
324 build_id__mark_dso_hit(tool
, event_sw
, &sample_sw
, evsel
, machine
);
325 return perf_event__repipe(tool
, event_sw
, &sample_sw
, machine
);
328 static void sig_handler(int sig __maybe_unused
)
333 static int perf_evsel__check_stype(struct perf_evsel
*evsel
,
334 u64 sample_type
, const char *sample_msg
)
336 struct perf_event_attr
*attr
= &evsel
->attr
;
337 const char *name
= perf_evsel__name(evsel
);
339 if (!(attr
->sample_type
& sample_type
)) {
340 pr_err("Samples for %s event do not have %s attribute set.",
348 static int __cmd_inject(struct perf_inject
*inject
)
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
);
355 signal(SIGINT
, sig_handler
);
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
;
364 if (inject
->build_ids
) {
365 inject
->tool
.sample
= perf_event__inject_buildid
;
366 } else if (inject
->sched_stat
) {
367 struct perf_evsel
*evsel
;
369 evlist__for_each(session
->evlist
, evsel
) {
370 const char *name
= perf_evsel__name(evsel
);
372 if (!strcmp(name
, "sched:sched_switch")) {
373 if (perf_evsel__check_stype(evsel
, PERF_SAMPLE_TID
, "TID"))
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
;
384 if (!file_out
->is_pipe
)
385 lseek(fd
, session
->header
.data_offset
, SEEK_SET
);
387 ret
= perf_session__process_events(session
);
389 if (!file_out
->is_pipe
) {
390 if (inject
->build_ids
)
391 perf_header__set_feat(&session
->header
,
393 session
->header
.data_size
= inject
->bytes_written
;
394 perf_session__write_header(session
, session
->evlist
, fd
, true);
400 int cmd_inject(int argc
, const char **argv
, const char *prefix __maybe_unused
)
402 struct perf_inject inject
= {
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
,
421 .samples
= LIST_HEAD_INIT(inject
.samples
),
424 .mode
= PERF_DATA_MODE_WRITE
,
427 struct perf_data_file file
= {
428 .mode
= PERF_DATA_MODE_READ
,
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",
437 OPT_STRING('o', "output", &inject
.output
.path
, "file",
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"),
449 const char * const inject_usage
[] = {
450 "perf inject [<options>]",
454 argc
= parse_options(argc
, argv
, options
, inject_usage
, 0);
457 * Any (unrecognized) arguments left?
460 usage_with_options(inject_usage
, options
);
462 if (perf_data_file__open(&inject
.output
)) {
463 perror("failed to create output file");
467 inject
.tool
.ordered_events
= inject
.sched_stat
;
469 file
.path
= inject
.input_name
;
470 inject
.session
= perf_session__new(&file
, true, &inject
.tool
);
471 if (inject
.session
== NULL
)
474 if (symbol__init(&inject
.session
->header
.env
) < 0)
477 ret
= __cmd_inject(&inject
);
479 perf_session__delete(inject
.session
);