perf tools: Add MEM_TOPOLOGY feature to perf data file
authorJiri Olsa <jolsa@kernel.org>
Wed, 7 Mar 2018 15:50:08 +0000 (16:50 +0100)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 8 Mar 2018 14:30:46 +0000 (11:30 -0300)
Adding MEM_TOPOLOGY feature to perf data file,
that will carry physical memory map and its
node assignments.

The format of data in MEM_TOPOLOGY is as follows:

  0 - version          | for future changes
  8 - block_size_bytes | /sys/devices/system/memory/block_size_bytes
 16 - count            | number of nodes

 For each node we store map of physical indexes for
 each node:

 32 - node id          | node index
 40 - size             | size of bitmap
 48 - bitmap           | bitmap of memory indexes that belongs to node
                       | /sys/devices/system/node/node<NODE>/memory<INDEX>

The MEM_TOPOLOGY could be displayed with following
report command:

  $ perf report --header-only -I
  ...
  # memory nodes (nr 1, block size 0x8000000):
  #    0 [7G]: 0-23,32-69

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20180307155020.32613-8-jolsa@kernel.org
[ Rename 'index' to 'idx', as this breaks the build in rhel5, 6 and other systems where this is used by glibc headers ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/include/linux/bitmap.h
tools/perf/util/env.h
tools/perf/util/header.c
tools/perf/util/header.h

index ca16027..63440cc 100644 (file)
@@ -98,7 +98,7 @@ static inline int test_and_set_bit(int nr, unsigned long *addr)
 
 /**
  * bitmap_alloc - Allocate bitmap
- * @nr: Bit to set
+ * @nbits: Number of bits
  */
 static inline unsigned long *bitmap_alloc(int nbits)
 {
index bf970f5..c4ef2e5 100644 (file)
@@ -27,6 +27,12 @@ struct numa_node {
        struct cpu_map  *map;
 };
 
+struct memory_node {
+       u64              node;
+       u64              size;
+       unsigned long   *set;
+};
+
 struct perf_env {
        char                    *hostname;
        char                    *os_release;
@@ -43,6 +49,7 @@ struct perf_env {
        int                     nr_sibling_cores;
        int                     nr_sibling_threads;
        int                     nr_numa_nodes;
+       int                     nr_memory_nodes;
        int                     nr_pmu_mappings;
        int                     nr_groups;
        char                    *cmdline;
@@ -54,6 +61,8 @@ struct perf_env {
        struct cpu_cache_level  *caches;
        int                      caches_cnt;
        struct numa_node        *numa_nodes;
+       struct memory_node      *memory_nodes;
+       unsigned long long       memory_bsize;
 };
 
 extern struct perf_env perf_env;
index e0c3cad..e14b3f7 100644 (file)
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 #include <sys/utsname.h>
 #include <linux/time64.h>
+#include <dirent.h>
 
 #include "evlist.h"
 #include "evsel.h"
@@ -37,6 +38,7 @@
 #include "asm/bug.h"
 #include "tool.h"
 #include "time-utils.h"
+#include "units.h"
 
 #include "sane_ctype.h"
 
@@ -132,6 +134,25 @@ int do_write(struct feat_fd *ff, const void *buf, size_t size)
 }
 
 /* Return: 0 if succeded, -ERR if failed. */
+static int do_write_bitmap(struct feat_fd *ff, unsigned long *set, u64 size)
+{
+       u64 *p = (u64 *) set;
+       int i, ret;
+
+       ret = do_write(ff, &size, sizeof(size));
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; (u64) i < BITS_TO_U64(size); i++) {
+               ret = do_write(ff, p + i, sizeof(*p));
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/* Return: 0 if succeded, -ERR if failed. */
 int write_padded(struct feat_fd *ff, const void *bf,
                 size_t count, size_t count_aligned)
 {
@@ -243,6 +264,38 @@ static char *do_read_string(struct feat_fd *ff)
        return NULL;
 }
 
+/* Return: 0 if succeded, -ERR if failed. */
+static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize)
+{
+       unsigned long *set;
+       u64 size, *p;
+       int i, ret;
+
+       ret = do_read_u64(ff, &size);
+       if (ret)
+               return ret;
+
+       set = bitmap_alloc(size);
+       if (!set)
+               return -ENOMEM;
+
+       bitmap_zero(set, size);
+
+       p = (u64 *) set;
+
+       for (i = 0; (u64) i < BITS_TO_U64(size); i++) {
+               ret = do_read_u64(ff, p + i);
+               if (ret < 0) {
+                       free(set);
+                       return ret;
+               }
+       }
+
+       *pset  = set;
+       *psize = size;
+       return 0;
+}
+
 static int write_tracing_data(struct feat_fd *ff,
                              struct perf_evlist *evlist)
 {
@@ -1196,6 +1249,176 @@ static int write_sample_time(struct feat_fd *ff,
                        sizeof(evlist->last_sample_time));
 }
 
+
+static int memory_node__read(struct memory_node *n, unsigned long idx)
+{
+       unsigned int phys, size = 0;
+       char path[PATH_MAX];
+       struct dirent *ent;
+       DIR *dir;
+
+#define for_each_memory(mem, dir)                                      \
+       while ((ent = readdir(dir)))                                    \
+               if (strcmp(ent->d_name, ".") &&                         \
+                   strcmp(ent->d_name, "..") &&                        \
+                   sscanf(ent->d_name, "memory%u", &mem) == 1)
+
+       scnprintf(path, PATH_MAX,
+                 "%s/devices/system/node/node%lu",
+                 sysfs__mountpoint(), idx);
+
+       dir = opendir(path);
+       if (!dir) {
+               pr_warning("failed: cant' open memory sysfs data\n");
+               return -1;
+       }
+
+       for_each_memory(phys, dir) {
+               size = max(phys, size);
+       }
+
+       size++;
+
+       n->set = bitmap_alloc(size);
+       if (!n->set) {
+               closedir(dir);
+               return -ENOMEM;
+       }
+
+       bitmap_zero(n->set, size);
+       n->node = idx;
+       n->size = size;
+
+       rewinddir(dir);
+
+       for_each_memory(phys, dir) {
+               set_bit(phys, n->set);
+       }
+
+       closedir(dir);
+       return 0;
+}
+
+static int memory_node__sort(const void *a, const void *b)
+{
+       const struct memory_node *na = a;
+       const struct memory_node *nb = b;
+
+       return na->node - nb->node;
+}
+
+static int build_mem_topology(struct memory_node *nodes, u64 size, u64 *cntp)
+{
+       char path[PATH_MAX];
+       struct dirent *ent;
+       DIR *dir;
+       u64 cnt = 0;
+       int ret = 0;
+
+       scnprintf(path, PATH_MAX, "%s/devices/system/node/",
+                 sysfs__mountpoint());
+
+       dir = opendir(path);
+       if (!dir) {
+               pr_warning("failed: can't open node sysfs data\n");
+               return -1;
+       }
+
+       while (!ret && (ent = readdir(dir))) {
+               unsigned int idx;
+               int r;
+
+               if (!strcmp(ent->d_name, ".") ||
+                   !strcmp(ent->d_name, ".."))
+                       continue;
+
+               r = sscanf(ent->d_name, "node%u", &idx);
+               if (r != 1)
+                       continue;
+
+               if (WARN_ONCE(cnt >= size,
+                             "failed to write MEM_TOPOLOGY, way too many nodes\n"))
+                       return -1;
+
+               ret = memory_node__read(&nodes[cnt++], idx);
+       }
+
+       *cntp = cnt;
+       closedir(dir);
+
+       if (!ret)
+               qsort(nodes, cnt, sizeof(nodes[0]), memory_node__sort);
+
+       return ret;
+}
+
+#define MAX_MEMORY_NODES 2000
+
+/*
+ * The MEM_TOPOLOGY holds physical memory map for every
+ * node in system. The format of data is as follows:
+ *
+ *  0 - version          | for future changes
+ *  8 - block_size_bytes | /sys/devices/system/memory/block_size_bytes
+ * 16 - count            | number of nodes
+ *
+ * For each node we store map of physical indexes for
+ * each node:
+ *
+ * 32 - node id          | node index
+ * 40 - size             | size of bitmap
+ * 48 - bitmap           | bitmap of memory indexes that belongs to node
+ */
+static int write_mem_topology(struct feat_fd *ff __maybe_unused,
+                             struct perf_evlist *evlist __maybe_unused)
+{
+       static struct memory_node nodes[MAX_MEMORY_NODES];
+       u64 bsize, version = 1, i, nr;
+       int ret;
+
+       ret = sysfs__read_xll("devices/system/memory/block_size_bytes",
+                             (unsigned long long *) &bsize);
+       if (ret)
+               return ret;
+
+       ret = build_mem_topology(&nodes[0], MAX_MEMORY_NODES, &nr);
+       if (ret)
+               return ret;
+
+       ret = do_write(ff, &version, sizeof(version));
+       if (ret < 0)
+               goto out;
+
+       ret = do_write(ff, &bsize, sizeof(bsize));
+       if (ret < 0)
+               goto out;
+
+       ret = do_write(ff, &nr, sizeof(nr));
+       if (ret < 0)
+               goto out;
+
+       for (i = 0; i < nr; i++) {
+               struct memory_node *n = &nodes[i];
+
+               #define _W(v)                                           \
+                       ret = do_write(ff, &n->v, sizeof(n->v));        \
+                       if (ret < 0)                                    \
+                               goto out;
+
+               _W(node)
+               _W(size)
+
+               #undef _W
+
+               ret = do_write_bitmap(ff, n->set, n->size);
+               if (ret < 0)
+                       goto out;
+       }
+
+out:
+       return ret;
+}
+
 static void print_hostname(struct feat_fd *ff, FILE *fp)
 {
        fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
@@ -1543,6 +1766,35 @@ static void print_sample_time(struct feat_fd *ff, FILE *fp)
        fprintf(fp, "# sample duration : %10.3f ms\n", d);
 }
 
+static void memory_node__fprintf(struct memory_node *n,
+                                unsigned long long bsize, FILE *fp)
+{
+       char buf_map[100], buf_size[50];
+       unsigned long long size;
+
+       size = bsize * bitmap_weight(n->set, n->size);
+       unit_number__scnprintf(buf_size, 50, size);
+
+       bitmap_scnprintf(n->set, n->size, buf_map, 100);
+       fprintf(fp, "#  %3" PRIu64 " [%s]: %s\n", n->node, buf_size, buf_map);
+}
+
+static void print_mem_topology(struct feat_fd *ff, FILE *fp)
+{
+       struct memory_node *nodes;
+       int i, nr;
+
+       nodes = ff->ph->env.memory_nodes;
+       nr    = ff->ph->env.nr_memory_nodes;
+
+       fprintf(fp, "# memory nodes (nr %d, block size 0x%llx):\n",
+               nr, ff->ph->env.memory_bsize);
+
+       for (i = 0; i < nr; i++) {
+               memory_node__fprintf(&nodes[i], ff->ph->env.memory_bsize, fp);
+       }
+}
+
 static int __event_process_build_id(struct build_id_event *bev,
                                    char *filename,
                                    struct perf_session *session)
@@ -2205,6 +2457,58 @@ static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused)
        return 0;
 }
 
+static int process_mem_topology(struct feat_fd *ff,
+                               void *data __maybe_unused)
+{
+       struct memory_node *nodes;
+       u64 version, i, nr, bsize;
+       int ret = -1;
+
+       if (do_read_u64(ff, &version))
+               return -1;
+
+       if (version != 1)
+               return -1;
+
+       if (do_read_u64(ff, &bsize))
+               return -1;
+
+       if (do_read_u64(ff, &nr))
+               return -1;
+
+       nodes = zalloc(sizeof(*nodes) * nr);
+       if (!nodes)
+               return -1;
+
+       for (i = 0; i < nr; i++) {
+               struct memory_node n;
+
+               #define _R(v)                           \
+                       if (do_read_u64(ff, &n.v))      \
+                               goto out;               \
+
+               _R(node)
+               _R(size)
+
+               #undef _R
+
+               if (do_read_bitmap(ff, &n.set, &n.size))
+                       goto out;
+
+               nodes[i] = n;
+       }
+
+       ff->ph->env.memory_bsize    = bsize;
+       ff->ph->env.memory_nodes    = nodes;
+       ff->ph->env.nr_memory_nodes = nr;
+       ret = 0;
+
+out:
+       if (ret)
+               free(nodes);
+       return ret;
+}
+
 struct feature_ops {
        int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
        void (*print)(struct feat_fd *ff, FILE *fp);
@@ -2263,6 +2567,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
        FEAT_OPN(STAT,          stat,           false),
        FEAT_OPN(CACHE,         cache,          true),
        FEAT_OPR(SAMPLE_TIME,   sample_time,    false),
+       FEAT_OPR(MEM_TOPOLOGY,  mem_topology,   true),
 };
 
 struct header_print_data {
index 942bdec..90d4577 100644 (file)
@@ -36,6 +36,7 @@ enum {
        HEADER_STAT,
        HEADER_CACHE,
        HEADER_SAMPLE_TIME,
+       HEADER_MEM_TOPOLOGY,
        HEADER_LAST_FEATURE,
        HEADER_FEAT_BITS        = 256,
 };