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"
19 #include "util/parse-options.h"
21 #include <linux/list.h>
24 struct perf_tool tool
;
27 const char *input_name
;
31 struct list_head samples
;
35 struct list_head node
;
37 union perf_event event
[0];
40 static int perf_event__repipe_synth(struct perf_tool
*tool
,
41 union perf_event
*event
)
43 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
, tool
);
47 size
= event
->header
.size
;
50 int ret
= write(inject
->output
, buf
, size
);
56 inject
->bytes_written
+= ret
;
62 static int perf_event__repipe_op2_synth(struct perf_tool
*tool
,
63 union perf_event
*event
,
64 struct perf_session
*session
67 return perf_event__repipe_synth(tool
, event
);
70 static int perf_event__repipe_attr(struct perf_tool
*tool
,
71 union perf_event
*event
,
72 struct perf_evlist
**pevlist
)
76 ret
= perf_event__process_attr(tool
, event
, pevlist
);
80 return perf_event__repipe_synth(tool
, event
);
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
)
88 return perf_event__repipe_synth(tool
, event
);
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
);
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
)
103 if (evsel
->handler
.func
) {
104 inject_handler f
= evsel
->handler
.func
;
105 return f(tool
, event
, sample
, evsel
, machine
);
108 build_id__mark_dso_hit(tool
, event
, sample
, evsel
, machine
);
110 return perf_event__repipe_synth(tool
, event
);
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
)
120 err
= perf_event__process_mmap(tool
, event
, sample
, machine
);
121 perf_event__repipe(tool
, event
, sample
, machine
);
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
)
133 err
= perf_event__process_fork(tool
, event
, sample
, machine
);
134 perf_event__repipe(tool
, event
, sample
, machine
);
139 static int perf_event__repipe_tracing_data(struct perf_tool
*tool
,
140 union perf_event
*event
,
141 struct perf_session
*session
)
145 perf_event__repipe_synth(tool
, event
);
146 err
= perf_event__process_tracing_data(tool
, event
, session
);
151 static int dso__read_build_id(struct dso
*self
)
153 if (self
->has_build_id
)
156 if (filename__read_build_id(self
->long_name
, self
->build_id
,
157 sizeof(self
->build_id
)) > 0) {
158 self
->has_build_id
= true;
165 static int dso__inject_build_id(struct dso
*self
, struct perf_tool
*tool
,
166 struct machine
*machine
)
168 u16 misc
= PERF_RECORD_MISC_USER
;
171 if (dso__read_build_id(self
) < 0) {
172 pr_debug("no build_id found for %s\n", self
->long_name
);
177 misc
= PERF_RECORD_MISC_KERNEL
;
179 err
= perf_event__synthesize_build_id(tool
, self
, misc
, perf_event__repipe
,
182 pr_err("Can't synthesize build_id event for %s\n", self
->long_name
);
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
)
195 struct addr_location al
;
196 struct thread
*thread
;
199 cpumode
= event
->header
.misc
& PERF_RECORD_MISC_CPUMODE_MASK
;
201 thread
= machine__findnew_thread(machine
, sample
->pid
, sample
->pid
);
202 if (thread
== NULL
) {
203 pr_err("problem processing %d event, skipping it.\n",
208 thread__find_addr_map(thread
, machine
, cpumode
, MAP__FUNCTION
,
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
);
217 * If this fails, too bad, let the other side
218 * account this as unresolved.
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
);
231 perf_event__repipe(tool
, event
, sample
, machine
);
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
)
241 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
, tool
);
242 struct event_entry
*ent
;
244 list_for_each_entry(ent
, &inject
->samples
, node
) {
245 if (sample
->tid
== ent
->tid
) {
246 list_del_init(&ent
->node
);
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
)
261 struct perf_inject
*inject
= container_of(tool
, struct perf_inject
, tool
);
262 struct event_entry
*ent
;
264 perf_inject__sched_process_exit(tool
, event
, sample
, evsel
, machine
);
266 ent
= malloc(event
->header
.size
+ sizeof(struct event_entry
));
268 color_fprintf(stderr
, PERF_COLOR_RED
,
269 "Not enough memory to process sched switch event!");
273 ent
->tid
= sample
->tid
;
274 memcpy(&ent
->event
, event
, event
->header
.size
);
275 list_add(&ent
->node
, &inject
->samples
);
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
)
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");
291 list_for_each_entry(ent
, &inject
->samples
, node
) {
298 event_sw
= &ent
->event
[0];
299 perf_evsel__parse_sample(evsel
, event_sw
, &sample_sw
);
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
,
307 build_id__mark_dso_hit(tool
, event_sw
, &sample_sw
, evsel
, machine
);
308 return perf_event__repipe(tool
, event_sw
, &sample_sw
, machine
);
311 extern volatile int session_done
;
313 static void sig_handler(int sig __maybe_unused
)
318 static int perf_evsel__check_stype(struct perf_evsel
*evsel
,
319 u64 sample_type
, const char *sample_msg
)
321 struct perf_event_attr
*attr
= &evsel
->attr
;
322 const char *name
= perf_evsel__name(evsel
);
324 if (!(attr
->sample_type
& sample_type
)) {
325 pr_err("Samples for %s event do not have %s attribute set.",
333 static int __cmd_inject(struct perf_inject
*inject
)
335 struct perf_session
*session
;
338 signal(SIGINT
, sig_handler
);
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
;
346 session
= perf_session__new(inject
->input_name
, O_RDONLY
, false, true, &inject
->tool
);
350 if (inject
->build_ids
) {
351 inject
->tool
.sample
= perf_event__inject_buildid
;
352 } else if (inject
->sched_stat
) {
353 struct perf_evsel
*evsel
;
355 inject
->tool
.ordered_samples
= true;
357 list_for_each_entry(evsel
, &session
->evlist
->entries
, node
) {
358 const char *name
= perf_evsel__name(evsel
);
360 if (!strcmp(name
, "sched:sched_switch")) {
361 if (perf_evsel__check_stype(evsel
, PERF_SAMPLE_TID
, "TID"))
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
;
372 if (!inject
->pipe_output
)
373 lseek(inject
->output
, session
->header
.data_offset
, SEEK_SET
);
375 ret
= perf_session__process_events(session
, &inject
->tool
);
377 if (!inject
->pipe_output
) {
378 session
->header
.data_size
= inject
->bytes_written
;
379 perf_session__write_header(session
, session
->evlist
, inject
->output
, true);
382 perf_session__delete(session
);
387 int cmd_inject(int argc
, const char **argv
, const char *prefix __maybe_unused
)
389 struct perf_inject inject
= {
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
,
406 .samples
= LIST_HEAD_INIT(inject
.samples
),
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",
414 OPT_STRING('o', "output", &output_name
, "file",
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)"),
423 const char * const inject_usage
[] = {
424 "perf inject [<options>]",
428 argc
= parse_options(argc
, argv
, options
, inject_usage
, 0);
431 * Any (unrecognized) arguments left?
434 usage_with_options(inject_usage
, options
);
436 if (!strcmp(output_name
, "-")) {
437 inject
.pipe_output
= 1;
438 inject
.output
= STDOUT_FILENO
;
440 inject
.output
= open(output_name
, O_CREAT
| O_WRONLY
| O_TRUNC
,
442 if (inject
.output
< 0) {
443 perror("failed to create output file");
448 if (symbol__init() < 0)
451 return __cmd_inject(&inject
);