Commit 91e48b7d 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:

User visible changes:

 - Add 'perf record' --all-user/--all-kernel options, so that one can tell
   that all the events in the command line should be restricted to the user
   or kernel levels (Jiri Olsa), i.e.:

	perf record -e cycles:u,instructions:u

   is equivalent to:

        perf record --all-user -e cycles,instructions

 - Fix percentage update on key press, due to the buffering code
   (that creates hist_entries that will later be consumed) touching
   per hists state that is used by the display thread (Namhyung Kim)

 - Bail out when event modifiers not supported by 'perf stat' are
   specified, i.e.: (Wang Nan)

   # perf stat -e cycles/no-inherit/ usleep 1
   event syntax error: 'cycles/no-inherit/'
                        \___ 'no-inherit' is not usable in 'perf stat'
   # perf stat -e cycles/foo/ usleep 1
   event syntax error: 'cycles/foo/'
                               \___ unknown term

   valid terms: config,config1,config2,name
   #

 - Enable setting names for legacy cache, raw and numeric events, e.g: (Wang Nan)

   # perf record -e cycles -e 4:0x6530160/name=evtx,call-graph=fp/ -a sleep 1
   [ perf record: Woken up 1 times to write data ]
   [ perf record: Captured and wrote 1.659 MB perf.data (844 samples) ]
   # perf evlist
   cycles
   evtx
   #

Miscelaneous/Infrastructure changes:

 - Handled scaled == -1 case for counters in 'perf stat', fixing
   recent, only in perf/core, regression (Andi Kleen)

 - Reference count the cpu and thread maps at set_maps(), fixing the
   'object code reading' 'perf test' entry when it was requesting a
   perf_event_attr.sample_freq > /proc/sys/kernel/perf_event_max_sample_rate
   (Arnaldo Carvalho de Melo)

 - Improve perf_evlist__strerror_open() to provide hints for -EINVAL due
   to perf_event_attr.sample_freq > /proc/sys/kernel/perf_event_max_sample_rate
   (Arnaldo Carvalho de Melo)

 - Add checks to various callchain and histogram routines (Namhyung Kim)

 - Fix checking asprintf return value when parsing additional event config terms (Wang Nan)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 3b364d7b 5b2ea6f2
......@@ -341,6 +341,12 @@ Specify vmlinux path which has debuginfo.
--buildid-all::
Record build-id of all DSOs regardless whether it's actually hit or not.
--all-kernel::
Configure all used events to run in kernel space.
--all-user::
Configure all used events to run in user space.
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1]
......@@ -1140,6 +1140,12 @@ struct option __record_options[] = {
"per thread proc mmap processing timeout in ms"),
OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
"Record context switch events"),
OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel,
"Configure all used events to run in kernel space.",
PARSE_OPT_EXCLUSIVE),
OPT_BOOLEAN_FLAG(0, "all-user", &record.opts.all_user,
"Configure all used events to run in user space.",
PARSE_OPT_EXCLUSIVE),
OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
"clang binary to use for compiling BPF scriptlets"),
OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options",
......
......@@ -469,10 +469,11 @@ static int report__browse_hists(struct report *rep)
return ret;
}
static void report__collapse_hists(struct report *rep)
static int report__collapse_hists(struct report *rep)
{
struct ui_progress prog;
struct perf_evsel *pos;
int ret = 0;
ui_progress__init(&prog, rep->nr_entries, "Merging related events...");
......@@ -484,7 +485,9 @@ static void report__collapse_hists(struct report *rep)
hists->socket_filter = rep->socket_filter;
hists__collapse_resort(hists, &prog);
ret = hists__collapse_resort(hists, &prog);
if (ret < 0)
break;
/* Non-group events are considered as leader */
if (symbol_conf.event_group &&
......@@ -497,6 +500,7 @@ static void report__collapse_hists(struct report *rep)
}
ui_progress__finish();
return ret;
}
static void report__output_resort(struct report *rep)
......@@ -564,7 +568,11 @@ static int __cmd_report(struct report *rep)
}
}
report__collapse_hists(rep);
ret = report__collapse_hists(rep);
if (ret) {
ui__error("failed to process hist entry\n");
return ret;
}
if (session_done())
return 0;
......
......@@ -860,7 +860,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
nl = new_line_std;
if (run == 0 || ena == 0) {
if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
aggr_printout(counter, id, nr);
fprintf(stat_config.output, "%*s%s",
......@@ -1831,6 +1831,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
if (evsel_list == NULL)
return -ENOMEM;
parse_events__shrink_config_terms();
argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands,
(const char **) stat_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
......
......@@ -58,6 +58,8 @@ struct record_opts {
bool full_auxtrace;
bool auxtrace_snapshot_mode;
bool record_switch_events;
bool all_kernel;
bool all_user;
unsigned int freq;
unsigned int mmap_pages;
unsigned int auxtrace_mmap_pages;
......
......@@ -439,7 +439,7 @@ static int do_test_code_reading(bool try_kcore)
.mmap_pages = UINT_MAX,
.user_freq = UINT_MAX,
.user_interval = ULLONG_MAX,
.freq = 4000,
.freq = 500,
.target = {
.uses_mmap = true,
},
......@@ -559,7 +559,13 @@ static int do_test_code_reading(bool try_kcore)
evlist = NULL;
continue;
}
pr_debug("perf_evlist__open failed\n");
if (verbose) {
char errbuf[512];
perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
pr_debug("perf_evlist__open() failed!\n%s\n", errbuf);
}
goto out_put;
}
break;
......
......@@ -1271,6 +1271,38 @@ static int test__checkevent_precise_max_modifier(struct perf_evlist *evlist)
return 0;
}
static int test__checkevent_config_symbol(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "insn") == 0);
return 0;
}
static int test__checkevent_config_raw(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "rawpmu") == 0);
return 0;
}
static int test__checkevent_config_num(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "numpmu") == 0);
return 0;
}
static int test__checkevent_config_cache(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "cachepmu") == 0);
return 0;
}
static int count_tracepoints(void)
{
struct dirent *events_ent;
......@@ -1579,6 +1611,26 @@ static struct evlist_test test__events[] = {
.check = test__checkevent_precise_max_modifier,
.id = 47,
},
{
.name = "instructions/name=insn/",
.check = test__checkevent_config_symbol,
.id = 48,
},
{
.name = "r1234/name=rawpmu/",
.check = test__checkevent_config_raw,
.id = 49,
},
{
.name = "4:0x6530160/name=numpmu/",
.check = test__checkevent_config_num,
.id = 50,
},
{
.name = "L1-dcache-misses/name=cachepmu/",
.check = test__checkevent_config_cache,
.id = 51,
},
};
static struct evlist_test test__events_pmu[] = {
......
......@@ -108,7 +108,7 @@ void bpf__clear(void)
}
static void
bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
clear_prog_priv(struct bpf_program *prog __maybe_unused,
void *_priv)
{
struct bpf_prog_priv *priv = _priv;
......@@ -337,7 +337,7 @@ config_bpf_program(struct bpf_program *prog)
}
pr_debug("bpf: config '%s' is ok\n", config_str);
err = bpf_program__set_private(prog, priv, bpf_prog_priv__clear);
err = bpf_program__set_private(prog, priv, clear_prog_priv);
if (err) {
pr_debug("Failed to set priv for program '%s'\n", config_str);
goto errout;
......
......@@ -416,7 +416,7 @@ create_child(struct callchain_node *parent, bool inherit_children)
/*
* Fill the node with callchain values
*/
static void
static int
fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
{
struct callchain_cursor_node *cursor_node;
......@@ -433,7 +433,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
call = zalloc(sizeof(*call));
if (!call) {
perror("not enough memory for the code path tree");
return;
return -1;
}
call->ip = cursor_node->ip;
call->ms.sym = cursor_node->sym;
......@@ -443,6 +443,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
callchain_cursor_advance(cursor);
cursor_node = callchain_cursor_current(cursor);
}
return 0;
}
static struct callchain_node *
......@@ -453,7 +454,19 @@ add_child(struct callchain_node *parent,
struct callchain_node *new;
new = create_child(parent, false);
fill_node(new, cursor);
if (new == NULL)
return NULL;
if (fill_node(new, cursor) < 0) {
struct callchain_list *call, *tmp;
list_for_each_entry_safe(call, tmp, &new->val, list) {
list_del(&call->list);
free(call);
}
free(new);
return NULL;
}
new->children_hit = 0;
new->hit = period;
......@@ -462,16 +475,32 @@ add_child(struct callchain_node *parent,
return new;
}
static s64 match_chain(struct callchain_cursor_node *node,
enum match_result {
MATCH_ERROR = -1,
MATCH_EQ,
MATCH_LT,
MATCH_GT,
};
static enum match_result match_chain(struct callchain_cursor_node *node,
struct callchain_list *cnode)
{
struct symbol *sym = node->sym;
u64 left, right;
if (cnode->ms.sym && sym &&
callchain_param.key == CCKEY_FUNCTION)
return cnode->ms.sym->start - sym->start;
else
return cnode->ip - node->ip;
callchain_param.key == CCKEY_FUNCTION) {
left = cnode->ms.sym->start;
right = sym->start;
} else {
left = cnode->ip;
right = node->ip;
}
if (left == right)
return MATCH_EQ;
return left > right ? MATCH_GT : MATCH_LT;
}
/*
......@@ -479,7 +508,7 @@ static s64 match_chain(struct callchain_cursor_node *node,
* give a part of its callchain to the created child.
* Then create another child to host the given callchain of new branch
*/
static void
static int
split_add_child(struct callchain_node *parent,
struct callchain_cursor *cursor,
struct callchain_list *to_split,
......@@ -491,6 +520,8 @@ split_add_child(struct callchain_node *parent,
/* split */
new = create_child(parent, true);
if (new == NULL)
return -1;
/* split the callchain and move a part to the new child */
old_tail = parent->val.prev;
......@@ -524,6 +555,8 @@ split_add_child(struct callchain_node *parent,
node = callchain_cursor_current(cursor);
new = add_child(parent, cursor, period);
if (new == NULL)
return -1;
/*
* This is second child since we moved parent's children
......@@ -534,7 +567,7 @@ split_add_child(struct callchain_node *parent,
cnode = list_first_entry(&first->val, struct callchain_list,
list);
if (match_chain(node, cnode) < 0)
if (match_chain(node, cnode) == MATCH_LT)
pp = &p->rb_left;
else
pp = &p->rb_right;
......@@ -545,14 +578,15 @@ split_add_child(struct callchain_node *parent,
parent->hit = period;
parent->count = 1;
}
return 0;
}
static int
static enum match_result
append_chain(struct callchain_node *root,
struct callchain_cursor *cursor,
u64 period);
static void
static int
append_chain_children(struct callchain_node *root,
struct callchain_cursor *cursor,
u64 period)
......@@ -564,36 +598,42 @@ append_chain_children(struct callchain_node *root,
node = callchain_cursor_current(cursor);
if (!node)
return;
return -1;
/* lookup in childrens */
while (*p) {
s64 ret;
enum match_result ret;
parent = *p;
rnode = rb_entry(parent, struct callchain_node, rb_node_in);
/* If at least first entry matches, rely to children */
ret = append_chain(rnode, cursor, period);
if (ret == 0)
if (ret == MATCH_EQ)
goto inc_children_hit;
if (ret == MATCH_ERROR)
return -1;
if (ret < 0)
if (ret == MATCH_LT)
p = &parent->rb_left;
else
p = &parent->rb_right;
}
/* nothing in children, add to the current node */
rnode = add_child(root, cursor, period);
if (rnode == NULL)
return -1;
rb_link_node(&rnode->rb_node_in, parent, p);
rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);
inc_children_hit:
root->children_hit += period;
root->children_count++;
return 0;
}
static int
static enum match_result
append_chain(struct callchain_node *root,
struct callchain_cursor *cursor,
u64 period)
......@@ -602,7 +642,7 @@ append_chain(struct callchain_node *root,
u64 start = cursor->pos;
bool found = false;
u64 matches;
int cmp = 0;
enum match_result cmp = MATCH_ERROR;
/*
* Lookup in the current node
......@@ -618,7 +658,7 @@ append_chain(struct callchain_node *root,
break;
cmp = match_chain(node, cnode);
if (cmp)
if (cmp != MATCH_EQ)
break;
found = true;
......@@ -628,7 +668,7 @@ append_chain(struct callchain_node *root,
/* matches not, relay no the parent */
if (!found) {
WARN_ONCE(!cmp, "Chain comparison error\n");
WARN_ONCE(cmp == MATCH_ERROR, "Chain comparison error\n");
return cmp;
}
......@@ -636,21 +676,25 @@ append_chain(struct callchain_node *root,
/* we match only a part of the node. Split it and add the new chain */
if (matches < root->val_nr) {
split_add_child(root, cursor, cnode, start, matches, period);
return 0;
if (split_add_child(root, cursor, cnode, start, matches,
period) < 0)
return MATCH_ERROR;
return MATCH_EQ;
}
/* we match 100% of the path, increment the hit */
if (matches == root->val_nr && cursor->pos == cursor->nr) {
root->hit += period;
root->count++;
return 0;
return MATCH_EQ;
}
/* We match the node and still have a part remaining */
append_chain_children(root, cursor, period);
if (append_chain_children(root, cursor, period) < 0)
return MATCH_ERROR;
return 0;
return MATCH_EQ;
}
int callchain_append(struct callchain_root *root,
......@@ -662,7 +706,8 @@ int callchain_append(struct callchain_root *root,
callchain_cursor_commit(cursor);
append_chain_children(&root->node, cursor, period);
if (append_chain_children(&root->node, cursor, period) < 0)
return -1;
if (cursor->nr > root->max_depth)
root->max_depth = cursor->nr;
......@@ -690,7 +735,8 @@ merge_chain_branch(struct callchain_cursor *cursor,
if (src->hit) {
callchain_cursor_commit(cursor);
append_chain_children(dst, cursor, src->hit);
if (append_chain_children(dst, cursor, src->hit) < 0)
return -1;
}
n = rb_first(&src->rb_root_in);
......
......@@ -1181,12 +1181,12 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
*/
if (cpus != evlist->cpus) {
cpu_map__put(evlist->cpus);
evlist->cpus = cpus;
evlist->cpus = cpu_map__get(cpus);
}
if (threads != evlist->threads) {
thread_map__put(evlist->threads);
evlist->threads = threads;
evlist->threads = thread_map__get(threads);
}
perf_evlist__propagate_maps(evlist);
......@@ -1624,7 +1624,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
return printed + fprintf(fp, "\n");
}
int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused,
int perf_evlist__strerror_open(struct perf_evlist *evlist,
int err, char *buf, size_t size)
{
int printed, value;
......@@ -1652,7 +1652,25 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused,
"Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n"
"Hint:\tThe current value is %d.", value);
break;
case EINVAL: {
struct perf_evsel *first = perf_evlist__first(evlist);
int max_freq;
if (sysctl__read_int("kernel/perf_event_max_sample_rate", &max_freq) < 0)
goto out_default;
if (first->attr.sample_freq < (u64)max_freq)
goto out_default;
printed = scnprintf(buf, size,
"Error:\t%s.\n"
"Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n"
"Hint:\tThe current value is %d and %" PRIu64 " is being requested.",
emsg, max_freq, first->attr.sample_freq);
break;
}
default:
out_default:
scnprintf(buf, size, "%s", emsg);
break;
}
......
......@@ -898,6 +898,16 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
if (evsel->precise_max)
perf_event_attr__set_max_precise_ip(attr);
if (opts->all_user) {
attr->exclude_kernel = 1;
attr->exclude_user = 0;
}
if (opts->all_kernel) {
attr->exclude_kernel = 0;
attr->exclude_user = 1;
}
/*
* Apply event specific term settings,
* it overloads any global configuration.
......
......@@ -405,6 +405,16 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return 0;
}
static void hist_entry__add_callchain_period(struct hist_entry *he, u64 period)
{
if (!symbol_conf.use_callchain)
return;
he->hists->callchain_period += period;
if (!he->filtered)
he->hists->callchain_non_filtered_period += period;
}
static struct hist_entry *hists__findnew_entry(struct hists *hists,
struct hist_entry *entry,
struct addr_location *al,
......@@ -434,9 +444,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
if (!cmp) {
if (sample_self) {
he_stat__add_period(&he->stat, period, weight);
hists->stats.total_period += period;
if (!he->filtered)
hists->stats.total_non_filtered_period += period;
hist_entry__add_callchain_period(he, period);
}
if (symbol_conf.cumulate_callchain)
he_stat__add_period(he->stat_acc, period, weight);
......@@ -471,8 +479,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
return NULL;
if (sample_self)
hists__inc_stats(hists, he);
else
hist_entry__add_callchain_period(he, period);
hists->nr_entries++;
rb_link_node(&he->rb_node_in, parent, p);
......@@ -1039,8 +1046,8 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp,
* collapse the histogram
*/
bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
struct rb_root *root, struct hist_entry *he)
int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,
struct hist_entry *he)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
......@@ -1054,18 +1061,21 @@ bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
cmp = hist_entry__collapse(iter, he);
if (!cmp) {
int ret = 0;
he_stat__add_stat(&iter->stat, &he->stat);
if (symbol_conf.cumulate_callchain)
he_stat__add_stat(iter->stat_acc, he->stat_acc);
if (symbol_conf.use_callchain) {
callchain_cursor_reset(&callchain_cursor);
callchain_merge(&callchain_cursor,
if (callchain_merge(&callchain_cursor,
iter->callchain,
he->callchain);
he->callchain) < 0)
ret = -1;
}
hist_entry__delete(he);
return false;
return ret;
}
if (cmp < 0)
......@@ -1077,7 +1087,7 @@ bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
rb_link_node(&he->rb_node_in, parent, p);
rb_insert_color(&he->rb_node_in, root);
return true;
return 1;
}
struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
......@@ -1103,14 +1113,15 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
hists__filter_entry_by_socket(hists, he);
}
void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
{
struct rb_root *root;
struct rb_node *next;
struct hist_entry *n;
int ret;
if (!sort__need_collapse)
return;
return 0;
hists->nr_entries = 0;
......@@ -1125,7 +1136,11 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
next = rb_next(&n->rb_node_in);
rb_erase(&n->rb_node_in, root);
if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
ret = hists__collapse_insert_entry(hists, &hists->entries_collapsed, n);
if (ret < 0)
return -1;
if (ret) {
/*
* If it wasn't combined with one of the entries already
* collapsed, we need to apply the filters that may have
......@@ -1136,6 +1151,7 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
if (prog)
ui_progress__update(prog, 1);
}
return 0;
}
static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
......@@ -1227,9 +1243,14 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
struct rb_root *root;
struct rb_node *next;
struct hist_entry *n;
u64 callchain_total;
u64 min_callchain_hits;
min_callchain_hits = hists__total_period(hists) * (callchain_param.min_percent / 100);
callchain_total = hists->callchain_period;
if (symbol_conf.filter_relative)
callchain_total = hists->callchain_non_filtered_period;
min_callchain_hits = callchain_total * (callchain_param.min_percent / 100);
if (sort__need_collapse)
root = &hists->entries_collapsed;
......
......@@ -66,6 +66,8 @@ struct hists {
struct rb_root entries_collapsed;
u64 nr_entries;
u64 nr_non_filtered_entries;
u64 callchain_period;
u64 callchain_non_filtered_period;
struct thread *thread_filter;
const struct dso *dso_filter;
const char *uid_filter_str;
......@@ -136,7 +138,7 @@ void hist_entry__delete(struct hist_entry *he);
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog);
void hists__output_resort(struct hists *hists, struct ui_progress *prog);
void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
int hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
void hists__delete_entries(struct hists *hists);
......@@ -195,7 +197,7 @@ int hists__init(void);
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list);
struct rb_root *hists__get_rotate_entries_in(struct hists *hists);
bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
int hists__collapse_insert_entry(struct hists *hists,
struct rb_root *root, struct hist_entry *he);
struct perf_hpp {
......
......@@ -279,7 +279,24 @@ const char *event_type(int type)
return "unknown";
}
static int parse_events__is_name_term(struct parse_events_term *term)
{
return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
}
static char *get_config_name(struct list_head *head_terms)
{
struct parse_events_term *term;
if (!head_terms)
return NULL;
list_for_each_entry(term, head_terms, list)
if (parse_events__is_name_term(term))
return term->val.str;
return NULL;
}
static struct perf_evsel *
__add_event(struct list_head *list, int *idx,
......@@ -333,11 +350,25 @@ static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES]
return -1;
}
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);
static int config_attr(struct perf_event_attr *attr,
struct list_head *head,
struct parse_events_error *err,
config_term_func_t config_term);
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2)
char *type, char *op_result1, char *op_result2,
struct parse_events_error *error,
struct list_head *head_config)
{
struct perf_event_attr attr;
char name[MAX_NAME_LEN];
LIST_HEAD(config_terms);
char name[MAX_NAME_LEN], *config_name;
int cache_type = -1, cache_op = -1, cache_result = -1;
char *op_result[2] = { op_result1, op_result2 };
int i, n;
......@@ -351,6 +382,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,
if (cache_type == -1)
return -EINVAL;
config_name = get_config_name(head_config);
n = snprintf(name, MAX_NAME_LEN, "%s", type);
for (i = 0; (i < 2) && (op_result[i]); i++) {
......@@ -391,7 +423,16 @@ 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, NULL);
if (head_config) {
if (config_attr(&attr, head_config, error,
config_term_common))
return -EINVAL;
if (get_config_terms(head_config, &config_terms))
return -ENOMEM;
}
return add_event(list, idx, &attr, config_name ? : name, &config_terms);
}
static void tracepoint_error(struct parse_events_error *e, int err,
......@@ -746,6 +787,60 @@ static int check_type_val(struct parse_events_term *term,
return -EINVAL;
}
/*
* Update according to parse-events.l
*/
static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
[PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>",
[PARSE_EVENTS__TERM_TYPE_CONFIG] = "config",
[PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1",
[PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2",
[PARSE_EVENTS__TERM_TYPE_NAME] = "name",
[PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period",
[PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq",
[PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type",
[PARSE_EVENTS__TERM_TYPE_TIME] = "time",
[PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph",
[PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size",
[PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit",
[PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit",
};
static bool config_term_shrinked;
static bool
config_term_avail(int term_type, struct parse_events_error *err)
{
if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) {
err->str = strdup("Invalid term_type");
return false;
}
if (!config_term_shrinked)
return true;
switch (term_type) {
case PARSE_EVENTS__TERM_TYPE_CONFIG:
case PARSE_EVENTS__TERM_TYPE_CONFIG1:
case PARSE_EVENTS__TERM_TYPE_CONFIG2:
case PARSE_EVENTS__TERM_TYPE_NAME:
return true;
default:
if (!err)
return false;
/* term_type is validated so indexing is safe */
if (asprintf(&err->str, "'%s' is not usable in 'perf stat'",
config_term_names[term_type]) < 0)
err->str = NULL;
return false;
}
}
void parse_events__shrink_config_terms(void)
{
config_term_shrinked = true;
}
typedef int config_term_func_t(struct perf_event_attr *attr,
struct parse_events_term *term,
struct parse_events_error *err);
......@@ -815,6 +910,17 @@ do { \
return -EINVAL;
}
/*
* Check term availbility after basic checking so
* PARSE_EVENTS__TERM_TYPE_USER can be found and filtered.
*
* If check availbility at the entry of this function,
* user will see "'<sysfs term>' is not usable in 'perf stat'"
* if an invalid config term is provided for legacy events
* (for example, instructions/badterm/...), which is confusing.
*/
if (!config_term_avail(term->type_term, err))
return -EINVAL;
return 0;
#undef CHECK_TYPE_VAL
}
......@@ -961,23 +1067,8 @@ int parse_events_add_numeric(struct parse_events_evlist *data,
return -ENOMEM;
}
return add_event(list, &data->idx, &attr, NULL, &config_terms);
}
static int parse_events__is_name_term(struct parse_events_term *term)
{
return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
}
static char *pmu_event_name(struct list_head *head_terms)
{
struct parse_events_term *term;
list_for_each_entry(term, head_terms, list)
if (parse_events__is_name_term(term))
return term->val.str;
return NULL;
return add_event(list, &data->idx, &attr,
get_config_name(head_config), &config_terms);
}
int parse_events_add_pmu(struct parse_events_evlist *data,
......@@ -1024,7 +1115,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data,
return -EINVAL;
evsel = __add_event(list, &data->idx, &attr,
pmu_event_name(head_config), pmu->cpus,
get_config_name(head_config), pmu->cpus,
&config_terms);
if (evsel) {
evsel->unit = info.unit;
......@@ -2097,6 +2188,33 @@ void parse_events_evlist_error(struct parse_events_evlist *data,
WARN_ONCE(!err->str, "WARNING: failed to allocate error string");
}
static void config_terms_list(char *buf, size_t buf_sz)
{
int i;
bool first = true;
buf[0] = '\0';
for (i = 0; i < __PARSE_EVENTS__TERM_TYPE_NR; i++) {
const char *name = config_term_names[i];
if (!config_term_avail(i, NULL))
continue;
if (!name)
continue;
if (name[0] == '<')
continue;
if (strlen(buf) + strlen(name) + 2 >= buf_sz)
return;
if (!first)
strcat(buf, ",");
else
first = false;
strcat(buf, name);
}
}
/*
* Return string contains valid config terms of an event.
* @additional_terms: For terms such as PMU sysfs terms.
......@@ -2104,17 +2222,18 @@ void parse_events_evlist_error(struct parse_events_evlist *data,
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";
/* "branch_type" is the longest name */
char static_terms[__PARSE_EVENTS__TERM_TYPE_NR *
(sizeof("branch_type") - 1)];
config_terms_list(static_terms, sizeof(static_terms));
/* valid terms */
if (additional_terms) {
if (!asprintf(&str, "valid terms: %s,%s",
additional_terms, static_terms))
if (asprintf(&str, "valid terms: %s,%s",
additional_terms, static_terms) < 0)
goto fail;
} else {
if (!asprintf(&str, "valid terms: %s", static_terms))
if (asprintf(&str, "valid terms: %s", static_terms) < 0)
goto fail;
}
return str;
......
......@@ -68,7 +68,8 @@ enum {
PARSE_EVENTS__TERM_TYPE_CALLGRAPH,
PARSE_EVENTS__TERM_TYPE_STACKSIZE,
PARSE_EVENTS__TERM_TYPE_NOINHERIT,
PARSE_EVENTS__TERM_TYPE_INHERIT
PARSE_EVENTS__TERM_TYPE_INHERIT,
__PARSE_EVENTS__TERM_TYPE_NR,
};
struct parse_events_term {
......@@ -104,6 +105,7 @@ struct parse_events_terms {
struct list_head *terms;
};
void parse_events__shrink_config_terms(void);
int parse_events__is_hardcoded_term(struct parse_events_term *term);
int parse_events_term__num(struct parse_events_term **term,
int type_term, char *config, u64 num,
......@@ -138,7 +140,9 @@ int parse_events_add_numeric(struct parse_events_evlist *data,
u32 type, u64 config,
struct list_head *head_config);
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2);
char *type, char *op_result1, char *op_result2,
struct parse_events_error *error,
struct list_head *head_config);
int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type, u64 len);
int parse_events_add_pmu(struct parse_events_evlist *data,
......
......@@ -178,8 +178,7 @@ modifier_bp [rwx]{1,3}
<config>{
/*
* Please update parse_events_formats_error_string any time
* new static term is added.
* Please update config_term_names when new static term is added.
*/
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
......
......@@ -64,6 +64,7 @@ static inc_group_count(struct list_head *list,
%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <num> value_sym
%type <head> event_config
%type <head> opt_event_config
%type <term> event_term
%type <head> event_pmu
%type <head> event_legacy_symbol
......@@ -222,16 +223,6 @@ PE_NAME '/' event_config '/'
$$ = list;
}
|
PE_NAME '/' '/'
{
struct parse_events_evlist *data = _data;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_pmu(data, list, $1, NULL));
$$ = list;
}
|
PE_KERNEL_PMU_EVENT sep_dc
{
struct parse_events_evlist *data = _data;
......@@ -302,33 +293,39 @@ value_sym sep_slash_dc
}
event_legacy_cache:
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_event_config
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, $5));
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, $5, error, $6));
parse_events_terms__delete($6);
$$ = list;
}
|
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, NULL));
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, NULL, error, $4));
parse_events_terms__delete($4);
$$ = list;
}
|
PE_NAME_CACHE_TYPE
PE_NAME_CACHE_TYPE opt_event_config
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, NULL, NULL));
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, NULL, NULL, error, $2));
parse_events_terms__delete($2);
$$ = list;
}
......@@ -378,24 +375,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc
}
event_legacy_tracepoint:
tracepoint_name
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
struct list_head *list;
ALLOC_LIST(list);
if (error)
error->idx = @1.first_column;
if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event,
error, NULL))
return -1;
$$ = list;
}
|
tracepoint_name '/' event_config '/'
tracepoint_name opt_event_config
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
......@@ -406,7 +386,7 @@ tracepoint_name '/' event_config '/'
error->idx = @1.first_column;
if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event,
error, $3))
error, $2))
return -1;
$$ = list;
......@@ -433,24 +413,26 @@ PE_NAME ':' PE_NAME
}
event_legacy_numeric:
PE_VALUE ':' PE_VALUE
PE_VALUE ':' PE_VALUE opt_event_config
{
struct parse_events_evlist *data = _data;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_numeric(data, list, (u32)$1, $3, NULL));
ABORT_ON(parse_events_add_numeric(data, list, (u32)$1, $3, $4));
parse_events_terms__delete($4);
$$ = list;
}
event_legacy_raw:
PE_RAW
PE_RAW opt_event_config
{
struct parse_events_evlist *data = _data;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_numeric(data, list, PERF_TYPE_RAW, $1, NULL));
ABORT_ON(parse_events_add_numeric(data, list, PERF_TYPE_RAW, $1, $2));
parse_events_terms__delete($2);
$$ = list;
}
......@@ -476,6 +458,21 @@ PE_BPF_SOURCE
$$ = list;
}
opt_event_config:
'/' event_config '/'
{
$$ = $2;
}
|
'/' '/'
{
$$ = NULL;
}
|
{
$$ = NULL;
}
start_terms: event_config
{
struct parse_events_terms *data = _data;
......
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