|
@@ -184,16 +184,20 @@ char *event_name(int counter)
|
|
|
return "unknown";
|
|
|
}
|
|
|
|
|
|
-static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size)
|
|
|
+static int parse_aliases(const char **str, char *names[][MAX_ALIASES], int size)
|
|
|
{
|
|
|
int i, j;
|
|
|
+ int n, longest = -1;
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
- for (j = 0; j < MAX_ALIASES; j++) {
|
|
|
- if (!names[i][j])
|
|
|
- break;
|
|
|
- if (strcasestr(str, names[i][j]))
|
|
|
- return i;
|
|
|
+ for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
|
|
|
+ n = strlen(names[i][j]);
|
|
|
+ if (n > longest && !strncasecmp(*str, names[i][j], n))
|
|
|
+ longest = n;
|
|
|
+ }
|
|
|
+ if (longest > 0) {
|
|
|
+ *str += longest;
|
|
|
+ return i;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -201,30 +205,53 @@ static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size)
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr)
|
|
|
+parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
- int cache_type = -1, cache_op = 0, cache_result = 0;
|
|
|
+ const char *s = *str;
|
|
|
+ int cache_type = -1, cache_op = -1, cache_result = -1;
|
|
|
|
|
|
- cache_type = parse_aliases(str, hw_cache, PERF_COUNT_HW_CACHE_MAX);
|
|
|
+ cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
|
|
|
/*
|
|
|
* No fallback - if we cannot get a clear cache type
|
|
|
* then bail out:
|
|
|
*/
|
|
|
if (cache_type == -1)
|
|
|
- return -EINVAL;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ while ((cache_op == -1 || cache_result == -1) && *s == '-') {
|
|
|
+ ++s;
|
|
|
+
|
|
|
+ if (cache_op == -1) {
|
|
|
+ cache_op = parse_aliases(&s, hw_cache_op,
|
|
|
+ PERF_COUNT_HW_CACHE_OP_MAX);
|
|
|
+ if (cache_op >= 0) {
|
|
|
+ if (!is_cache_op_valid(cache_type, cache_op))
|
|
|
+ return 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cache_result == -1) {
|
|
|
+ cache_result = parse_aliases(&s, hw_cache_result,
|
|
|
+ PERF_COUNT_HW_CACHE_RESULT_MAX);
|
|
|
+ if (cache_result >= 0)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Can't parse this as a cache op or result, so back up
|
|
|
+ * to the '-'.
|
|
|
+ */
|
|
|
+ --s;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- cache_op = parse_aliases(str, hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX);
|
|
|
/*
|
|
|
* Fall back to reads:
|
|
|
*/
|
|
|
if (cache_op == -1)
|
|
|
cache_op = PERF_COUNT_HW_CACHE_OP_READ;
|
|
|
|
|
|
- if (!is_cache_op_valid(cache_type, cache_op))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- cache_result = parse_aliases(str, hw_cache_result,
|
|
|
- PERF_COUNT_HW_CACHE_RESULT_MAX);
|
|
|
/*
|
|
|
* Fall back to accesses:
|
|
|
*/
|
|
@@ -234,93 +261,154 @@ parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr)
|
|
|
attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
|
|
|
attr->type = PERF_TYPE_HW_CACHE;
|
|
|
|
|
|
- return 0;
|
|
|
+ *str = s;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
static int check_events(const char *str, unsigned int i)
|
|
|
{
|
|
|
- if (!strncmp(str, event_symbols[i].symbol,
|
|
|
- strlen(event_symbols[i].symbol)))
|
|
|
- return 1;
|
|
|
+ int n;
|
|
|
|
|
|
- if (strlen(event_symbols[i].alias))
|
|
|
- if (!strncmp(str, event_symbols[i].alias,
|
|
|
- strlen(event_symbols[i].alias)))
|
|
|
- return 1;
|
|
|
+ n = strlen(event_symbols[i].symbol);
|
|
|
+ if (!strncmp(str, event_symbols[i].symbol, n))
|
|
|
+ return n;
|
|
|
+
|
|
|
+ n = strlen(event_symbols[i].alias);
|
|
|
+ if (n)
|
|
|
+ if (!strncmp(str, event_symbols[i].alias, n))
|
|
|
+ return n;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * 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 int
|
|
|
+parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
{
|
|
|
- u64 config, id;
|
|
|
- int type;
|
|
|
+ const char *str = *strp;
|
|
|
unsigned int i;
|
|
|
- const char *sep, *pstr;
|
|
|
+ int n;
|
|
|
|
|
|
- if (str[0] == 'r' && hex2u64(str + 1, &config) > 0) {
|
|
|
- attr->type = PERF_TYPE_RAW;
|
|
|
- attr->config = config;
|
|
|
+ for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
|
|
|
+ n = check_events(str, i);
|
|
|
+ if (n > 0) {
|
|
|
+ attr->type = event_symbols[i].type;
|
|
|
+ attr->config = event_symbols[i].config;
|
|
|
+ *strp = str + n;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int parse_raw_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
+{
|
|
|
+ const char *str = *strp;
|
|
|
+ u64 config;
|
|
|
+ int n;
|
|
|
|
|
|
+ if (*str != 'r')
|
|
|
return 0;
|
|
|
+ n = hex2u64(str + 1, &config);
|
|
|
+ if (n > 0) {
|
|
|
+ *strp = str + n + 1;
|
|
|
+ attr->type = PERF_TYPE_RAW;
|
|
|
+ attr->config = config;
|
|
|
+ return 1;
|
|
|
}
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- pstr = str;
|
|
|
- sep = strchr(pstr, ':');
|
|
|
- if (sep) {
|
|
|
- type = atoi(pstr);
|
|
|
- pstr = sep + 1;
|
|
|
- id = atoi(pstr);
|
|
|
- sep = strchr(pstr, ':');
|
|
|
- if (sep) {
|
|
|
- pstr = sep + 1;
|
|
|
- if (strchr(pstr, 'k'))
|
|
|
- attr->exclude_user = 1;
|
|
|
- if (strchr(pstr, 'u'))
|
|
|
- attr->exclude_kernel = 1;
|
|
|
+static int
|
|
|
+parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
|
|
|
+{
|
|
|
+ const char *str = *strp;
|
|
|
+ char *endp;
|
|
|
+ unsigned long type;
|
|
|
+ u64 config;
|
|
|
+
|
|
|
+ type = strtoul(str, &endp, 0);
|
|
|
+ if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
|
|
|
+ str = endp + 1;
|
|
|
+ config = strtoul(str, &endp, 0);
|
|
|
+ if (endp > str) {
|
|
|
+ attr->type = type;
|
|
|
+ attr->config = config;
|
|
|
+ *strp = endp;
|
|
|
+ return 1;
|
|
|
}
|
|
|
- attr->type = type;
|
|
|
- attr->config = id;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
|
|
|
+{
|
|
|
+ const char *str = *strp;
|
|
|
+ int eu = 1, ek = 1, eh = 1;
|
|
|
|
|
|
+ if (*str++ != ':')
|
|
|
return 0;
|
|
|
+ while (*str) {
|
|
|
+ if (*str == 'u')
|
|
|
+ eu = 0;
|
|
|
+ else if (*str == 'k')
|
|
|
+ ek = 0;
|
|
|
+ else if (*str == 'h')
|
|
|
+ eh = 0;
|
|
|
+ else
|
|
|
+ break;
|
|
|
+ ++str;
|
|
|
}
|
|
|
+ if (str >= *strp + 2) {
|
|
|
+ *strp = str;
|
|
|
+ attr->exclude_user = eu;
|
|
|
+ attr->exclude_kernel = ek;
|
|
|
+ attr->exclude_hv = eh;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
|
|
|
- if (check_events(str, i)) {
|
|
|
- attr->type = event_symbols[i].type;
|
|
|
- attr->config = event_symbols[i].config;
|
|
|
+/*
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+ if (!(parse_raw_event(str, attr) ||
|
|
|
+ parse_numeric_event(str, attr) ||
|
|
|
+ parse_symbolic_event(str, attr) ||
|
|
|
+ parse_generic_hw_event(str, attr)))
|
|
|
+ return 0;
|
|
|
|
|
|
- return 0;
|
|
|
- }
|
|
|
- }
|
|
|
+ parse_event_modifier(str, attr);
|
|
|
|
|
|
- return parse_generic_hw_symbols(str, attr);
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
int parse_events(const struct option *opt, const char *str, int unset)
|
|
|
{
|
|
|
struct perf_counter_attr attr;
|
|
|
- int ret;
|
|
|
|
|
|
- memset(&attr, 0, sizeof(attr));
|
|
|
-again:
|
|
|
- if (nr_counters == MAX_COUNTERS)
|
|
|
- return -1;
|
|
|
+ for (;;) {
|
|
|
+ if (nr_counters == MAX_COUNTERS)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ memset(&attr, 0, sizeof(attr));
|
|
|
+ if (!parse_event_symbols(&str, &attr))
|
|
|
+ return -1;
|
|
|
|
|
|
- ret = parse_event_symbols(str, &attr);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
+ if (!(*str == 0 || *str == ',' || isspace(*str)))
|
|
|
+ return -1;
|
|
|
|
|
|
- attrs[nr_counters] = attr;
|
|
|
- nr_counters++;
|
|
|
+ attrs[nr_counters] = attr;
|
|
|
+ nr_counters++;
|
|
|
|
|
|
- str = strstr(str, ",");
|
|
|
- if (str) {
|
|
|
- str++;
|
|
|
- goto again;
|
|
|
+ if (*str == 0)
|
|
|
+ break;
|
|
|
+ if (*str == ',')
|
|
|
+ ++str;
|
|
|
+ while (isspace(*str))
|
|
|
+ ++str;
|
|
|
}
|
|
|
|
|
|
return 0;
|