[PATCH v1 1/1] Extended events (platform-specific) support in perf

Tomasz Fujak t.fujak at samsung.com
Fri Jan 22 07:08:59 EST 2010


Signed-off-by: Tomasz Fujak <t.fujak at samsung.com>
Reviewed-by: Pawel Osciak <p.osciak at samsung.com>
Reviewed-by: Marek Szyprowski <m.szyprowski at samsung.com>
Reviewed-by: Kyungmin Park <kuyngmin.park at samsung.com>

---
 tools/perf/util/parse-events.c |  224 ++++++++++++++++++++++++++++++++++++++--
 1 files changed, 213 insertions(+), 11 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 05d0c5c..3e32198 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -9,6 +9,9 @@
 #include "header.h"
 #include "debugfs.h"
 
+#define			EXTENDED_EVENT_PATH	\
+			"/sys/devices/system/cpu/perf_events/extevents"
+
 int				nr_counters;
 
 struct perf_event_attr		attrs[MAX_COUNTERS];
@@ -60,6 +63,10 @@ static struct event_symbol event_symbols[] = {
 #define PERF_EVENT_TYPE(config)	__PERF_EVENT_FIELD(config, TYPE)
 #define PERF_EVENT_ID(config)		__PERF_EVENT_FIELD(config, EVENT)
 
+static struct event_symbol *extended_event_symbols;
+static unsigned int 	extended_event_count;
+static int 			extended_events_initialized;
+
 static const char *hw_event_names[] = {
 	"cycles",
 	"instructions",
@@ -241,6 +248,157 @@ static const char *tracepoint_id_to_name(u64 config)
 	return buf;
 }
 
+static unsigned count_lines(const char *str, unsigned size)
+{
+	unsigned count = 0;
+
+	while (size--)
+		count += ('\n' == *str++);
+	return count;
+}
+
+#define	BYTES_PER_CHUNK 4096
+/* returns the number of lines read;
+   on fail the return is negative and no memory is allocated
+   otherwise the *buf contains a memory chunk of which first
+   *size bytes are used for file contents
+ */
+static int read_file(int fd, char **buf, unsigned *size)
+{
+	unsigned bytes = BYTES_PER_CHUNK;
+	int lines = 0;
+	char *ptr = malloc(bytes);
+
+	*size = 0;
+	do {
+		int ret = read(fd, ptr + *size, bytes - *size);
+		if (ret < 0) {
+			if (EINTR == errno)
+				continue;
+			else {
+				free(ptr);
+				return -1;
+			}
+		}
+
+		if (!ret)
+			break;
+
+		lines += count_lines(ptr + *size, ret);
+		*size += ret;
+
+		if (*size == bytes) {
+			char *tmp = realloc(ptr, bytes <<= 1);
+			if (!tmp) {
+				free(ptr);
+				return -1;
+			}
+			ptr = tmp;
+		}
+	} while (1);
+
+	*buf = ptr;
+	return lines;
+}
+
+static int parse_extevent_file(struct event_symbol *symbols,
+			unsigned lines, char *buf)
+{
+	char *name, *description, *ptr, *end;
+	unsigned offset = 0, count = 0;
+	int items, eaten;
+	unsigned long long discard;
+
+/* each line format should be "0x%llx\t%s\t%lld-%lld\t%s\n" */
+	while (lines--) {
+		items = sscanf(buf + offset + 2, "%llx",
+				&symbols[count].config);
+		if (1 != items)
+			continue;
+
+		/* skip 0x%llx\t */
+		ptr = strchr(buf + offset, '\t') + 1;
+
+		name = description = NULL;
+
+		end = strchr(ptr, '\t');
+		if (end) {
+			name = strndup(ptr, end - ptr);
+			ptr = end + 1;
+		}
+
+		ptr = end;
+		items = sscanf(ptr, "%lld-%lld\t%n", &discard, &discard,
+			 &eaten);
+		if (2 != items)
+			continue;
+
+		ptr += eaten;
+		end = strchr(ptr, '\n');
+		if (end) {
+			description = strndup(ptr, end - ptr);
+			offset = end - buf + 1;
+		} else
+			break;
+
+		if (name && description) {
+			symbols[count].symbol = name;
+			symbols[count].alias = "";
+			++count;
+			/* description gets lost here */
+			free(description);
+		} else {
+			free(description);
+			free(name);
+		}
+	}
+
+	return count;
+}
+
+static int load_extended_events(const char *extevent_path)
+{
+	int fd;
+	int lines = 0;
+	unsigned size = 0;
+	char *buf = NULL;
+
+	fd = open(extevent_path, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	lines = read_file(fd, &buf, &size);
+	close(fd);
+
+	if (0 < lines) {
+		struct event_symbol *symbols = (struct event_symbol *)
+				calloc(sizeof(struct event_symbol), lines);
+		if (symbols) {
+			int parsed_symbols = parse_extevent_file(symbols,
+							lines, buf);
+			if (0 < parsed_symbols) {
+				extended_event_symbols = symbols;
+				extended_event_count = parsed_symbols;
+			} else
+				free(symbols);
+		} else
+			lines = -1;
+	}
+	free(buf);
+
+	return lines;
+}
+
+static struct event_symbol *extevent_find_config(u64 config)
+{
+	unsigned int i;
+	for (i = 0; i < extended_event_count; ++i)
+		if (extended_event_symbols[i].config == config)
+			return &extended_event_symbols[i];
+
+	return NULL;
+}
+
 static int is_cache_op_valid(u8 cache_type, u8 cache_op)
 {
 	if (hw_cache_stat[cache_type] & COP(cache_op))
@@ -283,10 +441,16 @@ const char *__event_name(int type, u64 config)
 	}
 
 	switch (type) {
-	case PERF_TYPE_HARDWARE:
+	case PERF_TYPE_HARDWARE: {
+		const struct event_symbol *event;
+
 		if (config < PERF_COUNT_HW_MAX)
 			return hw_event_names[config];
+		event = extevent_find_config(config);
+		if (event)
+			return event->symbol;
 		return "unknown-hardware";
+	}
 
 	case PERF_TYPE_HW_CACHE: {
 		u8 cache_type, cache_op, cache_result;
@@ -611,33 +775,34 @@ parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
 	return EVT_HANDLED;
 }
 
-static int check_events(const char *str, unsigned int i)
+static int check_event(const char *str, const struct event_symbol *event)
 {
 	int n;
 
-	n = strlen(event_symbols[i].symbol);
-	if (!strncmp(str, event_symbols[i].symbol, n))
+	n = strlen(event->symbol);
+	if (!strncmp(str, event->symbol, n))
 		return n;
 
-	n = strlen(event_symbols[i].alias);
+	n = strlen(event->alias);
 	if (n)
-		if (!strncmp(str, event_symbols[i].alias, n))
+		if (!strncmp(str, event->alias, n))
 			return n;
 	return 0;
 }
 
 static enum event_result
-parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+do_parse_symbolic_event(const char **strp, struct perf_event_attr *attr,
+			const struct event_symbol *symbols, unsigned int count)
 {
 	const char *str = *strp;
 	unsigned int i;
 	int n;
 
-	for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
-		n = check_events(str, i);
+	for (i = 0; i < count; ++i) {
+		n = check_event(str, &symbols[i]);
 		if (n > 0) {
-			attr->type = event_symbols[i].type;
-			attr->config = event_symbols[i].config;
+			attr->type = symbols[i].type;
+			attr->config = symbols[i].config;
 			*strp = str + n;
 			return EVT_HANDLED;
 		}
@@ -646,6 +811,27 @@ parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
 }
 
 static enum event_result
+parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+{
+	return do_parse_symbolic_event(strp, attr,
+			event_symbols, ARRAY_SIZE(event_symbols));
+}
+
+static enum event_result
+parse_extended_event(const char **strp, struct perf_event_attr *attr)
+{
+	if (!extended_events_initialized)
+		extended_events_initialized =
+			load_extended_events(EXTENDED_EVENT_PATH);
+
+	if (extended_events_initialized < 0)
+		return EVT_FAILED;
+
+	return do_parse_symbolic_event(strp, attr, extended_event_symbols,
+				extended_event_count);
+}
+
+static enum event_result
 parse_raw_event(const char **strp, struct perf_event_attr *attr)
 {
 	const char *str = *strp;
@@ -744,6 +930,10 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr)
 	if (ret != EVT_FAILED)
 		goto modifier;
 
+	ret = parse_extended_event(str, attr);
+	if (ret != EVT_FAILED)
+		goto modifier;
+
 	ret = parse_breakpoint_event(str, attr);
 	if (ret != EVT_FAILED)
 		goto modifier;
@@ -932,6 +1122,18 @@ void print_events(void)
 		}
 	}
 
+    if (!extended_events_initialized)
+		extended_events_initialized =
+			load_extended_events(EXTENDED_EVENT_PATH);
+
+    if (0 < extended_events_initialized) {
+		for (i = 0; i < extended_event_count; ++i)
+			printf("  %-42s [%s]\n",
+				extended_event_symbols[i].symbol,
+				"Hardware platform-specific event");
+	}
+
+
 	printf("\n");
 	printf("  %-42s [%s]\n",
 		"rNNN", event_type_descriptors[PERF_TYPE_RAW]);
-- 
1.5.4.3




More information about the linux-arm-kernel mailing list