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