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