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

Commit f6bdafef authored by Jason Baron's avatar Jason Baron Committed by Peter Zijlstra
Browse files

perf_counter: Add tracepoint support to perf list, perf stat



Add support to 'perf list' and 'perf stat' for kernel tracepoints. The
implementation creates a 'for_each_subsystem' and 'for_each_event' for
easy iteration over the tracepoints.

Signed-off-by: default avatarJason Baron <jbaron@redhat.com>
Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <426129bf9fcc8ee63bb094cf736e7316a7dcd77a.1248190728.git.jbaron@redhat.com>
parent 28ac909b
Loading
Loading
Loading
Loading
+174 −1
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@ int nr_counters;

struct perf_counter_attr		attrs[MAX_COUNTERS];

static char default_debugfs_path[] = "/sys/kernel/debug/tracing/events";

struct event_symbol {
	u8	type;
	u64	config;
@@ -110,6 +112,88 @@ static unsigned long hw_cache_stat[C(MAX)] = {
 [C(BPU)]	= (CACHE_READ),
};

#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st)	       \
	while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next)	       \
	if (snprintf(file, MAXPATHLEN, "%s/%s", default_debugfs_path,	       \
			sys_dirent.d_name) &&				       \
	   (!stat(file, &st)) && (S_ISDIR(st.st_mode)) &&		       \
	   (strcmp(sys_dirent.d_name, ".")) &&				       \
	   (strcmp(sys_dirent.d_name, "..")))

#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st)    \
	while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next)        \
	if (snprintf(file, MAXPATHLEN, "%s/%s/%s", default_debugfs_path,       \
			sys_dirent.d_name, evt_dirent.d_name) &&	       \
	   (!stat(file, &st)) && (S_ISDIR(st.st_mode)) &&		       \
	   (strcmp(evt_dirent.d_name, ".")) &&				       \
	   (strcmp(evt_dirent.d_name, "..")))

#define MAX_EVENT_LENGTH 30

static int valid_debugfs_mount(void)
{
	struct statfs st_fs;

	if (statfs(default_debugfs_path, &st_fs) < 0)
		return -ENOENT;
	else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
		return -ENOENT;
	return 0;
}

static char *tracepoint_id_to_name(u64 config)
{
	static char tracepoint_name[2 * MAX_EVENT_LENGTH];
	DIR *sys_dir, *evt_dir;
	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
	struct stat st;
	char id_buf[4];
	int fd;
	u64 id;
	char evt_path[MAXPATHLEN];

	if (valid_debugfs_mount())
		return "unkown";

	sys_dir = opendir(default_debugfs_path);
	if (!sys_dir)
		goto cleanup;

	for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
		evt_dir = opendir(evt_path);
		if (!evt_dir)
			goto cleanup;
		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
								evt_path, st) {
			snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id",
				 default_debugfs_path, sys_dirent.d_name,
				 evt_dirent.d_name);
			fd = open(evt_path, O_RDONLY);
			if (fd < 0)
				continue;
			if (read(fd, id_buf, sizeof(id_buf)) < 0) {
				close(fd);
				continue;
			}
			close(fd);
			id = atoll(id_buf);
			if (id == config) {
				closedir(evt_dir);
				closedir(sys_dir);
				snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH,
					"%s:%s", sys_dirent.d_name,
					evt_dirent.d_name);
				return tracepoint_name;
			}
		}
		closedir(evt_dir);
	}

cleanup:
	closedir(sys_dir);
	return "unkown";
}

static int is_cache_op_valid(u8 cache_type, u8 cache_op)
{
	if (hw_cache_stat[cache_type] & COP(cache_op))
@@ -177,6 +261,9 @@ char *event_name(int counter)
			return sw_event_names[config];
		return "unknown-software";

	case PERF_TYPE_TRACEPOINT:
		return tracepoint_id_to_name(config);

	default:
		break;
	}
@@ -265,6 +352,53 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
	return 1;
}

static int parse_tracepoint_event(const char **strp,
				    struct perf_counter_attr *attr)
{
	const char *evt_name;
	char sys_name[MAX_EVENT_LENGTH];
	char id_buf[4];
	int fd;
	unsigned int sys_length, evt_length;
	u64 id;
	char evt_path[MAXPATHLEN];

	if (valid_debugfs_mount())
		return 0;

	evt_name = strchr(*strp, ':');
	if (!evt_name)
		return 0;

	sys_length = evt_name - *strp;
	if (sys_length >= MAX_EVENT_LENGTH)
		return 0;

	strncpy(sys_name, *strp, sys_length);
	sys_name[sys_length] = '\0';
	evt_name = evt_name + 1;
	evt_length = strlen(evt_name);
	if (evt_length >= MAX_EVENT_LENGTH)
		return 0;

	snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", default_debugfs_path,
				sys_name, evt_name);
	fd = open(evt_path, O_RDONLY);
	if (fd < 0)
		return 0;

	if (read(fd, id_buf, sizeof(id_buf)) < 0) {
		close(fd);
		return 0;
	}
	close(fd);
	id = atoll(id_buf);
	attr->config = id;
	attr->type = PERF_TYPE_TRACEPOINT;
	*strp = evt_name + evt_length;
	return 1;
}

static int check_events(const char *str, unsigned int i)
{
	int n;
@@ -374,7 +508,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
 */
static int parse_event_symbols(const char **str, struct perf_counter_attr *attr)
{
	if (!(parse_raw_event(str, attr) ||
	if (!(parse_tracepoint_event(str, attr) ||
	      parse_raw_event(str, attr) ||
	      parse_numeric_event(str, attr) ||
	      parse_symbolic_event(str, attr) ||
	      parse_generic_hw_event(str, attr)))
@@ -422,6 +557,42 @@ static const char * const event_type_descriptors[] = {
	"Hardware cache event",
};

/*
 * Print the events from <debugfs_mount_point>/tracing/events
 */

static void print_tracepoint_events(void)
{
	DIR *sys_dir, *evt_dir;
	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
	struct stat st;
	char evt_path[MAXPATHLEN];

	if (valid_debugfs_mount())
		return;

	sys_dir = opendir(default_debugfs_path);
	if (!sys_dir)
		goto cleanup;

	for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
		evt_dir = opendir(evt_path);
		if (!evt_dir)
			goto cleanup;
		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
								evt_path, st) {
			snprintf(evt_path, MAXPATHLEN, "%s:%s",
				 sys_dirent.d_name, evt_dirent.d_name);
			fprintf(stderr, "  %-40s [%s]\n", evt_path,
				event_type_descriptors[PERF_TYPE_TRACEPOINT+1]);
		}
		closedir(evt_dir);
	}

cleanup:
	closedir(sys_dir);
}

/*
 * Print the help text for the event symbols:
 */
@@ -472,5 +643,7 @@ void print_events(void)
		"rNNN");
	fprintf(stderr, "\n");

	print_tracepoint_events();

	exit(129);
}
+2 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
@@ -80,6 +81,7 @@
#include <netdb.h>
#include <pwd.h>
#include <inttypes.h>
#include "../../../include/linux/magic.h"

#ifndef NO_ICONV
#include <iconv.h>