aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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