diff options
| -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 | ||
