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