diff options
author | Stanislav Fomichev <stfomichev@yandex-team.ru> | 2013-12-02 09:37:36 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-12-16 14:34:53 -0500 |
commit | c507999790438cde78b5618fa64daefd697035af (patch) | |
tree | 0ee428460b358e6cc362450622d9b8941db1feca | |
parent | 58b9a18ecd251cbd6e666ad792023ab77c7d100e (diff) |
perf timechart: Add support for topology
Add -t switch to sort CPUs topologically.
Signed-off-by: Stanislav Fomichev <stfomichev@yandex-team.ru>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ramkumar Ramachandra <artagnon@gmail.com>
Link: http://lkml.kernel.org/r/1385995056-20158-5-git-send-email-stfomichev@yandex-team.ru
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/Documentation/perf-timechart.txt | 3 | ||||
-rw-r--r-- | tools/perf/builtin-timechart.c | 17 | ||||
-rw-r--r-- | tools/perf/util/svghelper.c | 132 | ||||
-rw-r--r-- | tools/perf/util/svghelper.h | 2 |
4 files changed, 151 insertions, 3 deletions
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 271dd4ed5b05..367c1be0551c 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt | |||
@@ -59,6 +59,9 @@ $ perf timechart | |||
59 | -n:: | 59 | -n:: |
60 | --proc-num:: | 60 | --proc-num:: |
61 | Print task info for at least given number of tasks. | 61 | Print task info for at least given number of tasks. |
62 | -t:: | ||
63 | --topology:: | ||
64 | Sort CPUs according to topology. | ||
62 | 65 | ||
63 | RECORD OPTIONS | 66 | RECORD OPTIONS |
64 | -------------- | 67 | -------------- |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index db9c4c172587..8bde57c5c908 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -58,7 +58,8 @@ struct timechart { | |||
58 | first_time, last_time; | 58 | first_time, last_time; |
59 | bool power_only, | 59 | bool power_only, |
60 | tasks_only, | 60 | tasks_only, |
61 | with_backtrace; | 61 | with_backtrace, |
62 | topology; | ||
62 | }; | 63 | }; |
63 | 64 | ||
64 | struct per_pidcomm; | 65 | struct per_pidcomm; |
@@ -1077,6 +1078,18 @@ static int process_header(struct perf_file_section *section __maybe_unused, | |||
1077 | case HEADER_NRCPUS: | 1078 | case HEADER_NRCPUS: |
1078 | tchart->numcpus = ph->env.nr_cpus_avail; | 1079 | tchart->numcpus = ph->env.nr_cpus_avail; |
1079 | break; | 1080 | break; |
1081 | |||
1082 | case HEADER_CPU_TOPOLOGY: | ||
1083 | if (!tchart->topology) | ||
1084 | break; | ||
1085 | |||
1086 | if (svg_build_topology_map(ph->env.sibling_cores, | ||
1087 | ph->env.nr_sibling_cores, | ||
1088 | ph->env.sibling_threads, | ||
1089 | ph->env.nr_sibling_threads)) | ||
1090 | fprintf(stderr, "problem building topology\n"); | ||
1091 | break; | ||
1092 | |||
1080 | default: | 1093 | default: |
1081 | break; | 1094 | break; |
1082 | } | 1095 | } |
@@ -1267,6 +1280,8 @@ int cmd_timechart(int argc, const char **argv, | |||
1267 | "Look for files with symbols relative to this directory"), | 1280 | "Look for files with symbols relative to this directory"), |
1268 | OPT_INTEGER('n', "proc-num", &tchart.proc_num, | 1281 | OPT_INTEGER('n', "proc-num", &tchart.proc_num, |
1269 | "min. number of tasks to print"), | 1282 | "min. number of tasks to print"), |
1283 | OPT_BOOLEAN('t', "topology", &tchart.topology, | ||
1284 | "sort CPUs according to topology"), | ||
1270 | OPT_END() | 1285 | OPT_END() |
1271 | }; | 1286 | }; |
1272 | const char * const timechart_usage[] = { | 1287 | const char * const timechart_usage[] = { |
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 927851d05d03..9468136735ca 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c | |||
@@ -17,8 +17,11 @@ | |||
17 | #include <stdlib.h> | 17 | #include <stdlib.h> |
18 | #include <unistd.h> | 18 | #include <unistd.h> |
19 | #include <string.h> | 19 | #include <string.h> |
20 | #include <linux/bitops.h> | ||
20 | 21 | ||
22 | #include "perf.h" | ||
21 | #include "svghelper.h" | 23 | #include "svghelper.h" |
24 | #include "cpumap.h" | ||
22 | 25 | ||
23 | static u64 first_time, last_time; | 26 | static u64 first_time, last_time; |
24 | static u64 turbo_frequency, max_freq; | 27 | static u64 turbo_frequency, max_freq; |
@@ -39,9 +42,14 @@ static double cpu2slot(int cpu) | |||
39 | return 2 * cpu + 1; | 42 | return 2 * cpu + 1; |
40 | } | 43 | } |
41 | 44 | ||
45 | static int *topology_map; | ||
46 | |||
42 | static double cpu2y(int cpu) | 47 | static double cpu2y(int cpu) |
43 | { | 48 | { |
44 | return cpu2slot(cpu) * SLOT_MULT; | 49 | if (topology_map) |
50 | return cpu2slot(topology_map[cpu]) * SLOT_MULT; | ||
51 | else | ||
52 | return cpu2slot(cpu) * SLOT_MULT; | ||
45 | } | 53 | } |
46 | 54 | ||
47 | static double time2pixels(u64 __time) | 55 | static double time2pixels(u64 __time) |
@@ -275,7 +283,7 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq) | |||
275 | time2pixels(last_time)-time2pixels(first_time), | 283 | time2pixels(last_time)-time2pixels(first_time), |
276 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); | 284 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); |
277 | 285 | ||
278 | sprintf(cpu_string, "CPU %i", (int)cpu+1); | 286 | sprintf(cpu_string, "CPU %i", (int)cpu); |
279 | fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n", | 287 | fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n", |
280 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); | 288 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); |
281 | 289 | ||
@@ -568,3 +576,123 @@ void svg_close(void) | |||
568 | svgfile = NULL; | 576 | svgfile = NULL; |
569 | } | 577 | } |
570 | } | 578 | } |
579 | |||
580 | #define cpumask_bits(maskp) ((maskp)->bits) | ||
581 | typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t; | ||
582 | |||
583 | struct topology { | ||
584 | cpumask_t *sib_core; | ||
585 | int sib_core_nr; | ||
586 | cpumask_t *sib_thr; | ||
587 | int sib_thr_nr; | ||
588 | }; | ||
589 | |||
590 | static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos) | ||
591 | { | ||
592 | int i; | ||
593 | int thr; | ||
594 | |||
595 | for (i = 0; i < t->sib_thr_nr; i++) { | ||
596 | if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i]))) | ||
597 | continue; | ||
598 | |||
599 | for_each_set_bit(thr, | ||
600 | cpumask_bits(&t->sib_thr[i]), | ||
601 | MAX_NR_CPUS) | ||
602 | if (map[thr] == -1) | ||
603 | map[thr] = (*pos)++; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static void scan_core_topology(int *map, struct topology *t) | ||
608 | { | ||
609 | int pos = 0; | ||
610 | int i; | ||
611 | int cpu; | ||
612 | |||
613 | for (i = 0; i < t->sib_core_nr; i++) | ||
614 | for_each_set_bit(cpu, | ||
615 | cpumask_bits(&t->sib_core[i]), | ||
616 | MAX_NR_CPUS) | ||
617 | scan_thread_topology(map, t, cpu, &pos); | ||
618 | } | ||
619 | |||
620 | static int str_to_bitmap(char *s, cpumask_t *b) | ||
621 | { | ||
622 | int i; | ||
623 | int ret = 0; | ||
624 | struct cpu_map *m; | ||
625 | int c; | ||
626 | |||
627 | m = cpu_map__new(s); | ||
628 | if (!m) | ||
629 | return -1; | ||
630 | |||
631 | for (i = 0; i < m->nr; i++) { | ||
632 | c = m->map[i]; | ||
633 | if (c >= MAX_NR_CPUS) { | ||
634 | ret = -1; | ||
635 | break; | ||
636 | } | ||
637 | |||
638 | set_bit(c, cpumask_bits(b)); | ||
639 | } | ||
640 | |||
641 | cpu_map__delete(m); | ||
642 | |||
643 | return ret; | ||
644 | } | ||
645 | |||
646 | int svg_build_topology_map(char *sib_core, int sib_core_nr, | ||
647 | char *sib_thr, int sib_thr_nr) | ||
648 | { | ||
649 | int i; | ||
650 | struct topology t; | ||
651 | |||
652 | t.sib_core_nr = sib_core_nr; | ||
653 | t.sib_thr_nr = sib_thr_nr; | ||
654 | t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t)); | ||
655 | t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t)); | ||
656 | |||
657 | if (!t.sib_core || !t.sib_thr) { | ||
658 | fprintf(stderr, "topology: no memory\n"); | ||
659 | goto exit; | ||
660 | } | ||
661 | |||
662 | for (i = 0; i < sib_core_nr; i++) { | ||
663 | if (str_to_bitmap(sib_core, &t.sib_core[i])) { | ||
664 | fprintf(stderr, "topology: can't parse siblings map\n"); | ||
665 | goto exit; | ||
666 | } | ||
667 | |||
668 | sib_core += strlen(sib_core) + 1; | ||
669 | } | ||
670 | |||
671 | for (i = 0; i < sib_thr_nr; i++) { | ||
672 | if (str_to_bitmap(sib_thr, &t.sib_thr[i])) { | ||
673 | fprintf(stderr, "topology: can't parse siblings map\n"); | ||
674 | goto exit; | ||
675 | } | ||
676 | |||
677 | sib_thr += strlen(sib_thr) + 1; | ||
678 | } | ||
679 | |||
680 | topology_map = malloc(sizeof(int) * MAX_NR_CPUS); | ||
681 | if (!topology_map) { | ||
682 | fprintf(stderr, "topology: no memory\n"); | ||
683 | goto exit; | ||
684 | } | ||
685 | |||
686 | for (i = 0; i < MAX_NR_CPUS; i++) | ||
687 | topology_map[i] = -1; | ||
688 | |||
689 | scan_core_topology(topology_map, &t); | ||
690 | |||
691 | return 0; | ||
692 | |||
693 | exit: | ||
694 | free(t.sib_core); | ||
695 | free(t.sib_thr); | ||
696 | |||
697 | return -1; | ||
698 | } | ||
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index 8b77ca631686..1df4fb6c3a4a 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
@@ -23,6 +23,8 @@ extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, cha | |||
23 | extern void svg_interrupt(u64 start, int row, const char *backtrace); | 23 | extern void svg_interrupt(u64 start, int row, const char *backtrace); |
24 | extern void svg_text(int Yslot, u64 start, const char *text); | 24 | extern void svg_text(int Yslot, u64 start, const char *text); |
25 | extern void svg_close(void); | 25 | extern void svg_close(void); |
26 | extern int svg_build_topology_map(char *sib_core, int sib_core_nr, | ||
27 | char *sib_thr, int sib_thr_nr); | ||
26 | 28 | ||
27 | extern int svg_page_width; | 29 | extern int svg_page_width; |
28 | 30 | ||