mirror of https://gitee.com/openkylin/linux.git
perf/core improvements and fixes
. 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: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJQNj/MAAoJENZQFvNTUqpAd8kQAIcCkyYJdvKDdvLZui5pR3Xg +WKe43T/cmUUR0JMmdz7QTZ93rXnbmQA6dbFcYQr5NKp5FERmNDiP9ruf6zSles0 bXBs6JKBOuXX0ZmLtAPNDlJie76QwfMuhoK3MzB5dRW8THZX00QBIcbO+7cl5fO5 3ub10eyLXAzFc9dp4LJCUTCI8uD6OnnCLXRkkG/yi5fEDtrGhovC/DolbDFUp56d enaeViUYhoYnUG9J+93EqW4r9STgwmelyYolxRB5Ohy9UVWxXjNSYZ7i3Jwl2Jhp R4ufHGnpvhwC1jFfEQtX3ZCeIKCnGhkEPOd+86Pf3B14kcZyeamSF7I24jO50FDJ xUbQ6vkBUr6gPZQN9qHYr7wI5pjpGcBqDNq3ivywgo7JbPFT5CaPqwwIdE7uUX/5 tThs9CGN4UHnJUyl9SZLOxr7YILBJjSJsIPb8eVhUTZwExQyWXyM18+gPIMGfHEt 8wXHVtCOid+UJlKjqnC6DzbiZsFNkb9I66t/rEQwUEGUeWc8/ClYsPCmE629U2F6 NY+26P5Khyu0yEUE5nfN/5+yy7Fl7NlTBUsqe01lAH/ghwGBU6Mzb3qxP64wGhUb GllRWMek5CZYtQ5AuyzmCX3+hrL1ioQAsA+rv1+DO5WbozhBH895QrBQKZF8/wix cUz3A1J3ReYNq+hQZg1n =HPCu -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
734e9a26d6
|
@ -205,8 +205,8 @@ enum perf_event_read_format {
|
|||
#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
|
||||
#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
|
||||
#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
|
||||
#define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */
|
||||
#define PERF_ATTR_SIZE_VER4 96 /* add: sample_stack_user */
|
||||
#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
|
||||
/* add: sample_stack_user */
|
||||
|
||||
/*
|
||||
* 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)
|
|||
*
|
||||
* /sys/kernel/debug/tracing/events/.../.../format
|
||||
*/
|
||||
int pevent_parse_event(struct pevent *pevent,
|
||||
const char *buf, unsigned long size,
|
||||
const char *sys)
|
||||
enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
|
||||
unsigned long size, const char *sys)
|
||||
{
|
||||
struct event_format *event;
|
||||
int ret;
|
||||
|
@ -4697,17 +4696,16 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
|
||||
event = alloc_event();
|
||||
if (!event)
|
||||
return -ENOMEM;
|
||||
return PEVENT_ERRNO__MEM_ALLOC_FAILED;
|
||||
|
||||
event->name = event_read_name();
|
||||
if (!event->name) {
|
||||
/* Bad event? */
|
||||
free(event);
|
||||
return -1;
|
||||
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
|
||||
goto event_alloc_failed;
|
||||
}
|
||||
|
||||
if (strcmp(sys, "ftrace") == 0) {
|
||||
|
||||
event->flags |= EVENT_FL_ISFTRACE;
|
||||
|
||||
if (strcmp(event->name, "bprint") == 0)
|
||||
|
@ -4715,20 +4713,28 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
}
|
||||
|
||||
event->id = event_read_id();
|
||||
if (event->id < 0)
|
||||
die("failed to read event id");
|
||||
if (event->id < 0) {
|
||||
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);
|
||||
if (!event->system)
|
||||
die("failed to allocate system");
|
||||
if (!event->system) {
|
||||
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
|
||||
goto event_alloc_failed;
|
||||
}
|
||||
|
||||
/* Add pevent to event so that it can be referenced */
|
||||
event->pevent = pevent;
|
||||
|
||||
ret = event_read_format(event);
|
||||
if (ret < 0) {
|
||||
do_warning("failed to read event format for %s", event->name);
|
||||
goto event_failed;
|
||||
ret = PEVENT_ERRNO__READ_FORMAT_FAILED;
|
||||
goto event_parse_failed;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4740,10 +4746,9 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
|
||||
ret = event_read_print(event);
|
||||
if (ret < 0) {
|
||||
do_warning("failed to read event print fmt for %s",
|
||||
event->name);
|
||||
show_warning = 1;
|
||||
goto event_failed;
|
||||
ret = PEVENT_ERRNO__READ_PRINT_FAILED;
|
||||
goto event_parse_failed;
|
||||
}
|
||||
show_warning = 1;
|
||||
|
||||
|
@ -4754,20 +4759,19 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
struct print_arg *arg, **list;
|
||||
|
||||
/* old ftrace had no args */
|
||||
|
||||
list = &event->print_fmt.args;
|
||||
for (field = event->format.fields; field; field = field->next) {
|
||||
arg = alloc_arg();
|
||||
*list = arg;
|
||||
list = &arg->next;
|
||||
arg->type = PRINT_FIELD;
|
||||
arg->field.name = strdup(field->name);
|
||||
if (!arg->field.name) {
|
||||
do_warning("failed to allocate field name");
|
||||
event->flags |= EVENT_FL_FAILED;
|
||||
return -1;
|
||||
free_arg(arg);
|
||||
return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED;
|
||||
}
|
||||
arg->field.field = field;
|
||||
*list = arg;
|
||||
list = &arg->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -4778,11 +4782,65 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
|
||||
return 0;
|
||||
|
||||
event_failed:
|
||||
event_parse_failed:
|
||||
event->flags |= EVENT_FL_FAILED;
|
||||
/* still add it even if it failed */
|
||||
add_event(pevent, event);
|
||||
return -1;
|
||||
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;
|
||||
|
||||
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,
|
||||
|
|
|
@ -345,6 +345,34 @@ enum pevent_flag {
|
|||
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_list;
|
||||
struct func_map;
|
||||
|
@ -509,8 +537,8 @@ 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 long_size);
|
||||
|
||||
int pevent_parse_event(struct pevent *pevent, const char *buf,
|
||||
unsigned long size, const char *sys);
|
||||
enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
|
||||
unsigned long size, const char *sys);
|
||||
|
||||
void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
|
||||
const char *name, struct pevent_record *record,
|
||||
|
@ -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);
|
||||
void pevent_event_info(struct trace_seq *s, struct event_format *event,
|
||||
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 format_field **pevent_event_common_fields(struct event_format *event);
|
||||
|
|
|
@ -39,6 +39,12 @@ void __vdie(const char *fmt, ...);
|
|||
void __vwarning(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)
|
||||
{
|
||||
char *ret;
|
||||
|
|
|
@ -493,7 +493,7 @@ endif
|
|||
|
||||
FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
|
||||
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
|
||||
endif # Libunwind support
|
||||
endif # NO_LIBUNWIND
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "symbol.h"
|
||||
#include "debug.h"
|
||||
#include "cpumap.h"
|
||||
#include "pmu.h"
|
||||
|
||||
static bool no_buildid_cache = false;
|
||||
|
||||
|
@ -1003,6 +1004,45 @@ static int write_numa_topology(int fd, struct perf_header *h __used,
|
|||
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
|
||||
* 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)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
uint64_t id;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
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;
|
||||
char *str;
|
||||
u32 nre, sz, nr, i, j;
|
||||
ssize_t ret;
|
||||
size_t msz;
|
||||
|
@ -1173,18 +1230,22 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
|
|||
if (ph->needs_swap)
|
||||
sz = bswap_32(sz);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
/* buffer to hold on file attr struct */
|
||||
buf = malloc(sz);
|
||||
if (!buf)
|
||||
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)
|
||||
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
|
||||
|
@ -1197,7 +1258,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
|
|||
if (ph->needs_swap)
|
||||
perf_event__attr_swap(buf);
|
||||
|
||||
memcpy(&attr, buf, msz);
|
||||
memcpy(&evsel->attr, buf, msz);
|
||||
|
||||
ret = read(fd, &nr, 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)
|
|||
if (ph->needs_swap)
|
||||
nr = bswap_32(nr);
|
||||
|
||||
str = do_read_string(fd, ph);
|
||||
fprintf(fp, "# event : name = %s, ", str);
|
||||
free(str);
|
||||
evsel->name = do_read_string(fd, ph);
|
||||
|
||||
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
|
||||
", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
|
||||
attr.type,
|
||||
(u64)attr.config,
|
||||
(u64)attr.config1,
|
||||
(u64)attr.config2);
|
||||
evsel->attr.type,
|
||||
(u64)evsel->attr.config,
|
||||
(u64)evsel->attr.config1,
|
||||
(u64)evsel->attr.config2);
|
||||
|
||||
fprintf(fp, ", excl_usr = %d, excl_kern = %d",
|
||||
attr.exclude_user,
|
||||
attr.exclude_kernel);
|
||||
evsel->attr.exclude_user,
|
||||
evsel->attr.exclude_kernel);
|
||||
|
||||
fprintf(fp, ", excl_host = %d, excl_guest = %d",
|
||||
attr.exclude_host,
|
||||
attr.exclude_guest);
|
||||
evsel->attr.exclude_host,
|
||||
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 = {");
|
||||
|
||||
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)
|
||||
fputc(',', fp);
|
||||
|
||||
fprintf(fp, " %"PRIu64, id);
|
||||
}
|
||||
if (nr && j == nr)
|
||||
for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) {
|
||||
if (j)
|
||||
fputc(',', fp);
|
||||
fprintf(fp, " %"PRIu64, *id);
|
||||
}
|
||||
fprintf(fp, " }");
|
||||
}
|
||||
|
||||
fputc('\n', fp);
|
||||
}
|
||||
free(buf);
|
||||
return;
|
||||
error:
|
||||
fprintf(fp, "# event desc: not available or unable to read\n");
|
||||
|
||||
free_event_desc(events);
|
||||
}
|
||||
|
||||
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,
|
|||
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,
|
||||
char *filename,
|
||||
struct perf_session *session)
|
||||
|
@ -1504,6 +1633,56 @@ static int process_build_id(struct perf_file_section *section,
|
|||
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 {
|
||||
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
|
||||
void (*print)(struct perf_header *h, int fd, FILE *fp);
|
||||
|
@ -1537,11 +1716,12 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
|
|||
FEAT_OPA(HEADER_CPUDESC, cpudesc),
|
||||
FEAT_OPA(HEADER_CPUID, cpuid),
|
||||
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_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
|
||||
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
|
||||
FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
|
||||
FEAT_OPA(HEADER_PMU_MAPPINGS, pmu_mappings),
|
||||
};
|
||||
|
||||
struct header_print_data {
|
||||
|
@ -1831,7 +2011,6 @@ static const int attr_file_abi_sizes[] = {
|
|||
[1] = PERF_ATTR_SIZE_VER1,
|
||||
[2] = PERF_ATTR_SIZE_VER2,
|
||||
[3] = PERF_ATTR_SIZE_VER3,
|
||||
[4] = PERF_ATTR_SIZE_VER4,
|
||||
0,
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ enum {
|
|||
HEADER_CPU_TOPOLOGY,
|
||||
HEADER_NUMA_TOPOLOGY,
|
||||
HEADER_BRANCH_STACK,
|
||||
HEADER_PMU_MAPPINGS,
|
||||
HEADER_LAST_FEATURE,
|
||||
HEADER_FEAT_BITS = 256,
|
||||
};
|
||||
|
|
|
@ -301,12 +301,13 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
|
|||
{
|
||||
struct perf_evsel *evsel = perf_evlist__first(evlist);
|
||||
|
||||
|
||||
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_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
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);
|
||||
}
|
||||
|
@ -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 precise_ip", !evsel->attr.precise_ip);
|
||||
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);
|
||||
}
|
||||
|
@ -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 precise_ip", evsel->attr.precise_ip);
|
||||
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);
|
||||
}
|
||||
|
@ -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 precise_ip", evsel->attr.precise_ip);
|
||||
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);
|
||||
}
|
||||
|
@ -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 precise_ip", evsel->attr.precise_ip);
|
||||
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);
|
||||
}
|
||||
|
@ -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 config", 2 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong name",
|
||||
!strcmp(perf_evsel__name(evsel), "raw 0x2:u"));
|
||||
!strcmp(perf_evsel__name(evsel), "cpu/config=2/u"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -948,19 +949,19 @@ static int test_event(struct test__event_st *e)
|
|||
|
||||
static int test_events(struct test__event_st *events, unsigned cnt)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret1, ret2 = 0;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct test__event_st *e = &events[i];
|
||||
|
||||
pr_debug("running test %d '%s'\n", i, e->name);
|
||||
ret = test_event(e);
|
||||
if (ret)
|
||||
break;
|
||||
ret1 = test_event(e);
|
||||
if (ret1)
|
||||
ret2 = ret1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret2;
|
||||
}
|
||||
|
||||
static int test_term(struct test__term *t)
|
||||
|
@ -1021,13 +1022,13 @@ static int test_pmu(void)
|
|||
|
||||
int parse_events__test(void)
|
||||
{
|
||||
int ret;
|
||||
int ret1, ret2 = 0;
|
||||
|
||||
#define TEST_EVENTS(tests) \
|
||||
do { \
|
||||
ret = test_events(tests, ARRAY_SIZE(tests)); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
ret1 = test_events(tests, ARRAY_SIZE(tests)); \
|
||||
if (!ret2) \
|
||||
ret2 = ret1; \
|
||||
} while (0)
|
||||
|
||||
TEST_EVENTS(test__events);
|
||||
|
@ -1035,5 +1036,9 @@ do { \
|
|||
if (test_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)
|
|||
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)
|
||||
{
|
||||
YY_BUFFER_STATE buffer;
|
||||
|
|
|
@ -81,6 +81,7 @@ int parse_events__term_clone(struct parse_events__term **new,
|
|||
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_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,
|
||||
char *sys, char *event);
|
||||
int parse_events_add_numeric(struct list_head **list, int *idx,
|
||||
|
|
|
@ -70,6 +70,12 @@ static int term(yyscan_t scanner, int type)
|
|||
%}
|
||||
|
||||
%x mem
|
||||
%s config
|
||||
%x event
|
||||
|
||||
group [^,{}/]*[{][^}]*[}][^,{}/]*
|
||||
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
|
||||
event [^,{}/]+
|
||||
|
||||
num_dec [0-9]+
|
||||
num_hex 0x[a-fA-F0-9]+
|
||||
|
@ -84,7 +90,13 @@ modifier_bp [rwx]{1,3}
|
|||
{
|
||||
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) {
|
||||
parse_events_set_extra(NULL, yyscanner);
|
||||
return start_token;
|
||||
|
@ -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); }
|
||||
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); }
|
||||
|
@ -127,18 +159,16 @@ speculative-read|speculative-load |
|
|||
refs|Reference|ops|access |
|
||||
misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
|
||||
|
||||
/*
|
||||
* 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>{
|
||||
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
|
||||
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
|
||||
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
|
||||
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
|
||||
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
, { return ','; }
|
||||
"/" { BEGIN(INITIAL); return '/'; }
|
||||
}
|
||||
|
||||
mem: { BEGIN(mem); return PE_PREFIX_MEM; }
|
||||
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); }
|
||||
{name} { return str(yyscanner, PE_NAME); }
|
||||
"/" { return '/'; }
|
||||
"/" { BEGIN(config); return '/'; }
|
||||
- { return '-'; }
|
||||
, { return ','; }
|
||||
, { BEGIN(event); return ','; }
|
||||
: { return ':'; }
|
||||
"{" { return '{'; }
|
||||
"{" { BEGIN(event); return '{'; }
|
||||
"}" { return '}'; }
|
||||
= { return '='; }
|
||||
\n { }
|
||||
|
|
|
@ -27,6 +27,7 @@ do { \
|
|||
|
||||
%token PE_START_EVENTS PE_START_TERMS
|
||||
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
|
||||
%token PE_EVENT_NAME
|
||||
%token PE_NAME
|
||||
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
|
||||
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
|
||||
|
@ -42,6 +43,7 @@ do { \
|
|||
%type <str> PE_NAME_CACHE_OP_RESULT
|
||||
%type <str> PE_MODIFIER_EVENT
|
||||
%type <str> PE_MODIFIER_BP
|
||||
%type <str> PE_EVENT_NAME
|
||||
%type <num> value_sym
|
||||
%type <head> event_config
|
||||
%type <term> event_term
|
||||
|
@ -53,6 +55,8 @@ do { \
|
|||
%type <head> event_legacy_numeric
|
||||
%type <head> event_legacy_raw
|
||||
%type <head> event_def
|
||||
%type <head> event_mod
|
||||
%type <head> event_name
|
||||
%type <head> event
|
||||
%type <head> events
|
||||
%type <head> group_def
|
||||
|
@ -143,8 +147,10 @@ events ',' event
|
|||
|
|
||||
event
|
||||
|
||||
event:
|
||||
event_def PE_MODIFIER_EVENT
|
||||
event: event_mod
|
||||
|
||||
event_mod:
|
||||
event_name PE_MODIFIER_EVENT
|
||||
{
|
||||
struct list_head *list = $1;
|
||||
|
||||
|
@ -157,6 +163,16 @@ event_def PE_MODIFIER_EVENT
|
|||
$$ = 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_pmu |
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "pmu.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
|
||||
|
||||
int perf_pmu_parse(struct list_head *list, char *name);
|
||||
extern FILE *perf_pmu_in;
|
||||
|
||||
|
@ -69,7 +71,7 @@ static int pmu_format(char *name, struct list_head *format)
|
|||
return -1;
|
||||
|
||||
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)
|
||||
return 0; /* no error if format does not exist */
|
||||
|
@ -206,7 +208,7 @@ static int pmu_type(char *name, __u32 *type)
|
|||
return -1;
|
||||
|
||||
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)
|
||||
return -1;
|
||||
|
@ -222,6 +224,35 @@ static int pmu_type(char *name, __u32 *type)
|
|||
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)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
@ -267,6 +298,21 @@ static struct perf_pmu *pmu_find(char *name)
|
|||
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 *pmu;
|
||||
|
|
|
@ -46,5 +46,7 @@ int perf_pmu__new_format(struct list_head *list, char *name,
|
|||
int config, unsigned long *bits);
|
||||
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);
|
||||
#endif /* __PMU_H */
|
||||
|
|
Loading…
Reference in New Issue