aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
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
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')
-rw-r--r--tools/perf/Documentation/perf-timechart.txt3
-rw-r--r--tools/perf/builtin-timechart.c169
-rw-r--r--tools/perf/util/svghelper.c27
-rw-r--r--tools/perf/util/svghelper.h12
4 files changed, 175 insertions, 36 deletions
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt
index 8359cfadf0ac..271dd4ed5b05 100644
--- a/tools/perf/Documentation/perf-timechart.txt
+++ b/tools/perf/Documentation/perf-timechart.txt
@@ -68,6 +68,9 @@ RECORD OPTIONS
68-T:: 68-T::
69--tasks-only:: 69--tasks-only::
70 Record only tasks-related events 70 Record only tasks-related events
71-g::
72--callchain::
73 Do call-graph (stack chain/backtrace) recording
71 74
72SEE ALSO 75SEE ALSO
73-------- 76--------
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[] = {
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index 9a5b41392e01..8b79d3ad1246 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -130,7 +130,7 @@ void svg_box(int Yslot, u64 start, u64 end, const char *type)
130} 130}
131 131
132static char *time_to_string(u64 duration); 132static char *time_to_string(u64 duration);
133void svg_blocked(int Yslot, int cpu, u64 start, u64 end) 133void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
134{ 134{
135 if (!svgfile) 135 if (!svgfile)
136 return; 136 return;
@@ -138,11 +138,13 @@ void svg_blocked(int Yslot, int cpu, u64 start, u64 end)
138 fprintf(svgfile, "<g>\n"); 138 fprintf(svgfile, "<g>\n");
139 fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu, 139 fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu,
140 time_to_string(end - start)); 140 time_to_string(end - start));
141 if (backtrace)
142 fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace);
141 svg_box(Yslot, start, end, "blocked"); 143 svg_box(Yslot, start, end, "blocked");
142 fprintf(svgfile, "</g>\n"); 144 fprintf(svgfile, "</g>\n");
143} 145}
144 146
145void svg_running(int Yslot, int cpu, u64 start, u64 end) 147void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
146{ 148{
147 double text_size; 149 double text_size;
148 if (!svgfile) 150 if (!svgfile)
@@ -152,6 +154,8 @@ void svg_running(int Yslot, int cpu, u64 start, u64 end)
152 154
153 fprintf(svgfile, "<title>#%d running %s</title>\n", 155 fprintf(svgfile, "<title>#%d running %s</title>\n",
154 cpu, time_to_string(end - start)); 156 cpu, time_to_string(end - start));
157 if (backtrace)
158 fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace);
155 fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n", 159 fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n",
156 time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT); 160 time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT);
157 161
@@ -187,7 +191,7 @@ static char *time_to_string(u64 duration)
187 return text; 191 return text;
188} 192}
189 193
190void svg_waiting(int Yslot, int cpu, u64 start, u64 end) 194void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
191{ 195{
192 char *text; 196 char *text;
193 const char *style; 197 const char *style;
@@ -212,6 +216,8 @@ void svg_waiting(int Yslot, int cpu, u64 start, u64 end)
212 216
213 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); 217 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT);
214 fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); 218 fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start));
219 if (backtrace)
220 fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace);
215 fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", 221 fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
216 time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style); 222 time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);
217 if (font_size > MIN_TEXT_SIZE) 223 if (font_size > MIN_TEXT_SIZE)
@@ -382,7 +388,7 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
382} 388}
383 389
384 390
385void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2) 391void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace)
386{ 392{
387 double height; 393 double height;
388 394
@@ -396,6 +402,9 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
396 desc1 ? desc1 : "?", 402 desc1 ? desc1 : "?",
397 desc2 ? desc2 : "?"); 403 desc2 ? desc2 : "?");
398 404
405 if (backtrace)
406 fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
407
399 if (row1 < row2) { 408 if (row1 < row2) {
400 if (row1) { 409 if (row1) {
401 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", 410 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
@@ -437,7 +446,7 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
437 fprintf(svgfile, "</g>\n"); 446 fprintf(svgfile, "</g>\n");
438} 447}
439 448
440void svg_wakeline(u64 start, int row1, int row2) 449void svg_wakeline(u64 start, int row1, int row2, const char *backtrace)
441{ 450{
442 double height; 451 double height;
443 452
@@ -447,6 +456,9 @@ void svg_wakeline(u64 start, int row1, int row2)
447 456
448 fprintf(svgfile, "<g>\n"); 457 fprintf(svgfile, "<g>\n");
449 458
459 if (backtrace)
460 fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
461
450 if (row1 < row2) 462 if (row1 < row2)
451 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", 463 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
452 time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT); 464 time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT);
@@ -463,7 +475,7 @@ void svg_wakeline(u64 start, int row1, int row2)
463 fprintf(svgfile, "</g>\n"); 475 fprintf(svgfile, "</g>\n");
464} 476}
465 477
466void svg_interrupt(u64 start, int row) 478void svg_interrupt(u64 start, int row, const char *backtrace)
467{ 479{
468 if (!svgfile) 480 if (!svgfile)
469 return; 481 return;
@@ -472,6 +484,9 @@ void svg_interrupt(u64 start, int row)
472 484
473 fprintf(svgfile, "<title>Wakeup from interrupt</title>\n"); 485 fprintf(svgfile, "<title>Wakeup from interrupt</title>\n");
474 486
487 if (backtrace)
488 fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
489
475 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", 490 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
476 time2pixels(start), row * SLOT_MULT); 491 time2pixels(start), row * SLOT_MULT);
477 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", 492 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
index aa533459ab76..fad79ceeac1a 100644
--- a/tools/perf/util/svghelper.h
+++ b/tools/perf/util/svghelper.h
@@ -5,9 +5,9 @@
5 5
6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); 6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
7extern void svg_box(int Yslot, u64 start, u64 end, const char *type); 7extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
8extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end); 8extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
9extern void svg_running(int Yslot, int cpu, u64 start, u64 end); 9extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
10extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end); 10extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
11extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); 11extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
12 12
13 13
@@ -18,9 +18,9 @@ extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);
18 18
19extern void svg_time_grid(void); 19extern void svg_time_grid(void);
20extern void svg_legenda(void); 20extern void svg_legenda(void);
21extern void svg_wakeline(u64 start, int row1, int row2); 21extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace);
22extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2); 22extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace);
23extern void svg_interrupt(u64 start, int row); 23extern void svg_interrupt(u64 start, int row, const char *backtrace);
24extern void svg_text(int Yslot, u64 start, const char *text); 24extern void svg_text(int Yslot, u64 start, const char *text);
25extern void svg_close(void); 25extern void svg_close(void);
26 26