|
@@ -18,6 +18,12 @@ struct event_symbol {
|
|
|
const char *alias;
|
|
|
};
|
|
|
|
|
|
+enum event_result {
|
|
|
+ EVT_FAILED,
|
|
|
+ EVT_HANDLED,
|
|
|
+ EVT_HANDLED_ALL
|
|
|
+};
|
|
|
+
|
|
|
char debugfs_path[MAXPATHLEN];
|
|
|
|
|
|
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
|
|
@@ -344,7 +350,7 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
+static enum event_result
|
|
|
parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
const char *s = *str;
|
|
@@ -356,7 +362,7 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
|
|
|
* then bail out:
|
|
|
*/
|
|
|
if (cache_type == -1)
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
|
|
|
while ((cache_op == -1 || cache_result == -1) && *s == '-') {
|
|
|
++s;
|
|
@@ -402,27 +408,112 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
|
|
|
attr->type = PERF_TYPE_HW_CACHE;
|
|
|
|
|
|
*str = s;
|
|
|
- return 1;
|
|
|
+ return EVT_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+static enum event_result
|
|
|
+parse_single_tracepoint_event(char *sys_name,
|
|
|
+ const char *evt_name,
|
|
|
+ unsigned int evt_length,
|
|
|
+ char *flags,
|
|
|
+ struct perf_counter_attr *attr,
|
|
|
+ const char **strp)
|
|
|
+{
|
|
|
+ char evt_path[MAXPATHLEN];
|
|
|
+ char id_buf[4];
|
|
|
+ u64 id;
|
|
|
+ int fd;
|
|
|
+
|
|
|
+ if (flags) {
|
|
|
+ if (!strncmp(flags, "record", strlen(flags)))
|
|
|
+ attr->sample_type |= PERF_SAMPLE_RAW;
|
|
|
+ }
|
|
|
+
|
|
|
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
|
|
|
+ sys_name, evt_name);
|
|
|
+
|
|
|
+ fd = open(evt_path, O_RDONLY);
|
|
|
+ if (fd < 0)
|
|
|
+ return EVT_FAILED;
|
|
|
+
|
|
|
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
|
|
|
+ close(fd);
|
|
|
+ return EVT_FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
+ close(fd);
|
|
|
+ id = atoll(id_buf);
|
|
|
+ attr->config = id;
|
|
|
+ attr->type = PERF_TYPE_TRACEPOINT;
|
|
|
+ *strp = evt_name + evt_length;
|
|
|
+
|
|
|
+ return EVT_HANDLED;
|
|
|
}
|
|
|
|
|
|
-static int parse_tracepoint_event(const char **strp,
|
|
|
+/* sys + ':' + event + ':' + flags*/
|
|
|
+#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
|
|
|
+static enum event_result
|
|
|
+parse_subsystem_tracepoint_event(char *sys_name, char *flags)
|
|
|
+{
|
|
|
+ char evt_path[MAXPATHLEN];
|
|
|
+ struct dirent *evt_ent;
|
|
|
+ DIR *evt_dir;
|
|
|
+
|
|
|
+ snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
|
|
|
+ evt_dir = opendir(evt_path);
|
|
|
+
|
|
|
+ if (!evt_dir) {
|
|
|
+ perror("Can't open event dir");
|
|
|
+ return EVT_FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
+ while ((evt_ent = readdir(evt_dir))) {
|
|
|
+ char event_opt[MAX_EVOPT_LEN + 1];
|
|
|
+ int len;
|
|
|
+ unsigned int rem = MAX_EVOPT_LEN;
|
|
|
+
|
|
|
+ if (!strcmp(evt_ent->d_name, ".")
|
|
|
+ || !strcmp(evt_ent->d_name, "..")
|
|
|
+ || !strcmp(evt_ent->d_name, "enable")
|
|
|
+ || !strcmp(evt_ent->d_name, "filter"))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name,
|
|
|
+ evt_ent->d_name);
|
|
|
+ if (len < 0)
|
|
|
+ return EVT_FAILED;
|
|
|
+
|
|
|
+ rem -= len;
|
|
|
+ if (flags) {
|
|
|
+ if (rem < strlen(flags) + 1)
|
|
|
+ return EVT_FAILED;
|
|
|
+
|
|
|
+ strcat(event_opt, ":");
|
|
|
+ strcat(event_opt, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parse_events(NULL, event_opt, 0))
|
|
|
+ return EVT_FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
+ return EVT_HANDLED_ALL;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static enum event_result parse_tracepoint_event(const char **strp,
|
|
|
struct perf_counter_attr *attr)
|
|
|
{
|
|
|
const char *evt_name;
|
|
|
char *flags;
|
|
|
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(debugfs_path))
|
|
|
return 0;
|
|
|
|
|
|
evt_name = strchr(*strp, ':');
|
|
|
if (!evt_name)
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
|
|
|
sys_length = evt_name - *strp;
|
|
|
if (sys_length >= MAX_EVENT_LENGTH)
|
|
@@ -436,30 +527,19 @@ static int parse_tracepoint_event(const char **strp,
|
|
|
if (flags) {
|
|
|
*flags = '\0';
|
|
|
flags++;
|
|
|
- if (!strncmp(flags, "record", strlen(flags)))
|
|
|
- attr->sample_type |= PERF_SAMPLE_RAW;
|
|
|
}
|
|
|
|
|
|
evt_length = strlen(evt_name);
|
|
|
if (evt_length >= MAX_EVENT_LENGTH)
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
|
|
|
- snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", 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;
|
|
|
+ if (!strcmp(evt_name, "*")) {
|
|
|
+ *strp = evt_name + evt_length;
|
|
|
+ return parse_subsystem_tracepoint_event(sys_name, flags);
|
|
|
+ } else
|
|
|
+ return parse_single_tracepoint_event(sys_name, evt_name,
|
|
|
+ evt_length, flags,
|
|
|
+ attr, strp);
|
|
|
}
|
|
|
|
|
|
static int check_events(const char *str, unsigned int i)
|
|
@@ -477,7 +557,7 @@ static int check_events(const char *str, unsigned int i)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
+static enum event_result
|
|
|
parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
const char *str = *strp;
|
|
@@ -490,31 +570,32 @@ parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
attr->type = event_symbols[i].type;
|
|
|
attr->config = event_symbols[i].config;
|
|
|
*strp = str + n;
|
|
|
- return 1;
|
|
|
+ return EVT_HANDLED;
|
|
|
}
|
|
|
}
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
}
|
|
|
|
|
|
-static int parse_raw_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
+static enum event_result
|
|
|
+parse_raw_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
const char *str = *strp;
|
|
|
u64 config;
|
|
|
int n;
|
|
|
|
|
|
if (*str != 'r')
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
n = hex2u64(str + 1, &config);
|
|
|
if (n > 0) {
|
|
|
*strp = str + n + 1;
|
|
|
attr->type = PERF_TYPE_RAW;
|
|
|
attr->config = config;
|
|
|
- return 1;
|
|
|
+ return EVT_HANDLED;
|
|
|
}
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
+static enum event_result
|
|
|
parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
const char *str = *strp;
|
|
@@ -530,13 +611,13 @@ parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
attr->type = type;
|
|
|
attr->config = config;
|
|
|
*strp = endp;
|
|
|
- return 1;
|
|
|
+ return EVT_HANDLED;
|
|
|
}
|
|
|
}
|
|
|
- return 0;
|
|
|
+ return EVT_FAILED;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
+static enum event_result
|
|
|
parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
const char *str = *strp;
|
|
@@ -569,37 +650,60 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
|
|
|
* Each event can have multiple symbolic names.
|
|
|
* Symbolic names are (almost) exactly matched.
|
|
|
*/
|
|
|
-static int parse_event_symbols(const char **str, struct perf_counter_attr *attr)
|
|
|
+static enum event_result
|
|
|
+parse_event_symbols(const char **str, struct perf_counter_attr *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)))
|
|
|
- return 0;
|
|
|
+ enum event_result ret;
|
|
|
+
|
|
|
+ ret = parse_tracepoint_event(str, attr);
|
|
|
+ if (ret != EVT_FAILED)
|
|
|
+ goto modifier;
|
|
|
+
|
|
|
+ ret = parse_raw_event(str, attr);
|
|
|
+ if (ret != EVT_FAILED)
|
|
|
+ goto modifier;
|
|
|
|
|
|
+ ret = parse_numeric_event(str, attr);
|
|
|
+ if (ret != EVT_FAILED)
|
|
|
+ goto modifier;
|
|
|
+
|
|
|
+ ret = parse_symbolic_event(str, attr);
|
|
|
+ if (ret != EVT_FAILED)
|
|
|
+ goto modifier;
|
|
|
+
|
|
|
+ ret = parse_generic_hw_event(str, attr);
|
|
|
+ if (ret != EVT_FAILED)
|
|
|
+ goto modifier;
|
|
|
+
|
|
|
+ return EVT_FAILED;
|
|
|
+
|
|
|
+modifier:
|
|
|
parse_event_modifier(str, attr);
|
|
|
|
|
|
- return 1;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
int parse_events(const struct option *opt __used, const char *str, int unset __used)
|
|
|
{
|
|
|
struct perf_counter_attr attr;
|
|
|
+ enum event_result ret;
|
|
|
|
|
|
for (;;) {
|
|
|
if (nr_counters == MAX_COUNTERS)
|
|
|
return -1;
|
|
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
- if (!parse_event_symbols(&str, &attr))
|
|
|
+ ret = parse_event_symbols(&str, &attr);
|
|
|
+ if (ret == EVT_FAILED)
|
|
|
return -1;
|
|
|
|
|
|
if (!(*str == 0 || *str == ',' || isspace(*str)))
|
|
|
return -1;
|
|
|
|
|
|
- attrs[nr_counters] = attr;
|
|
|
- nr_counters++;
|
|
|
+ if (ret != EVT_HANDLED_ALL) {
|
|
|
+ attrs[nr_counters] = attr;
|
|
|
+ nr_counters++;
|
|
|
+ }
|
|
|
|
|
|
if (*str == 0)
|
|
|
break;
|