Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 720e98b5 authored by Jiri Olsa's avatar Jiri Olsa Committed by Arnaldo Carvalho de Melo
Browse files

perf tools: Add perf data cache feature



Storing CPU cache details under perf data. It's stored as new
HEADER_CACHE feature and it's displayed under header info with -I
option:

  $ perf report --header-only -I
  ...
  # CPU cache info:
  #  L1 Data                 32K [0-1]
  #  L1 Instruction          32K [0-1]
  #  L1 Data                 32K [2-3]
  #  L1 Instruction          32K [2-3]
  #  L2 Unified             256K [0-1]
  #  L2 Unified             256K [2-3]
  #  L3 Unified            4096K [0-3]
  ...

All distinct caches are stored/displayed.

Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Tested-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20160216150143.GA7119@krava.brq.redhat.com


[ Fixed leak on process_caches(), s/cache_level/cpu_cache_level/g ]
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent dd629cc0
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -6,6 +6,8 @@ struct perf_env perf_env;


void perf_env__exit(struct perf_env *env)
void perf_env__exit(struct perf_env *env)
{
{
	int i;

	zfree(&env->hostname);
	zfree(&env->hostname);
	zfree(&env->os_release);
	zfree(&env->os_release);
	zfree(&env->version);
	zfree(&env->version);
@@ -19,6 +21,10 @@ void perf_env__exit(struct perf_env *env)
	zfree(&env->numa_nodes);
	zfree(&env->numa_nodes);
	zfree(&env->pmu_mappings);
	zfree(&env->pmu_mappings);
	zfree(&env->cpu);
	zfree(&env->cpu);

	for (i = 0; i < env->caches_cnt; i++)
		cpu_cache_level__free(&env->caches[i]);
	zfree(&env->caches);
}
}


int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
@@ -75,3 +81,10 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
	env->nr_cpus_avail = nr_cpus;
	env->nr_cpus_avail = nr_cpus;
	return 0;
	return 0;
}
}

void cpu_cache_level__free(struct cpu_cache_level *cache)
{
	free(cache->type);
	free(cache->map);
	free(cache->size);
}
+15 −0
Original line number Original line Diff line number Diff line
#ifndef __PERF_ENV_H
#ifndef __PERF_ENV_H
#define __PERF_ENV_H
#define __PERF_ENV_H


#include <linux/types.h>

struct cpu_topology_map {
struct cpu_topology_map {
	int	socket_id;
	int	socket_id;
	int	core_id;
	int	core_id;
};
};


struct cpu_cache_level {
	u32	level;
	u32	line_size;
	u32	sets;
	u32	ways;
	char	*type;
	char	*size;
	char	*map;
};

struct perf_env {
struct perf_env {
	char			*hostname;
	char			*hostname;
	char			*os_release;
	char			*os_release;
@@ -31,6 +43,8 @@ struct perf_env {
	char			*numa_nodes;
	char			*numa_nodes;
	char			*pmu_mappings;
	char			*pmu_mappings;
	struct cpu_topology_map	*cpu;
	struct cpu_topology_map	*cpu;
	struct cpu_cache_level	*caches;
	int			 caches_cnt;
};
};


extern struct perf_env perf_env;
extern struct perf_env perf_env;
@@ -41,4 +55,5 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);


int perf_env__read_cpu_topology_map(struct perf_env *env);
int perf_env__read_cpu_topology_map(struct perf_env *env);


void cpu_cache_level__free(struct cpu_cache_level *cache);
#endif /* __PERF_ENV_H */
#endif /* __PERF_ENV_H */
+270 −0
Original line number Original line Diff line number Diff line
@@ -23,6 +23,8 @@
#include "strbuf.h"
#include "strbuf.h"
#include "build-id.h"
#include "build-id.h"
#include "data.h"
#include "data.h"
#include <api/fs/fs.h>
#include "asm/bug.h"


/*
/*
 * magic2 = "PERFILE2"
 * magic2 = "PERFILE2"
@@ -868,6 +870,199 @@ static int write_auxtrace(int fd, struct perf_header *h,
	return err;
	return err;
}
}


static int cpu_cache_level__sort(const void *a, const void *b)
{
	struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a;
	struct cpu_cache_level *cache_b = (struct cpu_cache_level *)b;

	return cache_a->level - cache_b->level;
}

static bool cpu_cache_level__cmp(struct cpu_cache_level *a, struct cpu_cache_level *b)
{
	if (a->level != b->level)
		return false;

	if (a->line_size != b->line_size)
		return false;

	if (a->sets != b->sets)
		return false;

	if (a->ways != b->ways)
		return false;

	if (strcmp(a->type, b->type))
		return false;

	if (strcmp(a->size, b->size))
		return false;

	if (strcmp(a->map, b->map))
		return false;

	return true;
}

static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 level)
{
	char path[PATH_MAX], file[PATH_MAX];
	struct stat st;
	size_t len;

	scnprintf(path, PATH_MAX, "devices/system/cpu/cpu%d/cache/index%d/", cpu, level);
	scnprintf(file, PATH_MAX, "%s/%s", sysfs__mountpoint(), path);

	if (stat(file, &st))
		return 1;

	scnprintf(file, PATH_MAX, "%s/level", path);
	if (sysfs__read_int(file, (int *) &cache->level))
		return -1;

	scnprintf(file, PATH_MAX, "%s/coherency_line_size", path);
	if (sysfs__read_int(file, (int *) &cache->line_size))
		return -1;

	scnprintf(file, PATH_MAX, "%s/number_of_sets", path);
	if (sysfs__read_int(file, (int *) &cache->sets))
		return -1;

	scnprintf(file, PATH_MAX, "%s/ways_of_associativity", path);
	if (sysfs__read_int(file, (int *) &cache->ways))
		return -1;

	scnprintf(file, PATH_MAX, "%s/type", path);
	if (sysfs__read_str(file, &cache->type, &len))
		return -1;

	cache->type[len] = 0;
	cache->type = rtrim(cache->type);

	scnprintf(file, PATH_MAX, "%s/size", path);
	if (sysfs__read_str(file, &cache->size, &len)) {
		free(cache->type);
		return -1;
	}

	cache->size[len] = 0;
	cache->size = rtrim(cache->size);

	scnprintf(file, PATH_MAX, "%s/shared_cpu_list", path);
	if (sysfs__read_str(file, &cache->map, &len)) {
		free(cache->map);
		free(cache->type);
		return -1;
	}

	cache->map[len] = 0;
	cache->map = rtrim(cache->map);
	return 0;
}

static void cpu_cache_level__fprintf(FILE *out, struct cpu_cache_level *c)
{
	fprintf(out, "L%d %-15s %8s [%s]\n", c->level, c->type, c->size, c->map);
}

static int build_caches(struct cpu_cache_level caches[], u32 size, u32 *cntp)
{
	u32 i, cnt = 0;
	long ncpus;
	u32 nr, cpu;
	u16 level;

	ncpus = sysconf(_SC_NPROCESSORS_CONF);
	if (ncpus < 0)
		return -1;

	nr = (u32)(ncpus & UINT_MAX);

	for (cpu = 0; cpu < nr; cpu++) {
		for (level = 0; level < 10; level++) {
			struct cpu_cache_level c;
			int err;

			err = cpu_cache_level__read(&c, cpu, level);
			if (err < 0)
				return err;

			if (err == 1)
				break;

			for (i = 0; i < cnt; i++) {
				if (cpu_cache_level__cmp(&c, &caches[i]))
					break;
			}

			if (i == cnt)
				caches[cnt++] = c;
			else
				cpu_cache_level__free(&c);

			if (WARN_ONCE(cnt == size, "way too many cpu caches.."))
				goto out;
		}
	}
 out:
	*cntp = cnt;
	return 0;
}

#define MAX_CACHES 2000

static int write_cache(int fd, struct perf_header *h __maybe_unused,
			  struct perf_evlist *evlist __maybe_unused)
{
	struct cpu_cache_level caches[MAX_CACHES];
	u32 cnt = 0, i, version = 1;
	int ret;

	ret = build_caches(caches, MAX_CACHES, &cnt);
	if (ret)
		goto out;

	qsort(&caches, cnt, sizeof(struct cpu_cache_level), cpu_cache_level__sort);

	ret = do_write(fd, &version, sizeof(u32));
	if (ret < 0)
		goto out;

	ret = do_write(fd, &cnt, sizeof(u32));
	if (ret < 0)
		goto out;

	for (i = 0; i < cnt; i++) {
		struct cpu_cache_level *c = &caches[i];

		#define _W(v)					\
			ret = do_write(fd, &c->v, sizeof(u32));	\
			if (ret < 0)				\
				goto out;

		_W(level)
		_W(line_size)
		_W(sets)
		_W(ways)
		#undef _W

		#define _W(v)						\
			ret = do_write_string(fd, (const char *) c->v);	\
			if (ret < 0)					\
				goto out;

		_W(type)
		_W(size)
		_W(map)
		#undef _W
	}

out:
	for (i = 0; i < cnt; i++)
		cpu_cache_level__free(&caches[i]);
	return ret;
}

static int write_stat(int fd __maybe_unused,
static int write_stat(int fd __maybe_unused,
		      struct perf_header *h __maybe_unused,
		      struct perf_header *h __maybe_unused,
		      struct perf_evlist *evlist __maybe_unused)
		      struct perf_evlist *evlist __maybe_unused)
@@ -1172,6 +1367,18 @@ static void print_stat(struct perf_header *ph __maybe_unused,
	fprintf(fp, "# contains stat data\n");
	fprintf(fp, "# contains stat data\n");
}
}


static void print_cache(struct perf_header *ph __maybe_unused,
			int fd __maybe_unused, FILE *fp __maybe_unused)
{
	int i;

	fprintf(fp, "# CPU cache info:\n");
	for (i = 0; i < ph->env.caches_cnt; i++) {
		fprintf(fp, "#  ");
		cpu_cache_level__fprintf(fp, &ph->env.caches[i]);
	}
}

static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
			       FILE *fp)
			       FILE *fp)
{
{
@@ -1920,6 +2127,68 @@ static int process_auxtrace(struct perf_file_section *section,
	return err;
	return err;
}
}


static int process_cache(struct perf_file_section *section __maybe_unused,
			 struct perf_header *ph __maybe_unused, int fd __maybe_unused,
			 void *data __maybe_unused)
{
	struct cpu_cache_level *caches;
	u32 cnt, i, version;

	if (readn(fd, &version, sizeof(version)) != sizeof(version))
		return -1;

	if (ph->needs_swap)
		version = bswap_32(version);

	if (version != 1)
		return -1;

	if (readn(fd, &cnt, sizeof(cnt)) != sizeof(cnt))
		return -1;

	if (ph->needs_swap)
		cnt = bswap_32(cnt);

	caches = zalloc(sizeof(*caches) * cnt);
	if (!caches)
		return -1;

	for (i = 0; i < cnt; i++) {
		struct cpu_cache_level c;

		#define _R(v)						\
			if (readn(fd, &c.v, sizeof(u32)) != sizeof(u32))\
				goto out_free_caches;			\
			if (ph->needs_swap)				\
				c.v = bswap_32(c.v);			\

		_R(level)
		_R(line_size)
		_R(sets)
		_R(ways)
		#undef _R

		#define _R(v)				\
			c.v = do_read_string(fd, ph);	\
			if (!c.v)			\
				goto out_free_caches;

		_R(type)
		_R(size)
		_R(map)
		#undef _R

		caches[i] = c;
	}

	ph->env.caches = caches;
	ph->env.caches_cnt = cnt;
	return 0;
out_free_caches:
	free(caches);
	return -1;
}

struct feature_ops {
struct feature_ops {
	int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
	int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
	void (*print)(struct perf_header *h, int fd, FILE *fp);
	void (*print)(struct perf_header *h, int fd, FILE *fp);
@@ -1962,6 +2231,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
	FEAT_OPP(HEADER_GROUP_DESC,	group_desc),
	FEAT_OPP(HEADER_GROUP_DESC,	group_desc),
	FEAT_OPP(HEADER_AUXTRACE,	auxtrace),
	FEAT_OPP(HEADER_AUXTRACE,	auxtrace),
	FEAT_OPA(HEADER_STAT,		stat),
	FEAT_OPA(HEADER_STAT,		stat),
	FEAT_OPF(HEADER_CACHE,		cache),
};
};


struct header_print_data {
struct header_print_data {
+1 −0
Original line number Original line Diff line number Diff line
@@ -32,6 +32,7 @@ enum {
	HEADER_GROUP_DESC,
	HEADER_GROUP_DESC,
	HEADER_AUXTRACE,
	HEADER_AUXTRACE,
	HEADER_STAT,
	HEADER_STAT,
	HEADER_CACHE,
	HEADER_LAST_FEATURE,
	HEADER_LAST_FEATURE,
	HEADER_FEAT_BITS	= 256,
	HEADER_FEAT_BITS	= 256,
};
};