mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
perf header: Support CPU PMU capabilities
To stitch LBR call stack, the max LBR information is required. So the CPU PMU capabilities information has to be stored in perf header. Add a new feature HEADER_CPU_PMU_CAPS for CPU PMU capabilities. Retrieve all CPU PMU capabilities, not just max LBR information. Add variable max_branches to facilitate future usage. Committer testing: # ls -la /sys/devices/cpu/caps/ total 0 drwxr-xr-x. 2 root root 0 Apr 17 10:53 . drwxr-xr-x. 6 root root 0 Apr 17 07:02 .. -r--r--r--. 1 root root 4096 Apr 17 10:53 max_precise # # cat /sys/devices/cpu/caps/max_precise 0 # perf record sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.033 MB perf.data (7 samples) ] # # perf report --header-only | egrep 'cpu(desc|.*capabilities)' # cpudesc : AMD Ryzen 5 3600X 6-Core Processor # cpu pmu capabilities: max_precise=0 # And then on an Intel machine: $ ls -la /sys/devices/cpu/caps/ total 0 drwxr-xr-x. 2 root root 0 Apr 17 10:51 . drwxr-xr-x. 6 root root 0 Apr 17 10:04 .. -r--r--r--. 1 root root 4096 Apr 17 11:37 branches -r--r--r--. 1 root root 4096 Apr 17 10:51 max_precise -r--r--r--. 1 root root 4096 Apr 17 11:37 pmu_name $ cat /sys/devices/cpu/caps/max_precise 3 $ cat /sys/devices/cpu/caps/branches 32 $ cat /sys/devices/cpu/caps/pmu_name skylake $ perf record sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.001 MB perf.data (8 samples) ] $ perf report --header-only | egrep 'cpu(desc|.*capabilities)' # cpudesc : Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz # cpu pmu capabilities: branches=32, max_precise=3, pmu_name=skylake $ Signed-off-by: Kan Liang <kan.liang@linux.intel.com> Reviewed-by: Andi Kleen <ak@linux.intel.com> Acked-by: Jiri Olsa <jolsa@redhat.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexey Budankov <alexey.budankov@linux.intel.com> Cc: Mathieu Poirier <mathieu.poirier@linaro.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Pavel Gerasimov <pavel.gerasimov@intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ravi Bangoria <ravi.bangoria@linux.ibm.com> Cc: Stephane Eranian <eranian@google.com> Cc: Vitaly Slobodskoy <vitaly.slobodskoy@intel.com> Link: http://lore.kernel.org/lkml/20200319202517.23423-3-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
3a6c51e4d6
commit
6f91ea283a
@ -373,6 +373,22 @@ struct {
|
|||||||
Indicates that trace contains records of PERF_RECORD_COMPRESSED type
|
Indicates that trace contains records of PERF_RECORD_COMPRESSED type
|
||||||
that have perf_events records in compressed form.
|
that have perf_events records in compressed form.
|
||||||
|
|
||||||
|
HEADER_CPU_PMU_CAPS = 28,
|
||||||
|
|
||||||
|
A list of cpu PMU capabilities. The format of data is as below.
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32 nr_cpu_pmu_caps;
|
||||||
|
{
|
||||||
|
char name[];
|
||||||
|
char value[];
|
||||||
|
} [nr_cpu_pmu_caps]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
cpu pmu capabilities: branches=32, max_precise=3, pmu_name=icelake
|
||||||
|
|
||||||
other bits are reserved and should ignored for now
|
other bits are reserved and should ignored for now
|
||||||
HEADER_FEAT_BITS = 256,
|
HEADER_FEAT_BITS = 256,
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ struct perf_env {
|
|||||||
char *cpuid;
|
char *cpuid;
|
||||||
unsigned long long total_mem;
|
unsigned long long total_mem;
|
||||||
unsigned int msr_pmu_type;
|
unsigned int msr_pmu_type;
|
||||||
|
unsigned int max_branches;
|
||||||
|
|
||||||
int nr_cmdline;
|
int nr_cmdline;
|
||||||
int nr_sibling_cores;
|
int nr_sibling_cores;
|
||||||
@ -57,12 +58,14 @@ struct perf_env {
|
|||||||
int nr_memory_nodes;
|
int nr_memory_nodes;
|
||||||
int nr_pmu_mappings;
|
int nr_pmu_mappings;
|
||||||
int nr_groups;
|
int nr_groups;
|
||||||
|
int nr_cpu_pmu_caps;
|
||||||
char *cmdline;
|
char *cmdline;
|
||||||
const char **cmdline_argv;
|
const char **cmdline_argv;
|
||||||
char *sibling_cores;
|
char *sibling_cores;
|
||||||
char *sibling_dies;
|
char *sibling_dies;
|
||||||
char *sibling_threads;
|
char *sibling_threads;
|
||||||
char *pmu_mappings;
|
char *pmu_mappings;
|
||||||
|
char *cpu_pmu_caps;
|
||||||
struct cpu_topology_map *cpu;
|
struct cpu_topology_map *cpu;
|
||||||
struct cpu_cache_level *caches;
|
struct cpu_cache_level *caches;
|
||||||
int caches_cnt;
|
int caches_cnt;
|
||||||
|
@ -1395,6 +1395,38 @@ static int write_compressed(struct feat_fd *ff __maybe_unused,
|
|||||||
return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));
|
return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_cpu_pmu_caps(struct feat_fd *ff,
|
||||||
|
struct evlist *evlist __maybe_unused)
|
||||||
|
{
|
||||||
|
struct perf_pmu *cpu_pmu = perf_pmu__find("cpu");
|
||||||
|
struct perf_pmu_caps *caps = NULL;
|
||||||
|
int nr_caps;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!cpu_pmu)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
nr_caps = perf_pmu__caps_parse(cpu_pmu);
|
||||||
|
if (nr_caps < 0)
|
||||||
|
return nr_caps;
|
||||||
|
|
||||||
|
ret = do_write(ff, &nr_caps, sizeof(nr_caps));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
list_for_each_entry(caps, &cpu_pmu->caps, list) {
|
||||||
|
ret = do_write_string(ff, caps->name);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = do_write_string(ff, caps->value);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void print_hostname(struct feat_fd *ff, FILE *fp)
|
static void print_hostname(struct feat_fd *ff, FILE *fp)
|
||||||
{
|
{
|
||||||
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
|
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
|
||||||
@ -1809,6 +1841,27 @@ static void print_compressed(struct feat_fd *ff, FILE *fp)
|
|||||||
ff->ph->env.comp_level, ff->ph->env.comp_ratio);
|
ff->ph->env.comp_level, ff->ph->env.comp_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
|
||||||
|
{
|
||||||
|
const char *delimiter = "# cpu pmu capabilities: ";
|
||||||
|
u32 nr_caps = ff->ph->env.nr_cpu_pmu_caps;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
if (!nr_caps) {
|
||||||
|
fprintf(fp, "# cpu pmu capabilities: not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = ff->ph->env.cpu_pmu_caps;
|
||||||
|
while (nr_caps--) {
|
||||||
|
fprintf(fp, "%s%s", delimiter, str);
|
||||||
|
delimiter = ", ";
|
||||||
|
str += strlen(str) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
|
static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
|
||||||
{
|
{
|
||||||
const char *delimiter = "# pmu mappings: ";
|
const char *delimiter = "# pmu mappings: ";
|
||||||
@ -2846,6 +2899,60 @@ static int process_compressed(struct feat_fd *ff,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int process_cpu_pmu_caps(struct feat_fd *ff,
|
||||||
|
void *data __maybe_unused)
|
||||||
|
{
|
||||||
|
char *name, *value;
|
||||||
|
struct strbuf sb;
|
||||||
|
u32 nr_caps;
|
||||||
|
|
||||||
|
if (do_read_u32(ff, &nr_caps))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!nr_caps) {
|
||||||
|
pr_debug("cpu pmu capabilities not available\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ff->ph->env.nr_cpu_pmu_caps = nr_caps;
|
||||||
|
|
||||||
|
if (strbuf_init(&sb, 128) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (nr_caps--) {
|
||||||
|
name = do_read_string(ff);
|
||||||
|
if (!name)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
value = do_read_string(ff);
|
||||||
|
if (!value)
|
||||||
|
goto free_name;
|
||||||
|
|
||||||
|
if (strbuf_addf(&sb, "%s=%s", name, value) < 0)
|
||||||
|
goto free_value;
|
||||||
|
|
||||||
|
/* include a NULL character at the end */
|
||||||
|
if (strbuf_add(&sb, "", 1) < 0)
|
||||||
|
goto free_value;
|
||||||
|
|
||||||
|
if (!strcmp(name, "branches"))
|
||||||
|
ff->ph->env.max_branches = atoi(value);
|
||||||
|
|
||||||
|
free(value);
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
ff->ph->env.cpu_pmu_caps = strbuf_detach(&sb, NULL);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
free_value:
|
||||||
|
free(value);
|
||||||
|
free_name:
|
||||||
|
free(name);
|
||||||
|
error:
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#define FEAT_OPR(n, func, __full_only) \
|
#define FEAT_OPR(n, func, __full_only) \
|
||||||
[HEADER_##n] = { \
|
[HEADER_##n] = { \
|
||||||
.name = __stringify(n), \
|
.name = __stringify(n), \
|
||||||
@ -2903,6 +3010,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
|
|||||||
FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
|
FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
|
||||||
FEAT_OPR(BPF_BTF, bpf_btf, false),
|
FEAT_OPR(BPF_BTF, bpf_btf, false),
|
||||||
FEAT_OPR(COMPRESSED, compressed, false),
|
FEAT_OPR(COMPRESSED, compressed, false),
|
||||||
|
FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct header_print_data {
|
struct header_print_data {
|
||||||
|
@ -43,6 +43,7 @@ enum {
|
|||||||
HEADER_BPF_PROG_INFO,
|
HEADER_BPF_PROG_INFO,
|
||||||
HEADER_BPF_BTF,
|
HEADER_BPF_BTF,
|
||||||
HEADER_COMPRESSED,
|
HEADER_COMPRESSED,
|
||||||
|
HEADER_CPU_PMU_CAPS,
|
||||||
HEADER_LAST_FEATURE,
|
HEADER_LAST_FEATURE,
|
||||||
HEADER_FEAT_BITS = 256,
|
HEADER_FEAT_BITS = 256,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user