Commit 734e9a26 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

 * Improve warning message when libunwind devel packages not present, from Jiri Olsa

 * Remove perf_event_attr needless version inflation, from Jiri Olsa

 * Introduce libtraceevent strerror like error reporting facility, from Namhyung Kim

 * Add pmu mappings to perf.data header and use event names from cmd line, from Robert Richter
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents d57c5d51 f63fe79f
...@@ -205,8 +205,8 @@ enum perf_event_read_format { ...@@ -205,8 +205,8 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
#define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */ #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
#define PERF_ATTR_SIZE_VER4 96 /* add: sample_stack_user */ /* add: sample_stack_user */
/* /*
* Hardware event_id to monitor via a performance monitoring event: * Hardware event_id to monitor via a performance monitoring event:
......
...@@ -4686,9 +4686,8 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event) ...@@ -4686,9 +4686,8 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event)
* *
* /sys/kernel/debug/tracing/events/.../.../format * /sys/kernel/debug/tracing/events/.../.../format
*/ */
int pevent_parse_event(struct pevent *pevent, enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
const char *buf, unsigned long size, unsigned long size, const char *sys)
const char *sys)
{ {
struct event_format *event; struct event_format *event;
int ret; int ret;
...@@ -4697,17 +4696,16 @@ int pevent_parse_event(struct pevent *pevent, ...@@ -4697,17 +4696,16 @@ int pevent_parse_event(struct pevent *pevent,
event = alloc_event(); event = alloc_event();
if (!event) if (!event)
return -ENOMEM; return PEVENT_ERRNO__MEM_ALLOC_FAILED;
event->name = event_read_name(); event->name = event_read_name();
if (!event->name) { if (!event->name) {
/* Bad event? */ /* Bad event? */
free(event); ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
return -1; goto event_alloc_failed;
} }
if (strcmp(sys, "ftrace") == 0) { if (strcmp(sys, "ftrace") == 0) {
event->flags |= EVENT_FL_ISFTRACE; event->flags |= EVENT_FL_ISFTRACE;
if (strcmp(event->name, "bprint") == 0) if (strcmp(event->name, "bprint") == 0)
...@@ -4715,20 +4713,28 @@ int pevent_parse_event(struct pevent *pevent, ...@@ -4715,20 +4713,28 @@ int pevent_parse_event(struct pevent *pevent,
} }
event->id = event_read_id(); event->id = event_read_id();
if (event->id < 0) if (event->id < 0) {
die("failed to read event id"); ret = PEVENT_ERRNO__READ_ID_FAILED;
/*
* This isn't an allocation error actually.
* But as the ID is critical, just bail out.
*/
goto event_alloc_failed;
}
event->system = strdup(sys); event->system = strdup(sys);
if (!event->system) if (!event->system) {
die("failed to allocate system"); ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
goto event_alloc_failed;
}
/* Add pevent to event so that it can be referenced */ /* Add pevent to event so that it can be referenced */
event->pevent = pevent; event->pevent = pevent;
ret = event_read_format(event); ret = event_read_format(event);
if (ret < 0) { if (ret < 0) {
do_warning("failed to read event format for %s", event->name); ret = PEVENT_ERRNO__READ_FORMAT_FAILED;
goto event_failed; goto event_parse_failed;
} }
/* /*
...@@ -4740,10 +4746,9 @@ int pevent_parse_event(struct pevent *pevent, ...@@ -4740,10 +4746,9 @@ int pevent_parse_event(struct pevent *pevent,
ret = event_read_print(event); ret = event_read_print(event);
if (ret < 0) { if (ret < 0) {
do_warning("failed to read event print fmt for %s",
event->name);
show_warning = 1; show_warning = 1;
goto event_failed; ret = PEVENT_ERRNO__READ_PRINT_FAILED;
goto event_parse_failed;
} }
show_warning = 1; show_warning = 1;
...@@ -4754,20 +4759,19 @@ int pevent_parse_event(struct pevent *pevent, ...@@ -4754,20 +4759,19 @@ int pevent_parse_event(struct pevent *pevent,
struct print_arg *arg, **list; struct print_arg *arg, **list;
/* old ftrace had no args */ /* old ftrace had no args */
list = &event->print_fmt.args; list = &event->print_fmt.args;
for (field = event->format.fields; field; field = field->next) { for (field = event->format.fields; field; field = field->next) {
arg = alloc_arg(); arg = alloc_arg();
*list = arg;
list = &arg->next;
arg->type = PRINT_FIELD; arg->type = PRINT_FIELD;
arg->field.name = strdup(field->name); arg->field.name = strdup(field->name);
if (!arg->field.name) { if (!arg->field.name) {
do_warning("failed to allocate field name");
event->flags |= EVENT_FL_FAILED; event->flags |= EVENT_FL_FAILED;
return -1; free_arg(arg);
return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED;
} }
arg->field.field = field; arg->field.field = field;
*list = arg;
list = &arg->next;
} }
return 0; return 0;
} }
...@@ -4778,11 +4782,65 @@ int pevent_parse_event(struct pevent *pevent, ...@@ -4778,11 +4782,65 @@ int pevent_parse_event(struct pevent *pevent,
return 0; return 0;
event_failed: event_parse_failed:
event->flags |= EVENT_FL_FAILED; event->flags |= EVENT_FL_FAILED;
/* still add it even if it failed */ /* still add it even if it failed */
add_event(pevent, event); add_event(pevent, event);
return ret;
event_alloc_failed:
free(event->system);
free(event->name);
free(event);
return ret;
}
#undef _PE
#define _PE(code, str) str
static const char * const pevent_error_str[] = {
PEVENT_ERRORS
};
#undef _PE
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
char *buf, size_t buflen)
{
int idx;
const char *msg;
if (errnum >= 0) {
msg = strerror_r(errnum, buf, buflen);
if (msg != buf) {
size_t len = strlen(msg);
char *c = mempcpy(buf, msg, min(buflen-1, len));
*c = '\0';
}
return 0;
}
if (errnum <= __PEVENT_ERRNO__START ||
errnum >= __PEVENT_ERRNO__END)
return -1; return -1;
idx = errnum - __PEVENT_ERRNO__START - 1;
msg = pevent_error_str[idx];
switch (errnum) {
case PEVENT_ERRNO__MEM_ALLOC_FAILED:
case PEVENT_ERRNO__PARSE_EVENT_FAILED:
case PEVENT_ERRNO__READ_ID_FAILED:
case PEVENT_ERRNO__READ_FORMAT_FAILED:
case PEVENT_ERRNO__READ_PRINT_FAILED:
case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED:
snprintf(buf, buflen, "%s", msg);
break;
default:
/* cannot reach here */
break;
}
return 0;
} }
int get_field_val(struct trace_seq *s, struct format_field *field, int get_field_val(struct trace_seq *s, struct format_field *field,
......
...@@ -345,6 +345,34 @@ enum pevent_flag { ...@@ -345,6 +345,34 @@ enum pevent_flag {
PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */
}; };
#define PEVENT_ERRORS \
_PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \
_PE(PARSE_EVENT_FAILED, "failed to parse event"), \
_PE(READ_ID_FAILED, "failed to read event id"), \
_PE(READ_FORMAT_FAILED, "failed to read event format"), \
_PE(READ_PRINT_FAILED, "failed to read event print fmt"), \
_PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace")
#undef _PE
#define _PE(__code, __str) PEVENT_ERRNO__ ## __code
enum pevent_errno {
PEVENT_ERRNO__SUCCESS = 0,
/*
* Choose an arbitrary negative big number not to clash with standard
* errno since SUS requires the errno has distinct positive values.
* See 'Issue 6' in the link below.
*
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
*/
__PEVENT_ERRNO__START = -100000,
PEVENT_ERRORS,
__PEVENT_ERRNO__END,
};
#undef _PE
struct cmdline; struct cmdline;
struct cmdline_list; struct cmdline_list;
struct func_map; struct func_map;
...@@ -509,7 +537,7 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, ...@@ -509,7 +537,7 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
int long_size); int long_size);
int pevent_parse_event(struct pevent *pevent, const char *buf, enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
unsigned long size, const char *sys); unsigned long size, const char *sys);
void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
...@@ -561,6 +589,8 @@ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); ...@@ -561,6 +589,8 @@ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
void pevent_event_info(struct trace_seq *s, struct event_format *event, void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record); struct pevent_record *record);
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
char *buf, size_t buflen);
struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type);
struct format_field **pevent_event_common_fields(struct event_format *event); struct format_field **pevent_event_common_fields(struct event_format *event);
......
...@@ -39,6 +39,12 @@ void __vdie(const char *fmt, ...); ...@@ -39,6 +39,12 @@ void __vdie(const char *fmt, ...);
void __vwarning(const char *fmt, ...); void __vwarning(const char *fmt, ...);
void __vpr_stat(const char *fmt, ...); void __vpr_stat(const char *fmt, ...);
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
static inline char *strim(char *string) static inline char *strim(char *string)
{ {
char *ret; char *ret;
......
...@@ -493,7 +493,7 @@ endif ...@@ -493,7 +493,7 @@ endif
FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y) ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y)
msg := $(warning No libunwind found. Please install libunwind >= 0.99); msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99);
NO_LIBUNWIND := 1 NO_LIBUNWIND := 1
endif # Libunwind support endif # Libunwind support
endif # NO_LIBUNWIND endif # NO_LIBUNWIND
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "symbol.h" #include "symbol.h"
#include "debug.h" #include "debug.h"
#include "cpumap.h" #include "cpumap.h"
#include "pmu.h"
static bool no_buildid_cache = false; static bool no_buildid_cache = false;
...@@ -1003,6 +1004,45 @@ static int write_numa_topology(int fd, struct perf_header *h __used, ...@@ -1003,6 +1004,45 @@ static int write_numa_topology(int fd, struct perf_header *h __used,
return ret; return ret;
} }
/*
* File format:
*
* struct pmu_mappings {
* u32 pmu_num;
* struct pmu_map {
* u32 type;
* char name[];
* }[pmu_num];
* };
*/
static int write_pmu_mappings(int fd, struct perf_header *h __used,
struct perf_evlist *evlist __used)
{
struct perf_pmu *pmu = NULL;
off_t offset = lseek(fd, 0, SEEK_CUR);
__u32 pmu_num = 0;
/* write real pmu_num later */
do_write(fd, &pmu_num, sizeof(pmu_num));
while ((pmu = perf_pmu__scan(pmu))) {
if (!pmu->name)
continue;
pmu_num++;
do_write(fd, &pmu->type, sizeof(pmu->type));
do_write_string(fd, pmu->name);
}
if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) {
/* discard all */
lseek(fd, offset, SEEK_SET);
return -1;
}
return 0;
}
/* /*
* default get_cpuid(): nothing gets recorded * default get_cpuid(): nothing gets recorded
* actual implementation must be in arch/$(ARCH)/util/header.c * actual implementation must be in arch/$(ARCH)/util/header.c
...@@ -1148,12 +1188,29 @@ static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) ...@@ -1148,12 +1188,29 @@ static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
} }
} }
static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) static void free_event_desc(struct perf_evsel *events)
{ {
struct perf_event_attr attr; struct perf_evsel *evsel;
uint64_t id;
if (!events)
return;
for (evsel = events; evsel->attr.size; evsel++) {
if (evsel->name)
free(evsel->name);
if (evsel->id)
free(evsel->id);
}
free(events);
}
static struct perf_evsel *
read_event_desc(struct perf_header *ph, int fd)
{
struct perf_evsel *evsel, *events = NULL;
u64 *id;
void *buf = NULL; void *buf = NULL;
char *str;
u32 nre, sz, nr, i, j; u32 nre, sz, nr, i, j;
ssize_t ret; ssize_t ret;
size_t msz; size_t msz;
...@@ -1173,18 +1230,22 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) ...@@ -1173,18 +1230,22 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
if (ph->needs_swap) if (ph->needs_swap)
sz = bswap_32(sz); sz = bswap_32(sz);
memset(&attr, 0, sizeof(attr));
/* buffer to hold on file attr struct */ /* buffer to hold on file attr struct */
buf = malloc(sz); buf = malloc(sz);
if (!buf) if (!buf)
goto error; goto error;
msz = sizeof(attr); /* the last event terminates with evsel->attr.size == 0: */
events = calloc(nre + 1, sizeof(*events));
if (!events)
goto error;
msz = sizeof(evsel->attr);
if (sz < msz) if (sz < msz)
msz = sz; msz = sz;
for (i = 0 ; i < nre; i++) { for (i = 0, evsel = events; i < nre; evsel++, i++) {
evsel->idx = i;
/* /*
* must read entire on-file attr struct to * must read entire on-file attr struct to
...@@ -1197,7 +1258,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) ...@@ -1197,7 +1258,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
if (ph->needs_swap) if (ph->needs_swap)
perf_event__attr_swap(buf); perf_event__attr_swap(buf);
memcpy(&attr, buf, msz); memcpy(&evsel->attr, buf, msz);
ret = read(fd, &nr, sizeof(nr)); ret = read(fd, &nr, sizeof(nr));
if (ret != (ssize_t)sizeof(nr)) if (ret != (ssize_t)sizeof(nr))
...@@ -1206,51 +1267,82 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) ...@@ -1206,51 +1267,82 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
if (ph->needs_swap) if (ph->needs_swap)
nr = bswap_32(nr); nr = bswap_32(nr);
str = do_read_string(fd, ph); evsel->name = do_read_string(fd, ph);
fprintf(fp, "# event : name = %s, ", str);
free(str); if (!nr)
continue;
id = calloc(nr, sizeof(*id));
if (!id)
goto error;
evsel->ids = nr;
evsel->id = id;
for (j = 0 ; j < nr; j++) {
ret = read(fd, id, sizeof(*id));
if (ret != (ssize_t)sizeof(*id))
goto error;
if (ph->needs_swap)
*id = bswap_64(*id);
id++;
}
}
out:
if (buf)
free(buf);
return events;
error:
if (events)
free_event_desc(events);
events = NULL;
goto out;
}
static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
{
struct perf_evsel *evsel, *events = read_event_desc(ph, fd);
u32 j;
u64 *id;
if (!events) {
fprintf(fp, "# event desc: not available or unable to read\n");
return;
}
for (evsel = events; evsel->attr.size; evsel++) {
fprintf(fp, "# event : name = %s, ", evsel->name);
fprintf(fp, "type = %d, config = 0x%"PRIx64 fprintf(fp, "type = %d, config = 0x%"PRIx64
", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
attr.type, evsel->attr.type,
(u64)attr.config, (u64)evsel->attr.config,
(u64)attr.config1, (u64)evsel->attr.config1,
(u64)attr.config2); (u64)evsel->attr.config2);
fprintf(fp, ", excl_usr = %d, excl_kern = %d", fprintf(fp, ", excl_usr = %d, excl_kern = %d",
attr.exclude_user, evsel->attr.exclude_user,
attr.exclude_kernel); evsel->attr.exclude_kernel);
fprintf(fp, ", excl_host = %d, excl_guest = %d", fprintf(fp, ", excl_host = %d, excl_guest = %d",
attr.exclude_host, evsel->attr.exclude_host,
attr.exclude_guest); evsel->attr.exclude_guest);
fprintf(fp, ", precise_ip = %d", attr.precise_ip); fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip);
if (nr) if (evsel->ids) {
fprintf(fp, ", id = {"); fprintf(fp, ", id = {");
for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) {
for (j = 0 ; j < nr; j++) {
ret = read(fd, &id, sizeof(id));
if (ret != (ssize_t)sizeof(id))
goto error;
if (ph->needs_swap)
id = bswap_64(id);
if (j) if (j)
fputc(',', fp); fputc(',', fp);
fprintf(fp, " %"PRIu64, *id);
fprintf(fp, " %"PRIu64, id);
} }
if (nr && j == nr)
fprintf(fp, " }"); fprintf(fp, " }");
}
fputc('\n', fp); fputc('\n', fp);
} }
free(buf);
return; free_event_desc(events);
error:
fprintf(fp, "# event desc: not available or unable to read\n");
} }
static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
...@@ -1337,6 +1429,43 @@ static void print_branch_stack(struct perf_header *ph __used, int fd __used, ...@@ -1337,6 +1429,43 @@ static void print_branch_stack(struct perf_header *ph __used, int fd __used,
fprintf(fp, "# contains samples with branch stack\n"); fprintf(fp, "# contains samples with branch stack\n");
} }
static void print_pmu_mappings(struct perf_header *ph, int fd, FILE *fp)
{
const char *delimiter = "# pmu mappings: ";
char *name;
int ret;
u32 pmu_num;
u32 type;
ret = read(fd, &pmu_num, sizeof(pmu_num));
if (ret != sizeof(pmu_num))
goto error;
if (!pmu_num) {
fprintf(fp, "# pmu mappings: not available\n");
return;
}
while (pmu_num) {
if (read(fd, &type, sizeof(type)) != sizeof(type))
break;
name = do_read_string(fd, ph);
if (!name)
break;
pmu_num--;
fprintf(fp, "%s%s = %" PRIu32, delimiter, name, type);
free(name);
delimiter = ", ";
}
fprintf(fp, "\n");
if (!pmu_num)
return;
error:
fprintf(fp, "# pmu mappings: unable to read\n");
}
static int __event_process_build_id(struct build_id_event *bev, static int __event_process_build_id(struct build_id_event *bev,
char *filename, char *filename,
struct perf_session *session) struct perf_session *session)
...@@ -1504,6 +1633,56 @@ static int process_build_id(struct perf_file_section *section, ...@@ -1504,6 +1633,56 @@ static int process_build_id(struct perf_file_section *section,
return 0; return 0;
} }
static struct perf_evsel *
perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)
{
struct perf_evsel *evsel;
list_for_each_entry(evsel, &evlist->entries, node) {
if (evsel->idx == idx)
return evsel;
}
return NULL;
}
static void
perf_evlist__set_event_name(struct perf_evlist *evlist, struct perf_evsel *event)
{
struct perf_evsel *evsel;
if (!event->name)
return;
evsel = perf_evlist__find_by_index(evlist, event->idx);
if (!evsel)
return;
if (evsel->name)
return;
evsel->name = strdup(event->name);
}
static int
process_event_desc(struct perf_file_section *section __unused,
struct perf_header *header, int feat __unused, int fd,
void *data __used)
{
struct perf_session *session = container_of(header, struct perf_session, header);
struct perf_evsel *evsel, *events = read_event_desc(header, fd);
if (!events)
return 0;
for (evsel = events; evsel->attr.size; evsel++)
perf_evlist__set_event_name(session->evlist, evsel);
free_event_desc(events);
return 0;
}
struct feature_ops { struct feature_ops {
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
void (*print)(struct perf_header *h, int fd, FILE *fp); void (*print)(struct perf_header *h, int fd, FILE *fp);
...@@ -1537,11 +1716,12 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { ...@@ -1537,11 +1716,12 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPA(HEADER_CPUDESC, cpudesc), FEAT_OPA(HEADER_CPUDESC, cpudesc),
FEAT_OPA(HEADER_CPUID, cpuid), FEAT_OPA(HEADER_CPUID, cpuid),
FEAT_OPA(HEADER_TOTAL_MEM, total_mem), FEAT_OPA(HEADER_TOTAL_MEM, total_mem),
FEAT_OPA(HEADER_EVENT_DESC, event_desc), FEAT_OPP(HEADER_EVENT_DESC, event_desc),
FEAT_OPA(HEADER_CMDLINE, cmdline), FEAT_OPA(HEADER_CMDLINE, cmdline),
FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
FEAT_OPA(HEADER_PMU_MAPPINGS, pmu_mappings),
}; };
struct header_print_data { struct header_print_data {
...@@ -1831,7 +2011,6 @@ static const int attr_file_abi_sizes[] = { ...@@ -1831,7 +2011,6 @@ static const int attr_file_abi_sizes[] = {
[1] = PERF_ATTR_SIZE_VER1, [1] = PERF_ATTR_SIZE_VER1,
[2] = PERF_ATTR_SIZE_VER2, [2] = PERF_ATTR_SIZE_VER2,
[3] = PERF_ATTR_SIZE_VER3, [3] = PERF_ATTR_SIZE_VER3,
[4] = PERF_ATTR_SIZE_VER4,
0, 0,
}; };
......
...@@ -28,6 +28,7 @@ enum { ...@@ -28,6 +28,7 @@ enum {
HEADER_CPU_TOPOLOGY, HEADER_CPU_TOPOLOGY,
HEADER_NUMA_TOPOLOGY, HEADER_NUMA_TOPOLOGY,
HEADER_BRANCH_STACK, HEADER_BRANCH_STACK,
HEADER_PMU_MAPPINGS,
HEADER_LAST_FEATURE, HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256, HEADER_FEAT_BITS = 256,
}; };
......
...@@ -301,12 +301,13 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) ...@@ -301,12 +301,13 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
{ {
struct perf_evsel *evsel = perf_evlist__first(evlist); struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name", TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u")); !strcmp(perf_evsel__name(evsel), "mem:0:u"));
return test__checkevent_breakpoint(evlist); return test__checkevent_breakpoint(evlist);
} }
...@@ -320,7 +321,7 @@ static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) ...@@ -320,7 +321,7 @@ static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name", TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:x:k")); !strcmp(perf_evsel__name(evsel), "mem:0:x:k"));
return test__checkevent_breakpoint_x(evlist); return test__checkevent_breakpoint_x(evlist);
} }
...@@ -334,7 +335,7 @@ static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) ...@@ -334,7 +335,7 @@ static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name", TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp")); !strcmp(perf_evsel__name(evsel), "mem:0:r:hp"));
return test__checkevent_breakpoint_r(evlist); return test__checkevent_breakpoint_r(evlist);
} }
...@@ -348,7 +349,7 @@ static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) ...@@ -348,7 +349,7 @@ static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name", TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:w:up")); !strcmp(perf_evsel__name(evsel), "mem:0:w:up"));
return test__checkevent_breakpoint_w(evlist); return test__checkevent_breakpoint_w(evlist);
} }
...@@ -362,7 +363,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist) ...@@ -362,7 +363,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name", TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp")); !strcmp(perf_evsel__name(evsel), "mem:0:rw:kp"));
return test__checkevent_breakpoint_rw(evlist); return test__checkevent_breakpoint_rw(evlist);
} }
...@@ -437,7 +438,7 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist) ...@@ -437,7 +438,7 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name", TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "raw 0x2:u")); !strcmp(perf_evsel__name(evsel), "cpu/config=2/u"));
return 0; return 0;
} }
...@@ -948,19 +949,19 @@ static int test_event(struct test__event_st *e) ...@@ -948,19 +949,19 @@ static int test_event(struct test__event_st *e)
static int test_events(struct test__event_st *events, unsigned cnt) static int test_events(struct test__event_st *events, unsigned cnt)
{ {
int ret = 0; int ret1, ret2 = 0;
unsigned i; unsigned i;
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
struct test__event_st *e = &events[i]; struct test__event_st *e = &events[i];
pr_debug("running test %d '%s'\n", i, e->name); pr_debug("running test %d '%s'\n", i, e->name);
ret = test_event(e); ret1 = test_event(e);
if (ret) if (ret1)
break; ret2 = ret1;
} }
return ret; return ret2;
} }
static int test_term(struct test__term *t) static int test_term(struct test__term *t)
...@@ -1021,13 +1022,13 @@ static int test_pmu(void) ...@@ -1021,13 +1022,13 @@ static int test_pmu(void)
int parse_events__test(void) int parse_events__test(void)
{ {
int ret; int ret1, ret2 = 0;
#define TEST_EVENTS(tests) \ #define TEST_EVENTS(tests) \
do { \ do { \
ret = test_events(tests, ARRAY_SIZE(tests)); \ ret1 = test_events(tests, ARRAY_SIZE(tests)); \
if (ret) \ if (!ret2) \
return ret; \ ret2 = ret1; \
} while (0) } while (0)
TEST_EVENTS(test__events); TEST_EVENTS(test__events);
...@@ -1035,5 +1036,9 @@ do { \ ...@@ -1035,5 +1036,9 @@ do { \
if (test_pmu()) if (test_pmu())
TEST_EVENTS(test__events_pmu); TEST_EVENTS(test__events_pmu);
return test_terms(test__terms, ARRAY_SIZE(test__terms)); ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms));
if (!ret2)
ret2 = ret1;
return ret2;
} }
...@@ -751,6 +751,18 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) ...@@ -751,6 +751,18 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
return 0; return 0;
} }
int parse_events_name(struct list_head *list, char *name)
{
struct perf_evsel *evsel;
list_for_each_entry(evsel, list, node) {
if (!evsel->name)
evsel->name = strdup(name);
}
return 0;
}
static int parse_events__scanner(const char *str, void *data, int start_token) static int parse_events__scanner(const char *str, void *data, int start_token)
{ {
YY_BUFFER_STATE buffer; YY_BUFFER_STATE buffer;
......
...@@ -81,6 +81,7 @@ int parse_events__term_clone(struct parse_events__term **new, ...@@ -81,6 +81,7 @@ int parse_events__term_clone(struct parse_events__term **new,
void parse_events__free_terms(struct list_head *terms); void parse_events__free_terms(struct list_head *terms);
int parse_events__modifier_event(struct list_head *list, char *str, bool add); int parse_events__modifier_event(struct list_head *list, char *str, bool add);
int parse_events__modifier_group(struct list_head *list, char *event_mod); int parse_events__modifier_group(struct list_head *list, char *event_mod);
int parse_events_name(struct list_head *list, char *name);
int parse_events_add_tracepoint(struct list_head **list, int *idx, int parse_events_add_tracepoint(struct list_head **list, int *idx,
char *sys, char *event); char *sys, char *event);
int parse_events_add_numeric(struct list_head **list, int *idx, int parse_events_add_numeric(struct list_head **list, int *idx,
......
...@@ -70,6 +70,12 @@ static int term(yyscan_t scanner, int type) ...@@ -70,6 +70,12 @@ static int term(yyscan_t scanner, int type)
%} %}
%x mem %x mem
%s config
%x event
group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
num_dec [0-9]+ num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+ num_hex 0x[a-fA-F0-9]+
...@@ -84,7 +90,13 @@ modifier_bp [rwx]{1,3} ...@@ -84,7 +90,13 @@ modifier_bp [rwx]{1,3}
{ {
int start_token; int start_token;
start_token = (int) parse_events_get_extra(yyscanner); start_token = parse_events_get_extra(yyscanner);
if (start_token == PE_START_TERMS)
BEGIN(config);
else if (start_token == PE_START_EVENTS)
BEGIN(event);
if (start_token) { if (start_token) {
parse_events_set_extra(NULL, yyscanner); parse_events_set_extra(NULL, yyscanner);
return start_token; return start_token;
...@@ -92,6 +104,26 @@ modifier_bp [rwx]{1,3} ...@@ -92,6 +104,26 @@ modifier_bp [rwx]{1,3}
} }
%} %}
<event>{
{group} {
BEGIN(INITIAL); yyless(0);
}
{event_pmu} |
{event} {
str(yyscanner, PE_EVENT_NAME);
BEGIN(INITIAL); yyless(0);
return PE_EVENT_NAME;
}
. |
<<EOF>> {
BEGIN(INITIAL); yyless(0);
}
}
cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
...@@ -127,18 +159,16 @@ speculative-read|speculative-load | ...@@ -127,18 +159,16 @@ speculative-read|speculative-load |
refs|Reference|ops|access | refs|Reference|ops|access |
misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
/* <config>{
* These are event config hardcoded term names to be specified
* within xxx/.../ syntax. So far we dont clash with other names,
* so we can put them here directly. In case the we have a conflict
* in future, this needs to go into '//' condition block.
*/
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
}
mem: { BEGIN(mem); return PE_PREFIX_MEM; } mem: { BEGIN(mem); return PE_PREFIX_MEM; }
r{num_raw_hex} { return raw(yyscanner); } r{num_raw_hex} { return raw(yyscanner); }
...@@ -147,11 +177,11 @@ r{num_raw_hex} { return raw(yyscanner); } ...@@ -147,11 +177,11 @@ r{num_raw_hex} { return raw(yyscanner); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return str(yyscanner, PE_NAME); } {name} { return str(yyscanner, PE_NAME); }
"/" { return '/'; } "/" { BEGIN(config); return '/'; }
- { return '-'; } - { return '-'; }
, { return ','; } , { BEGIN(event); return ','; }
: { return ':'; } : { return ':'; }
"{" { return '{'; } "{" { BEGIN(event); return '{'; }
"}" { return '}'; } "}" { return '}'; }
= { return '='; } = { return '='; }
\n { } \n { }
......
...@@ -27,6 +27,7 @@ do { \ ...@@ -27,6 +27,7 @@ do { \
%token PE_START_EVENTS PE_START_TERMS %token PE_START_EVENTS PE_START_TERMS
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
%token PE_EVENT_NAME
%token PE_NAME %token PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP %token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
...@@ -42,6 +43,7 @@ do { \ ...@@ -42,6 +43,7 @@ do { \
%type <str> PE_NAME_CACHE_OP_RESULT %type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT %type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP %type <str> PE_MODIFIER_BP
%type <str> PE_EVENT_NAME
%type <num> value_sym %type <num> value_sym
%type <head> event_config %type <head> event_config
%type <term> event_term %type <term> event_term
...@@ -53,6 +55,8 @@ do { \ ...@@ -53,6 +55,8 @@ do { \
%type <head> event_legacy_numeric %type <head> event_legacy_numeric
%type <head> event_legacy_raw %type <head> event_legacy_raw
%type <head> event_def %type <head> event_def
%type <head> event_mod
%type <head> event_name
%type <head> event %type <head> event
%type <head> events %type <head> events
%type <head> group_def %type <head> group_def
...@@ -143,8 +147,10 @@ events ',' event ...@@ -143,8 +147,10 @@ events ',' event
| |
event event
event: event: event_mod
event_def PE_MODIFIER_EVENT
event_mod:
event_name PE_MODIFIER_EVENT
{ {
struct list_head *list = $1; struct list_head *list = $1;
...@@ -157,6 +163,16 @@ event_def PE_MODIFIER_EVENT ...@@ -157,6 +163,16 @@ event_def PE_MODIFIER_EVENT
$$ = list; $$ = list;
} }
| |
event_name
event_name:
PE_EVENT_NAME event_def
{
ABORT_ON(parse_events_name($2, $1));
free($1);
$$ = $2;
}
|
event_def event_def
event_def: event_pmu | event_def: event_pmu |
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "pmu.h" #include "pmu.h"
#include "parse-events.h" #include "parse-events.h"
#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
int perf_pmu_parse(struct list_head *list, char *name); int perf_pmu_parse(struct list_head *list, char *name);
extern FILE *perf_pmu_in; extern FILE *perf_pmu_in;
...@@ -69,7 +71,7 @@ static int pmu_format(char *name, struct list_head *format) ...@@ -69,7 +71,7 @@ static int pmu_format(char *name, struct list_head *format)
return -1; return -1;
snprintf(path, PATH_MAX, snprintf(path, PATH_MAX,
"%s/bus/event_source/devices/%s/format", sysfs, name); "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name);
if (stat(path, &st) < 0) if (stat(path, &st) < 0)
return 0; /* no error if format does not exist */ return 0; /* no error if format does not exist */
...@@ -206,7 +208,7 @@ static int pmu_type(char *name, __u32 *type) ...@@ -206,7 +208,7 @@ static int pmu_type(char *name, __u32 *type)
return -1; return -1;
snprintf(path, PATH_MAX, snprintf(path, PATH_MAX,
"%s/bus/event_source/devices/%s/type", sysfs, name); "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name);
if (stat(path, &st) < 0) if (stat(path, &st) < 0)
return -1; return -1;
...@@ -222,6 +224,35 @@ static int pmu_type(char *name, __u32 *type) ...@@ -222,6 +224,35 @@ static int pmu_type(char *name, __u32 *type)
return ret; return ret;
} }
/* Add all pmus in sysfs to pmu list: */
static void pmu_read_sysfs(void)
{
char path[PATH_MAX];
const char *sysfs;
DIR *dir;
struct dirent *dent;
sysfs = sysfs_find_mountpoint();
if (!sysfs)
return;
snprintf(path, PATH_MAX,
"%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
dir = opendir(path);
if (!dir)
return;
while ((dent = readdir(dir))) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
/* add to static LIST_HEAD(pmus): */
perf_pmu__find(dent->d_name);
}
closedir(dir);
}
static struct perf_pmu *pmu_lookup(char *name) static struct perf_pmu *pmu_lookup(char *name)
{ {
struct perf_pmu *pmu; struct perf_pmu *pmu;
...@@ -267,6 +298,21 @@ static struct perf_pmu *pmu_find(char *name) ...@@ -267,6 +298,21 @@ static struct perf_pmu *pmu_find(char *name)
return NULL; return NULL;
} }
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
{
/*
* pmu iterator: If pmu is NULL, we start at the begin,
* otherwise return the next pmu. Returns NULL on end.
*/
if (!pmu) {
pmu_read_sysfs();
pmu = list_prepare_entry(pmu, &pmus, list);
}
list_for_each_entry_continue(pmu, &pmus, list)
return pmu;
return NULL;
}
struct perf_pmu *perf_pmu__find(char *name) struct perf_pmu *perf_pmu__find(char *name)
{ {
struct perf_pmu *pmu; struct perf_pmu *pmu;
......
...@@ -46,5 +46,7 @@ int perf_pmu__new_format(struct list_head *list, char *name, ...@@ -46,5 +46,7 @@ int perf_pmu__new_format(struct list_head *list, char *name,
int config, unsigned long *bits); int config, unsigned long *bits);
void perf_pmu__set_format(unsigned long *bits, long from, long to); void perf_pmu__set_format(unsigned long *bits, long from, long to);
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
int perf_pmu__test(void); int perf_pmu__test(void);
#endif /* __PMU_H */ #endif /* __PMU_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment