|
@@ -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);
|
|
|
}
|