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