builtin-trace.c 19.9 KB
Newer Older
1
#include <traceevent/event-parse.h>
A
Arnaldo Carvalho de Melo 已提交
2
#include "builtin.h"
3
#include "util/color.h"
A
Arnaldo Carvalho de Melo 已提交
4
#include "util/evlist.h"
5 6
#include "util/machine.h"
#include "util/thread.h"
A
Arnaldo Carvalho de Melo 已提交
7
#include "util/parse-options.h"
8
#include "util/strlist.h"
A
Arnaldo Carvalho de Melo 已提交
9 10 11 12 13 14 15
#include "util/thread_map.h"

#include <libaudit.h>
#include <stdlib.h>

static struct syscall_fmt {
	const char *name;
16
	const char *alias;
A
Arnaldo Carvalho de Melo 已提交
17 18 19
	bool	   errmsg;
	bool	   timeout;
} syscall_fmts[] = {
20
	{ .name	    = "access",	    .errmsg = true, },
21
	{ .name	    = "arch_prctl", .errmsg = true, .alias = "prctl", },
22
	{ .name	    = "connect",    .errmsg = true, },
23 24 25
	{ .name	    = "fstat",	    .errmsg = true, .alias = "newfstat", },
	{ .name	    = "fstatat",    .errmsg = true, .alias = "newfstatat", },
	{ .name	    = "futex",	    .errmsg = true, },
26
	{ .name	    = "open",	    .errmsg = true, },
27 28 29 30 31
	{ .name	    = "poll",	    .errmsg = true, .timeout = true, },
	{ .name	    = "ppoll",	    .errmsg = true, .timeout = true, },
	{ .name	    = "read",	    .errmsg = true, },
	{ .name	    = "recvfrom",   .errmsg = true, },
	{ .name	    = "select",	    .errmsg = true, .timeout = true, },
32
	{ .name	    = "socket",	    .errmsg = true, },
33
	{ .name	    = "stat",	    .errmsg = true, .alias = "newstat", },
A
Arnaldo Carvalho de Melo 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
};

static int syscall_fmt__cmp(const void *name, const void *fmtp)
{
	const struct syscall_fmt *fmt = fmtp;
	return strcmp(name, fmt->name);
}

static struct syscall_fmt *syscall_fmt__find(const char *name)
{
	const int nmemb = ARRAY_SIZE(syscall_fmts);
	return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp);
}

struct syscall {
	struct event_format *tp_format;
	const char	    *name;
51
	bool		    filtered;
A
Arnaldo Carvalho de Melo 已提交
52 53 54
	struct syscall_fmt  *fmt;
};

55 56 57 58 59 60 61 62 63 64 65
static size_t fprintf_duration(unsigned long t, FILE *fp)
{
	double duration = (double)t / NSEC_PER_MSEC;
	size_t printed = fprintf(fp, "(");

	if (duration >= 1.0)
		printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration);
	else if (duration >= 0.01)
		printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration);
	else
		printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration);
66
	return printed + fprintf(fp, "): ");
67 68
}

69 70 71 72
struct thread_trace {
	u64		  entry_time;
	u64		  exit_time;
	bool		  entry_pending;
73
	unsigned long	  nr_events;
74
	char		  *entry_str;
75
	double		  runtime_ms;
76 77 78 79 80 81 82
};

static struct thread_trace *thread_trace__new(void)
{
	return zalloc(sizeof(struct thread_trace));
}

83
static struct thread_trace *thread__trace(struct thread *thread, FILE *fp)
84
{
85 86
	struct thread_trace *ttrace;

87 88 89 90 91
	if (thread == NULL)
		goto fail;

	if (thread->priv == NULL)
		thread->priv = thread_trace__new();
92
		
93 94 95
	if (thread->priv == NULL)
		goto fail;

96 97 98 99
	ttrace = thread->priv;
	++ttrace->nr_events;

	return ttrace;
100
fail:
101
	color_fprintf(fp, PERF_COLOR_RED,
102 103 104 105
		      "WARNING: not enough memory, dropping samples!\n");
	return NULL;
}

A
Arnaldo Carvalho de Melo 已提交
106
struct trace {
107
	struct perf_tool	tool;
A
Arnaldo Carvalho de Melo 已提交
108 109 110 111 112 113
	int			audit_machine;
	struct {
		int		max;
		struct syscall  *table;
	} syscalls;
	struct perf_record_opts opts;
114 115
	struct machine		host;
	u64			base_time;
116
	FILE			*output;
117
	unsigned long		nr_events;
118 119
	struct strlist		*ev_qualifier;
	bool			not_ev_qualifier;
120
	bool			sched;
121
	bool			multiple_threads;
122
	double			duration_filter;
123
	double			runtime_ms;
A
Arnaldo Carvalho de Melo 已提交
124 125
};

126 127 128 129 130
static bool trace__filter_duration(struct trace *trace, double t)
{
	return t < (trace->duration_filter * NSEC_PER_MSEC);
}

131 132 133 134
static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
{
	double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC;

135
	return fprintf(fp, "%10.3f ", ts);
136 137
}

138 139 140 141 142 143 144
static bool done = false;

static void sig_handler(int sig __maybe_unused)
{
	done = true;
}

145
static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread,
146
					u64 duration, u64 tstamp, FILE *fp)
147 148
{
	size_t printed = trace__fprintf_tstamp(trace, tstamp, fp);
149
	printed += fprintf_duration(duration, fp);
150 151

	if (trace->multiple_threads)
152
		printed += fprintf(fp, "%d ", thread->tid);
153 154 155 156

	return printed;
}

157 158
static int trace__process_event(struct trace *trace, struct machine *machine,
				union perf_event *event)
159 160 161 162 163
{
	int ret = 0;

	switch (event->header.type) {
	case PERF_RECORD_LOST:
164
		color_fprintf(trace->output, PERF_COLOR_RED,
165 166 167 168 169 170 171 172 173 174
			      "LOST %" PRIu64 " events!\n", event->lost.lost);
		ret = machine__process_lost_event(machine, event);
	default:
		ret = machine__process_event(machine, event);
		break;
	}

	return ret;
}

175
static int trace__tool_process(struct perf_tool *tool,
176 177 178 179
			       union perf_event *event,
			       struct perf_sample *sample __maybe_unused,
			       struct machine *machine)
{
180 181
	struct trace *trace = container_of(tool, struct trace, tool);
	return trace__process_event(trace, machine, event);
182 183 184 185 186 187 188 189 190 191 192 193 194
}

static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
{
	int err = symbol__init();

	if (err)
		return err;

	machine__init(&trace->host, "", HOST_KERNEL_ID);
	machine__create_kernel_maps(&trace->host);

	if (perf_target__has_task(&trace->opts.target)) {
195
		err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads,
196 197 198
							trace__tool_process,
							&trace->host);
	} else {
199
		err = perf_event__synthesize_threads(&trace->tool, trace__tool_process,
200 201 202 203 204 205 206 207 208
						     &trace->host);
	}

	if (err)
		symbol__exit();

	return err;
}

A
Arnaldo Carvalho de Melo 已提交
209 210 211 212
static int trace__read_syscall_info(struct trace *trace, int id)
{
	char tp_name[128];
	struct syscall *sc;
213 214 215 216
	const char *name = audit_syscall_to_name(id, trace->audit_machine);

	if (name == NULL)
		return -1;
A
Arnaldo Carvalho de Melo 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235

	if (id > trace->syscalls.max) {
		struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));

		if (nsyscalls == NULL)
			return -1;

		if (trace->syscalls.max != -1) {
			memset(nsyscalls + trace->syscalls.max + 1, 0,
			       (id - trace->syscalls.max) * sizeof(*sc));
		} else {
			memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
		}

		trace->syscalls.table = nsyscalls;
		trace->syscalls.max   = id;
	}

	sc = trace->syscalls.table + id;
236
	sc->name = name;
237

238 239 240 241 242 243 244 245 246 247 248
	if (trace->ev_qualifier) {
		bool in = strlist__find(trace->ev_qualifier, name) != NULL;

		if (!(in ^ trace->not_ev_qualifier)) {
			sc->filtered = true;
			/*
			 * No need to do read tracepoint information since this will be
			 * filtered out.
			 */
			return 0;
		}
249 250
	}

251
	sc->fmt  = syscall_fmt__find(sc->name);
A
Arnaldo Carvalho de Melo 已提交
252

253
	snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
A
Arnaldo Carvalho de Melo 已提交
254
	sc->tp_format = event_format__new("syscalls", tp_name);
255 256 257 258 259

	if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
		snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
		sc->tp_format = event_format__new("syscalls", tp_name);
	}
A
Arnaldo Carvalho de Melo 已提交
260 261 262 263

	return sc->tp_format != NULL ? 0 : -1;
}

264 265
static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
				      unsigned long *args)
A
Arnaldo Carvalho de Melo 已提交
266 267 268 269 270 271 272 273
{
	int i = 0;
	size_t printed = 0;

	if (sc->tp_format != NULL) {
		struct format_field *field;

		for (field = sc->tp_format->format.fields->next; field; field = field->next) {
274 275 276
			printed += scnprintf(bf + printed, size - printed,
					     "%s%s: %ld", printed ? ", " : "",
					     field->name, args[i++]);
A
Arnaldo Carvalho de Melo 已提交
277 278 279
		}
	} else {
		while (i < 6) {
280 281 282
			printed += scnprintf(bf + printed, size - printed,
					     "%sarg%d: %ld",
					     printed ? ", " : "", i, args[i]);
A
Arnaldo Carvalho de Melo 已提交
283 284 285 286 287 288 289
			++i;
		}
	}

	return printed;
}

290 291 292 293 294 295 296 297 298 299
typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,
				  struct perf_sample *sample);

static struct syscall *trace__syscall_info(struct trace *trace,
					   struct perf_evsel *evsel,
					   struct perf_sample *sample)
{
	int id = perf_evsel__intval(evsel, sample, "id");

	if (id < 0) {
300
		fprintf(trace->output, "Invalid syscall %d id, skipping...\n", id);
301 302 303 304 305 306 307 308 309 310 311 312 313
		return NULL;
	}

	if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
	    trace__read_syscall_info(trace, id))
		goto out_cant_read;

	if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
		goto out_cant_read;

	return &trace->syscalls.table[id];

out_cant_read:
314
	fprintf(trace->output, "Problems reading syscall %d", id);
315
	if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL)
316 317
		fprintf(trace->output, "(%s)", trace->syscalls.table[id].name);
	fputs(" information", trace->output);
318 319 320 321 322 323
	return NULL;
}

static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
			    struct perf_sample *sample)
{
324
	char *msg;
325
	void *args;
326
	size_t printed = 0;
327
	struct thread *thread;
328
	struct syscall *sc = trace__syscall_info(trace, evsel, sample);
329 330 331 332
	struct thread_trace *ttrace;

	if (sc == NULL)
		return -1;
333

334 335 336 337
	if (sc->filtered)
		return 0;

	thread = machine__findnew_thread(&trace->host, sample->tid);
338
	ttrace = thread__trace(thread, trace->output);
339
	if (ttrace == NULL)
340 341 342 343
		return -1;

	args = perf_evsel__rawptr(evsel, sample, "args");
	if (args == NULL) {
344
		fprintf(trace->output, "Problems reading syscall arguments\n");
345 346 347
		return -1;
	}

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	ttrace = thread->priv;

	if (ttrace->entry_str == NULL) {
		ttrace->entry_str = malloc(1024);
		if (!ttrace->entry_str)
			return -1;
	}

	ttrace->entry_time = sample->time;
	msg = ttrace->entry_str;
	printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name);

	printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed,  args);

	if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) {
363
		if (!trace->duration_filter) {
364 365
			trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);
			fprintf(trace->output, "%-70s\n", ttrace->entry_str);
366
		}
367 368
	} else
		ttrace->entry_pending = true;
369 370 371 372 373 374 375 376

	return 0;
}

static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
			   struct perf_sample *sample)
{
	int ret;
377
	u64 duration = 0;
378
	struct thread *thread;
379
	struct syscall *sc = trace__syscall_info(trace, evsel, sample);
380 381 382 383
	struct thread_trace *ttrace;

	if (sc == NULL)
		return -1;
384

385 386 387 388
	if (sc->filtered)
		return 0;

	thread = machine__findnew_thread(&trace->host, sample->tid);
389
	ttrace = thread__trace(thread, trace->output);
390
	if (ttrace == NULL)
391 392 393 394
		return -1;

	ret = perf_evsel__intval(evsel, sample, "ret");

395 396 397 398
	ttrace = thread->priv;

	ttrace->exit_time = sample->time;

399
	if (ttrace->entry_time) {
400
		duration = sample->time - ttrace->entry_time;
401 402 403 404
		if (trace__filter_duration(trace, duration))
			goto out;
	} else if (trace->duration_filter)
		goto out;
405

406
	trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output);
407 408

	if (ttrace->entry_pending) {
409
		fprintf(trace->output, "%-70s", ttrace->entry_str);
410
	} else {
411 412 413
		fprintf(trace->output, " ... [");
		color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued");
		fprintf(trace->output, "]: %s()", sc->name);
414 415
	}

416 417 418 419 420
	if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
		char bf[256];
		const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
			   *e = audit_errno_to_name(-ret);

421
		fprintf(trace->output, ") = -1 %s %s", e, emsg);
422
	} else if (ret == 0 && sc->fmt && sc->fmt->timeout)
423
		fprintf(trace->output, ") = 0 Timeout");
424
	else
425
		fprintf(trace->output, ") = %d", ret);
426

427
	fputc('\n', trace->output);
428
out:
429 430
	ttrace->entry_pending = false;

431 432 433
	return 0;
}

434 435 436 437 438 439
static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel,
				     struct perf_sample *sample)
{
        u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
	double runtime_ms = (double)runtime / NSEC_PER_MSEC;
	struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
440
	struct thread_trace *ttrace = thread__trace(thread, trace->output);
441 442 443 444 445 446 447 448 449

	if (ttrace == NULL)
		goto out_dump;

	ttrace->runtime_ms += runtime_ms;
	trace->runtime_ms += runtime_ms;
	return 0;

out_dump:
450
	fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n",
451 452 453 454 455 456 457 458
	       evsel->name,
	       perf_evsel__strval(evsel, sample, "comm"),
	       (pid_t)perf_evsel__intval(evsel, sample, "pid"),
	       runtime,
	       perf_evsel__intval(evsel, sample, "vruntime"));
	return 0;
}

459
static int trace__run(struct trace *trace, int argc, const char **argv)
A
Arnaldo Carvalho de Melo 已提交
460
{
461
	struct perf_evlist *evlist = perf_evlist__new();
462
	struct perf_evsel *evsel;
463 464
	int err = -1, i;
	unsigned long before;
465
	const bool forks = argc > 0;
A
Arnaldo Carvalho de Melo 已提交
466 467

	if (evlist == NULL) {
468
		fprintf(trace->output, "Not enough memory to run!\n");
A
Arnaldo Carvalho de Melo 已提交
469 470 471
		goto out;
	}

472 473
	if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) ||
	    perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) {
474
		fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n");
A
Arnaldo Carvalho de Melo 已提交
475 476 477
		goto out_delete_evlist;
	}

478 479 480
	if (trace->sched &&
	    perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
				   trace__sched_stat_runtime)) {
481
		fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n");
482 483 484
		goto out_delete_evlist;
	}

A
Arnaldo Carvalho de Melo 已提交
485 486
	err = perf_evlist__create_maps(evlist, &trace->opts.target);
	if (err < 0) {
487
		fprintf(trace->output, "Problems parsing the target to trace, check your options!\n");
A
Arnaldo Carvalho de Melo 已提交
488 489 490
		goto out_delete_evlist;
	}

491 492
	err = trace__symbols_init(trace, evlist);
	if (err < 0) {
493
		fprintf(trace->output, "Problems initializing symbol libraries!\n");
494
		goto out_delete_maps;
495 496
	}

497
	perf_evlist__config(evlist, &trace->opts);
A
Arnaldo Carvalho de Melo 已提交
498

499 500 501 502
	signal(SIGCHLD, sig_handler);
	signal(SIGINT, sig_handler);

	if (forks) {
503
		err = perf_evlist__prepare_workload(evlist, &trace->opts.target,
504
						    argv, false, false);
505
		if (err < 0) {
506
			fprintf(trace->output, "Couldn't run the workload!\n");
507
			goto out_delete_maps;
508 509 510
		}
	}

A
Arnaldo Carvalho de Melo 已提交
511 512
	err = perf_evlist__open(evlist);
	if (err < 0) {
513
		fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno));
514
		goto out_delete_maps;
A
Arnaldo Carvalho de Melo 已提交
515 516 517 518
	}

	err = perf_evlist__mmap(evlist, UINT_MAX, false);
	if (err < 0) {
519
		fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno));
520
		goto out_close_evlist;
A
Arnaldo Carvalho de Melo 已提交
521 522 523
	}

	perf_evlist__enable(evlist);
524 525 526 527

	if (forks)
		perf_evlist__start_workload(evlist);

528
	trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1;
A
Arnaldo Carvalho de Melo 已提交
529
again:
530
	before = trace->nr_events;
A
Arnaldo Carvalho de Melo 已提交
531 532 533 534 535 536

	for (i = 0; i < evlist->nr_mmaps; i++) {
		union perf_event *event;

		while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
			const u32 type = event->header.type;
537
			tracepoint_handler handler;
A
Arnaldo Carvalho de Melo 已提交
538 539
			struct perf_sample sample;

540
			++trace->nr_events;
A
Arnaldo Carvalho de Melo 已提交
541 542 543

			err = perf_evlist__parse_sample(evlist, event, &sample);
			if (err) {
544
				fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err);
A
Arnaldo Carvalho de Melo 已提交
545 546 547
				continue;
			}

548 549 550 551
			if (trace->base_time == 0)
				trace->base_time = sample.time;

			if (type != PERF_RECORD_SAMPLE) {
552
				trace__process_event(trace, &trace->host, event);
553 554 555
				continue;
			}

A
Arnaldo Carvalho de Melo 已提交
556 557
			evsel = perf_evlist__id2evsel(evlist, sample.id);
			if (evsel == NULL) {
558
				fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
A
Arnaldo Carvalho de Melo 已提交
559 560 561
				continue;
			}

562
			if (sample.raw_data == NULL) {
563
				fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
564 565 566 567 568
				       perf_evsel__name(evsel), sample.tid,
				       sample.cpu, sample.raw_size);
				continue;
			}

569 570
			handler = evsel->handler.func;
			handler(trace, evsel, &sample);
A
Arnaldo Carvalho de Melo 已提交
571 572 573
		}
	}

574
	if (trace->nr_events == before) {
575
		if (done)
576
			goto out_unmap_evlist;
577

A
Arnaldo Carvalho de Melo 已提交
578
		poll(evlist->pollfd, evlist->nr_fds, -1);
579 580 581 582
	}

	if (done)
		perf_evlist__disable(evlist);
A
Arnaldo Carvalho de Melo 已提交
583 584 585

	goto again;

586 587 588 589 590 591
out_unmap_evlist:
	perf_evlist__munmap(evlist);
out_close_evlist:
	perf_evlist__close(evlist);
out_delete_maps:
	perf_evlist__delete_maps(evlist);
A
Arnaldo Carvalho de Melo 已提交
592 593 594 595 596 597
out_delete_evlist:
	perf_evlist__delete(evlist);
out:
	return err;
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
static size_t trace__fprintf_threads_header(FILE *fp)
{
	size_t printed;

	printed  = fprintf(fp, "\n _____________________________________________________________________\n");
	printed += fprintf(fp," __)    Summary of events    (__\n\n");
	printed += fprintf(fp,"              [ task - pid ]     [ events ] [ ratio ]  [ runtime ]\n");
	printed += fprintf(fp," _____________________________________________________________________\n\n");

	return printed;
}

static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp)
{
	size_t printed = trace__fprintf_threads_header(fp);
	struct rb_node *nd;

	for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) {
		struct thread *thread = rb_entry(nd, struct thread, rb_node);
		struct thread_trace *ttrace = thread->priv;
		const char *color;
		double ratio;

		if (ttrace == NULL)
			continue;

		ratio = (double)ttrace->nr_events / trace->nr_events * 100.0;

		color = PERF_COLOR_NORMAL;
		if (ratio > 50.0)
			color = PERF_COLOR_RED;
		else if (ratio > 25.0)
			color = PERF_COLOR_GREEN;
		else if (ratio > 5.0)
			color = PERF_COLOR_YELLOW;

		printed += color_fprintf(fp, color, "%20s", thread->comm);
635
		printed += fprintf(fp, " - %-5d :%11lu   [", thread->tid, ttrace->nr_events);
636 637 638 639 640 641 642
		printed += color_fprintf(fp, color, "%5.1f%%", ratio);
		printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms);
	}

	return printed;
}

643 644 645 646 647 648 649 650 651
static int trace__set_duration(const struct option *opt, const char *str,
			       int unset __maybe_unused)
{
	struct trace *trace = opt->value;

	trace->duration_filter = atof(str);
	return 0;
}

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
static int trace__open_output(struct trace *trace, const char *filename)
{
	struct stat st;

	if (!stat(filename, &st) && st.st_size) {
		char oldname[PATH_MAX];

		scnprintf(oldname, sizeof(oldname), "%s.old", filename);
		unlink(oldname);
		rename(filename, oldname);
	}

	trace->output = fopen(filename, "w");

	return trace->output == NULL ? -errno : 0;
}

A
Arnaldo Carvalho de Melo 已提交
669 670 671
int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
{
	const char * const trace_usage[] = {
672 673
		"perf trace [<options>] [<command>]",
		"perf trace [<options>] -- <command> [<options>]",
A
Arnaldo Carvalho de Melo 已提交
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
		NULL
	};
	struct trace trace = {
		.audit_machine = audit_detect_machine(),
		.syscalls = {
			. max = -1,
		},
		.opts = {
			.target = {
				.uid	   = UINT_MAX,
				.uses_mmap = true,
			},
			.user_freq     = UINT_MAX,
			.user_interval = ULLONG_MAX,
			.no_delay      = true,
			.mmap_pages    = 1024,
		},
691
		.output = stdout,
A
Arnaldo Carvalho de Melo 已提交
692
	};
693
	const char *output_name = NULL;
694
	const char *ev_qualifier_str = NULL;
A
Arnaldo Carvalho de Melo 已提交
695
	const struct option trace_options[] = {
696 697
	OPT_STRING('e', "expr", &ev_qualifier_str, "expr",
		    "list of events to trace"),
698
	OPT_STRING('o', "output", &output_name, "file", "output file name"),
A
Arnaldo Carvalho de Melo 已提交
699 700
	OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
		    "trace events on existing process id"),
701
	OPT_STRING('t', "tid", &trace.opts.target.tid, "tid",
A
Arnaldo Carvalho de Melo 已提交
702
		    "trace events on existing thread id"),
703
	OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide,
A
Arnaldo Carvalho de Melo 已提交
704
		    "system-wide collection from all CPUs"),
705
	OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu",
A
Arnaldo Carvalho de Melo 已提交
706
		    "list of cpus to monitor"),
707
	OPT_BOOLEAN('i', "no-inherit", &trace.opts.no_inherit,
A
Arnaldo Carvalho de Melo 已提交
708
		    "child tasks do not inherit counters"),
709
	OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages,
A
Arnaldo Carvalho de Melo 已提交
710
		     "number of mmap data pages"),
711
	OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user",
A
Arnaldo Carvalho de Melo 已提交
712
		   "user to profile"),
713 714 715
	OPT_CALLBACK(0, "duration", &trace, "float",
		     "show only events with duration > N.M ms",
		     trace__set_duration),
716
	OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"),
A
Arnaldo Carvalho de Melo 已提交
717 718 719
	OPT_END()
	};
	int err;
720
	char bf[BUFSIZ];
A
Arnaldo Carvalho de Melo 已提交
721 722 723

	argc = parse_options(argc, argv, trace_options, trace_usage, 0);

724 725 726 727 728 729 730 731
	if (output_name != NULL) {
		err = trace__open_output(&trace, output_name);
		if (err < 0) {
			perror("failed to create output file");
			goto out;
		}
	}

732
	if (ev_qualifier_str != NULL) {
733 734 735 736 737 738
		const char *s = ev_qualifier_str;

		trace.not_ev_qualifier = *s == '!';
		if (trace.not_ev_qualifier)
			++s;
		trace.ev_qualifier = strlist__new(true, s);
739
		if (trace.ev_qualifier == NULL) {
740 741 742 743
			fputs("Not enough memory to parse event qualifier",
			      trace.output);
			err = -ENOMEM;
			goto out_close;
744 745 746
		}
	}

747 748 749
	err = perf_target__validate(&trace.opts.target);
	if (err) {
		perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
750 751
		fprintf(trace.output, "%s", bf);
		goto out_close;
752 753
	}

A
Arnaldo Carvalho de Melo 已提交
754 755 756
	err = perf_target__parse_uid(&trace.opts.target);
	if (err) {
		perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
757 758
		fprintf(trace.output, "%s", bf);
		goto out_close;
A
Arnaldo Carvalho de Melo 已提交
759 760
	}

761
	if (!argc && perf_target__none(&trace.opts.target))
762 763
		trace.opts.target.system_wide = true;

764 765 766
	err = trace__run(&trace, argc, argv);

	if (trace.sched && !err)
767
		trace__fprintf_thread_summary(&trace, trace.output);
768

769 770 771 772
out_close:
	if (output_name != NULL)
		fclose(trace.output);
out:
773
	return err;
A
Arnaldo Carvalho de Melo 已提交
774
}