mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-04 13:24:22 -04:00
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: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -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,8 +108,8 @@ void bpf__clear(void)
|
||||
}
|
||||
|
||||
static void
|
||||
bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
|
||||
void *_priv)
|
||||
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,
|
||||
struct callchain_list *cnode)
|
||||
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,9 +479,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
|
||||
return NULL;
|
||||
|
||||
if (sample_self)
|
||||
hists__inc_stats(hists, he);
|
||||
else
|
||||
hists->nr_entries++;
|
||||
hist_entry__add_callchain_period(he, period);
|
||||
hists->nr_entries++;
|
||||
|
||||
rb_link_node(&he->rb_node_in, parent, p);
|
||||
rb_insert_color(&he->rb_node_in, hists->entries_in);
|
||||
@@ -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,
|
||||
iter->callchain,
|
||||
he->callchain);
|
||||
if (callchain_merge(&callchain_cursor,
|
||||
iter->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,7 +375,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc
|
||||
}
|
||||
|
||||
event_legacy_tracepoint:
|
||||
tracepoint_name
|
||||
tracepoint_name opt_event_config
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct parse_events_error *error = data->error;
|
||||
@@ -389,24 +386,7 @@ tracepoint_name
|
||||
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_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, $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;
|
||||
|
||||
Reference in New Issue
Block a user