builtin-inject.c 11.4 KB
Newer Older
T
Tom Zanussi 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * builtin-inject.c
 *
 * Builtin inject command: Examine the live mode (stdin) event stream
 * and repipe it to stdout while optionally injecting additional
 * events into it.
 */
#include "builtin.h"

#include "perf.h"
11 12 13
#include "util/color.h"
#include "util/evlist.h"
#include "util/evsel.h"
T
Tom Zanussi 已提交
14
#include "util/session.h"
15
#include "util/tool.h"
T
Tom Zanussi 已提交
16
#include "util/debug.h"
17
#include "util/build-id.h"
T
Tom Zanussi 已提交
18 19 20

#include "util/parse-options.h"

21 22
#include <linux/list.h>

23 24 25
struct perf_inject {
	struct perf_tool tool;
	bool		 build_ids;
26
	bool		 sched_stat;
A
Andrew Vagin 已提交
27 28 29 30
	const char	 *input_name;
	int		 pipe_output,
			 output;
	u64		 bytes_written;
31 32 33 34 35 36 37
	struct list_head samples;
};

struct event_entry {
	struct list_head node;
	u32		 tid;
	union perf_event event[0];
38
};
T
Tom Zanussi 已提交
39

A
Andrew Vagin 已提交
40
static int perf_event__repipe_synth(struct perf_tool *tool,
41
				    union perf_event *event)
T
Tom Zanussi 已提交
42
{
A
Andrew Vagin 已提交
43
	struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
T
Tom Zanussi 已提交
44 45 46 47 48 49
	uint32_t size;
	void *buf = event;

	size = event->header.size;

	while (size) {
A
Andrew Vagin 已提交
50
		int ret = write(inject->output, buf, size);
T
Tom Zanussi 已提交
51 52 53 54 55
		if (ret < 0)
			return -errno;

		size -= ret;
		buf += ret;
A
Andrew Vagin 已提交
56
		inject->bytes_written += ret;
T
Tom Zanussi 已提交
57 58 59 60 61
	}

	return 0;
}

62
static int perf_event__repipe_op2_synth(struct perf_tool *tool,
63
					union perf_event *event,
64 65
					struct perf_session *session
					__maybe_unused)
66
{
67
	return perf_event__repipe_synth(tool, event);
68 69
}

70
static int perf_event__repipe_event_type_synth(struct perf_tool *tool,
71 72
					       union perf_event *event)
{
73
	return perf_event__repipe_synth(tool, event);
74 75
}

76
static int perf_event__repipe_tracing_data_synth(union perf_event *event,
77 78
						 struct perf_session *session
						 __maybe_unused)
79
{
80
	return perf_event__repipe_synth(NULL, event);
81 82
}

83
static int perf_event__repipe_attr(union perf_event *event,
84
				   struct perf_evlist **pevlist __maybe_unused)
85
{
86 87 88 89 90
	int ret;
	ret = perf_event__process_attr(event, pevlist);
	if (ret)
		return ret;

91
	return perf_event__repipe_synth(NULL, event);
92 93
}

94
static int perf_event__repipe(struct perf_tool *tool,
95
			      union perf_event *event,
96
			      struct perf_sample *sample __maybe_unused,
97
			      struct machine *machine __maybe_unused)
98
{
99
	return perf_event__repipe_synth(tool, event);
100 101
}

102 103 104 105 106 107
typedef int (*inject_handler)(struct perf_tool *tool,
			      union perf_event *event,
			      struct perf_sample *sample,
			      struct perf_evsel *evsel,
			      struct machine *machine);

108
static int perf_event__repipe_sample(struct perf_tool *tool,
109
				     union perf_event *event,
110 111 112
				     struct perf_sample *sample,
				     struct perf_evsel *evsel,
				     struct machine *machine)
113
{
114 115 116 117 118
	if (evsel->handler.func) {
		inject_handler f = evsel->handler.func;
		return f(tool, event, sample, evsel, machine);
	}

119 120
	build_id__mark_dso_hit(tool, event, sample, evsel, machine);

121
	return perf_event__repipe_synth(tool, event);
122 123
}

124
static int perf_event__repipe_mmap(struct perf_tool *tool,
125
				   union perf_event *event,
126
				   struct perf_sample *sample,
127
				   struct machine *machine)
T
Tom Zanussi 已提交
128 129 130
{
	int err;

131 132
	err = perf_event__process_mmap(tool, event, sample, machine);
	perf_event__repipe(tool, event, sample, machine);
T
Tom Zanussi 已提交
133 134 135 136

	return err;
}

137
static int perf_event__repipe_fork(struct perf_tool *tool,
138
				   union perf_event *event,
139
				   struct perf_sample *sample,
140
				   struct machine *machine)
T
Tom Zanussi 已提交
141 142 143
{
	int err;

144
	err = perf_event__process_fork(tool, event, sample, machine);
145
	perf_event__repipe(tool, event, sample, machine);
T
Tom Zanussi 已提交
146 147 148 149

	return err;
}

150 151
static int perf_event__repipe_tracing_data(union perf_event *event,
					   struct perf_session *session)
T
Tom Zanussi 已提交
152 153 154
{
	int err;

155
	perf_event__repipe_synth(NULL, event);
156
	err = perf_event__process_tracing_data(event, session);
T
Tom Zanussi 已提交
157 158 159 160

	return err;
}

161
static int dso__read_build_id(struct dso *self)
T
Tom Zanussi 已提交
162
{
163 164
	if (self->has_build_id)
		return 0;
T
Tom Zanussi 已提交
165

166 167 168 169 170
	if (filename__read_build_id(self->long_name, self->build_id,
				    sizeof(self->build_id)) > 0) {
		self->has_build_id = true;
		return 0;
	}
T
Tom Zanussi 已提交
171

172 173
	return -1;
}
T
Tom Zanussi 已提交
174

175
static int dso__inject_build_id(struct dso *self, struct perf_tool *tool,
176
				struct machine *machine)
177 178 179
{
	u16 misc = PERF_RECORD_MISC_USER;
	int err;
T
Tom Zanussi 已提交
180

181 182 183 184
	if (dso__read_build_id(self) < 0) {
		pr_debug("no build_id found for %s\n", self->long_name);
		return -1;
	}
T
Tom Zanussi 已提交
185

186 187
	if (self->kernel)
		misc = PERF_RECORD_MISC_KERNEL;
T
Tom Zanussi 已提交
188

189
	err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe,
190
					      machine);
191 192
	if (err) {
		pr_err("Can't synthesize build_id event for %s\n", self->long_name);
T
Tom Zanussi 已提交
193 194 195 196 197 198
		return -1;
	}

	return 0;
}

199
static int perf_event__inject_buildid(struct perf_tool *tool,
200
				      union perf_event *event,
201
				      struct perf_sample *sample,
202
				      struct perf_evsel *evsel __maybe_unused,
203
				      struct machine *machine)
T
Tom Zanussi 已提交
204 205 206 207 208 209 210
{
	struct addr_location al;
	struct thread *thread;
	u8 cpumode;

	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;

211
	thread = machine__findnew_thread(machine, event->ip.pid);
T
Tom Zanussi 已提交
212 213 214 215 216 217
	if (thread == NULL) {
		pr_err("problem processing %d event, skipping it.\n",
		       event->header.type);
		goto repipe;
	}

218 219
	thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
			      event->ip.ip, &al);
T
Tom Zanussi 已提交
220 221 222 223

	if (al.map != NULL) {
		if (!al.map->dso->hit) {
			al.map->dso->hit = 1;
224
			if (map__load(al.map, NULL) >= 0) {
225
				dso__inject_build_id(al.map->dso, tool, machine);
226 227 228 229
				/*
				 * If this fails, too bad, let the other side
				 * account this as unresolved.
				 */
230
			} else {
231
#ifdef LIBELF_SUPPORT
T
Tom Zanussi 已提交
232 233 234
				pr_warning("no symbols found in %s, maybe "
					   "install a debug package?\n",
					   al.map->dso->long_name);
235 236
#endif
			}
T
Tom Zanussi 已提交
237 238 239 240
		}
	}

repipe:
241
	perf_event__repipe(tool, event, sample, machine);
242
	return 0;
T
Tom Zanussi 已提交
243 244
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
static int perf_inject__sched_process_exit(struct perf_tool *tool,
					   union perf_event *event __maybe_unused,
					   struct perf_sample *sample,
					   struct perf_evsel *evsel __maybe_unused,
					   struct machine *machine __maybe_unused)
{
	struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
	struct event_entry *ent;

	list_for_each_entry(ent, &inject->samples, node) {
		if (sample->tid == ent->tid) {
			list_del_init(&ent->node);
			free(ent);
			break;
		}
	}

	return 0;
}

static int perf_inject__sched_switch(struct perf_tool *tool,
				     union perf_event *event,
				     struct perf_sample *sample,
				     struct perf_evsel *evsel,
				     struct machine *machine)
{
	struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
	struct event_entry *ent;

	perf_inject__sched_process_exit(tool, event, sample, evsel, machine);

	ent = malloc(event->header.size + sizeof(struct event_entry));
	if (ent == NULL) {
		color_fprintf(stderr, PERF_COLOR_RED,
			     "Not enough memory to process sched switch event!");
		return -1;
	}

	ent->tid = sample->tid;
	memcpy(&ent->event, event, event->header.size);
	list_add(&ent->node, &inject->samples);
	return 0;
}

static int perf_inject__sched_stat(struct perf_tool *tool,
				   union perf_event *event __maybe_unused,
				   struct perf_sample *sample,
				   struct perf_evsel *evsel,
				   struct machine *machine)
{
	struct event_entry *ent;
	union perf_event *event_sw;
	struct perf_sample sample_sw;
	struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
	u32 pid = perf_evsel__intval(evsel, sample, "pid");

	list_for_each_entry(ent, &inject->samples, node) {
		if (pid == ent->tid)
			goto found;
	}

	return 0;
found:
	event_sw = &ent->event[0];
	perf_evsel__parse_sample(evsel, event_sw, &sample_sw);

	sample_sw.period = sample->period;
	sample_sw.time	 = sample->time;
	perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
				      &sample_sw, false);
315
	build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
316 317 318
	return perf_event__repipe(tool, event_sw, &sample_sw, machine);
}

T
Tom Zanussi 已提交
319 320
extern volatile int session_done;

321
static void sig_handler(int sig __maybe_unused)
T
Tom Zanussi 已提交
322 323 324 325
{
	session_done = 1;
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
static int perf_evsel__check_stype(struct perf_evsel *evsel,
				   u64 sample_type, const char *sample_msg)
{
	struct perf_event_attr *attr = &evsel->attr;
	const char *name = perf_evsel__name(evsel);

	if (!(attr->sample_type & sample_type)) {
		pr_err("Samples for %s event do not have %s attribute set.",
			name, sample_msg);
		return -EINVAL;
	}

	return 0;
}

341
static int __cmd_inject(struct perf_inject *inject)
T
Tom Zanussi 已提交
342 343 344 345 346 347
{
	struct perf_session *session;
	int ret = -EINVAL;

	signal(SIGINT, sig_handler);

348
	if (inject->build_ids || inject->sched_stat) {
349
		inject->tool.mmap	  = perf_event__repipe_mmap;
350
		inject->tool.fork	  = perf_event__repipe_fork;
351
		inject->tool.tracing_data = perf_event__repipe_tracing_data;
T
Tom Zanussi 已提交
352 353
	}

A
Andrew Vagin 已提交
354
	session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool);
T
Tom Zanussi 已提交
355 356 357
	if (session == NULL)
		return -ENOMEM;

358 359 360
	if (inject->build_ids) {
		inject->tool.sample = perf_event__inject_buildid;
	} else if (inject->sched_stat) {
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
		struct perf_evsel *evsel;

		inject->tool.ordered_samples = true;

		list_for_each_entry(evsel, &session->evlist->entries, node) {
			const char *name = perf_evsel__name(evsel);

			if (!strcmp(name, "sched:sched_switch")) {
				if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
					return -EINVAL;

				evsel->handler.func = perf_inject__sched_switch;
			} else if (!strcmp(name, "sched:sched_process_exit"))
				evsel->handler.func = perf_inject__sched_process_exit;
			else if (!strncmp(name, "sched:sched_stat_", 17))
				evsel->handler.func = perf_inject__sched_stat;
		}
	}

A
Andrew Vagin 已提交
380 381 382
	if (!inject->pipe_output)
		lseek(inject->output, session->header.data_offset, SEEK_SET);

383
	ret = perf_session__process_events(session, &inject->tool);
T
Tom Zanussi 已提交
384

A
Andrew Vagin 已提交
385 386 387 388 389
	if (!inject->pipe_output) {
		session->header.data_size = inject->bytes_written;
		perf_session__write_header(session, session->evlist, inject->output, true);
	}

T
Tom Zanussi 已提交
390 391 392 393 394
	perf_session__delete(session);

	return ret;
}

395
int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
T
Tom Zanussi 已提交
396
{
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
	struct perf_inject inject = {
		.tool = {
			.sample		= perf_event__repipe_sample,
			.mmap		= perf_event__repipe,
			.comm		= perf_event__repipe,
			.fork		= perf_event__repipe,
			.exit		= perf_event__repipe,
			.lost		= perf_event__repipe,
			.read		= perf_event__repipe_sample,
			.throttle	= perf_event__repipe,
			.unthrottle	= perf_event__repipe,
			.attr		= perf_event__repipe_attr,
			.event_type	= perf_event__repipe_event_type_synth,
			.tracing_data	= perf_event__repipe_tracing_data_synth,
			.build_id	= perf_event__repipe_op2_synth,
		},
A
Andrew Vagin 已提交
413
		.input_name  = "-",
414
		.samples = LIST_HEAD_INIT(inject.samples),
415
	};
A
Andrew Vagin 已提交
416
	const char *output_name = "-";
417 418 419
	const struct option options[] = {
		OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
			    "Inject build-ids into the output stream"),
A
Andrew Vagin 已提交
420 421 422 423
		OPT_STRING('i', "input", &inject.input_name, "file",
			   "input file name"),
		OPT_STRING('o', "output", &output_name, "file",
			   "output file name"),
424 425 426
		OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
			    "Merge sched-stat and sched-switch for getting events "
			    "where and how long tasks slept"),
427 428 429 430
		OPT_INCR('v', "verbose", &verbose,
			 "be more verbose (show build ids, etc)"),
		OPT_END()
	};
431 432 433 434
	const char * const inject_usage[] = {
		"perf inject [<options>]",
		NULL
	};
435

436
	argc = parse_options(argc, argv, options, inject_usage, 0);
T
Tom Zanussi 已提交
437 438 439 440 441

	/*
	 * Any (unrecognized) arguments left?
	 */
	if (argc)
442
		usage_with_options(inject_usage, options);
T
Tom Zanussi 已提交
443

A
Andrew Vagin 已提交
444 445 446 447 448 449 450 451 452 453 454 455
	if (!strcmp(output_name, "-")) {
		inject.pipe_output = 1;
		inject.output = STDOUT_FILENO;
	} else {
		inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC,
						  S_IRUSR | S_IWUSR);
		if (inject.output < 0) {
			perror("failed to create output file");
			return -1;
		}
	}

T
Tom Zanussi 已提交
456 457 458
	if (symbol__init() < 0)
		return -1;

459
	return __cmd_inject(&inject);
T
Tom Zanussi 已提交
460
}