summaryrefslogtreecommitdiffstats
path: root/kernel/tools/perf/util/parse-events.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/tools/perf/util/parse-events.c')
-rw-r--r--kernel/tools/perf/util/parse-events.c740
1 files changed, 659 insertions, 81 deletions
diff --git a/kernel/tools/perf/util/parse-events.c b/kernel/tools/perf/util/parse-events.c
index be0655388..b48e87693 100644
--- a/kernel/tools/perf/util/parse-events.c
+++ b/kernel/tools/perf/util/parse-events.c
@@ -1,4 +1,5 @@
#include <linux/hw_breakpoint.h>
+#include <linux/err.h>
#include "util.h"
#include "../perf.h"
#include "evlist.h"
@@ -10,13 +11,16 @@
#include "symbol.h"
#include "cache.h"
#include "header.h"
+#include "bpf-loader.h"
#include "debug.h"
-#include <api/fs/debugfs.h>
+#include <api/fs/tracing_path.h>
#include "parse-events-bison.h"
#define YY_EXTRA_TYPE int
#include "parse-events-flex.h"
#include "pmu.h"
#include "thread_map.h"
+#include "cpumap.h"
+#include "asm/bug.h"
#define MAX_NAME_LEN 100
@@ -24,6 +28,8 @@
extern int parse_events_debug;
#endif
int parse_events_parse(void *data, void *scanner);
+static int get_config_terms(struct list_head *head_config,
+ struct list_head *head_terms __maybe_unused);
static struct perf_pmu_event_symbol *perf_pmu_events_list;
/*
@@ -118,6 +124,10 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
.symbol = "dummy",
.alias = "",
},
+ [PERF_COUNT_SW_BPF_OUTPUT] = {
+ .symbol = "bpf-output",
+ .alias = "",
+ },
};
#define __PERF_EVENT_FIELD(config, name) \
@@ -274,7 +284,8 @@ const char *event_type(int type)
static struct perf_evsel *
__add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr,
- char *name, struct cpu_map *cpus)
+ char *name, struct cpu_map *cpus,
+ struct list_head *config_terms)
{
struct perf_evsel *evsel;
@@ -284,17 +295,24 @@ __add_event(struct list_head *list, int *idx,
if (!evsel)
return NULL;
- evsel->cpus = cpus;
+ evsel->cpus = cpu_map__get(cpus);
+ evsel->own_cpus = cpu_map__get(cpus);
+
if (name)
evsel->name = strdup(name);
+
+ if (config_terms)
+ list_splice(config_terms, &evsel->config_terms);
+
list_add_tail(&evsel->node, list);
return evsel;
}
static int add_event(struct list_head *list, int *idx,
- struct perf_event_attr *attr, char *name)
+ struct perf_event_attr *attr, char *name,
+ struct list_head *config_terms)
{
- return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;
+ return __add_event(list, idx, attr, name, NULL, config_terms) ? 0 : -ENOMEM;
}
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
@@ -373,35 +391,78 @@ int parse_events_add_cache(struct list_head *list, int *idx,
memset(&attr, 0, sizeof(attr));
attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
attr.type = PERF_TYPE_HW_CACHE;
- return add_event(list, idx, &attr, name);
+ return add_event(list, idx, &attr, name, NULL);
+}
+
+static void tracepoint_error(struct parse_events_error *e, int err,
+ char *sys, char *name)
+{
+ char help[BUFSIZ];
+
+ if (!e)
+ return;
+
+ /*
+ * We get error directly from syscall errno ( > 0),
+ * or from encoded pointer's error ( < 0).
+ */
+ err = abs(err);
+
+ switch (err) {
+ case EACCES:
+ e->str = strdup("can't access trace events");
+ break;
+ case ENOENT:
+ e->str = strdup("unknown tracepoint");
+ break;
+ default:
+ e->str = strdup("failed to add tracepoint");
+ break;
+ }
+
+ tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name);
+ e->help = strdup(help);
}
static int add_tracepoint(struct list_head *list, int *idx,
- char *sys_name, char *evt_name)
+ char *sys_name, char *evt_name,
+ struct parse_events_error *err,
+ struct list_head *head_config)
{
struct perf_evsel *evsel;
evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);
- if (!evsel)
- return -ENOMEM;
+ if (IS_ERR(evsel)) {
+ tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name);
+ return PTR_ERR(evsel);
+ }
- list_add_tail(&evsel->node, list);
+ if (head_config) {
+ LIST_HEAD(config_terms);
+ if (get_config_terms(head_config, &config_terms))
+ return -ENOMEM;
+ list_splice(&config_terms, &evsel->config_terms);
+ }
+
+ list_add_tail(&evsel->node, list);
return 0;
}
static int add_tracepoint_multi_event(struct list_head *list, int *idx,
- char *sys_name, char *evt_name)
+ char *sys_name, char *evt_name,
+ struct parse_events_error *err,
+ struct list_head *head_config)
{
char evt_path[MAXPATHLEN];
struct dirent *evt_ent;
DIR *evt_dir;
- int ret = 0;
+ int ret = 0, found = 0;
snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
evt_dir = opendir(evt_path);
if (!evt_dir) {
- perror("Can't open event dir");
+ tracepoint_error(err, errno, sys_name, evt_name);
return -1;
}
@@ -415,7 +476,15 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
if (!strglobmatch(evt_ent->d_name, evt_name))
continue;
- ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
+ found++;
+
+ ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name,
+ err, head_config);
+ }
+
+ if (!found) {
+ tracepoint_error(err, ENOENT, sys_name, evt_name);
+ ret = -1;
}
closedir(evt_dir);
@@ -423,15 +492,21 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
}
static int add_tracepoint_event(struct list_head *list, int *idx,
- char *sys_name, char *evt_name)
+ char *sys_name, char *evt_name,
+ struct parse_events_error *err,
+ struct list_head *head_config)
{
return strpbrk(evt_name, "*?") ?
- add_tracepoint_multi_event(list, idx, sys_name, evt_name) :
- add_tracepoint(list, idx, sys_name, evt_name);
+ add_tracepoint_multi_event(list, idx, sys_name, evt_name,
+ err, head_config) :
+ add_tracepoint(list, idx, sys_name, evt_name,
+ err, head_config);
}
static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
- char *sys_name, char *evt_name)
+ char *sys_name, char *evt_name,
+ struct parse_events_error *err,
+ struct list_head *head_config)
{
struct dirent *events_ent;
DIR *events_dir;
@@ -439,7 +514,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
events_dir = opendir(tracing_events_path);
if (!events_dir) {
- perror("Can't open event dir");
+ tracepoint_error(err, errno, sys_name, evt_name);
return -1;
}
@@ -455,20 +530,136 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
continue;
ret = add_tracepoint_event(list, idx, events_ent->d_name,
- evt_name);
+ evt_name, err, head_config);
}
closedir(events_dir);
return ret;
}
-int parse_events_add_tracepoint(struct list_head *list, int *idx,
- char *sys, char *event)
+struct __add_bpf_event_param {
+ struct parse_events_evlist *data;
+ struct list_head *list;
+};
+
+static int add_bpf_event(struct probe_trace_event *tev, int fd,
+ void *_param)
{
- if (strpbrk(sys, "*?"))
- return add_tracepoint_multi_sys(list, idx, sys, event);
- else
- return add_tracepoint_event(list, idx, sys, event);
+ LIST_HEAD(new_evsels);
+ struct __add_bpf_event_param *param = _param;
+ struct parse_events_evlist *evlist = param->data;
+ struct list_head *list = param->list;
+ struct perf_evsel *pos;
+ int err;
+
+ pr_debug("add bpf event %s:%s and attach bpf program %d\n",
+ tev->group, tev->event, fd);
+
+ err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group,
+ tev->event, evlist->error, NULL);
+ if (err) {
+ struct perf_evsel *evsel, *tmp;
+
+ pr_debug("Failed to add BPF event %s:%s\n",
+ tev->group, tev->event);
+ list_for_each_entry_safe(evsel, tmp, &new_evsels, node) {
+ list_del(&evsel->node);
+ perf_evsel__delete(evsel);
+ }
+ return err;
+ }
+ pr_debug("adding %s:%s\n", tev->group, tev->event);
+
+ list_for_each_entry(pos, &new_evsels, node) {
+ pr_debug("adding %s:%s to %p\n",
+ tev->group, tev->event, pos);
+ pos->bpf_fd = fd;
+ }
+ list_splice(&new_evsels, list);
+ return 0;
+}
+
+int parse_events_load_bpf_obj(struct parse_events_evlist *data,
+ struct list_head *list,
+ struct bpf_object *obj)
+{
+ int err;
+ char errbuf[BUFSIZ];
+ struct __add_bpf_event_param param = {data, list};
+ static bool registered_unprobe_atexit = false;
+
+ if (IS_ERR(obj) || !obj) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Internal error: load bpf obj with NULL");
+ err = -EINVAL;
+ goto errout;
+ }
+
+ /*
+ * Register atexit handler before calling bpf__probe() so
+ * bpf__probe() don't need to unprobe probe points its already
+ * created when failure.
+ */
+ if (!registered_unprobe_atexit) {
+ atexit(bpf__clear);
+ registered_unprobe_atexit = true;
+ }
+
+ err = bpf__probe(obj);
+ if (err) {
+ bpf__strerror_probe(obj, err, errbuf, sizeof(errbuf));
+ goto errout;
+ }
+
+ err = bpf__load(obj);
+ if (err) {
+ bpf__strerror_load(obj, err, errbuf, sizeof(errbuf));
+ goto errout;
+ }
+
+ err = bpf__foreach_tev(obj, add_bpf_event, &param);
+ if (err) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Attach events in BPF object failed");
+ goto errout;
+ }
+
+ return 0;
+errout:
+ data->error->help = strdup("(add -v to see detail)");
+ data->error->str = strdup(errbuf);
+ return err;
+}
+
+int parse_events_load_bpf(struct parse_events_evlist *data,
+ struct list_head *list,
+ char *bpf_file_name,
+ bool source)
+{
+ struct bpf_object *obj;
+
+ obj = bpf__prepare_load(bpf_file_name, source);
+ if (IS_ERR(obj)) {
+ char errbuf[BUFSIZ];
+ int err;
+
+ err = PTR_ERR(obj);
+
+ if (err == -ENOTSUP)
+ snprintf(errbuf, sizeof(errbuf),
+ "BPF support is not compiled");
+ else
+ bpf__strerror_prepare_load(bpf_file_name,
+ source,
+ -err, errbuf,
+ sizeof(errbuf));
+
+ data->error->help = strdup("(add -v to see detail)");
+ data->error->str = strdup(errbuf);
+ return err;
+ }
+
+ return parse_events_load_bpf_obj(data, list, obj);
}
static int
@@ -535,16 +726,38 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
attr.type = PERF_TYPE_BREAKPOINT;
attr.sample_period = 1;
- return add_event(list, idx, &attr, NULL);
+ return add_event(list, idx, &attr, NULL, NULL);
}
-static int config_term(struct perf_event_attr *attr,
- struct parse_events_term *term)
+static int check_type_val(struct parse_events_term *term,
+ struct parse_events_error *err,
+ int type)
{
-#define CHECK_TYPE_VAL(type) \
-do { \
- if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val) \
- return -EINVAL; \
+ if (type == term->type_val)
+ return 0;
+
+ if (err) {
+ err->idx = term->err_val;
+ if (type == PARSE_EVENTS__TERM_TYPE_NUM)
+ err->str = strdup("expected numeric value");
+ else
+ err->str = strdup("expected string value");
+ }
+ return -EINVAL;
+}
+
+typedef int config_term_func_t(struct perf_event_attr *attr,
+ struct parse_events_term *term,
+ struct parse_events_error *err);
+
+static int config_term_common(struct perf_event_attr *attr,
+ struct parse_events_term *term,
+ struct parse_events_error *err)
+{
+#define CHECK_TYPE_VAL(type) \
+do { \
+ if (check_type_val(term, err, PARSE_EVENTS__TERM_TYPE_ ## type)) \
+ return -EINVAL; \
} while (0)
switch (term->type_term) {
@@ -562,7 +775,9 @@ do { \
break;
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
CHECK_TYPE_VAL(NUM);
- attr->sample_period = term->val.num;
+ break;
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
+ CHECK_TYPE_VAL(NUM);
break;
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
/*
@@ -570,10 +785,33 @@ do { \
* attr->branch_sample_type = term->val.num;
*/
break;
+ case PARSE_EVENTS__TERM_TYPE_TIME:
+ CHECK_TYPE_VAL(NUM);
+ if (term->val.num > 1) {
+ err->str = strdup("expected 0 or 1");
+ err->idx = term->err_val;
+ return -EINVAL;
+ }
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+ CHECK_TYPE_VAL(STR);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+ CHECK_TYPE_VAL(NUM);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_INHERIT:
+ CHECK_TYPE_VAL(NUM);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+ CHECK_TYPE_VAL(NUM);
+ break;
case PARSE_EVENTS__TERM_TYPE_NAME:
CHECK_TYPE_VAL(STR);
break;
default:
+ err->str = strdup("unknown term");
+ err->idx = term->err_term;
+ err->help = parse_events_formats_error_string(NULL);
return -EINVAL;
}
@@ -581,33 +819,149 @@ do { \
#undef CHECK_TYPE_VAL
}
+static int config_term_pmu(struct perf_event_attr *attr,
+ struct parse_events_term *term,
+ struct parse_events_error *err)
+{
+ if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER)
+ /*
+ * Always succeed for sysfs terms, as we dont know
+ * at this point what type they need to have.
+ */
+ return 0;
+ else
+ return config_term_common(attr, term, err);
+}
+
+static int config_term_tracepoint(struct perf_event_attr *attr,
+ struct parse_events_term *term,
+ struct parse_events_error *err)
+{
+ switch (term->type_term) {
+ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+ case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+ case PARSE_EVENTS__TERM_TYPE_INHERIT:
+ case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+ return config_term_common(attr, term, err);
+ default:
+ if (err) {
+ err->idx = term->err_term;
+ err->str = strdup("unknown term");
+ err->help = strdup("valid terms: call-graph,stack-size\n");
+ }
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int config_attr(struct perf_event_attr *attr,
- struct list_head *head, int fail)
+ struct list_head *head,
+ struct parse_events_error *err,
+ config_term_func_t config_term)
{
struct parse_events_term *term;
list_for_each_entry(term, head, list)
- if (config_term(attr, term) && fail)
+ if (config_term(attr, term, err))
return -EINVAL;
return 0;
}
-int parse_events_add_numeric(struct list_head *list, int *idx,
+static int get_config_terms(struct list_head *head_config,
+ struct list_head *head_terms __maybe_unused)
+{
+#define ADD_CONFIG_TERM(__type, __name, __val) \
+do { \
+ struct perf_evsel_config_term *__t; \
+ \
+ __t = zalloc(sizeof(*__t)); \
+ if (!__t) \
+ return -ENOMEM; \
+ \
+ INIT_LIST_HEAD(&__t->list); \
+ __t->type = PERF_EVSEL__CONFIG_TERM_ ## __type; \
+ __t->val.__name = __val; \
+ list_add_tail(&__t->list, head_terms); \
+} while (0)
+
+ struct parse_events_term *term;
+
+ list_for_each_entry(term, head_config, list) {
+ switch (term->type_term) {
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+ ADD_CONFIG_TERM(PERIOD, period, term->val.num);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
+ ADD_CONFIG_TERM(FREQ, freq, term->val.num);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_TIME:
+ ADD_CONFIG_TERM(TIME, time, term->val.num);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+ ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+ ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_INHERIT:
+ ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 1 : 0);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+ ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 0 : 1);
+ break;
+ default:
+ break;
+ }
+ }
+#undef ADD_EVSEL_CONFIG
+ return 0;
+}
+
+int parse_events_add_tracepoint(struct list_head *list, int *idx,
+ char *sys, char *event,
+ struct parse_events_error *err,
+ struct list_head *head_config)
+{
+ if (head_config) {
+ struct perf_event_attr attr;
+
+ if (config_attr(&attr, head_config, err,
+ config_term_tracepoint))
+ return -EINVAL;
+ }
+
+ if (strpbrk(sys, "*?"))
+ return add_tracepoint_multi_sys(list, idx, sys, event,
+ err, head_config);
+ else
+ return add_tracepoint_event(list, idx, sys, event,
+ err, head_config);
+}
+
+int parse_events_add_numeric(struct parse_events_evlist *data,
+ struct list_head *list,
u32 type, u64 config,
struct list_head *head_config)
{
struct perf_event_attr attr;
+ LIST_HEAD(config_terms);
memset(&attr, 0, sizeof(attr));
attr.type = type;
attr.config = config;
- if (head_config &&
- config_attr(&attr, head_config, 1))
- return -EINVAL;
+ if (head_config) {
+ if (config_attr(&attr, head_config, data->error,
+ config_term_common))
+ return -EINVAL;
+
+ if (get_config_terms(head_config, &config_terms))
+ return -ENOMEM;
+ }
- return add_event(list, idx, &attr, NULL);
+ return add_event(list, &data->idx, &attr, NULL, &config_terms);
}
static int parse_events__is_name_term(struct parse_events_term *term)
@@ -626,13 +980,15 @@ static char *pmu_event_name(struct list_head *head_terms)
return NULL;
}
-int parse_events_add_pmu(struct list_head *list, int *idx,
- char *name, struct list_head *head_config)
+int parse_events_add_pmu(struct parse_events_evlist *data,
+ struct list_head *list, char *name,
+ struct list_head *head_config)
{
struct perf_event_attr attr;
struct perf_pmu_info info;
struct perf_pmu *pmu;
struct perf_evsel *evsel;
+ LIST_HEAD(config_terms);
pmu = perf_pmu__find(name);
if (!pmu)
@@ -647,7 +1003,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
if (!head_config) {
attr.type = pmu->type;
- evsel = __add_event(list, idx, &attr, NULL, pmu->cpus);
+ evsel = __add_event(list, &data->idx, &attr, NULL, pmu->cpus, NULL);
return evsel ? 0 : -ENOMEM;
}
@@ -658,13 +1014,18 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
* Configure hardcoded terms first, no need to check
* return value when called with fail == 0 ;)
*/
- config_attr(&attr, head_config, 0);
+ if (config_attr(&attr, head_config, data->error, config_term_pmu))
+ return -EINVAL;
- if (perf_pmu__config(pmu, &attr, head_config))
+ if (get_config_terms(head_config, &config_terms))
+ return -ENOMEM;
+
+ if (perf_pmu__config(pmu, &attr, head_config, data->error))
return -EINVAL;
- evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
- pmu->cpus);
+ evsel = __add_event(list, &data->idx, &attr,
+ pmu_event_name(head_config), pmu->cpus,
+ &config_terms);
if (evsel) {
evsel->unit = info.unit;
evsel->scale = info.scale;
@@ -685,6 +1046,11 @@ void parse_events__set_leader(char *name, struct list_head *list)
{
struct perf_evsel *leader;
+ if (list_empty(list)) {
+ WARN_ONCE(true, "WARNING: failed to set leader: empty list");
+ return;
+ }
+
__perf_evlist__set_leader(list);
leader = list_entry(list->next, struct perf_evsel, node);
leader->group_name = name ? strdup(name) : NULL;
@@ -711,6 +1077,7 @@ struct event_modifier {
int eG;
int eI;
int precise;
+ int precise_max;
int exclude_GH;
int sample_read;
int pinned;
@@ -726,6 +1093,7 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
int eG = evsel ? evsel->attr.exclude_guest : 0;
int eI = evsel ? evsel->attr.exclude_idle : 0;
int precise = evsel ? evsel->attr.precise_ip : 0;
+ int precise_max = 0;
int sample_read = 0;
int pinned = evsel ? evsel->attr.pinned : 0;
@@ -762,6 +1130,8 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
/* use of precise requires exclude_guest */
if (!exclude_GH)
eG = 1;
+ } else if (*str == 'P') {
+ precise_max = 1;
} else if (*str == 'S') {
sample_read = 1;
} else if (*str == 'D') {
@@ -792,6 +1162,7 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
mod->eG = eG;
mod->eI = eI;
mod->precise = precise;
+ mod->precise_max = precise_max;
mod->exclude_GH = exclude_GH;
mod->sample_read = sample_read;
mod->pinned = pinned;
@@ -808,7 +1179,7 @@ static int check_modifier(char *str)
char *p = str;
/* The sizeof includes 0 byte as well. */
- if (strlen(str) > (sizeof("ukhGHpppSDI") - 1))
+ if (strlen(str) > (sizeof("ukhGHpppPSDI") - 1))
return -1;
while (*p) {
@@ -847,6 +1218,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
evsel->attr.exclude_idle = mod.eI;
evsel->exclude_GH = mod.exclude_GH;
evsel->sample_read = mod.sample_read;
+ evsel->precise_max = mod.precise_max;
if (perf_evsel__is_group_leader(evsel))
evsel->attr.pinned = mod.pinned;
@@ -1019,20 +1391,31 @@ int parse_events_terms(struct list_head *terms, const char *str)
return ret;
}
-int parse_events(struct perf_evlist *evlist, const char *str)
+int parse_events(struct perf_evlist *evlist, const char *str,
+ struct parse_events_error *err)
{
struct parse_events_evlist data = {
- .list = LIST_HEAD_INIT(data.list),
- .idx = evlist->nr_entries,
+ .list = LIST_HEAD_INIT(data.list),
+ .idx = evlist->nr_entries,
+ .error = err,
};
int ret;
ret = parse_events__scanner(str, &data, PE_START_EVENTS);
perf_pmu__parse_cleanup();
if (!ret) {
- int entries = data.idx - evlist->nr_entries;
- perf_evlist__splice_list_tail(evlist, &data.list, entries);
+ struct perf_evsel *last;
+
+ if (list_empty(&data.list)) {
+ WARN_ONCE(true, "WARNING: event parser found nothing");
+ return -1;
+ }
+
+ perf_evlist__splice_list_tail(evlist, &data.list);
evlist->nr_groups += data.nr_groups;
+ last = perf_evlist__last(evlist);
+ last->cmdline_group_boundary = true;
+
return 0;
}
@@ -1044,43 +1427,183 @@ int parse_events(struct perf_evlist *evlist, const char *str)
return ret;
}
+#define MAX_WIDTH 1000
+static int get_term_width(void)
+{
+ struct winsize ws;
+
+ get_term_dimensions(&ws);
+ return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col;
+}
+
+static void parse_events_print_error(struct parse_events_error *err,
+ const char *event)
+{
+ const char *str = "invalid or unsupported event: ";
+ char _buf[MAX_WIDTH];
+ char *buf = (char *) event;
+ int idx = 0;
+
+ if (err->str) {
+ /* -2 for extra '' in the final fprintf */
+ int width = get_term_width() - 2;
+ int len_event = strlen(event);
+ int len_str, max_len, cut = 0;
+
+ /*
+ * Maximum error index indent, we will cut
+ * the event string if it's bigger.
+ */
+ int max_err_idx = 13;
+
+ /*
+ * Let's be specific with the message when
+ * we have the precise error.
+ */
+ str = "event syntax error: ";
+ len_str = strlen(str);
+ max_len = width - len_str;
+
+ buf = _buf;
+
+ /* We're cutting from the beggining. */
+ if (err->idx > max_err_idx)
+ cut = err->idx - max_err_idx;
+
+ strncpy(buf, event + cut, max_len);
+
+ /* Mark cut parts with '..' on both sides. */
+ if (cut)
+ buf[0] = buf[1] = '.';
+
+ if ((len_event - cut) > max_len) {
+ buf[max_len - 1] = buf[max_len - 2] = '.';
+ buf[max_len] = 0;
+ }
+
+ idx = len_str + err->idx - cut;
+ }
+
+ fprintf(stderr, "%s'%s'\n", str, buf);
+ if (idx) {
+ fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str);
+ if (err->help)
+ fprintf(stderr, "\n%s\n", err->help);
+ free(err->str);
+ free(err->help);
+ }
+
+ fprintf(stderr, "Run 'perf list' for a list of valid events\n");
+}
+
+#undef MAX_WIDTH
+
int parse_events_option(const struct option *opt, const char *str,
int unset __maybe_unused)
{
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
- int ret = parse_events(evlist, str);
+ struct parse_events_error err = { .idx = 0, };
+ int ret = parse_events(evlist, str, &err);
+
+ if (ret)
+ parse_events_print_error(&err, str);
- if (ret) {
- fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
- fprintf(stderr, "Run 'perf list' for a list of valid events\n");
- }
return ret;
}
-int parse_filter(const struct option *opt, const char *str,
- int unset __maybe_unused)
+static int
+foreach_evsel_in_last_glob(struct perf_evlist *evlist,
+ int (*func)(struct perf_evsel *evsel,
+ const void *arg),
+ const void *arg)
{
- struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
struct perf_evsel *last = NULL;
+ int err;
+ /*
+ * Don't return when list_empty, give func a chance to report
+ * error when it found last == NULL.
+ *
+ * So no need to WARN here, let *func do this.
+ */
if (evlist->nr_entries > 0)
last = perf_evlist__last(evlist);
- if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
+ do {
+ err = (*func)(last, arg);
+ if (err)
+ return -1;
+ if (!last)
+ return 0;
+
+ if (last->node.prev == &evlist->entries)
+ return 0;
+ last = list_entry(last->node.prev, struct perf_evsel, node);
+ } while (!last->cmdline_group_boundary);
+
+ return 0;
+}
+
+static int set_filter(struct perf_evsel *evsel, const void *arg)
+{
+ const char *str = arg;
+
+ if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
fprintf(stderr,
"--filter option should follow a -e tracepoint option\n");
return -1;
}
- last->filter = strdup(str);
- if (last->filter == NULL) {
- fprintf(stderr, "not enough memory to hold filter string\n");
+ if (perf_evsel__append_filter(evsel, "&&", str) < 0) {
+ fprintf(stderr,
+ "not enough memory to hold filter string\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int parse_filter(const struct option *opt, const char *str,
+ int unset __maybe_unused)
+{
+ struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+
+ return foreach_evsel_in_last_glob(evlist, set_filter,
+ (const void *)str);
+}
+
+static int add_exclude_perf_filter(struct perf_evsel *evsel,
+ const void *arg __maybe_unused)
+{
+ char new_filter[64];
+
+ if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ fprintf(stderr,
+ "--exclude-perf option should follow a -e tracepoint option\n");
+ return -1;
+ }
+
+ snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid());
+
+ if (perf_evsel__append_filter(evsel, "&&", new_filter) < 0) {
+ fprintf(stderr,
+ "not enough memory to hold filter string\n");
return -1;
}
return 0;
}
+int exclude_perf(const struct option *opt,
+ const char *arg __maybe_unused,
+ int unset __maybe_unused)
+{
+ struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+
+ return foreach_evsel_in_last_glob(evlist, add_exclude_perf_filter,
+ NULL);
+}
+
static const char * const event_type_descriptors[] = {
"Hardware event",
"Software event",
@@ -1171,7 +1694,7 @@ restart:
printf(" %-50s [%s]\n", evt_list[evt_i++],
event_type_descriptors[PERF_TYPE_TRACEPOINT]);
}
- if (evt_num)
+ if (evt_num && pager_in_use())
printf("\n");
out_free:
@@ -1327,7 +1850,7 @@ restart:
printf(" %-50s [%s]\n", evt_list[evt_i++],
event_type_descriptors[PERF_TYPE_HW_CACHE]);
}
- if (evt_num)
+ if (evt_num && pager_in_use())
printf("\n");
out_free:
@@ -1363,7 +1886,7 @@ restart:
for (i = 0; i < max; i++, syms++) {
- if (event_glob != NULL &&
+ if (event_glob != NULL && syms->symbol != NULL &&
!(strglobmatch(syms->symbol, event_glob) ||
(syms->alias && strglobmatch(syms->alias, event_glob))))
continue;
@@ -1400,7 +1923,7 @@ restart:
}
printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
}
- if (evt_num)
+ if (evt_num && pager_in_use())
printf("\n");
out_free:
@@ -1441,13 +1964,14 @@ void print_events(const char *event_glob, bool name_only)
printf(" %-50s [%s]\n",
"cpu/t1=v1[,t2=v2,t3 ...]/modifier",
event_type_descriptors[PERF_TYPE_RAW]);
- printf(" (see 'man perf-list' on how to encode it)\n");
- printf("\n");
+ if (pager_in_use())
+ printf(" (see 'man perf-list' on how to encode it)\n\n");
printf(" %-50s [%s]\n",
"mem:<addr>[/len][:access]",
event_type_descriptors[PERF_TYPE_BREAKPOINT]);
- printf("\n");
+ if (pager_in_use())
+ printf("\n");
}
print_tracepoint_events(NULL, NULL, name_only);
@@ -1460,7 +1984,7 @@ int parse_events__is_hardcoded_term(struct parse_events_term *term)
static int new_term(struct parse_events_term **_term, int type_val,
int type_term, char *config,
- char *str, u64 num)
+ char *str, u64 num, int err_term, int err_val)
{
struct parse_events_term *term;
@@ -1472,6 +1996,8 @@ static int new_term(struct parse_events_term **_term, int type_val,
term->type_val = type_val;
term->type_term = type_term;
term->config = config;
+ term->err_term = err_term;
+ term->err_val = err_val;
switch (type_val) {
case PARSE_EVENTS__TERM_TYPE_NUM:
@@ -1490,17 +2016,29 @@ static int new_term(struct parse_events_term **_term, int type_val,
}
int parse_events_term__num(struct parse_events_term **term,
- int type_term, char *config, u64 num)
+ int type_term, char *config, u64 num,
+ void *loc_term_, void *loc_val_)
{
+ YYLTYPE *loc_term = loc_term_;
+ YYLTYPE *loc_val = loc_val_;
+
return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term,
- config, NULL, num);
+ config, NULL, num,
+ loc_term ? loc_term->first_column : 0,
+ loc_val ? loc_val->first_column : 0);
}
int parse_events_term__str(struct parse_events_term **term,
- int type_term, char *config, char *str)
+ int type_term, char *config, char *str,
+ void *loc_term_, void *loc_val_)
{
+ YYLTYPE *loc_term = loc_term_;
+ YYLTYPE *loc_val = loc_val_;
+
return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term,
- config, str, 0);
+ config, str, 0,
+ loc_term ? loc_term->first_column : 0,
+ loc_val ? loc_val->first_column : 0);
}
int parse_events_term__sym_hw(struct parse_events_term **term,
@@ -1514,18 +2052,20 @@ int parse_events_term__sym_hw(struct parse_events_term **term,
if (config)
return new_term(term, PARSE_EVENTS__TERM_TYPE_STR,
PARSE_EVENTS__TERM_TYPE_USER, config,
- (char *) sym->symbol, 0);
+ (char *) sym->symbol, 0, 0, 0);
else
return new_term(term, PARSE_EVENTS__TERM_TYPE_STR,
PARSE_EVENTS__TERM_TYPE_USER,
- (char *) "event", (char *) sym->symbol, 0);
+ (char *) "event", (char *) sym->symbol,
+ 0, 0, 0);
}
int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term)
{
return new_term(new, term->type_val, term->type_term, term->config,
- term->val.str, term->val.num);
+ term->val.str, term->val.num,
+ term->err_term, term->err_val);
}
void parse_events__free_terms(struct list_head *terms)
@@ -1535,3 +2075,41 @@ void parse_events__free_terms(struct list_head *terms)
list_for_each_entry_safe(term, h, terms, list)
free(term);
}
+
+void parse_events_evlist_error(struct parse_events_evlist *data,
+ int idx, const char *str)
+{
+ struct parse_events_error *err = data->error;
+
+ if (!err)
+ return;
+ err->idx = idx;
+ err->str = strdup(str);
+ WARN_ONCE(!err->str, "WARNING: failed to allocate error string");
+}
+
+/*
+ * Return string contains valid config terms of an event.
+ * @additional_terms: For terms such as PMU sysfs terms.
+ */
+char *parse_events_formats_error_string(char *additional_terms)
+{
+ char *str;
+ static const char *static_terms = "config,config1,config2,name,"
+ "period,freq,branch_type,time,"
+ "call-graph,stack-size\n";
+
+ /* valid terms */
+ if (additional_terms) {
+ if (!asprintf(&str, "valid terms: %s,%s",
+ additional_terms, static_terms))
+ goto fail;
+ } else {
+ if (!asprintf(&str, "valid terms: %s", static_terms))
+ goto fail;
+ }
+ return str;
+
+fail:
+ return NULL;
+}