diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 405 |
1 files changed, 371 insertions, 34 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7aaee39f6774..d222d7fc7e96 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include "builtin.h" | 1 | #include "builtin.h" |
2 | #include "util/color.h" | ||
2 | #include "util/evlist.h" | 3 | #include "util/evlist.h" |
4 | #include "util/machine.h" | ||
5 | #include "util/thread.h" | ||
3 | #include "util/parse-options.h" | 6 | #include "util/parse-options.h" |
4 | #include "util/thread_map.h" | 7 | #include "util/thread_map.h" |
5 | #include "event-parse.h" | 8 | #include "event-parse.h" |
@@ -13,15 +16,18 @@ static struct syscall_fmt { | |||
13 | bool errmsg; | 16 | bool errmsg; |
14 | bool timeout; | 17 | bool timeout; |
15 | } syscall_fmts[] = { | 18 | } syscall_fmts[] = { |
19 | { .name = "access", .errmsg = true, }, | ||
16 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, | 20 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, |
17 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, | 21 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, |
18 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, | 22 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, |
19 | { .name = "futex", .errmsg = true, }, | 23 | { .name = "futex", .errmsg = true, }, |
24 | { .name = "open", .errmsg = true, }, | ||
20 | { .name = "poll", .errmsg = true, .timeout = true, }, | 25 | { .name = "poll", .errmsg = true, .timeout = true, }, |
21 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 26 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
22 | { .name = "read", .errmsg = true, }, | 27 | { .name = "read", .errmsg = true, }, |
23 | { .name = "recvfrom", .errmsg = true, }, | 28 | { .name = "recvfrom", .errmsg = true, }, |
24 | { .name = "select", .errmsg = true, .timeout = true, }, | 29 | { .name = "select", .errmsg = true, .timeout = true, }, |
30 | { .name = "socket", .errmsg = true, }, | ||
25 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 31 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
26 | }; | 32 | }; |
27 | 33 | ||
@@ -43,6 +49,57 @@ struct syscall { | |||
43 | struct syscall_fmt *fmt; | 49 | struct syscall_fmt *fmt; |
44 | }; | 50 | }; |
45 | 51 | ||
52 | static size_t fprintf_duration(unsigned long t, FILE *fp) | ||
53 | { | ||
54 | double duration = (double)t / NSEC_PER_MSEC; | ||
55 | size_t printed = fprintf(fp, "("); | ||
56 | |||
57 | if (duration >= 1.0) | ||
58 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); | ||
59 | else if (duration >= 0.01) | ||
60 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | ||
61 | else | ||
62 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | ||
63 | return printed + fprintf(stdout, "): "); | ||
64 | } | ||
65 | |||
66 | struct thread_trace { | ||
67 | u64 entry_time; | ||
68 | u64 exit_time; | ||
69 | bool entry_pending; | ||
70 | unsigned long nr_events; | ||
71 | char *entry_str; | ||
72 | double runtime_ms; | ||
73 | }; | ||
74 | |||
75 | static struct thread_trace *thread_trace__new(void) | ||
76 | { | ||
77 | return zalloc(sizeof(struct thread_trace)); | ||
78 | } | ||
79 | |||
80 | static struct thread_trace *thread__trace(struct thread *thread) | ||
81 | { | ||
82 | struct thread_trace *ttrace; | ||
83 | |||
84 | if (thread == NULL) | ||
85 | goto fail; | ||
86 | |||
87 | if (thread->priv == NULL) | ||
88 | thread->priv = thread_trace__new(); | ||
89 | |||
90 | if (thread->priv == NULL) | ||
91 | goto fail; | ||
92 | |||
93 | ttrace = thread->priv; | ||
94 | ++ttrace->nr_events; | ||
95 | |||
96 | return ttrace; | ||
97 | fail: | ||
98 | color_fprintf(stdout, PERF_COLOR_RED, | ||
99 | "WARNING: not enough memory, dropping samples!\n"); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
46 | struct trace { | 103 | struct trace { |
47 | int audit_machine; | 104 | int audit_machine; |
48 | struct { | 105 | struct { |
@@ -50,8 +107,96 @@ struct trace { | |||
50 | struct syscall *table; | 107 | struct syscall *table; |
51 | } syscalls; | 108 | } syscalls; |
52 | struct perf_record_opts opts; | 109 | struct perf_record_opts opts; |
110 | struct machine host; | ||
111 | u64 base_time; | ||
112 | unsigned long nr_events; | ||
113 | bool sched; | ||
114 | bool multiple_threads; | ||
115 | double duration_filter; | ||
116 | double runtime_ms; | ||
53 | }; | 117 | }; |
54 | 118 | ||
119 | static bool trace__filter_duration(struct trace *trace, double t) | ||
120 | { | ||
121 | return t < (trace->duration_filter * NSEC_PER_MSEC); | ||
122 | } | ||
123 | |||
124 | static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) | ||
125 | { | ||
126 | double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; | ||
127 | |||
128 | return fprintf(fp, "%10.3f ", ts); | ||
129 | } | ||
130 | |||
131 | static bool done = false; | ||
132 | |||
133 | static void sig_handler(int sig __maybe_unused) | ||
134 | { | ||
135 | done = true; | ||
136 | } | ||
137 | |||
138 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | ||
139 | u64 duration, u64 tstamp, FILE *fp) | ||
140 | { | ||
141 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | ||
142 | printed += fprintf_duration(duration, fp); | ||
143 | |||
144 | if (trace->multiple_threads) | ||
145 | printed += fprintf(fp, "%d ", thread->pid); | ||
146 | |||
147 | return printed; | ||
148 | } | ||
149 | |||
150 | static int trace__process_event(struct machine *machine, union perf_event *event) | ||
151 | { | ||
152 | int ret = 0; | ||
153 | |||
154 | switch (event->header.type) { | ||
155 | case PERF_RECORD_LOST: | ||
156 | color_fprintf(stdout, PERF_COLOR_RED, | ||
157 | "LOST %" PRIu64 " events!\n", event->lost.lost); | ||
158 | ret = machine__process_lost_event(machine, event); | ||
159 | default: | ||
160 | ret = machine__process_event(machine, event); | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | ||
168 | union perf_event *event, | ||
169 | struct perf_sample *sample __maybe_unused, | ||
170 | struct machine *machine) | ||
171 | { | ||
172 | return trace__process_event(machine, event); | ||
173 | } | ||
174 | |||
175 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | ||
176 | { | ||
177 | int err = symbol__init(); | ||
178 | |||
179 | if (err) | ||
180 | return err; | ||
181 | |||
182 | machine__init(&trace->host, "", HOST_KERNEL_ID); | ||
183 | machine__create_kernel_maps(&trace->host); | ||
184 | |||
185 | if (perf_target__has_task(&trace->opts.target)) { | ||
186 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | ||
187 | trace__tool_process, | ||
188 | &trace->host); | ||
189 | } else { | ||
190 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | ||
191 | &trace->host); | ||
192 | } | ||
193 | |||
194 | if (err) | ||
195 | symbol__exit(); | ||
196 | |||
197 | return err; | ||
198 | } | ||
199 | |||
55 | static int trace__read_syscall_info(struct trace *trace, int id) | 200 | static int trace__read_syscall_info(struct trace *trace, int id) |
56 | { | 201 | { |
57 | char tp_name[128]; | 202 | char tp_name[128]; |
@@ -93,7 +238,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
93 | return sc->tp_format != NULL ? 0 : -1; | 238 | return sc->tp_format != NULL ? 0 : -1; |
94 | } | 239 | } |
95 | 240 | ||
96 | static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp) | 241 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
242 | unsigned long *args) | ||
97 | { | 243 | { |
98 | int i = 0; | 244 | int i = 0; |
99 | size_t printed = 0; | 245 | size_t printed = 0; |
@@ -102,12 +248,15 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL | |||
102 | struct format_field *field; | 248 | struct format_field *field; |
103 | 249 | ||
104 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 250 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
105 | printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "", | 251 | printed += scnprintf(bf + printed, size - printed, |
106 | field->name, args[i++]); | 252 | "%s%s: %ld", printed ? ", " : "", |
253 | field->name, args[i++]); | ||
107 | } | 254 | } |
108 | } else { | 255 | } else { |
109 | while (i < 6) { | 256 | while (i < 6) { |
110 | printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]); | 257 | printed += scnprintf(bf + printed, size - printed, |
258 | "%sarg%d: %ld", | ||
259 | printed ? ", " : "", i, args[i]); | ||
111 | ++i; | 260 | ++i; |
112 | } | 261 | } |
113 | } | 262 | } |
@@ -139,17 +288,24 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
139 | return &trace->syscalls.table[id]; | 288 | return &trace->syscalls.table[id]; |
140 | 289 | ||
141 | out_cant_read: | 290 | out_cant_read: |
142 | printf("Problems reading syscall %d information\n", id); | 291 | printf("Problems reading syscall %d", id); |
292 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | ||
293 | printf("(%s)", trace->syscalls.table[id].name); | ||
294 | puts(" information"); | ||
143 | return NULL; | 295 | return NULL; |
144 | } | 296 | } |
145 | 297 | ||
146 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 298 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
147 | struct perf_sample *sample) | 299 | struct perf_sample *sample) |
148 | { | 300 | { |
301 | char *msg; | ||
149 | void *args; | 302 | void *args; |
303 | size_t printed = 0; | ||
304 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
150 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 305 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
306 | struct thread_trace *ttrace = thread__trace(thread); | ||
151 | 307 | ||
152 | if (sc == NULL) | 308 | if (ttrace == NULL || sc == NULL) |
153 | return -1; | 309 | return -1; |
154 | 310 | ||
155 | args = perf_evsel__rawptr(evsel, sample, "args"); | 311 | args = perf_evsel__rawptr(evsel, sample, "args"); |
@@ -158,8 +314,27 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
158 | return -1; | 314 | return -1; |
159 | } | 315 | } |
160 | 316 | ||
161 | printf("%s(", sc->name); | 317 | ttrace = thread->priv; |
162 | syscall__fprintf_args(sc, args, stdout); | 318 | |
319 | if (ttrace->entry_str == NULL) { | ||
320 | ttrace->entry_str = malloc(1024); | ||
321 | if (!ttrace->entry_str) | ||
322 | return -1; | ||
323 | } | ||
324 | |||
325 | ttrace->entry_time = sample->time; | ||
326 | msg = ttrace->entry_str; | ||
327 | printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); | ||
328 | |||
329 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); | ||
330 | |||
331 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | ||
332 | if (!trace->duration_filter) { | ||
333 | trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout); | ||
334 | printf("%-70s\n", ttrace->entry_str); | ||
335 | } | ||
336 | } else | ||
337 | ttrace->entry_pending = true; | ||
163 | 338 | ||
164 | return 0; | 339 | return 0; |
165 | } | 340 | } |
@@ -168,13 +343,37 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
168 | struct perf_sample *sample) | 343 | struct perf_sample *sample) |
169 | { | 344 | { |
170 | int ret; | 345 | int ret; |
346 | u64 duration = 0; | ||
347 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
348 | struct thread_trace *ttrace = thread__trace(thread); | ||
171 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 349 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
172 | 350 | ||
173 | if (sc == NULL) | 351 | if (ttrace == NULL || sc == NULL) |
174 | return -1; | 352 | return -1; |
175 | 353 | ||
176 | ret = perf_evsel__intval(evsel, sample, "ret"); | 354 | ret = perf_evsel__intval(evsel, sample, "ret"); |
177 | 355 | ||
356 | ttrace = thread->priv; | ||
357 | |||
358 | ttrace->exit_time = sample->time; | ||
359 | |||
360 | if (ttrace->entry_time) { | ||
361 | duration = sample->time - ttrace->entry_time; | ||
362 | if (trace__filter_duration(trace, duration)) | ||
363 | goto out; | ||
364 | } else if (trace->duration_filter) | ||
365 | goto out; | ||
366 | |||
367 | trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout); | ||
368 | |||
369 | if (ttrace->entry_pending) { | ||
370 | printf("%-70s", ttrace->entry_str); | ||
371 | } else { | ||
372 | printf(" ... ["); | ||
373 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | ||
374 | printf("]: %s()", sc->name); | ||
375 | } | ||
376 | |||
178 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 377 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { |
179 | char bf[256]; | 378 | char bf[256]; |
180 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 379 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
@@ -187,14 +386,44 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
187 | printf(") = %d", ret); | 386 | printf(") = %d", ret); |
188 | 387 | ||
189 | putchar('\n'); | 388 | putchar('\n'); |
389 | out: | ||
390 | ttrace->entry_pending = false; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, | ||
396 | struct perf_sample *sample) | ||
397 | { | ||
398 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | ||
399 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | ||
400 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
401 | struct thread_trace *ttrace = thread__trace(thread); | ||
402 | |||
403 | if (ttrace == NULL) | ||
404 | goto out_dump; | ||
405 | |||
406 | ttrace->runtime_ms += runtime_ms; | ||
407 | trace->runtime_ms += runtime_ms; | ||
408 | return 0; | ||
409 | |||
410 | out_dump: | ||
411 | printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | ||
412 | evsel->name, | ||
413 | perf_evsel__strval(evsel, sample, "comm"), | ||
414 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | ||
415 | runtime, | ||
416 | perf_evsel__intval(evsel, sample, "vruntime")); | ||
190 | return 0; | 417 | return 0; |
191 | } | 418 | } |
192 | 419 | ||
193 | static int trace__run(struct trace *trace) | 420 | static int trace__run(struct trace *trace, int argc, const char **argv) |
194 | { | 421 | { |
195 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 422 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); |
196 | struct perf_evsel *evsel; | 423 | struct perf_evsel *evsel; |
197 | int err = -1, i, nr_events = 0, before; | 424 | int err = -1, i; |
425 | unsigned long before; | ||
426 | const bool forks = argc > 0; | ||
198 | 427 | ||
199 | if (evlist == NULL) { | 428 | if (evlist == NULL) { |
200 | printf("Not enough memory to run!\n"); | 429 | printf("Not enough memory to run!\n"); |
@@ -207,13 +436,37 @@ static int trace__run(struct trace *trace) | |||
207 | goto out_delete_evlist; | 436 | goto out_delete_evlist; |
208 | } | 437 | } |
209 | 438 | ||
439 | if (trace->sched && | ||
440 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | ||
441 | trace__sched_stat_runtime)) { | ||
442 | printf("Couldn't read the sched_stat_runtime tracepoint information!\n"); | ||
443 | goto out_delete_evlist; | ||
444 | } | ||
445 | |||
210 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 446 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
211 | if (err < 0) { | 447 | if (err < 0) { |
212 | printf("Problems parsing the target to trace, check your options!\n"); | 448 | printf("Problems parsing the target to trace, check your options!\n"); |
213 | goto out_delete_evlist; | 449 | goto out_delete_evlist; |
214 | } | 450 | } |
215 | 451 | ||
216 | perf_evlist__config_attrs(evlist, &trace->opts); | 452 | err = trace__symbols_init(trace, evlist); |
453 | if (err < 0) { | ||
454 | printf("Problems initializing symbol libraries!\n"); | ||
455 | goto out_delete_evlist; | ||
456 | } | ||
457 | |||
458 | perf_evlist__config(evlist, &trace->opts); | ||
459 | |||
460 | signal(SIGCHLD, sig_handler); | ||
461 | signal(SIGINT, sig_handler); | ||
462 | |||
463 | if (forks) { | ||
464 | err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); | ||
465 | if (err < 0) { | ||
466 | printf("Couldn't run the workload!\n"); | ||
467 | goto out_delete_evlist; | ||
468 | } | ||
469 | } | ||
217 | 470 | ||
218 | err = perf_evlist__open(evlist); | 471 | err = perf_evlist__open(evlist); |
219 | if (err < 0) { | 472 | if (err < 0) { |
@@ -228,8 +481,13 @@ static int trace__run(struct trace *trace) | |||
228 | } | 481 | } |
229 | 482 | ||
230 | perf_evlist__enable(evlist); | 483 | perf_evlist__enable(evlist); |
484 | |||
485 | if (forks) | ||
486 | perf_evlist__start_workload(evlist); | ||
487 | |||
488 | trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1; | ||
231 | again: | 489 | again: |
232 | before = nr_events; | 490 | before = trace->nr_events; |
233 | 491 | ||
234 | for (i = 0; i < evlist->nr_mmaps; i++) { | 492 | for (i = 0; i < evlist->nr_mmaps; i++) { |
235 | union perf_event *event; | 493 | union perf_event *event; |
@@ -239,19 +497,7 @@ again: | |||
239 | tracepoint_handler handler; | 497 | tracepoint_handler handler; |
240 | struct perf_sample sample; | 498 | struct perf_sample sample; |
241 | 499 | ||
242 | ++nr_events; | 500 | ++trace->nr_events; |
243 | |||
244 | switch (type) { | ||
245 | case PERF_RECORD_SAMPLE: | ||
246 | break; | ||
247 | case PERF_RECORD_LOST: | ||
248 | printf("LOST %" PRIu64 " events!\n", event->lost.lost); | ||
249 | continue; | ||
250 | default: | ||
251 | printf("Unexpected %s event, skipping...\n", | ||
252 | perf_event__name(type)); | ||
253 | continue; | ||
254 | } | ||
255 | 501 | ||
256 | err = perf_evlist__parse_sample(evlist, event, &sample); | 502 | err = perf_evlist__parse_sample(evlist, event, &sample); |
257 | if (err) { | 503 | if (err) { |
@@ -259,14 +505,26 @@ again: | |||
259 | continue; | 505 | continue; |
260 | } | 506 | } |
261 | 507 | ||
508 | if (trace->base_time == 0) | ||
509 | trace->base_time = sample.time; | ||
510 | |||
511 | if (type != PERF_RECORD_SAMPLE) { | ||
512 | trace__process_event(&trace->host, event); | ||
513 | continue; | ||
514 | } | ||
515 | |||
262 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 516 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
263 | if (evsel == NULL) { | 517 | if (evsel == NULL) { |
264 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 518 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
265 | continue; | 519 | continue; |
266 | } | 520 | } |
267 | 521 | ||
268 | if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) | 522 | if (sample.raw_data == NULL) { |
269 | printf("%d ", sample.tid); | 523 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
524 | perf_evsel__name(evsel), sample.tid, | ||
525 | sample.cpu, sample.raw_size); | ||
526 | continue; | ||
527 | } | ||
270 | 528 | ||
271 | if (sample.raw_data == NULL) { | 529 | if (sample.raw_data == NULL) { |
272 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 530 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
@@ -280,8 +538,15 @@ again: | |||
280 | } | 538 | } |
281 | } | 539 | } |
282 | 540 | ||
283 | if (nr_events == before) | 541 | if (trace->nr_events == before) { |
542 | if (done) | ||
543 | goto out_delete_evlist; | ||
544 | |||
284 | poll(evlist->pollfd, evlist->nr_fds, -1); | 545 | poll(evlist->pollfd, evlist->nr_fds, -1); |
546 | } | ||
547 | |||
548 | if (done) | ||
549 | perf_evlist__disable(evlist); | ||
285 | 550 | ||
286 | goto again; | 551 | goto again; |
287 | 552 | ||
@@ -291,10 +556,65 @@ out: | |||
291 | return err; | 556 | return err; |
292 | } | 557 | } |
293 | 558 | ||
559 | static size_t trace__fprintf_threads_header(FILE *fp) | ||
560 | { | ||
561 | size_t printed; | ||
562 | |||
563 | printed = fprintf(fp, "\n _____________________________________________________________________\n"); | ||
564 | printed += fprintf(fp," __) Summary of events (__\n\n"); | ||
565 | printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | ||
566 | printed += fprintf(fp," _____________________________________________________________________\n\n"); | ||
567 | |||
568 | return printed; | ||
569 | } | ||
570 | |||
571 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) | ||
572 | { | ||
573 | size_t printed = trace__fprintf_threads_header(fp); | ||
574 | struct rb_node *nd; | ||
575 | |||
576 | for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { | ||
577 | struct thread *thread = rb_entry(nd, struct thread, rb_node); | ||
578 | struct thread_trace *ttrace = thread->priv; | ||
579 | const char *color; | ||
580 | double ratio; | ||
581 | |||
582 | if (ttrace == NULL) | ||
583 | continue; | ||
584 | |||
585 | ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; | ||
586 | |||
587 | color = PERF_COLOR_NORMAL; | ||
588 | if (ratio > 50.0) | ||
589 | color = PERF_COLOR_RED; | ||
590 | else if (ratio > 25.0) | ||
591 | color = PERF_COLOR_GREEN; | ||
592 | else if (ratio > 5.0) | ||
593 | color = PERF_COLOR_YELLOW; | ||
594 | |||
595 | printed += color_fprintf(fp, color, "%20s", thread->comm); | ||
596 | printed += fprintf(fp, " - %-5d :%11lu [", thread->pid, ttrace->nr_events); | ||
597 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | ||
598 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | ||
599 | } | ||
600 | |||
601 | return printed; | ||
602 | } | ||
603 | |||
604 | static int trace__set_duration(const struct option *opt, const char *str, | ||
605 | int unset __maybe_unused) | ||
606 | { | ||
607 | struct trace *trace = opt->value; | ||
608 | |||
609 | trace->duration_filter = atof(str); | ||
610 | return 0; | ||
611 | } | ||
612 | |||
294 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 613 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
295 | { | 614 | { |
296 | const char * const trace_usage[] = { | 615 | const char * const trace_usage[] = { |
297 | "perf trace [<options>]", | 616 | "perf trace [<options>] [<command>]", |
617 | "perf trace [<options>] -- <command> [<options>]", | ||
298 | NULL | 618 | NULL |
299 | }; | 619 | }; |
300 | struct trace trace = { | 620 | struct trace trace = { |
@@ -328,21 +648,38 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
328 | "number of mmap data pages"), | 648 | "number of mmap data pages"), |
329 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", | 649 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", |
330 | "user to profile"), | 650 | "user to profile"), |
651 | OPT_CALLBACK(0, "duration", &trace, "float", | ||
652 | "show only events with duration > N.M ms", | ||
653 | trace__set_duration), | ||
654 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), | ||
331 | OPT_END() | 655 | OPT_END() |
332 | }; | 656 | }; |
333 | int err; | 657 | int err; |
658 | char bf[BUFSIZ]; | ||
334 | 659 | ||
335 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 660 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
336 | if (argc) | 661 | |
337 | usage_with_options(trace_usage, trace_options); | 662 | err = perf_target__validate(&trace.opts.target); |
663 | if (err) { | ||
664 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | ||
665 | printf("%s", bf); | ||
666 | return err; | ||
667 | } | ||
338 | 668 | ||
339 | err = perf_target__parse_uid(&trace.opts.target); | 669 | err = perf_target__parse_uid(&trace.opts.target); |
340 | if (err) { | 670 | if (err) { |
341 | char bf[BUFSIZ]; | ||
342 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 671 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
343 | printf("%s", bf); | 672 | printf("%s", bf); |
344 | return err; | 673 | return err; |
345 | } | 674 | } |
346 | 675 | ||
347 | return trace__run(&trace); | 676 | if (!argc && perf_target__none(&trace.opts.target)) |
677 | trace.opts.target.system_wide = true; | ||
678 | |||
679 | err = trace__run(&trace, argc, argv); | ||
680 | |||
681 | if (trace.sched && !err) | ||
682 | trace__fprintf_thread_summary(&trace, stdout); | ||
683 | |||
684 | return err; | ||
348 | } | 685 | } |