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 {
#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 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,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 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);
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);
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);
for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) {
if (j)
fputc(',', fp);
fprintf(fp, " %"PRIu64, id);
fprintf(fp, " %"PRIu64, *id);
}
if (nr && j == nr)
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 */
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