Commit | Line | Data |
---|---|---|
454c407e TZ |
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/session.h" | |
12 | #include "util/debug.h" | |
13 | ||
14 | #include "util/parse-options.h" | |
15 | ||
16 | static char const *input_name = "-"; | |
17 | static bool inject_build_ids; | |
18 | ||
640c03ce ACM |
19 | static int event__repipe_synth(event_t *event, |
20 | struct perf_session *session __used) | |
454c407e TZ |
21 | { |
22 | uint32_t size; | |
23 | void *buf = event; | |
24 | ||
25 | size = event->header.size; | |
26 | ||
27 | while (size) { | |
28 | int ret = write(STDOUT_FILENO, buf, size); | |
29 | if (ret < 0) | |
30 | return -errno; | |
31 | ||
32 | size -= ret; | |
33 | buf += ret; | |
34 | } | |
35 | ||
36 | return 0; | |
37 | } | |
38 | ||
640c03ce ACM |
39 | static int event__repipe(event_t *event, struct sample_data *sample __used, |
40 | struct perf_session *session) | |
41 | { | |
42 | return event__repipe_synth(event, session); | |
43 | } | |
44 | ||
45 | static int event__repipe_mmap(event_t *self, struct sample_data *sample, | |
46 | struct perf_session *session) | |
454c407e TZ |
47 | { |
48 | int err; | |
49 | ||
640c03ce ACM |
50 | err = event__process_mmap(self, sample, session); |
51 | event__repipe(self, sample, session); | |
454c407e TZ |
52 | |
53 | return err; | |
54 | } | |
55 | ||
640c03ce ACM |
56 | static int event__repipe_task(event_t *self, struct sample_data *sample, |
57 | struct perf_session *session) | |
454c407e TZ |
58 | { |
59 | int err; | |
60 | ||
640c03ce ACM |
61 | err = event__process_task(self, sample, session); |
62 | event__repipe(self, sample, session); | |
454c407e TZ |
63 | |
64 | return err; | |
65 | } | |
66 | ||
67 | static int event__repipe_tracing_data(event_t *self, | |
68 | struct perf_session *session) | |
69 | { | |
70 | int err; | |
71 | ||
640c03ce | 72 | event__repipe_synth(self, session); |
454c407e TZ |
73 | err = event__process_tracing_data(self, session); |
74 | ||
75 | return err; | |
76 | } | |
77 | ||
090f7204 | 78 | static int dso__read_build_id(struct dso *self) |
454c407e | 79 | { |
090f7204 ACM |
80 | if (self->has_build_id) |
81 | return 0; | |
454c407e | 82 | |
090f7204 ACM |
83 | if (filename__read_build_id(self->long_name, self->build_id, |
84 | sizeof(self->build_id)) > 0) { | |
85 | self->has_build_id = true; | |
86 | return 0; | |
87 | } | |
454c407e | 88 | |
090f7204 ACM |
89 | return -1; |
90 | } | |
454c407e | 91 | |
090f7204 ACM |
92 | static int dso__inject_build_id(struct dso *self, struct perf_session *session) |
93 | { | |
94 | u16 misc = PERF_RECORD_MISC_USER; | |
95 | struct machine *machine; | |
96 | int err; | |
454c407e | 97 | |
090f7204 ACM |
98 | if (dso__read_build_id(self) < 0) { |
99 | pr_debug("no build_id found for %s\n", self->long_name); | |
100 | return -1; | |
101 | } | |
454c407e | 102 | |
090f7204 ACM |
103 | machine = perf_session__find_host_machine(session); |
104 | if (machine == NULL) { | |
105 | pr_err("Can't find machine for session\n"); | |
106 | return -1; | |
107 | } | |
454c407e | 108 | |
090f7204 ACM |
109 | if (self->kernel) |
110 | misc = PERF_RECORD_MISC_KERNEL; | |
454c407e | 111 | |
090f7204 ACM |
112 | err = event__synthesize_build_id(self, misc, event__repipe, |
113 | machine, session); | |
114 | if (err) { | |
115 | pr_err("Can't synthesize build_id event for %s\n", self->long_name); | |
454c407e TZ |
116 | return -1; |
117 | } | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
640c03ce ACM |
122 | static int event__inject_buildid(event_t *event, struct sample_data *sample, |
123 | struct perf_session *session) | |
454c407e TZ |
124 | { |
125 | struct addr_location al; | |
126 | struct thread *thread; | |
127 | u8 cpumode; | |
454c407e TZ |
128 | |
129 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | |
130 | ||
131 | thread = perf_session__findnew(session, event->ip.pid); | |
132 | if (thread == NULL) { | |
133 | pr_err("problem processing %d event, skipping it.\n", | |
134 | event->header.type); | |
454c407e TZ |
135 | goto repipe; |
136 | } | |
137 | ||
138 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | |
139 | event->ip.pid, event->ip.ip, &al); | |
140 | ||
141 | if (al.map != NULL) { | |
142 | if (!al.map->dso->hit) { | |
143 | al.map->dso->hit = 1; | |
090f7204 ACM |
144 | if (map__load(al.map, NULL) >= 0) { |
145 | dso__inject_build_id(al.map->dso, session); | |
146 | /* | |
147 | * If this fails, too bad, let the other side | |
148 | * account this as unresolved. | |
149 | */ | |
150 | } else | |
454c407e TZ |
151 | pr_warning("no symbols found in %s, maybe " |
152 | "install a debug package?\n", | |
153 | al.map->dso->long_name); | |
154 | } | |
155 | } | |
156 | ||
157 | repipe: | |
640c03ce | 158 | event__repipe(event, sample, session); |
090f7204 | 159 | return 0; |
454c407e TZ |
160 | } |
161 | ||
162 | struct perf_event_ops inject_ops = { | |
163 | .sample = event__repipe, | |
164 | .mmap = event__repipe, | |
165 | .comm = event__repipe, | |
166 | .fork = event__repipe, | |
167 | .exit = event__repipe, | |
168 | .lost = event__repipe, | |
169 | .read = event__repipe, | |
170 | .throttle = event__repipe, | |
171 | .unthrottle = event__repipe, | |
640c03ce ACM |
172 | .attr = event__repipe_synth, |
173 | .event_type = event__repipe_synth, | |
174 | .tracing_data = event__repipe_synth, | |
175 | .build_id = event__repipe_synth, | |
454c407e TZ |
176 | }; |
177 | ||
178 | extern volatile int session_done; | |
179 | ||
180 | static void sig_handler(int sig __attribute__((__unused__))) | |
181 | { | |
182 | session_done = 1; | |
183 | } | |
184 | ||
185 | static int __cmd_inject(void) | |
186 | { | |
187 | struct perf_session *session; | |
188 | int ret = -EINVAL; | |
189 | ||
190 | signal(SIGINT, sig_handler); | |
191 | ||
192 | if (inject_build_ids) { | |
193 | inject_ops.sample = event__inject_buildid; | |
194 | inject_ops.mmap = event__repipe_mmap; | |
195 | inject_ops.fork = event__repipe_task; | |
196 | inject_ops.tracing_data = event__repipe_tracing_data; | |
197 | } | |
198 | ||
21ef97f0 | 199 | session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops); |
454c407e TZ |
200 | if (session == NULL) |
201 | return -ENOMEM; | |
202 | ||
203 | ret = perf_session__process_events(session, &inject_ops); | |
204 | ||
205 | perf_session__delete(session); | |
206 | ||
207 | return ret; | |
208 | } | |
209 | ||
210 | static const char * const report_usage[] = { | |
211 | "perf inject [<options>]", | |
212 | NULL | |
213 | }; | |
214 | ||
215 | static const struct option options[] = { | |
11d232ec | 216 | OPT_BOOLEAN('b', "build-ids", &inject_build_ids, |
454c407e TZ |
217 | "Inject build-ids into the output stream"), |
218 | OPT_INCR('v', "verbose", &verbose, | |
219 | "be more verbose (show build ids, etc)"), | |
220 | OPT_END() | |
221 | }; | |
222 | ||
223 | int cmd_inject(int argc, const char **argv, const char *prefix __used) | |
224 | { | |
225 | argc = parse_options(argc, argv, options, report_usage, 0); | |
226 | ||
227 | /* | |
228 | * Any (unrecognized) arguments left? | |
229 | */ | |
230 | if (argc) | |
231 | usage_with_options(report_usage, options); | |
232 | ||
233 | if (symbol__init() < 0) | |
234 | return -1; | |
235 | ||
236 | return __cmd_inject(); | |
237 | } |