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