perf tools: Parse the pmu event prefix and suffix
There are two types of event formats for PMU events. E.g. el-abort OR cpu/el-abort/. However, the lexer mistakenly recognizes the simple style format as two events. The parse_events_pmu_check function uses bsearch to search the name in known pmu event list. It can tell the lexer that the name is a PE_NAME or a PMU event name prefix or a PMU event name suffix. All these information will be used for accurately parsing kernel PMU events. The pmu events list will be read from sysfs at runtime. Note: Currently, the patch only want to handle the PMU event name as "a-b" and "a". The only exception, "stalled-cycles-frontend" and "stalled-cycles-fronted", are already hardcoded in lexer. Signed-off-by: Kan Liang <kan.liang@intel.com> Acked-by: Jiri Olsa <jolsa@redhat.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/r/1412694532-23391-3-git-send-email-kan.liang@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
42f60c2d63
commit
dcb4e1022b
@ -30,6 +30,15 @@ extern int parse_events_debug;
|
||||
#endif
|
||||
int parse_events_parse(void *data, void *scanner);
|
||||
|
||||
static struct perf_pmu_event_symbol *perf_pmu_events_list;
|
||||
/*
|
||||
* The variable indicates the number of supported pmu event symbols.
|
||||
* 0 means not initialized and ready to init
|
||||
* -1 means failed to init, don't try anymore
|
||||
* >0 is the number of supported pmu event symbols
|
||||
*/
|
||||
static int perf_pmu_events_list_num;
|
||||
|
||||
static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = {
|
||||
.symbol = "cpu-cycles",
|
||||
@ -863,6 +872,113 @@ int parse_events_name(struct list_head *list, char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
comp_pmu(const void *p1, const void *p2)
|
||||
{
|
||||
struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
|
||||
struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
|
||||
|
||||
return strcmp(pmu1->symbol, pmu2->symbol);
|
||||
}
|
||||
|
||||
static void perf_pmu__parse_cleanup(void)
|
||||
{
|
||||
if (perf_pmu_events_list_num > 0) {
|
||||
struct perf_pmu_event_symbol *p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < perf_pmu_events_list_num; i++) {
|
||||
p = perf_pmu_events_list + i;
|
||||
free(p->symbol);
|
||||
}
|
||||
free(perf_pmu_events_list);
|
||||
perf_pmu_events_list = NULL;
|
||||
perf_pmu_events_list_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define SET_SYMBOL(str, stype) \
|
||||
do { \
|
||||
p->symbol = str; \
|
||||
if (!p->symbol) \
|
||||
goto err; \
|
||||
p->type = stype; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Read the pmu events list from sysfs
|
||||
* Save it into perf_pmu_events_list
|
||||
*/
|
||||
static void perf_pmu__parse_init(void)
|
||||
{
|
||||
|
||||
struct perf_pmu *pmu = NULL;
|
||||
struct perf_pmu_alias *alias;
|
||||
int len = 0;
|
||||
|
||||
pmu = perf_pmu__find("cpu");
|
||||
if ((pmu == NULL) || list_empty(&pmu->aliases)) {
|
||||
perf_pmu_events_list_num = -1;
|
||||
return;
|
||||
}
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
if (strchr(alias->name, '-'))
|
||||
len++;
|
||||
len++;
|
||||
}
|
||||
perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
|
||||
if (!perf_pmu_events_list)
|
||||
return;
|
||||
perf_pmu_events_list_num = len;
|
||||
|
||||
len = 0;
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
|
||||
char *tmp = strchr(alias->name, '-');
|
||||
|
||||
if (tmp != NULL) {
|
||||
SET_SYMBOL(strndup(alias->name, tmp - alias->name),
|
||||
PMU_EVENT_SYMBOL_PREFIX);
|
||||
p++;
|
||||
SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
|
||||
len += 2;
|
||||
} else {
|
||||
SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
|
||||
len++;
|
||||
}
|
||||
}
|
||||
qsort(perf_pmu_events_list, len,
|
||||
sizeof(struct perf_pmu_event_symbol), comp_pmu);
|
||||
|
||||
return;
|
||||
err:
|
||||
perf_pmu__parse_cleanup();
|
||||
}
|
||||
|
||||
enum perf_pmu_event_symbol_type
|
||||
perf_pmu__parse_check(const char *name)
|
||||
{
|
||||
struct perf_pmu_event_symbol p, *r;
|
||||
|
||||
/* scan kernel pmu events from sysfs if needed */
|
||||
if (perf_pmu_events_list_num == 0)
|
||||
perf_pmu__parse_init();
|
||||
/*
|
||||
* name "cpu" could be prefix of cpu-cycles or cpu// events.
|
||||
* cpu-cycles has been handled by hardcode.
|
||||
* So it must be cpu// events, not kernel pmu event.
|
||||
*/
|
||||
if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu"))
|
||||
return PMU_EVENT_SYMBOL_ERR;
|
||||
|
||||
p.symbol = strdup(name);
|
||||
r = bsearch(&p, perf_pmu_events_list,
|
||||
(size_t) perf_pmu_events_list_num,
|
||||
sizeof(struct perf_pmu_event_symbol), comp_pmu);
|
||||
free(p.symbol);
|
||||
return r ? r->type : PMU_EVENT_SYMBOL_ERR;
|
||||
}
|
||||
|
||||
static int parse_events__scanner(const char *str, void *data, int start_token)
|
||||
{
|
||||
YY_BUFFER_STATE buffer;
|
||||
@ -917,6 +1033,7 @@ int parse_events(struct perf_evlist *evlist, const char *str)
|
||||
int ret;
|
||||
|
||||
ret = parse_events__scanner(str, &data, PE_START_EVENTS);
|
||||
perf_pmu__parse_cleanup();
|
||||
if (!ret) {
|
||||
int entries = data.idx - evlist->nr_entries;
|
||||
perf_evlist__splice_list_tail(evlist, &data.list, entries);
|
||||
|
@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
|
||||
|
||||
#define EVENTS_HELP_MAX (128*1024)
|
||||
|
||||
enum perf_pmu_event_symbol_type {
|
||||
PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */
|
||||
PMU_EVENT_SYMBOL, /* normal style PMU event */
|
||||
PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */
|
||||
PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */
|
||||
};
|
||||
|
||||
struct perf_pmu_event_symbol {
|
||||
char *symbol;
|
||||
enum perf_pmu_event_symbol_type type;
|
||||
};
|
||||
|
||||
enum {
|
||||
PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
PARSE_EVENTS__TERM_TYPE_STR,
|
||||
@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
void *ptr, char *type);
|
||||
int parse_events_add_pmu(struct list_head *list, int *idx,
|
||||
char *pmu , struct list_head *head_config);
|
||||
enum perf_pmu_event_symbol_type
|
||||
perf_pmu__parse_check(const char *name);
|
||||
void parse_events__set_leader(char *name, struct list_head *list);
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
struct list_head *list_all);
|
||||
|
@ -12,16 +12,6 @@
|
||||
#include "parse-events.h"
|
||||
#include "cpumap.h"
|
||||
|
||||
#define UNIT_MAX_LEN 31 /* max length for event unit name */
|
||||
|
||||
struct perf_pmu_alias {
|
||||
char *name;
|
||||
struct list_head terms; /* HEAD struct parse_events_term -> list */
|
||||
struct list_head list; /* ELEM */
|
||||
char unit[UNIT_MAX_LEN+1];
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct perf_pmu_format {
|
||||
char *name;
|
||||
int value;
|
||||
|
@ -30,6 +30,16 @@ struct perf_pmu_info {
|
||||
double scale;
|
||||
};
|
||||
|
||||
#define UNIT_MAX_LEN 31 /* max length for event unit name */
|
||||
|
||||
struct perf_pmu_alias {
|
||||
char *name;
|
||||
struct list_head terms; /* HEAD struct parse_events_term -> list */
|
||||
struct list_head list; /* ELEM */
|
||||
char unit[UNIT_MAX_LEN+1];
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct perf_pmu *perf_pmu__find(const char *name);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
|
Loading…
Reference in New Issue
Block a user