perf header: Support HYBRID_TOPOLOGY feature
authorJin Yao <yao.jin@linux.intel.com>
Fri, 14 May 2021 12:29:47 +0000 (20:29 +0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 17 May 2021 13:55:10 +0000 (10:55 -0300)
It is useful to let the user know about the hybrid topology.

Add the HYBRID_TOPOLOGY feature in header to indicate the core CPUs
and the atom CPUs.

With this patch a perf.data generated on a hybrid platform reports
the hybrid CPU list:

  root@otcpl-adl-s-2:~# perf report --header-only -I
  ...
  # hybrid cpu system:
  # cpu_core cpu list : 0-15
  # cpu_atom cpu list : 16-23

For a perf.data generated on a non-hybrid platform, reports a message
that HYBRID_TOPOLOGY is missing:

  root@kbl-ppc:~# perf report --header-only -I
  ...
  # missing features: TRACING_DATA BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY

Signed-off-by: Jin Yao <yao.jin@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jin Yao <yao.jin@intel.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lore.kernel.org/lkml/20210514122948.9472-2-yao.jin@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf.data-file-format.txt
tools/perf/util/cputopo.c
tools/perf/util/cputopo.h
tools/perf/util/env.c
tools/perf/util/env.h
tools/perf/util/header.c
tools/perf/util/header.h
tools/perf/util/pmu-hybrid.h

index 9ee9664..fbee9e5 100644 (file)
@@ -402,6 +402,23 @@ struct {
        u64 clockid_time_ns;
 };
 
+       HEADER_HYBRID_TOPOLOGY = 30,
+
+Indicate the hybrid CPUs. The format of data is as below.
+
+struct {
+       u32 nr;
+       struct {
+               char pmu_name[];
+               char cpus[];
+       } [nr]; /* Variable length records */
+};
+
+Example:
+  hybrid cpu system:
+  cpu_core cpu list : 0-15
+  cpu_atom cpu list : 16-23
+
        other bits are reserved and should ignored for now
        HEADER_FEAT_BITS        = 256,
 
index 1b52402..ec77e2a 100644 (file)
@@ -12,6 +12,7 @@
 #include "cpumap.h"
 #include "debug.h"
 #include "env.h"
+#include "pmu-hybrid.h"
 
 #define CORE_SIB_FMT \
        "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
@@ -351,3 +352,82 @@ void numa_topology__delete(struct numa_topology *tp)
 
        free(tp);
 }
+
+static int load_hybrid_node(struct hybrid_topology_node *node,
+                           struct perf_pmu *pmu)
+{
+       const char *sysfs;
+       char path[PATH_MAX];
+       char *buf = NULL, *p;
+       FILE *fp;
+       size_t len = 0;
+
+       node->pmu_name = strdup(pmu->name);
+       if (!node->pmu_name)
+               return -1;
+
+       sysfs = sysfs__mountpoint();
+       if (!sysfs)
+               goto err;
+
+       snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
+       fp = fopen(path, "r");
+       if (!fp)
+               goto err;
+
+       if (getline(&buf, &len, fp) <= 0) {
+               fclose(fp);
+               goto err;
+       }
+
+       p = strchr(buf, '\n');
+       if (p)
+               *p = '\0';
+
+       fclose(fp);
+       node->cpus = buf;
+       return 0;
+
+err:
+       zfree(&node->pmu_name);
+       free(buf);
+       return -1;
+}
+
+struct hybrid_topology *hybrid_topology__new(void)
+{
+       struct perf_pmu *pmu;
+       struct hybrid_topology *tp = NULL;
+       u32 nr, i = 0;
+
+       nr = perf_pmu__hybrid_pmu_num();
+       if (nr == 0)
+               return NULL;
+
+       tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
+       if (!tp)
+               return NULL;
+
+       tp->nr = nr;
+       perf_pmu__for_each_hybrid_pmu(pmu) {
+               if (load_hybrid_node(&tp->nodes[i], pmu)) {
+                       hybrid_topology__delete(tp);
+                       return NULL;
+               }
+               i++;
+       }
+
+       return tp;
+}
+
+void hybrid_topology__delete(struct hybrid_topology *tp)
+{
+       u32 i;
+
+       for (i = 0; i < tp->nr; i++) {
+               zfree(&tp->nodes[i].pmu_name);
+               zfree(&tp->nodes[i].cpus);
+       }
+
+       free(tp);
+}
index 6201c37..d9af971 100644 (file)
@@ -25,10 +25,23 @@ struct numa_topology {
        struct numa_topology_node       nodes[];
 };
 
+struct hybrid_topology_node {
+       char            *pmu_name;
+       char            *cpus;
+};
+
+struct hybrid_topology {
+       u32                             nr;
+       struct hybrid_topology_node     nodes[];
+};
+
 struct cpu_topology *cpu_topology__new(void);
 void cpu_topology__delete(struct cpu_topology *tp);
 
 struct numa_topology *numa_topology__new(void);
 void numa_topology__delete(struct numa_topology *tp);
 
+struct hybrid_topology *hybrid_topology__new(void);
+void hybrid_topology__delete(struct hybrid_topology *tp);
+
 #endif /* __PERF_CPUTOPO_H */
index 9130f6f..744ae87 100644 (file)
@@ -202,6 +202,12 @@ void perf_env__exit(struct perf_env *env)
        for (i = 0; i < env->nr_memory_nodes; i++)
                zfree(&env->memory_nodes[i].set);
        zfree(&env->memory_nodes);
+
+       for (i = 0; i < env->nr_hybrid_nodes; i++) {
+               zfree(&env->hybrid_nodes[i].pmu_name);
+               zfree(&env->hybrid_nodes[i].cpus);
+       }
+       zfree(&env->hybrid_nodes);
 }
 
 void perf_env__init(struct perf_env *env __maybe_unused)
index ca249bf..e5e5dee 100644 (file)
@@ -37,6 +37,11 @@ struct memory_node {
        unsigned long   *set;
 };
 
+struct hybrid_node {
+       char    *pmu_name;
+       char    *cpus;
+};
+
 struct perf_env {
        char                    *hostname;
        char                    *os_release;
@@ -59,6 +64,7 @@ struct perf_env {
        int                     nr_pmu_mappings;
        int                     nr_groups;
        int                     nr_cpu_pmu_caps;
+       int                     nr_hybrid_nodes;
        char                    *cmdline;
        const char              **cmdline_argv;
        char                    *sibling_cores;
@@ -77,6 +83,7 @@ struct perf_env {
        struct numa_node        *numa_nodes;
        struct memory_node      *memory_nodes;
        unsigned long long       memory_bsize;
+       struct hybrid_node      *hybrid_nodes;
 #ifdef HAVE_LIBBPF_SUPPORT
        /*
         * bpf_info_lock protects bpf rbtrees. This is needed because the
index 02b13c7..ebf4203 100644 (file)
@@ -932,6 +932,40 @@ static int write_clock_data(struct feat_fd *ff,
        return do_write(ff, data64, sizeof(*data64));
 }
 
+static int write_hybrid_topology(struct feat_fd *ff,
+                                struct evlist *evlist __maybe_unused)
+{
+       struct hybrid_topology *tp;
+       int ret;
+       u32 i;
+
+       tp = hybrid_topology__new();
+       if (!tp)
+               return -ENOENT;
+
+       ret = do_write(ff, &tp->nr, sizeof(u32));
+       if (ret < 0)
+               goto err;
+
+       for (i = 0; i < tp->nr; i++) {
+               struct hybrid_topology_node *n = &tp->nodes[i];
+
+               ret = do_write_string(ff, n->pmu_name);
+               if (ret < 0)
+                       goto err;
+
+               ret = do_write_string(ff, n->cpus);
+               if (ret < 0)
+                       goto err;
+       }
+
+       ret = 0;
+
+err:
+       hybrid_topology__delete(tp);
+       return ret;
+}
+
 static int write_dir_format(struct feat_fd *ff,
                            struct evlist *evlist __maybe_unused)
 {
@@ -1623,6 +1657,18 @@ static void print_clock_data(struct feat_fd *ff, FILE *fp)
                    clockid_name(clockid));
 }
 
+static void print_hybrid_topology(struct feat_fd *ff, FILE *fp)
+{
+       int i;
+       struct hybrid_node *n;
+
+       fprintf(fp, "# hybrid cpu system:\n");
+       for (i = 0; i < ff->ph->env.nr_hybrid_nodes; i++) {
+               n = &ff->ph->env.hybrid_nodes[i];
+               fprintf(fp, "# %s cpu list : %s\n", n->pmu_name, n->cpus);
+       }
+}
+
 static void print_dir_format(struct feat_fd *ff, FILE *fp)
 {
        struct perf_session *session;
@@ -2849,6 +2895,46 @@ static int process_clock_data(struct feat_fd *ff,
        return 0;
 }
 
+static int process_hybrid_topology(struct feat_fd *ff,
+                                  void *data __maybe_unused)
+{
+       struct hybrid_node *nodes, *n;
+       u32 nr, i;
+
+       /* nr nodes */
+       if (do_read_u32(ff, &nr))
+               return -1;
+
+       nodes = zalloc(sizeof(*nodes) * nr);
+       if (!nodes)
+               return -ENOMEM;
+
+       for (i = 0; i < nr; i++) {
+               n = &nodes[i];
+
+               n->pmu_name = do_read_string(ff);
+               if (!n->pmu_name)
+                       goto error;
+
+               n->cpus = do_read_string(ff);
+               if (!n->cpus)
+                       goto error;
+       }
+
+       ff->ph->env.nr_hybrid_nodes = nr;
+       ff->ph->env.hybrid_nodes = nodes;
+       return 0;
+
+error:
+       for (i = 0; i < nr; i++) {
+               free(nodes[i].pmu_name);
+               free(nodes[i].cpus);
+       }
+
+       free(nodes);
+       return -1;
+}
+
 static int process_dir_format(struct feat_fd *ff,
                              void *_data __maybe_unused)
 {
@@ -3117,6 +3203,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
        FEAT_OPR(COMPRESSED,    compressed,     false),
        FEAT_OPR(CPU_PMU_CAPS,  cpu_pmu_caps,   false),
        FEAT_OPR(CLOCK_DATA,    clock_data,     false),
+       FEAT_OPN(HYBRID_TOPOLOGY,       hybrid_topology,        true),
 };
 
 struct header_print_data {
index 2aca717..3f12ec0 100644 (file)
@@ -45,6 +45,7 @@ enum {
        HEADER_COMPRESSED,
        HEADER_CPU_PMU_CAPS,
        HEADER_CLOCK_DATA,
+       HEADER_HYBRID_TOPOLOGY,
        HEADER_LAST_FEATURE,
        HEADER_FEAT_BITS        = 256,
 };
index d0fa7bc..2b186c2 100644 (file)
@@ -19,4 +19,15 @@ struct perf_pmu *perf_pmu__find_hybrid_pmu(const char *name);
 bool perf_pmu__is_hybrid(const char *name);
 char *perf_pmu__hybrid_type_to_pmu(const char *type);
 
+static inline int perf_pmu__hybrid_pmu_num(void)
+{
+       struct perf_pmu *pmu;
+       int num = 0;
+
+       perf_pmu__for_each_hybrid_pmu(pmu)
+               num++;
+
+       return num;
+}
+
 #endif /* __PMU_HYBRID_H */