Skip to content

Commit

Permalink
perf/tool: Add PMU event alias support
Browse files Browse the repository at this point in the history
Add support to specify alias term within the event description.

The definition of pmu event alias is located at:

  ${sysfs_mount}/bus/event_source/devices/${pmu}/events/

Each file in the 'events' directory defines a event alias. Its contents
are like:

  config=1,config1=2

Using pmu event alias, an event can be now specified like:

  uncore/CLOCKTICKS/ or uncore/event=CLOCKTICKS/

Signed-off-by: Zheng Yan <zheng.z.yan@intel.com>
[ Cleaned it up. ]
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1339741902-8449-13-git-send-email-zheng.z.yan@intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Zheng Yan authored and Ingo Molnar committed Jun 18, 2012
1 parent 90e2b22 commit a6146d5
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 1 deletion.
10 changes: 10 additions & 0 deletions tools/perf/util/parse-events.c
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,9 @@ int parse_events_add_pmu(struct list_head **list, int *idx,

memset(&attr, 0, sizeof(attr));

if (perf_pmu__check_alias(pmu, head_config))
return -EINVAL;

/*
* Configure hardcoded terms first, no need to check
* return value when called with fail == 0 ;)
Expand Down Expand Up @@ -1143,6 +1146,13 @@ int parse_events__term_str(struct parse_events__term **term,
config, str, 0);
}

int parse_events__term_clone(struct parse_events__term **new,
struct parse_events__term *term)
{
return new_term(new, term->type_val, term->type_term, term->config,
term->val.str, term->val.num);
}

void parse_events__free_terms(struct list_head *terms)
{
struct parse_events__term *term, *h;
Expand Down
2 changes: 2 additions & 0 deletions tools/perf/util/parse-events.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ int parse_events__term_num(struct parse_events__term **_term,
int type_term, char *config, long num);
int parse_events__term_str(struct parse_events__term **_term,
int type_term, char *config, char *str);
int parse_events__term_clone(struct parse_events__term **new,
struct parse_events__term *term);
void parse_events__free_terms(struct list_head *terms);
int parse_events_modifier(struct list_head *list, char *str);
int parse_events_add_tracepoint(struct list_head **list, int *idx,
Expand Down
166 changes: 166 additions & 0 deletions tools/perf/util/pmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,114 @@ static int pmu_format(char *name, struct list_head *format)
return 0;
}

static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
{
struct perf_pmu__alias *alias;
char buf[256];
int ret;

ret = fread(buf, 1, sizeof(buf), file);
if (ret == 0)
return -EINVAL;
buf[ret] = 0;

alias = malloc(sizeof(*alias));
if (!alias)
return -ENOMEM;

INIT_LIST_HEAD(&alias->terms);
ret = parse_events_terms(&alias->terms, buf);
if (ret) {
free(alias);
return ret;
}

alias->name = strdup(name);
list_add_tail(&alias->list, list);
return 0;
}

/*
* Process all the sysfs attributes located under the directory
* specified in 'dir' parameter.
*/
static int pmu_aliases_parse(char *dir, struct list_head *head)
{
struct dirent *evt_ent;
DIR *event_dir;
int ret = 0;

event_dir = opendir(dir);
if (!event_dir)
return -EINVAL;

while (!ret && (evt_ent = readdir(event_dir))) {
char path[PATH_MAX];
char *name = evt_ent->d_name;
FILE *file;

if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;

snprintf(path, PATH_MAX, "%s/%s", dir, name);

ret = -EINVAL;
file = fopen(path, "r");
if (!file)
break;
ret = perf_pmu__new_alias(head, name, file);
fclose(file);
}

closedir(event_dir);
return ret;
}

/*
* Reading the pmu event aliases definition, which should be located at:
* /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
*/
static int pmu_aliases(char *name, struct list_head *head)
{
struct stat st;
char path[PATH_MAX];
const char *sysfs;

sysfs = sysfs_find_mountpoint();
if (!sysfs)
return -1;

snprintf(path, PATH_MAX,
"%s/bus/event_source/devices/%s/events", sysfs, name);

if (stat(path, &st) < 0)
return -1;

if (pmu_aliases_parse(path, head))
return -1;

return 0;
}

static int pmu_alias_terms(struct perf_pmu__alias *alias,
struct list_head *terms)
{
struct parse_events__term *term, *clone;
LIST_HEAD(list);
int ret;

list_for_each_entry(term, &alias->terms, list) {
ret = parse_events__term_clone(&clone, term);
if (ret) {
parse_events__free_terms(&list);
return ret;
}
list_add_tail(&clone->list, &list);
}
list_splice(&list, terms);
return 0;
}

/*
* Reading/parsing the default pmu type value, which should be
* located at:
Expand Down Expand Up @@ -118,6 +226,7 @@ static struct perf_pmu *pmu_lookup(char *name)
{
struct perf_pmu *pmu;
LIST_HEAD(format);
LIST_HEAD(aliases);
__u32 type;

/*
Expand All @@ -135,8 +244,12 @@ static struct perf_pmu *pmu_lookup(char *name)
if (!pmu)
return NULL;

pmu_aliases(name, &aliases);

INIT_LIST_HEAD(&pmu->format);
INIT_LIST_HEAD(&pmu->aliases);
list_splice(&format, &pmu->format);
list_splice(&aliases, &pmu->aliases);
pmu->name = strdup(name);
pmu->type = type;
return pmu;
Expand Down Expand Up @@ -279,6 +392,59 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
return pmu_config(&pmu->format, attr, head_terms);
}

static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu,
struct parse_events__term *term)
{
struct perf_pmu__alias *alias;
char *name;

if (parse_events__is_hardcoded_term(term))
return NULL;

if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
if (term->val.num != 1)
return NULL;
if (pmu_find_format(&pmu->format, term->config))
return NULL;
name = term->config;
} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
if (strcasecmp(term->config, "event"))
return NULL;
name = term->val.str;
} else {
return NULL;
}

list_for_each_entry(alias, &pmu->aliases, list) {
if (!strcasecmp(alias->name, name))
return alias;
}
return NULL;
}

/*
* Find alias in the terms list and replace it with the terms
* defined for the alias
*/
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
{
struct parse_events__term *term, *h;
struct perf_pmu__alias *alias;
int ret;

list_for_each_entry_safe(term, h, head_terms, list) {
alias = pmu_find_alias(pmu, term);
if (!alias)
continue;
ret = pmu_alias_terms(alias, &term->list);
if (ret)
return ret;
list_del(&term->list);
free(term);
}
return 0;
}

int perf_pmu__new_format(struct list_head *list, char *name,
int config, unsigned long *bits)
{
Expand Down
11 changes: 10 additions & 1 deletion tools/perf/util/pmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@ struct perf_pmu__format {
struct list_head list;
};

struct perf_pmu__alias {
char *name;
struct list_head terms;
struct list_head list;
};

struct perf_pmu {
char *name;
__u32 type;
struct list_head format;
struct list_head aliases;
struct list_head list;
};

struct perf_pmu *perf_pmu__find(char *name);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms);

int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
struct list_head *head_terms);
int perf_pmu_wrap(void);
void perf_pmu_error(struct list_head *list, char *name, char const *msg);

Expand Down

0 comments on commit a6146d5

Please sign in to comment.