aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-timechart.c
diff options
context:
space:
mode:
authorStanislav Fomichev <stfomichev@yandex-team.ru>2013-11-01 12:25:51 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2013-11-27 12:58:37 -0500
commit6f8d67fa0c6bdca535ecab137c44b095023cc1b4 (patch)
tree4c624985d3666affdb2800eb0ca59384fbd9be6d /tools/perf/builtin-timechart.c
parent367b3152d72c20d789b07650bd1189ce0fe266b8 (diff)
perf timechart: Add backtrace support
Add -g flag to `perf timechart record` which saves callchain info in the perf.data. When generating SVG, add backtrace information to the figure details, so now it's possible to see which code path woke up the task and why some task went to sleep. Signed-off-by: Stanislav Fomichev <stfomichev@yandex-team.ru> Acked-by: Namhyung Kim <namhyung@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1383323151-19810-8-git-send-email-stfomichev@yandex-team.ru Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-timechart.c')
-rw-r--r--tools/perf/builtin-timechart.c169
1 files changed, 145 insertions, 24 deletions
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 1c60ed3f5b97..491662bdfe0b 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -52,6 +52,7 @@ static u64 first_time, last_time;
52 52
53static bool power_only; 53static bool power_only;
54static bool tasks_only; 54static bool tasks_only;
55static bool with_backtrace;
55 56
56 57
57struct per_pid; 58struct per_pid;
@@ -126,6 +127,7 @@ struct cpu_sample {
126 u64 end_time; 127 u64 end_time;
127 int type; 128 int type;
128 int cpu; 129 int cpu;
130 const char *backtrace;
129}; 131};
130 132
131static struct per_pid *all_data; 133static struct per_pid *all_data;
@@ -147,6 +149,7 @@ struct wake_event {
147 int waker; 149 int waker;
148 int wakee; 150 int wakee;
149 u64 time; 151 u64 time;
152 const char *backtrace;
150}; 153};
151 154
152static struct power_event *power_events; 155static struct power_event *power_events;
@@ -231,7 +234,8 @@ static void pid_exit(int pid, u64 timestamp)
231} 234}
232 235
233static void 236static void
234pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) 237pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end,
238 const char *backtrace)
235{ 239{
236 struct per_pid *p; 240 struct per_pid *p;
237 struct per_pidcomm *c; 241 struct per_pidcomm *c;
@@ -254,6 +258,7 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
254 sample->type = type; 258 sample->type = type;
255 sample->next = c->samples; 259 sample->next = c->samples;
256 sample->cpu = cpu; 260 sample->cpu = cpu;
261 sample->backtrace = backtrace;
257 c->samples = sample; 262 c->samples = sample;
258 263
259 if (sample->type == TYPE_RUNNING && end > start && start > 0) { 264 if (sample->type == TYPE_RUNNING && end > start && start > 0) {
@@ -405,7 +410,8 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
405} 410}
406 411
407static void 412static void
408sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te) 413sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te,
414 const char *backtrace)
409{ 415{
410 struct per_pid *p; 416 struct per_pid *p;
411 struct wakeup_entry *wake = (void *)te; 417 struct wakeup_entry *wake = (void *)te;
@@ -416,6 +422,7 @@ sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
416 422
417 we->time = timestamp; 423 we->time = timestamp;
418 we->waker = pid; 424 we->waker = pid;
425 we->backtrace = backtrace;
419 426
420 if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ)) 427 if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
421 we->waker = -1; 428 we->waker = -1;
@@ -430,13 +437,15 @@ sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
430 p->current->state = TYPE_WAITING; 437 p->current->state = TYPE_WAITING;
431 } 438 }
432 if (p && p->current && p->current->state == TYPE_BLOCKED) { 439 if (p && p->current && p->current->state == TYPE_BLOCKED) {
433 pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp); 440 pid_put_sample(p->pid, p->current->state, cpu,
441 p->current->state_since, timestamp, NULL);
434 p->current->state_since = timestamp; 442 p->current->state_since = timestamp;
435 p->current->state = TYPE_WAITING; 443 p->current->state = TYPE_WAITING;
436 } 444 }
437} 445}
438 446
439static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) 447static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te,
448 const char *backtrace)
440{ 449{
441 struct per_pid *p = NULL, *prev_p; 450 struct per_pid *p = NULL, *prev_p;
442 struct sched_switch *sw = (void *)te; 451 struct sched_switch *sw = (void *)te;
@@ -447,10 +456,14 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
447 p = find_create_pid(sw->next_pid); 456 p = find_create_pid(sw->next_pid);
448 457
449 if (prev_p->current && prev_p->current->state != TYPE_NONE) 458 if (prev_p->current && prev_p->current->state != TYPE_NONE)
450 pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp); 459 pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu,
460 prev_p->current->state_since, timestamp,
461 backtrace);
451 if (p && p->current) { 462 if (p && p->current) {
452 if (p->current->state != TYPE_NONE) 463 if (p->current->state != TYPE_NONE)
453 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); 464 pid_put_sample(sw->next_pid, p->current->state, cpu,
465 p->current->state_since, timestamp,
466 backtrace);
454 467
455 p->current->state_since = timestamp; 468 p->current->state_since = timestamp;
456 p->current->state = TYPE_RUNNING; 469 p->current->state = TYPE_RUNNING;
@@ -466,8 +479,87 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
466 } 479 }
467} 480}
468 481
482static const char *cat_backtrace(union perf_event *event,
483 struct perf_sample *sample,
484 struct machine *machine)
485{
486 struct addr_location al;
487 unsigned int i;
488 char *p = NULL;
489 size_t p_len;
490 u8 cpumode = PERF_RECORD_MISC_USER;
491 struct addr_location tal;
492 struct ip_callchain *chain = sample->callchain;
493 FILE *f = open_memstream(&p, &p_len);
494
495 if (!f) {
496 perror("open_memstream error");
497 return NULL;
498 }
499
500 if (!chain)
501 goto exit;
502
503 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
504 fprintf(stderr, "problem processing %d event, skipping it.\n",
505 event->header.type);
506 goto exit;
507 }
508
509 for (i = 0; i < chain->nr; i++) {
510 u64 ip;
511
512 if (callchain_param.order == ORDER_CALLEE)
513 ip = chain->ips[i];
514 else
515 ip = chain->ips[chain->nr - i - 1];
516
517 if (ip >= PERF_CONTEXT_MAX) {
518 switch (ip) {
519 case PERF_CONTEXT_HV:
520 cpumode = PERF_RECORD_MISC_HYPERVISOR;
521 break;
522 case PERF_CONTEXT_KERNEL:
523 cpumode = PERF_RECORD_MISC_KERNEL;
524 break;
525 case PERF_CONTEXT_USER:
526 cpumode = PERF_RECORD_MISC_USER;
527 break;
528 default:
529 pr_debug("invalid callchain context: "
530 "%"PRId64"\n", (s64) ip);
531
532 /*
533 * It seems the callchain is corrupted.
534 * Discard all.
535 */
536 free(p);
537 p = NULL;
538 goto exit;
539 }
540 continue;
541 }
542
543 tal.filtered = false;
544 thread__find_addr_location(al.thread, machine, cpumode,
545 MAP__FUNCTION, ip, &tal);
546
547 if (tal.sym)
548 fprintf(f, "..... %016" PRIx64 " %s\n", ip,
549 tal.sym->name);
550 else
551 fprintf(f, "..... %016" PRIx64 "\n", ip);
552 }
553
554exit:
555 fclose(f);
556
557 return p;
558}
559
469typedef int (*tracepoint_handler)(struct perf_evsel *evsel, 560typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
470 struct perf_sample *sample); 561 struct perf_sample *sample,
562 const char *backtrace);
471 563
472static int process_sample_event(struct perf_tool *tool __maybe_unused, 564static int process_sample_event(struct perf_tool *tool __maybe_unused,
473 union perf_event *event __maybe_unused, 565 union perf_event *event __maybe_unused,
@@ -487,7 +579,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
487 579
488 if (evsel->handler != NULL) { 580 if (evsel->handler != NULL) {
489 tracepoint_handler f = evsel->handler; 581 tracepoint_handler f = evsel->handler;
490 return f(evsel, sample); 582 return f(evsel, sample, cat_backtrace(event, sample, machine));
491 } 583 }
492 584
493 return 0; 585 return 0;
@@ -495,7 +587,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
495 587
496static int 588static int
497process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused, 589process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused,
498 struct perf_sample *sample) 590 struct perf_sample *sample,
591 const char *backtrace __maybe_unused)
499{ 592{
500 struct power_processor_entry *ppe = sample->raw_data; 593 struct power_processor_entry *ppe = sample->raw_data;
501 594
@@ -508,7 +601,8 @@ process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused,
508 601
509static int 602static int
510process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused, 603process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused,
511 struct perf_sample *sample) 604 struct perf_sample *sample,
605 const char *backtrace __maybe_unused)
512{ 606{
513 struct power_processor_entry *ppe = sample->raw_data; 607 struct power_processor_entry *ppe = sample->raw_data;
514 608
@@ -518,28 +612,31 @@ process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused,
518 612
519static int 613static int
520process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused, 614process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused,
521 struct perf_sample *sample) 615 struct perf_sample *sample,
616 const char *backtrace)
522{ 617{
523 struct trace_entry *te = sample->raw_data; 618 struct trace_entry *te = sample->raw_data;
524 619
525 sched_wakeup(sample->cpu, sample->time, sample->pid, te); 620 sched_wakeup(sample->cpu, sample->time, sample->pid, te, backtrace);
526 return 0; 621 return 0;
527} 622}
528 623
529static int 624static int
530process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused, 625process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused,
531 struct perf_sample *sample) 626 struct perf_sample *sample,
627 const char *backtrace)
532{ 628{
533 struct trace_entry *te = sample->raw_data; 629 struct trace_entry *te = sample->raw_data;
534 630
535 sched_switch(sample->cpu, sample->time, te); 631 sched_switch(sample->cpu, sample->time, te, backtrace);
536 return 0; 632 return 0;
537} 633}
538 634
539#ifdef SUPPORT_OLD_POWER_EVENTS 635#ifdef SUPPORT_OLD_POWER_EVENTS
540static int 636static int
541process_sample_power_start(struct perf_evsel *evsel __maybe_unused, 637process_sample_power_start(struct perf_evsel *evsel __maybe_unused,
542 struct perf_sample *sample) 638 struct perf_sample *sample,
639 const char *backtrace __maybe_unused)
543{ 640{
544 struct power_entry_old *peo = sample->raw_data; 641 struct power_entry_old *peo = sample->raw_data;
545 642
@@ -549,7 +646,8 @@ process_sample_power_start(struct perf_evsel *evsel __maybe_unused,
549 646
550static int 647static int
551process_sample_power_end(struct perf_evsel *evsel __maybe_unused, 648process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
552 struct perf_sample *sample) 649 struct perf_sample *sample,
650 const char *backtrace __maybe_unused)
553{ 651{
554 c_state_end(sample->cpu, sample->time); 652 c_state_end(sample->cpu, sample->time);
555 return 0; 653 return 0;
@@ -557,7 +655,8 @@ process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
557 655
558static int 656static int
559process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused, 657process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused,
560 struct perf_sample *sample) 658 struct perf_sample *sample,
659 const char *backtrace __maybe_unused)
561{ 660{
562 struct power_entry_old *peo = sample->raw_data; 661 struct power_entry_old *peo = sample->raw_data;
563 662
@@ -741,11 +840,12 @@ static void draw_wakeups(void)
741 } 840 }
742 841
743 if (we->waker == -1) 842 if (we->waker == -1)
744 svg_interrupt(we->time, to); 843 svg_interrupt(we->time, to, we->backtrace);
745 else if (from && to && abs(from - to) == 1) 844 else if (from && to && abs(from - to) == 1)
746 svg_wakeline(we->time, from, to); 845 svg_wakeline(we->time, from, to, we->backtrace);
747 else 846 else
748 svg_partial_wakeline(we->time, from, task_from, to, task_to); 847 svg_partial_wakeline(we->time, from, task_from, to,
848 task_to, we->backtrace);
749 we = we->next; 849 we = we->next;
750 850
751 free(task_from); 851 free(task_from);
@@ -798,11 +898,20 @@ static void draw_process_bars(void)
798 sample = c->samples; 898 sample = c->samples;
799 while (sample) { 899 while (sample) {
800 if (sample->type == TYPE_RUNNING) 900 if (sample->type == TYPE_RUNNING)
801 svg_running(Y, sample->cpu, sample->start_time, sample->end_time); 901 svg_running(Y, sample->cpu,
902 sample->start_time,
903 sample->end_time,
904 sample->backtrace);
802 if (sample->type == TYPE_BLOCKED) 905 if (sample->type == TYPE_BLOCKED)
803 svg_blocked(Y, sample->cpu, sample->start_time, sample->end_time); 906 svg_blocked(Y, sample->cpu,
907 sample->start_time,
908 sample->end_time,
909 sample->backtrace);
804 if (sample->type == TYPE_WAITING) 910 if (sample->type == TYPE_WAITING)
805 svg_waiting(Y, sample->cpu, sample->start_time, sample->end_time); 911 svg_waiting(Y, sample->cpu,
912 sample->start_time,
913 sample->end_time,
914 sample->backtrace);
806 sample = sample->next; 915 sample = sample->next;
807 } 916 }
808 917
@@ -1050,6 +1159,11 @@ static int __cmd_record(int argc, const char **argv)
1050 }; 1159 };
1051 unsigned int common_args_nr = ARRAY_SIZE(common_args); 1160 unsigned int common_args_nr = ARRAY_SIZE(common_args);
1052 1161
1162 const char * const backtrace_args[] = {
1163 "-g",
1164 };
1165 unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
1166
1053 const char * const power_args[] = { 1167 const char * const power_args[] = {
1054 "-e", "power:cpu_frequency", 1168 "-e", "power:cpu_frequency",
1055 "-e", "power:cpu_idle", 1169 "-e", "power:cpu_idle",
@@ -1089,8 +1203,11 @@ static int __cmd_record(int argc, const char **argv)
1089 old_power_args_nr = 0; 1203 old_power_args_nr = 0;
1090 } 1204 }
1091 1205
1206 if (!with_backtrace)
1207 backtrace_args_no = 0;
1208
1092 record_elems = common_args_nr + tasks_args_nr + 1209 record_elems = common_args_nr + tasks_args_nr +
1093 power_args_nr + old_power_args_nr; 1210 power_args_nr + old_power_args_nr + backtrace_args_no;
1094 1211
1095 rec_argc = record_elems + argc; 1212 rec_argc = record_elems + argc;
1096 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 1213 rec_argv = calloc(rec_argc + 1, sizeof(char *));
@@ -1102,6 +1219,9 @@ static int __cmd_record(int argc, const char **argv)
1102 for (i = 0; i < common_args_nr; i++) 1219 for (i = 0; i < common_args_nr; i++)
1103 *p++ = strdup(common_args[i]); 1220 *p++ = strdup(common_args[i]);
1104 1221
1222 for (i = 0; i < backtrace_args_no; i++)
1223 *p++ = strdup(backtrace_args[i]);
1224
1105 for (i = 0; i < tasks_args_nr; i++) 1225 for (i = 0; i < tasks_args_nr; i++)
1106 *p++ = strdup(tasks_args[i]); 1226 *p++ = strdup(tasks_args[i]);
1107 1227
@@ -1155,6 +1275,7 @@ int cmd_timechart(int argc, const char **argv,
1155 OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"), 1275 OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
1156 OPT_BOOLEAN('T', "tasks-only", &tasks_only, 1276 OPT_BOOLEAN('T', "tasks-only", &tasks_only,
1157 "output processes data only"), 1277 "output processes data only"),
1278 OPT_BOOLEAN('g', "callchain", &with_backtrace, "record callchain"),
1158 OPT_END() 1279 OPT_END()
1159 }; 1280 };
1160 const char * const record_usage[] = { 1281 const char * const record_usage[] = {