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