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>
This commit is contained in:
committed by
Arnaldo Carvalho de Melo
parent
58b9a18ecd
commit
c507999790
@@ -59,6 +59,9 @@ $ perf timechart
|
|||||||
-n::
|
-n::
|
||||||
--proc-num::
|
--proc-num::
|
||||||
Print task info for at least given number of tasks.
|
Print task info for at least given number of tasks.
|
||||||
|
-t::
|
||||||
|
--topology::
|
||||||
|
Sort CPUs according to topology.
|
||||||
|
|
||||||
RECORD OPTIONS
|
RECORD OPTIONS
|
||||||
--------------
|
--------------
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ struct timechart {
|
|||||||
first_time, last_time;
|
first_time, last_time;
|
||||||
bool power_only,
|
bool power_only,
|
||||||
tasks_only,
|
tasks_only,
|
||||||
with_backtrace;
|
with_backtrace,
|
||||||
|
topology;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct per_pidcomm;
|
struct per_pidcomm;
|
||||||
@@ -1077,6 +1078,18 @@ static int process_header(struct perf_file_section *section __maybe_unused,
|
|||||||
case HEADER_NRCPUS:
|
case HEADER_NRCPUS:
|
||||||
tchart->numcpus = ph->env.nr_cpus_avail;
|
tchart->numcpus = ph->env.nr_cpus_avail;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HEADER_CPU_TOPOLOGY:
|
||||||
|
if (!tchart->topology)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (svg_build_topology_map(ph->env.sibling_cores,
|
||||||
|
ph->env.nr_sibling_cores,
|
||||||
|
ph->env.sibling_threads,
|
||||||
|
ph->env.nr_sibling_threads))
|
||||||
|
fprintf(stderr, "problem building topology\n");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1267,6 +1280,8 @@ int cmd_timechart(int argc, const char **argv,
|
|||||||
"Look for files with symbols relative to this directory"),
|
"Look for files with symbols relative to this directory"),
|
||||||
OPT_INTEGER('n', "proc-num", &tchart.proc_num,
|
OPT_INTEGER('n', "proc-num", &tchart.proc_num,
|
||||||
"min. number of tasks to print"),
|
"min. number of tasks to print"),
|
||||||
|
OPT_BOOLEAN('t', "topology", &tchart.topology,
|
||||||
|
"sort CPUs according to topology"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
const char * const timechart_usage[] = {
|
const char * const timechart_usage[] = {
|
||||||
|
|||||||
@@ -17,8 +17,11 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
|
||||||
|
#include "perf.h"
|
||||||
#include "svghelper.h"
|
#include "svghelper.h"
|
||||||
|
#include "cpumap.h"
|
||||||
|
|
||||||
static u64 first_time, last_time;
|
static u64 first_time, last_time;
|
||||||
static u64 turbo_frequency, max_freq;
|
static u64 turbo_frequency, max_freq;
|
||||||
@@ -39,9 +42,14 @@ static double cpu2slot(int cpu)
|
|||||||
return 2 * cpu + 1;
|
return 2 * cpu + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int *topology_map;
|
||||||
|
|
||||||
static double cpu2y(int cpu)
|
static double cpu2y(int cpu)
|
||||||
{
|
{
|
||||||
return cpu2slot(cpu) * SLOT_MULT;
|
if (topology_map)
|
||||||
|
return cpu2slot(topology_map[cpu]) * SLOT_MULT;
|
||||||
|
else
|
||||||
|
return cpu2slot(cpu) * SLOT_MULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double time2pixels(u64 __time)
|
static double time2pixels(u64 __time)
|
||||||
@@ -275,7 +283,7 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
|
|||||||
time2pixels(last_time)-time2pixels(first_time),
|
time2pixels(last_time)-time2pixels(first_time),
|
||||||
cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
|
cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
|
||||||
|
|
||||||
sprintf(cpu_string, "CPU %i", (int)cpu+1);
|
sprintf(cpu_string, "CPU %i", (int)cpu);
|
||||||
fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",
|
fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",
|
||||||
10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);
|
10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);
|
||||||
|
|
||||||
@@ -568,3 +576,123 @@ void svg_close(void)
|
|||||||
svgfile = NULL;
|
svgfile = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define cpumask_bits(maskp) ((maskp)->bits)
|
||||||
|
typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t;
|
||||||
|
|
||||||
|
struct topology {
|
||||||
|
cpumask_t *sib_core;
|
||||||
|
int sib_core_nr;
|
||||||
|
cpumask_t *sib_thr;
|
||||||
|
int sib_thr_nr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int thr;
|
||||||
|
|
||||||
|
for (i = 0; i < t->sib_thr_nr; i++) {
|
||||||
|
if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i])))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for_each_set_bit(thr,
|
||||||
|
cpumask_bits(&t->sib_thr[i]),
|
||||||
|
MAX_NR_CPUS)
|
||||||
|
if (map[thr] == -1)
|
||||||
|
map[thr] = (*pos)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scan_core_topology(int *map, struct topology *t)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
int i;
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
for (i = 0; i < t->sib_core_nr; i++)
|
||||||
|
for_each_set_bit(cpu,
|
||||||
|
cpumask_bits(&t->sib_core[i]),
|
||||||
|
MAX_NR_CPUS)
|
||||||
|
scan_thread_topology(map, t, cpu, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int str_to_bitmap(char *s, cpumask_t *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
struct cpu_map *m;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
m = cpu_map__new(s);
|
||||||
|
if (!m)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < m->nr; i++) {
|
||||||
|
c = m->map[i];
|
||||||
|
if (c >= MAX_NR_CPUS) {
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_bit(c, cpumask_bits(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_map__delete(m);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int svg_build_topology_map(char *sib_core, int sib_core_nr,
|
||||||
|
char *sib_thr, int sib_thr_nr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct topology t;
|
||||||
|
|
||||||
|
t.sib_core_nr = sib_core_nr;
|
||||||
|
t.sib_thr_nr = sib_thr_nr;
|
||||||
|
t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t));
|
||||||
|
t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t));
|
||||||
|
|
||||||
|
if (!t.sib_core || !t.sib_thr) {
|
||||||
|
fprintf(stderr, "topology: no memory\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sib_core_nr; i++) {
|
||||||
|
if (str_to_bitmap(sib_core, &t.sib_core[i])) {
|
||||||
|
fprintf(stderr, "topology: can't parse siblings map\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sib_core += strlen(sib_core) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sib_thr_nr; i++) {
|
||||||
|
if (str_to_bitmap(sib_thr, &t.sib_thr[i])) {
|
||||||
|
fprintf(stderr, "topology: can't parse siblings map\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sib_thr += strlen(sib_thr) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
topology_map = malloc(sizeof(int) * MAX_NR_CPUS);
|
||||||
|
if (!topology_map) {
|
||||||
|
fprintf(stderr, "topology: no memory\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NR_CPUS; i++)
|
||||||
|
topology_map[i] = -1;
|
||||||
|
|
||||||
|
scan_core_topology(topology_map, &t);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
free(t.sib_core);
|
||||||
|
free(t.sib_thr);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, cha
|
|||||||
extern void svg_interrupt(u64 start, int row, const char *backtrace);
|
extern void svg_interrupt(u64 start, int row, const char *backtrace);
|
||||||
extern void svg_text(int Yslot, u64 start, const char *text);
|
extern void svg_text(int Yslot, u64 start, const char *text);
|
||||||
extern void svg_close(void);
|
extern void svg_close(void);
|
||||||
|
extern int svg_build_topology_map(char *sib_core, int sib_core_nr,
|
||||||
|
char *sib_thr, int sib_thr_nr);
|
||||||
|
|
||||||
extern int svg_page_width;
|
extern int svg_page_width;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user