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