builtin-inject.c 15.8 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"
18
#include "util/data.h"
19
#include "util/auxtrace.h"
T
Tom Zanussi 已提交
20 21 22

#include "util/parse-options.h"

23 24
#include <linux/list.h>

25
struct perf_inject {
26
	struct perf_tool	tool;
27
	struct perf_session	*session;
28 29
	bool			build_ids;
	bool			sched_stat;
30
	bool			have_auxtrace;
31 32 33 34
	const char		*input_name;
	struct perf_data_file	output;
	u64			bytes_written;
	struct list_head	samples;
35
	struct itrace_synth_opts itrace_synth_opts;
36 37 38 39 40 41
};

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

44
static int output_bytes(struct perf_inject *inject, void *buf, size_t sz)
T
Tom Zanussi 已提交
45
{
46
	ssize_t size;
T
Tom Zanussi 已提交
47

48
	size = perf_data_file__write(&inject->output, buf, sz);
49 50
	if (size < 0)
		return -errno;
T
Tom Zanussi 已提交
51

52
	inject->bytes_written += size;
T
Tom Zanussi 已提交
53 54 55
	return 0;
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
{
	char buf[4096];
	ssize_t ssz;
	int ret;

	while (size > 0) {
		ssz = read(fd, buf, min(size, (off_t)sizeof(buf)));
		if (ssz < 0)
			return -errno;
		ret = output_bytes(inject, buf, ssz);
		if (ret)
			return ret;
		size -= ssz;
	}

	return 0;
}

static int perf_event__repipe_synth(struct perf_tool *tool,
				    union perf_event *event)
{
	struct perf_inject *inject = container_of(tool, struct perf_inject,
						  tool);

	return output_bytes(inject, event, event->header.size);
}

84 85 86 87 88 89 90
static int perf_event__repipe_oe_synth(struct perf_tool *tool,
				       union perf_event *event,
				       struct ordered_events *oe __maybe_unused)
{
	return perf_event__repipe_synth(tool, event);
}

91
static int perf_event__repipe_op2_synth(struct perf_tool *tool,
92
					union perf_event *event,
93 94
					struct perf_session *session
					__maybe_unused)
95
{
96
	return perf_event__repipe_synth(tool, event);
97 98
}

99 100 101
static int perf_event__repipe_attr(struct perf_tool *tool,
				   union perf_event *event,
				   struct perf_evlist **pevlist)
102
{
103 104
	struct perf_inject *inject = container_of(tool, struct perf_inject,
						  tool);
105
	int ret;
106 107

	ret = perf_event__process_attr(tool, event, pevlist);
108 109 110
	if (ret)
		return ret;

111
	if (!inject->output.is_pipe)
112 113
		return 0;

114
	return perf_event__repipe_synth(tool, event);
115 116
}

117 118 119 120 121 122 123 124 125
static s64 perf_event__repipe_auxtrace(struct perf_tool *tool,
				       union perf_event *event,
				       struct perf_session *session
				       __maybe_unused)
{
	struct perf_inject *inject = container_of(tool, struct perf_inject,
						  tool);
	int ret;

126 127
	inject->have_auxtrace = true;

128 129 130 131 132 133 134 135 136 137 138 139
	if (!inject->output.is_pipe) {
		off_t offset;

		offset = lseek(inject->output.fd, 0, SEEK_CUR);
		if (offset == -1)
			return -errno;
		ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
						     event, offset);
		if (ret < 0)
			return ret;
	}

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
	if (perf_data_file__is_pipe(session->file) || !session->one_mmap) {
		ret = output_bytes(inject, event, event->header.size);
		if (ret < 0)
			return ret;
		ret = copy_bytes(inject, perf_data_file__fd(session->file),
				 event->auxtrace.size);
	} else {
		ret = output_bytes(inject, event,
				   event->header.size + event->auxtrace.size);
	}
	if (ret < 0)
		return ret;

	return event->auxtrace.size;
}

156
static int perf_event__repipe(struct perf_tool *tool,
157
			      union perf_event *event,
158
			      struct perf_sample *sample __maybe_unused,
159
			      struct machine *machine __maybe_unused)
160
{
161
	return perf_event__repipe_synth(tool, event);
162 163
}

164 165 166 167 168 169
typedef int (*inject_handler)(struct perf_tool *tool,
			      union perf_event *event,
			      struct perf_sample *sample,
			      struct perf_evsel *evsel,
			      struct machine *machine);

170
static int perf_event__repipe_sample(struct perf_tool *tool,
171
				     union perf_event *event,
172 173 174
				     struct perf_sample *sample,
				     struct perf_evsel *evsel,
				     struct machine *machine)
175
{
176 177
	if (evsel->handler) {
		inject_handler f = evsel->handler;
178 179 180
		return f(tool, event, sample, evsel, machine);
	}

181 182
	build_id__mark_dso_hit(tool, event, sample, evsel, machine);

183
	return perf_event__repipe_synth(tool, event);
184 185
}

186
static int perf_event__repipe_mmap(struct perf_tool *tool,
187
				   union perf_event *event,
188
				   struct perf_sample *sample,
189
				   struct machine *machine)
T
Tom Zanussi 已提交
190 191 192
{
	int err;

193 194
	err = perf_event__process_mmap(tool, event, sample, machine);
	perf_event__repipe(tool, event, sample, machine);
T
Tom Zanussi 已提交
195 196 197 198

	return err;
}

199 200 201 202 203 204 205 206 207 208 209 210 211
static int perf_event__repipe_mmap2(struct perf_tool *tool,
				   union perf_event *event,
				   struct perf_sample *sample,
				   struct machine *machine)
{
	int err;

	err = perf_event__process_mmap2(tool, event, sample, machine);
	perf_event__repipe(tool, event, sample, machine);

	return err;
}

212
static int perf_event__repipe_fork(struct perf_tool *tool,
213
				   union perf_event *event,
214
				   struct perf_sample *sample,
215
				   struct machine *machine)
T
Tom Zanussi 已提交
216 217 218
{
	int err;

219
	err = perf_event__process_fork(tool, event, sample, machine);
220
	perf_event__repipe(tool, event, sample, machine);
T
Tom Zanussi 已提交
221 222 223 224

	return err;
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
static int perf_event__repipe_comm(struct perf_tool *tool,
				   union perf_event *event,
				   struct perf_sample *sample,
				   struct machine *machine)
{
	int err;

	err = perf_event__process_comm(tool, event, sample, machine);
	perf_event__repipe(tool, event, sample, machine);

	return err;
}

static int perf_event__repipe_exit(struct perf_tool *tool,
				   union perf_event *event,
				   struct perf_sample *sample,
				   struct machine *machine)
{
	int err;

	err = perf_event__process_exit(tool, event, sample, machine);
	perf_event__repipe(tool, event, sample, machine);

	return err;
}

251 252
static int perf_event__repipe_tracing_data(struct perf_tool *tool,
					   union perf_event *event,
253
					   struct perf_session *session)
T
Tom Zanussi 已提交
254 255 256
{
	int err;

257 258
	perf_event__repipe_synth(tool, event);
	err = perf_event__process_tracing_data(tool, event, session);
T
Tom Zanussi 已提交
259 260 261 262

	return err;
}

263 264 265 266 267 268 269 270 271 272 273 274
static int perf_event__repipe_id_index(struct perf_tool *tool,
				       union perf_event *event,
				       struct perf_session *session)
{
	int err;

	perf_event__repipe_synth(tool, event);
	err = perf_event__process_id_index(tool, event, session);

	return err;
}

275
static int dso__read_build_id(struct dso *dso)
T
Tom Zanussi 已提交
276
{
277
	if (dso->has_build_id)
278
		return 0;
T
Tom Zanussi 已提交
279

280 281 282
	if (filename__read_build_id(dso->long_name, dso->build_id,
				    sizeof(dso->build_id)) > 0) {
		dso->has_build_id = true;
283 284
		return 0;
	}
T
Tom Zanussi 已提交
285

286 287
	return -1;
}
T
Tom Zanussi 已提交
288

289
static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool,
290
				struct machine *machine)
291 292 293
{
	u16 misc = PERF_RECORD_MISC_USER;
	int err;
T
Tom Zanussi 已提交
294

295 296
	if (dso__read_build_id(dso) < 0) {
		pr_debug("no build_id found for %s\n", dso->long_name);
297 298
		return -1;
	}
T
Tom Zanussi 已提交
299

300
	if (dso->kernel)
301
		misc = PERF_RECORD_MISC_KERNEL;
T
Tom Zanussi 已提交
302

303
	err = perf_event__synthesize_build_id(tool, dso, misc, perf_event__repipe,
304
					      machine);
305
	if (err) {
306
		pr_err("Can't synthesize build_id event for %s\n", dso->long_name);
T
Tom Zanussi 已提交
307 308 309 310 311 312
		return -1;
	}

	return 0;
}

313
static int perf_event__inject_buildid(struct perf_tool *tool,
314
				      union perf_event *event,
315
				      struct perf_sample *sample,
316
				      struct perf_evsel *evsel __maybe_unused,
317
				      struct machine *machine)
T
Tom Zanussi 已提交
318 319 320 321 322 323 324
{
	struct addr_location al;
	struct thread *thread;
	u8 cpumode;

	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;

325
	thread = machine__findnew_thread(machine, sample->pid, sample->tid);
T
Tom Zanussi 已提交
326 327 328 329 330 331
	if (thread == NULL) {
		pr_err("problem processing %d event, skipping it.\n",
		       event->header.type);
		goto repipe;
	}

332
	thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
T
Tom Zanussi 已提交
333 334 335 336

	if (al.map != NULL) {
		if (!al.map->dso->hit) {
			al.map->dso->hit = 1;
337
			if (map__load(al.map, NULL) >= 0) {
338
				dso__inject_build_id(al.map->dso, tool, machine);
339 340 341 342
				/*
				 * If this fails, too bad, let the other side
				 * account this as unresolved.
				 */
343
			} else {
344
#ifdef HAVE_LIBELF_SUPPORT
T
Tom Zanussi 已提交
345 346 347
				pr_warning("no symbols found in %s, maybe "
					   "install a debug package?\n",
					   al.map->dso->long_name);
348 349
#endif
			}
T
Tom Zanussi 已提交
350 351 352 353
		}
	}

repipe:
354
	perf_event__repipe(tool, event, sample, machine);
355
	return 0;
T
Tom Zanussi 已提交
356 357
}

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
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,
427 428
				      evsel->attr.read_format, &sample_sw,
				      false);
429
	build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
430 431 432
	return perf_event__repipe(tool, event_sw, &sample_sw, machine);
}

433
static void sig_handler(int sig __maybe_unused)
T
Tom Zanussi 已提交
434 435 436 437
{
	session_done = 1;
}

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
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;
}

453
static int __cmd_inject(struct perf_inject *inject)
T
Tom Zanussi 已提交
454 455
{
	int ret = -EINVAL;
456
	struct perf_session *session = inject->session;
457
	struct perf_data_file *file_out = &inject->output;
458
	int fd = perf_data_file__fd(file_out);
459
	u64 output_data_offset;
T
Tom Zanussi 已提交
460 461 462

	signal(SIGINT, sig_handler);

463 464
	if (inject->build_ids || inject->sched_stat ||
	    inject->itrace_synth_opts.set) {
465
		inject->tool.mmap	  = perf_event__repipe_mmap;
466
		inject->tool.mmap2	  = perf_event__repipe_mmap2;
467
		inject->tool.fork	  = perf_event__repipe_fork;
468
		inject->tool.tracing_data = perf_event__repipe_tracing_data;
T
Tom Zanussi 已提交
469 470
	}

471 472
	output_data_offset = session->header.data_offset;

473 474 475
	if (inject->build_ids) {
		inject->tool.sample = perf_event__inject_buildid;
	} else if (inject->sched_stat) {
476 477
		struct perf_evsel *evsel;

478
		evlist__for_each(session->evlist, evsel) {
479 480 481 482 483 484
			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;

485
				evsel->handler = perf_inject__sched_switch;
486
			} else if (!strcmp(name, "sched:sched_process_exit"))
487
				evsel->handler = perf_inject__sched_process_exit;
488
			else if (!strncmp(name, "sched:sched_stat_", 17))
489
				evsel->handler = perf_inject__sched_stat;
490
		}
491 492 493 494 495 496 497 498 499 500 501 502
	} else if (inject->itrace_synth_opts.set) {
		session->itrace_synth_opts = &inject->itrace_synth_opts;
		inject->itrace_synth_opts.inject = true;
		inject->tool.comm	    = perf_event__repipe_comm;
		inject->tool.exit	    = perf_event__repipe_exit;
		inject->tool.id_index	    = perf_event__repipe_id_index;
		inject->tool.auxtrace_info  = perf_event__process_auxtrace_info;
		inject->tool.auxtrace	    = perf_event__process_auxtrace;
		inject->tool.ordered_events = true;
		inject->tool.ordering_requires_timestamps = true;
		/* Allow space in the header for new attributes */
		output_data_offset = 4096;
503 504
	}

505 506 507
	if (!inject->itrace_synth_opts.set)
		auxtrace_index__free(&session->auxtrace_index);

508
	if (!file_out->is_pipe)
509
		lseek(fd, output_data_offset, SEEK_SET);
A
Andrew Vagin 已提交
510

511
	ret = perf_session__process_events(session);
T
Tom Zanussi 已提交
512

513
	if (!file_out->is_pipe) {
514
		if (inject->build_ids) {
515 516
			perf_header__set_feat(&session->header,
					      HEADER_BUILD_ID);
517 518 519
			if (inject->have_auxtrace)
				dsos__hit_all(session);
		}
520 521 522 523 524 525 526 527
		/*
		 * The AUX areas have been removed and replaced with
		 * synthesized hardware events, so clear the feature flag.
		 */
		if (inject->itrace_synth_opts.set)
			perf_header__clear_feat(&session->header,
						HEADER_AUXTRACE);
		session->header.data_offset = output_data_offset;
A
Andrew Vagin 已提交
528
		session->header.data_size = inject->bytes_written;
529
		perf_session__write_header(session, session->evlist, fd, true);
A
Andrew Vagin 已提交
530 531
	}

T
Tom Zanussi 已提交
532 533 534
	return ret;
}

535
int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
T
Tom Zanussi 已提交
536
{
537 538 539 540
	struct perf_inject inject = {
		.tool = {
			.sample		= perf_event__repipe_sample,
			.mmap		= perf_event__repipe,
541
			.mmap2		= perf_event__repipe,
542 543 544 545 546 547 548 549
			.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,
550
			.tracing_data	= perf_event__repipe_op2_synth,
551 552 553
			.auxtrace_info	= perf_event__repipe_op2_synth,
			.auxtrace	= perf_event__repipe_auxtrace,
			.auxtrace_error	= perf_event__repipe_op2_synth,
554
			.finished_round	= perf_event__repipe_oe_synth,
555
			.build_id	= perf_event__repipe_op2_synth,
A
Adrian Hunter 已提交
556
			.id_index	= perf_event__repipe_op2_synth,
557
		},
A
Andrew Vagin 已提交
558
		.input_name  = "-",
559
		.samples = LIST_HEAD_INIT(inject.samples),
560 561 562 563
		.output = {
			.path = "-",
			.mode = PERF_DATA_MODE_WRITE,
		},
564
	};
565 566 567 568 569
	struct perf_data_file file = {
		.mode = PERF_DATA_MODE_READ,
	};
	int ret;

570 571 572
	const struct option options[] = {
		OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
			    "Inject build-ids into the output stream"),
A
Andrew Vagin 已提交
573 574
		OPT_STRING('i', "input", &inject.input_name, "file",
			   "input file name"),
575
		OPT_STRING('o', "output", &inject.output.path, "file",
A
Andrew Vagin 已提交
576
			   "output file name"),
577 578 579
		OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
			    "Merge sched-stat and sched-switch for getting events "
			    "where and how long tasks slept"),
580 581
		OPT_INCR('v', "verbose", &verbose,
			 "be more verbose (show build ids, etc)"),
582 583
		OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
			   "kallsyms pathname"),
584
		OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
585 586 587
		OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts,
				    NULL, "opts", "Instruction Tracing options",
				    itrace_parse_synth_opts),
588 589
		OPT_END()
	};
590 591 592 593
	const char * const inject_usage[] = {
		"perf inject [<options>]",
		NULL
	};
594

595
	argc = parse_options(argc, argv, options, inject_usage, 0);
T
Tom Zanussi 已提交
596 597 598 599 600

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

603 604 605
	if (perf_data_file__open(&inject.output)) {
		perror("failed to create output file");
		return -1;
A
Andrew Vagin 已提交
606 607
	}

608 609
	inject.tool.ordered_events = inject.sched_stat;

610 611 612
	file.path = inject.input_name;
	inject.session = perf_session__new(&file, true, &inject.tool);
	if (inject.session == NULL)
613
		return -1;
614

615
	if (symbol__init(&inject.session->header.env) < 0)
T
Tom Zanussi 已提交
616 617
		return -1;

618 619 620 621 622
	ret = __cmd_inject(&inject);

	perf_session__delete(inject.session);

	return ret;
T
Tom Zanussi 已提交
623
}