perf/core improvements and fixes
Arnaldo Carvalho de Melo
maintainerHi Ingo,
Please consider pulling,
- Arnaldo
The following changes since commit 91e48b7df15196b8ce01f40455219d3ed7889988:
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core (2016-02-20 11:52:16 +0100)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo
for you to fetch changes up to 03e0a7df3efd959e40cd7ff40b1fabddc234ec5a:
perf tools: Introduce bpf-output event (2016-02-22 14:37:21 -0300)
----------------------------------------------------------------
perf/core improvements and fixes:
New features:
- Add API to set values of map entries in a BPF object, be it individual map slots or ranges (Wang Nan)
- Introduce support for the 'bpf-output' event (Wang Nan)
- Add glue to read perf events in a BPF program (Wang Nan)
Fixes:
- Sort key fixes: Alignment for srcline, file, trace; fix segfault for dynamic, trace events related sort keys (Namyung Kim)
Build fixes:
- Remove duplicate typedef config_term_func_t definition, fixing the build on older systems (Arnaldo Carvalho de Melo)
----------------------------------------------------------------
Arnaldo Carvalho de Melo (2):
perf tools: Fix build on older systems
perf tools: Remove duplicate typedef config_term_func_t definition
Namhyung Kim (5):
perf tools: Fix segfault on dynamic entries perf tools: Update srcline/file if needed
perf tools: Fix alignment on some sort keys perf tools: Fix column width setting on 'trace' sort key perf tools: Fix assertion failure on dynamic entry
Wang Nan (8):
perf bpf: Add API to set values to map entries in a bpf object perf tools: Enable BPF object configure syntax
perf record: Apply config to BPF objects before recording perf tools: Enable passing event to BPF object
perf tools: Support setting different slots in a BPF map separately perf tools: Enable indices setting syntax for BPF map perf tools: Apply tracepoint event definition options to BPF script perf tools: Introduce bpf-output event
tools/perf/builtin-record.c | 11 + tools/perf/tests/bpf.c | 2 +- tools/perf/ui/hist.c | 3 + tools/perf/util/bpf-loader.c | 718 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 59 ++++ tools/perf/util/evlist.c | 16 + tools/perf/util/evlist.h | 3 + tools/perf/util/evsel.c | 5 + tools/perf/util/evsel.h | 8 + tools/perf/util/hist.c | 3 + tools/perf/util/parse-events.c | 130 +++++++- tools/perf/util/parse-events.h | 17 +- tools/perf/util/parse-events.l | 16 +- tools/perf/util/parse-events.y | 95 +++++- tools/perf/util/sort.c | 90 +++--- 15 files changed, 1112 insertions(+), 64 deletions(-)
Arnaldo Carvalho de Melo
maintainerPATCH 1/15 perf tools: Fix build on older systems
From: Arnaldo Carvalho de Melo <[email protected]>
In RHEL 6.7:
CC /tmp/build/perf/util/parse-events.o
cc1: warnings being treated as errors
util/parse-events.c: In function ‘parse_events_add_cache’:
util/parse-events.c:366: error: declaration of ‘error’ shadows a global declaration util/util.h:136: error: shadowed declaration is here
Rename it to 'err'.
--- tools/perf/util/parse-events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/parse-events.c +2 -2
@@ -363,7 +363,7 @@ static int config_attr(struct perf_event_attr *attr,int parse_events_add_cache (struct list_head *list,int *idx,char *type,char *op_result1,char *op_result2, -struct parse_events_error *error, +struct parse_events_error *err,struct list_head *head_config) {struct perf_event_attr attr;
@@ -425,7 +425,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, attr.type =PERF_TYPE_HW_CACHE ;if (head_config) { -if (config_attr (&attr, head_config, error, +if (config_attr (&attr, head_config, err, config_term_common))return -EINVAL ;
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 2/15 perf tools: Remove duplicate typedef config_term_func_t definition
In reply to Arnaldo Carvalho de MeloFrom: Arnaldo Carvalho de Melo <[email protected]>
Older compilers don't like this, for instance, on RHEL6.7:
CC /tmp/build/perf/util/parse-events.o
util/parse-events.c:844: error: redefinition of typedef ‘config_term_func_t’ util/parse-events.c:353: note: previous declaration of ‘config_term_func_t’ was here
So remove the second definition, that should've been just moved in 43d0b97817a4 ("perf tools: Enable config and setting names for legacy cache events"), not copied.
--- tools/perf/util/parse-events.c | 4 ---- 1 file changed, 4 deletions(-)
tools/perf/util/parse-events.c +0 -4
@@ -841,10 +841,6 @@ 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); -static int config_term_common (struct perf_event_attr *attr,struct parse_events_term *term,struct parse_events_error *err)
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 3/15 perf tools: Fix segfault on dynamic entries
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
A dynamic entry is created for each tracepoint event. When it sets up the sort key, it checks with existing keys using ->equal() callback. But it missed to set the ->equal for dynamic entries. The following segfault was due to the missing ->equal() callback.
(gdb) bt
#0 0x0000000000140003 in ?? ()
#1 0x0000000000537769 in fmt_equal (b=0x2106980, a=0x21067a0) at ui/hist.c:548
#2 perf_hpp__setup_output_field (list=0x8c6d80 <perf_hpp_list>) at ui/hist.c:560
#3 0x00000000004e927e in setup_sorting (evlist=<optimized out>) at util/sort.c:2642
#4 0x000000000043cf50 in cmd_report (argc=<optimized out>, argv=<optimized out>, prefix=<optimized out>)
at builtin-report.c:932
#5 0x00000000004865a1 in run_builtin (p=p@entry=0x8bbce0 <commands+192>, argc=argc@entry=7,
argv=argv@entry=0x7ffd24d56ce0) at perf.c:390
#6 0x000000000042dc1f in handle_internal_command (argv=0x7ffd24d56ce0, argc=7) at perf.c:451
#7 run_argv (argv=0x7ffd24d56a70, argcp=0x7ffd24d56a7c) at perf.c:495
#8 main (argc=7, argv=0x7ffd24d56ce0) at perf.c:620--- tools/perf/util/sort.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
tools/perf/util/sort.c +15 -0
@@ -1835,6 +1835,20 @@ bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)return fmt->cmp == __sort__hde_cmp; } +static bool __sort__hde_equal (struct perf_hpp_fmt *a,struct perf_hpp_fmt *b) +{ +struct hpp_dynamic_entry *hde_a; +struct hpp_dynamic_entry *hde_b; + +if (!perf_hpp__is_dynamic_entry (a) || !perf_hpp__is_dynamic_entry (b)) +return false; + + hde_a = container_of(a, struct hpp_dynamic_entry, hpp); + hde_b = container_of(b, struct hpp_dynamic_entry, hpp); + +return hde_a->field == hde_b->field; +} +static void hde_free (struct perf_hpp_fmt *fmt) {struct hpp_dynamic_entry *hde;
@@ -1867,6 +1881,7 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) hde->hpp.cmp = __sort__hde_cmp; hde->hpp.collapse = __sort__hde_cmp; hde->hpp.sort = __sort__hde_cmp; + hde->hpp.equal = __sort__hde_equal; hde->hpp.free = hde_free;INIT_LIST_HEAD (&hde->hpp.list);
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 4/15 perf tools: Update srcline/file if needed
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
Normally the hist entry's srcline and/or srcfile is set during sorting. However sometime it's possible to a hist entry's srcline is not set yet after the sorting. This is because the entry is so unique and other sort keys already make it distinct. Then the srcline/file sort didn't have a chance to be called during the sorting. In that case it has NULL srcline/srcfile field and shows nothing.
Before:
$ perf report -s comm,sym,srcline
...
Overhead Command Symbol
-----------------------------------------------------------------
34.42% swapper [k] intel_idle intel_idle.c:0
2.44% perf [.] __poll_nocancel (null)
1.70% gnome-shell [k] fw_domains_get (null)
1.04% Xorg [k] sock_poll (null)After:
34.42% swapper [k] intel_idle intel_idle.c:0
2.44% perf [.] __poll_nocancel .:0
1.70% gnome-shell [k] fw_domains_get fw_domains_get+42
1.04% Xorg [k] sock_poll socket.c:0--- tools/perf/util/sort.c | 64 ++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 33 deletions(-)
tools/perf/util/sort.c +31 -33
@@ -286,35 +286,34 @@ struct sort_entry sort_sym = {/* --sort srcline */ +static char *hist_entry__get_srcline (struct hist_entry *he) +{ +struct map *map = he->ms.map; + +if (!map) +return SRCLINE_UNKNOWN ; + +return get_srcline (map->dso,map__rip_2objdump (map, he->ip), + he->ms.sym, true); +} +static int64_t sort__srcline_cmp (struct hist_entry *left,struct hist_entry *right) { -if (!left->srcline) { -if (!left->ms.map) - left->srcline =SRCLINE_UNKNOWN ; -else { -struct map *map = left->ms.map; - left->srcline =get_srcline (map->dso, -map__rip_2objdump (map, left->ip), - left->ms.sym, true); - } - } -if (!right->srcline) { -if (!right->ms.map) - right->srcline =SRCLINE_UNKNOWN ; -else { -struct map *map = right->ms.map; - right->srcline =get_srcline (map->dso, -map__rip_2objdump (map, right->ip), - right->ms.sym, true); - } - } +if (!left->srcline) + left->srcline =hist_entry__get_srcline (left); +if (!right->srcline) + right->srcline =hist_entry__get_srcline (right); +return strcmp (right->srcline, left->srcline); }static int hist_entry__srcline_snprintf (struct hist_entry *he,char *bf,size_t size,unsigned int width) { +if (!he->srcline) + he->srcline =hist_entry__get_srcline (he); +return repsep_snprintf (bf, size,"%-*.*s" , width, width, he->srcline); }
@@ -329,11 +328,14 @@ struct sort_entry sort_srcline = {static char no_srcfile[1 ]; -static char *get_srcfile (struct hist_entry *e) +static char *hist_entry__get_srcfile (struct hist_entry *e) {char *sf, *p;struct map *map = e->ms.map; +if (!map) +return no_srcfile; + sf =__get_srcline (map->dso,map__rip_2objdump (map, e->ip), e->ms.sym, false, true);if (!strcmp (sf,SRCLINE_UNKNOWN ))
@@ -350,24 +352,20 @@ static char *get_srcfile(struct hist_entry *e)static int64_t sort__srcfile_cmp (struct hist_entry *left,struct hist_entry *right) { -if (!left->srcfile) { -if (!left->ms.map) - left->srcfile = no_srcfile; -else - left->srcfile =get_srcfile (left); - } -if (!right->srcfile) { -if (!right->ms.map) - right->srcfile = no_srcfile; -else - right->srcfile =get_srcfile (right); - } +if (!left->srcfile) + left->srcfile =hist_entry__get_srcfile (left); +if (!right->srcfile) + right->srcfile =hist_entry__get_srcfile (right); +return strcmp (right->srcfile, left->srcfile); }static int hist_entry__srcfile_snprintf (struct hist_entry *he,char *bf,size_t size,unsigned int width) { +if (!he->srcfile) + he->srcfile =hist_entry__get_srcfile (he); +return repsep_snprintf (bf, size,"%-*.*s" , width, width, he->srcfile); }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 5/15 perf tools: Fix alignment on some sort keys
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The srcline, srcfile and trace sort keys can have long entries. With commit 89fee7094323 ("perf hists: Do column alignment on the format iterator"), it now aligns output with hist_entry__snprintf_alignment(). So each (possibly long) sort entries don't need to do it themselves.
--- tools/perf/util/sort.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
tools/perf/util/sort.c +4 -4
@@ -314,7 +314,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,if (!he->srcline) he->srcline =hist_entry__get_srcline (he); -return repsep_snprintf (bf, size,"%-*.*s" , width, width, he->srcline); +return repsep_snprintf (bf, size,"%-.*s" , width, he->srcline); }struct sort_entry sort_srcline = {
@@ -366,7 +366,7 @@ static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,if (!he->srcfile) he->srcfile =hist_entry__get_srcfile (he); -return repsep_snprintf (bf, size,"%-*.*s" , width, width, he->srcfile); +return repsep_snprintf (bf, size,"%-.*s" , width, he->srcfile); }struct sort_entry sort_srcfile = {
@@ -496,11 +496,11 @@ static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, evsel =hists_to_evsel (he->hists);if (evsel->attr.type !=PERF_TYPE_TRACEPOINT ) -return scnprintf (bf, size,"%-*.*s" , width, width,"N/A" ); +return scnprintf (bf, size,"%-.*s" , width,"N/A" );if (he->trace_output ==NULL ) he->trace_output =get_trace_output (he); -return repsep_snprintf (bf, size,"%-*.*s" , width, width, he->trace_output); +return repsep_snprintf (bf, size,"%-.*s" , width, he->trace_output); }struct sort_entry sort_trace = {
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 6/15 perf tools: Fix column width setting on 'trace' sort key
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
It missed to update column length of the 'trace' sort key in the hists__calc_col_len() so it might truncate the output. It calculated the column length in the ->cmp() callback originally but it doesn't guarantee it's called always.
--- tools/perf/util/hist.c | 3 +++ tools/perf/util/sort.c | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-)
tools/perf/util/hist.c +3 -0
@@ -179,6 +179,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)if (h->transaction)hists__new_col_len (hists,HISTC_TRANSACTION ,hist_entry__transaction_len ()); + +if (h->trace_output) +hists__new_col_len (hists,HISTC_TRACE ,strlen (h->trace_output)); }void hists__output_recalc_col_len (struct hists *hists,int max_rows)
tools/perf/util/sort.c +0 -3
@@ -483,9 +483,6 @@ sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)if (right->trace_output ==NULL ) right->trace_output =get_trace_output (right); -hists__new_col_len (left->hists,HISTC_TRACE ,strlen (left->trace_output)); -hists__new_col_len (right->hists,HISTC_TRACE ,strlen (right->trace_output)); -return strcmp (right->trace_output, left->trace_output); }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 7/15 perf tools: Fix assertion failure on dynamic entry
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The dynamic entry is created for each field in a tracepoint event. Since they have no fixed hpp format index, it should skip when perf_hpp__reset_width() is called.
This caused following assertion failure..
$ perf record -e sched:sched_switch -a sleep 1
$ perf report -s comm,next_pid --stdio
perf: ui/hist.c:651: perf_hpp__reset_width:
Assertion `!(fmt->idx >= PERF_HPP__MAX_INDEX)' failed.--- tools/perf/ui/hist.c | 3 +++ 1 file changed, 3 insertions(+)
tools/perf/ui/hist.c +3 -0
@@ -645,6 +645,9 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)if (perf_hpp__is_sort_entry (fmt))return perf_hpp__reset_sort_width (fmt, hists); +if (perf_hpp__is_dynamic_entry (fmt)) +return ; +BUG_ON (fmt->idx >=PERF_HPP__MAX_INDEX );switch (fmt->idx) {
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 8/15 perf bpf: Add API to set values to map entries in a bpf object
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
bpf__config_obj() is introduced as a core API to config BPF object after loading. One configuration option of maps is introduced. After this patch BPF object can accept assignments like:
map:my_map.value=1234
(map.my_map.value looks pretty. However, there's a small but hard to fix problem related to flex's greedy matching. Please see [1]. Choose ':' to avoid it in a simpler way.)
This patch is more complex than the work it does because the consideration of extension. In designing BPF map configuration, the following things should be considered:
1. Array indices selection: perf should allow user setting different value for different slots in an array, with syntax like:
map:my_map.value[0,3...6]=1234;
2. A map should be set by different config terms, each for a part
of it. For example, set each slot to the pid of a thread;
3. Type of value: integer is not the only valid value type. A perf counter can also be put into a map after commit 35578d798400 ("bpf: Implement function bpf_perf_event_read() that get the selected hardware PMU counter")
4. For a hash table, it should be possible to use a string or other
value as a key;
5. It is possible that map configuration is unable to be setup during parsing. A perf counter is an example.
Therefore, this patch does the following:
1. Instead of updating map element during parsing, this patch stores map config options in 'struct bpf_map_priv'. Following patches will apply those configs at an appropriate time;
2. Link map operations in a list so a map can have multiple config
terms attached, so different parts can be configured separately;
3. Make 'struct bpf_map_priv' extensible so that the following patches
can add new types of keys and operations;
4. Use bpf_obj_config__map_funcs array to support more map config options.
Since the patch changing the event parser to parse BPF object config is relative large, I've put it in another commit. Code in this patch can be tested after applying the next patch.
[1] http://lkml.kernel.org/g/[email protected]
--- tools/perf/util/bpf-loader.c | 276 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 38 ++++++ 2 files changed, 314 insertions(+)
tools/perf/util/bpf-loader.c +276 -0
@@ -739,6 +739,261 @@ int bpf__foreach_tev(struct bpf_object *obj,return 0 ; } +enum bpf_map_op_type { +BPF_MAP_OP_SET_VALUE , +}; + +enum bpf_map_key_type { +BPF_MAP_KEY_ALL , +}; + +struct bpf_map_op { +struct list_head list; +enum bpf_map_op_type op_type; +enum bpf_map_key_type key_type; +union { +u64 value; + } v; +}; + +struct bpf_map_priv { +struct list_head ops_list; +}; + +static void +bpf_map_op__delete (struct bpf_map_op *op) +{ +if (!list_empty (&op->list)) +list_del (&op->list); +free (op); +} + +static void +bpf_map_priv__purge (struct bpf_map_priv *priv) +{ +struct bpf_map_op *pos, *n; + +list_for_each_entry_safe (pos, n, &priv->ops_list, list) { +list_del_init (&pos->list); +bpf_map_op__delete (pos); + } +} + +static void +bpf_map_priv__clear (struct bpf_map *map__maybe_unused , +void *_priv) +{ +struct bpf_map_priv *priv = _priv; + +bpf_map_priv__purge (priv); +free (priv); +} + +static struct bpf_map_op * +bpf_map_op__new (void ) +{ +struct bpf_map_op *op; + + op =zalloc (sizeof (*op)); +if (!op) { +pr_debug ("Failed to alloc bpf_map_op\n" ); +return ERR_PTR (-ENOMEM ); + } +INIT_LIST_HEAD (&op->list); + + op->key_type =BPF_MAP_KEY_ALL ; +return op; +} + +static int +bpf_map__add_op (struct bpf_map *map,struct bpf_map_op *op) +{ +struct bpf_map_priv *priv; +const char *map_name; +int err; + + map_name =bpf_map__get_name (map); + err =bpf_map__get_private (map, (void **)&priv); +if (err) { +pr_debug ("Failed to get private from map %s\n" , map_name); +return err; + } + +if (!priv) { + priv =zalloc (sizeof (*priv)); +if (!priv) { +pr_debug ("No enough memory to alloc map private\n" ); +return -ENOMEM ; + } +INIT_LIST_HEAD (&priv->ops_list); + +if (bpf_map__set_private (map, priv, bpf_map_priv__clear)) { +free (priv); +return -BPF_LOADER_ERRNO__INTERNAL ; + } + } + +list_add_tail (&op->list, &priv->ops_list); +return 0 ; +} + +static int +__bpf_map__config_value (struct bpf_map *map, +struct parse_events_term *term) +{ +struct bpf_map_def def; +struct bpf_map_op *op; +const char *map_name; +int err; + + map_name =bpf_map__get_name (map); + + err =bpf_map__get_def (map, &def); +if (err) { +pr_debug ("Unable to get map definition from '%s'\n" , + map_name); +return -BPF_LOADER_ERRNO__INTERNAL ; + } + +if (def.type !=BPF_MAP_TYPE_ARRAY ) { +pr_debug ("Map %s type is not BPF_MAP_TYPE_ARRAY\n" , + map_name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE ; + } +if (def.key_size <sizeof (unsigned int )) { +pr_debug ("Map %s has incorrect key size\n" , map_name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE ; + } +switch (def.value_size) { +case 1 : +case 2 : +case 4 : +case 8 : +break ; +default : +pr_debug ("Map %s has incorrect value size\n" , map_name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE ; + } + + op =bpf_map_op__new (); +if (IS_ERR (op)) +return PTR_ERR (op); + op->op_type =BPF_MAP_OP_SET_VALUE ; + op->v.value = term->val.num; + + err =bpf_map__add_op (map, op); +if (err) +bpf_map_op__delete (op); +return err; +} + +static int +bpf_map__config_value (struct bpf_map *map, +struct parse_events_term *term, +struct perf_evlist *evlist__maybe_unused ) +{ +if (!term->err_val) { +pr_debug ("Config value not set\n" ); +return -BPF_LOADER_ERRNO__OBJCONF_CONF ; + } + +if (term->type_val !=PARSE_EVENTS__TERM_TYPE_NUM ) { +pr_debug ("ERROR: wrong value type\n" ); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE ; + } + +return __bpf_map__config_value (map, term); +} + +struct bpf_obj_config__map_func { +const char *config_opt; +int (*config_func)(struct bpf_map *,struct parse_events_term *, +struct perf_evlist *); +}; + +struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = { + {"value" , bpf_map__config_value}, +}; + +static int +bpf__obj_config_map (struct bpf_object *obj, +struct parse_events_term *term, +struct perf_evlist *evlist, +int *key_scan_pos) +{ +/* key is "map:<mapname>.<config opt>" */ +char *map_name =strdup (term->config +sizeof ("map:" ) -1 ); +struct bpf_map *map; +int err = -BPF_LOADER_ERRNO__OBJCONF_OPT ; +char *map_opt; +size_t i; + +if (!map_name) +return -ENOMEM ; + + map_opt =strchr (map_name,'.' ); +if (!map_opt) { +pr_debug ("ERROR: Invalid map config: %s\n" , map_name); + goto out; + } + + *map_opt++ ='\0' ; +if (*map_opt =='\0' ) { +pr_debug ("ERROR: Invalid map option: %s\n" , term->config); + goto out; + } + + map =bpf_object__get_map_by_name (obj, map_name); +if (!map) { +pr_debug ("ERROR: Map %s doesn't exist\n" , map_name); + err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST ; + goto out; + } + + *key_scan_pos += map_opt - map_name; +for (i =0 ; i <ARRAY_SIZE (bpf_obj_config__map_funcs); i++) { +struct bpf_obj_config__map_func *func = + &bpf_obj_config__map_funcs[i]; + +if (strcmp (map_opt, func->config_opt) ==0 ) { + err = func->config_func (map, term, evlist); + goto out; + } + } + +pr_debug ("ERROR: Invalid map config option '%s'\n" , map_opt); + err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT ; +out: +free (map_name); +if (!err) + key_scan_pos +=strlen (map_opt); +return err; +} + +int bpf__config_obj (struct bpf_object *obj, +struct parse_events_term *term, +struct perf_evlist *evlist, +int *error_pos) +{ +int key_scan_pos =0 ; +int err; + +if (!obj || !term || !term->config) +return -EINVAL ; + +if (!prefixcmp (term->config,"map:" )) { + key_scan_pos =sizeof ("map:" ) -1 ; + err =bpf__obj_config_map (obj, term, evlist, &key_scan_pos); + goto out; + } + err = -BPF_LOADER_ERRNO__OBJCONF_OPT ; +out: +if (error_pos) + *error_pos = key_scan_pos; +return err; + +} +#define ERRNO_OFFSET (e) ((e) - __BPF_LOADER_ERRNO__START)#define ERRCODE_OFFSET (c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
@@ -753,6 +1008,14 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", + [ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option", + [ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (missing '=')", + [ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object map config option", + [ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map doesn't exist", + [ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map", + [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type", + [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size", + [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size", }; static int
@@ -872,3 +1135,16 @@ int bpf__strerror_load(struct bpf_object *obj,bpf__strerror_end (buf, size);return 0 ; } + +int bpf__strerror_config_obj (struct bpf_object *obj__maybe_unused , +struct parse_events_term *term__maybe_unused , +struct perf_evlist *evlist__maybe_unused , +int *error_pos__maybe_unused ,int err, +char *buf,size_t size) +{ +bpf__strerror_head (err, buf, size); +bpf__strerror_entry (BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE , +"Can't use this config term with this map type" ); +bpf__strerror_end (buf, size); +return 0 ; +}
tools/perf/util/bpf-loader.h +38 -0
@@ -10,6 +10,7 @@#include <string.h> #include <bpf/libbpf.h> #include "probe-event.h" +#include "evlist.h" #include "debug.h" enum bpf_loader_errno {
@@ -24,10 +25,19 @@ enum bpf_loader_errno {BPF_LOADER_ERRNO__PROLOGUE ,/* Failed to generate prologue */ BPF_LOADER_ERRNO__PROLOGUE2BIG ,/* Prologue too big for program */ BPF_LOADER_ERRNO__PROLOGUEOOB ,/* Offset out of bound for prologue */ +BPF_LOADER_ERRNO__OBJCONF_OPT ,/* Invalid object config option */ +BPF_LOADER_ERRNO__OBJCONF_CONF ,/* Config value not set (lost '=')) */ +BPF_LOADER_ERRNO__OBJCONF_MAP_OPT ,/* Invalid object map config option */ +BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST ,/* Target map not exist */ +BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE ,/* Incorrect value type for map */ +BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE ,/* Incorrect map type */ +BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE ,/* Incorrect map key size */ +BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE ,/* Incorrect map value size */ __BPF_LOADER_ERRNO__END, };struct bpf_object ; +struct parse_events_term ;#define PERF_BPF_PROBE_GROUP "perf_bpf_probe" typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
@@ -53,6 +63,14 @@ int bpf__strerror_load(struct bpf_object *obj, int err, char *buf, size_t size);int bpf__foreach_tev (struct bpf_object *obj,bpf_prog_iter_callback_t func,void *arg); + +int bpf__config_obj (struct bpf_object *obj,struct parse_events_term *term, +struct perf_evlist *evlist,int *error_pos); +int bpf__strerror_config_obj (struct bpf_object *obj, +struct parse_events_term *term, +struct perf_evlist *evlist, +int *error_pos,int err,char *buf, +size_t size);#else static inline struct bpf_object * bpf__prepare_load(const char *filename __maybe_unused,
@@ -84,6 +102,15 @@ bpf__foreach_tev(struct bpf_object *obj __maybe_unused, }static inline int +bpf__config_obj (struct bpf_object *obj__maybe_unused , +struct parse_events_term *term__maybe_unused , +struct perf_evlist *evlist__maybe_unused , +int *error_pos__maybe_unused ) +{ +return 0 ; +} + +static inline int __bpf_strerror (char *buf,size_t size) {if (!size)
@@ -118,5 +145,16 @@ static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused, {return __bpf_strerror (buf, size); } + +static inline int +bpf__strerror_config_obj (struct bpf_object *obj__maybe_unused , +struct parse_events_term *term__maybe_unused , +struct perf_evlist *evlist__maybe_unused , +int *error_pos__maybe_unused , +int err__maybe_unused , +char *buf,size_t size) +{ +return __bpf_strerror (buf, size); +}#endif #endif
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 9/15 perf tools: Enable BPF object configure syntax
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch adds the final step for BPF map configuration. A new syntax is appended into parser so user can config BPF objects through '/' '/' enclosed config terms.
After this patch, following syntax is available:
# perf record -e ./test_bpf_map_1.c/map:channel.value=10/ ...
It would takes effect after appling following commits.
Test result:
# cat ./test_bpf_map_1.c
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)BPF_FUNC_map_lookup_elem;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1,
};
SEC("func=sys_nanosleep")
int func(void *ctx)
{
int key = 0;
char fmt[] = "%d\n";
int *pval = map_lookup_elem(&channel, &key);
if (!pval)
return 0;
trace_printk(fmt, sizeof(fmt), *pval);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/- Normal case:
# ./perf record -e './test_bpf_map_1.c/map:channel.value=10/' usleep 10 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.012 MB perf.data ]
- Error case:
# ./perf record -e './test_bpf_map_1.c/map:channel.value/' usleep 10
event syntax error: '..ps:channel:value/'
\___ Config value not set (missing '=')
Hint: Valid config term:
map:[<arraymap>]:value=[value]
(add -v to see detail)
Run 'perf list' for a list of valid events Usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
# ./perf record -e './test_bpf_map_1.c/xmap:channel.value=10/' usleep 10
event syntax error: '..pf_map_1.c/xmap:channel.value=10/'
\___ Invalid object config option
[SNIP] # ./perf record -e './test_bpf_map_1.c/map:xchannel.value=10/' usleep 10
event syntax error: '..p_1.c/map:xchannel.value=10/'
\___ Target map not exist
[SNIP] # ./perf record -e './test_bpf_map_1.c/map:channel.xvalue=10/' usleep 10
event syntax error: '..ps:channel.xvalue=10/'
\___ Invalid object map config option
[SNIP] # ./perf record -e './test_bpf_map_1.c/map:channel.value=x10/' usleep 10
event syntax error: '..nnel.value=x10/'
\___ Incorrect value type for map
[SNIP] Change BPF_MAP_TYPE_ARRAY to '1' in test_bpf_map_1.c:
# ./perf record -e './test_bpf_map_1.c/map:channel.value=10/' usleep 10
event syntax error: '..ps:channel.value=10/'
\___ Can't use this config term to this type of map Hint: Valid config term:
map:[<arraymap>].value=[value]
(add -v to see detail)
--- tools/perf/util/parse-events.c | 55 +++++++++++++++++++++++++++++++++++++++--- tools/perf/util/parse-events.h | 3 ++- tools/perf/util/parse-events.l | 2 +- tools/perf/util/parse-events.y | 10 +++++--- 4 files changed, 61 insertions(+), 9 deletions(-)
tools/perf/util/parse-events.c +52 -3
@@ -672,17 +672,63 @@ errout:return err; } +static int +parse_events_config_bpf (struct parse_events_evlist *data, +struct bpf_object *obj, +struct list_head *head_config) +{ +struct parse_events_term *term; +int error_pos; + +if (!head_config ||list_empty (head_config)) +return 0 ; + +list_for_each_entry (term, head_config, list) { +char errbuf[BUFSIZ ]; +int err; + +if (term->type_term !=PARSE_EVENTS__TERM_TYPE_USER ) { +snprintf (errbuf,sizeof (errbuf), +"Invalid config term for BPF object" ); + errbuf[BUFSIZ -1 ] ='\0' ; + + data->error->idx = term->err_term; + data->error->str =strdup (errbuf); +return -EINVAL ; + } + + err =bpf__config_obj (obj, term,NULL , &error_pos); +if (err) { +bpf__strerror_config_obj (obj, term,NULL , + &error_pos, err, errbuf, +sizeof (errbuf)); + data->error->help =strdup ( +"Hint:\tValid config term:\n" +" \tmap:[<arraymap>].value=[value]\n" +" \t(add -v to see detail)" ); + data->error->str =strdup (errbuf); +if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE ) + data->error->idx = term->err_val; +else + data->error->idx = term->err_term + error_pos; +return err; + } + } +return 0 ; +} +int parse_events_load_bpf (struct parse_events_evlist *data,struct list_head *list,char *bpf_file_name, -bool source) +bool source, +struct list_head *head_config) {struct bpf_object *obj; +int err; obj =bpf__prepare_load (bpf_file_name, source);if (IS_ERR (obj)) {char errbuf[BUFSIZ ]; -int err; err =PTR_ERR (obj);
@@ -700,7 +746,10 @@ int parse_events_load_bpf(struct parse_events_evlist *data,return err; } -return parse_events_load_bpf_obj (data, list, obj); + err =parse_events_load_bpf_obj (data, list, obj); +if (err) +return err; +return parse_events_config_bpf (data, obj, head_config); } static int
tools/perf/util/parse-events.h +2 -1
@@ -129,7 +129,8 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,int parse_events_load_bpf (struct parse_events_evlist *data,struct list_head *list,char *bpf_file_name, -bool source); +bool source, +struct list_head *head_config);/* Provide this function for perf test */ struct bpf_object ; int parse_events_load_bpf_obj(struct parse_events_evlist *data,
tools/perf/util/parse-events.l +1 -1
@@ -122,7 +122,7 @@ num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+ name [a-zA-Z_*?][a-zA-Z0-9_*?.]* -name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]* +name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]* /* If you add a modifier you need to update check_modifier() */ modifier_event [ukhpPGHSDI]+ modifier_bp [rwx]{1,3}
tools/perf/util/parse-events.y +6 -4
@@ -437,24 +437,26 @@ PE_RAW opt_event_config } event_bpf_file: -PE_BPF_OBJECT +PE_BPF_OBJECT 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_load_bpf(data, list, $1, false)); + ABORT_ON(parse_events_load_bpf(data, list, $1, false, $2)); + parse_events_terms__delete($2); $$ = list; } | -PE_BPF_SOURCE +PE_BPF_SOURCE opt_event_config { struct parse_events_evlist *data = _data; struct list_head *list; ALLOC_LIST(list); - ABORT_ON(parse_events_load_bpf(data, list, $1, true)); + ABORT_ON(parse_events_load_bpf(data, list, $1, true, $2)); + parse_events_terms__delete($2); $$ = list; }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 10/15 perf record: Apply config to BPF objects before recording
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
bpf__apply_obj_config() is introduced as the core API to apply object config options to all BPF objects. This patch also does the real work for setting values for BPF_MAP_TYPE_PERF_ARRAY maps by inserting value stored in map's private field into the BPF map.
This patch is required because we are not always able to set all BPF config during parsing. Further patch will set events created by perf to BPF_MAP_TYPE_PERF_EVENT_ARRAY maps, which is not exist until perf_evsel__open().
bpf_map_foreach_key() is introduced to iterate over each key needs to be configured. This function would be extended to support more map types and different key settings.
In perf record, before start recording, call bpf__apply_config() to turn on all BPF config options.
Test result:
# cat ./test_bpf_map_1.c
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)BPF_FUNC_map_lookup_elem;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1,
};
SEC("func=sys_nanosleep")
int func(void *ctx)
{
int key = 0;
char fmt[] = "%d\n";
int *pval = map_lookup_elem(&channel, &key);
if (!pval)
return 0;
trace_printk(fmt, sizeof(fmt), *pval);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/ # echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e './test_bpf_map_1.c/map:channel.value=11/' usleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1 #P:8
[SNIP]
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
usleep-18593 [007] d... 2394714.395539: : 11
# ./perf record -e './test_bpf_map_1.c/map:channel.value=101/' usleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1 #P:8
[SNIP]
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
usleep-18593 [007] d... 2394714.395539: : 11
usleep-19000 [006] d... 2394831.057840: : 101--- tools/perf/builtin-record.c | 11 +++ tools/perf/util/bpf-loader.c | 184 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 15 ++++ 3 files changed, 210 insertions(+)
tools/perf/builtin-record.c +11 -0
@@ -32,6 +32,7 @@#include "util/parse-branch-options.h" #include "util/parse-regs-options.h" #include "util/llvm-utils.h" +#include "util/bpf-loader.h" #include <unistd.h> #include <sched.h>
@@ -536,6 +537,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } + err =bpf__apply_obj_config (); +if (err) { +char errbuf[BUFSIZ ]; + +bpf__strerror_apply_obj_config (err, errbuf,sizeof (errbuf)); +pr_err ("ERROR: Apply config to BPF failed: %s\n" , + errbuf); + goto out_child; + } +/* * Normally perf_session__new would do this, but it doesn't have the * evlist.
tools/perf/util/bpf-loader.c +184 -0
199 lines not on this page · raw message
tools/perf/util/bpf-loader.h +15 -0
@@ -71,6 +71,8 @@ int bpf__strerror_config_obj(struct bpf_object *obj,struct perf_evlist *evlist, int *error_pos, int err, char *buf, size_t size); +int bpf__apply_obj_config (void ); +int bpf__strerror_apply_obj_config (int err,char *buf,size_t size);#else static inline struct bpf_object * bpf__prepare_load(const char *filename __maybe_unused,
@@ -111,6 +113,12 @@ bpf__config_obj(struct bpf_object *obj __maybe_unused, }static inline int +bpf__apply_obj_config (void ) +{ +return 0 ; +} + +static inline int __bpf_strerror (char *buf,size_t size) {if (!size)
@@ -156,5 +164,12 @@ bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused, {return __bpf_strerror (buf, size); } + +static inline int +bpf__strerror_apply_obj_config (int err__maybe_unused , +char *buf,size_t size) +{ +return __bpf_strerror (buf, size); +}#endif #endif
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 11/15 perf tools: Enable passing event to BPF object
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
A new syntax is added to the parser so that the user can access predefined perf events in BPF objects.
After this patch, BPF programs for perf are finally able to utilize bpf_perf_event_read() introduced in commit 35578d798400 ("bpf: Implement function bpf_perf_event_read() that get the selected hardware PMU counter").
Test result:
# cat test_bpf_map_2.c
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
static int (*get_smp_processor_id)(void) =
(void *)BPF_FUNC_get_smp_processor_id;
static int (*perf_event_read)(struct bpf_map_def *, int) =
(void *)BPF_FUNC_perf_event_read; struct bpf_map_def SEC("maps") pmu_map = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = __NR_CPUS__,
};
SEC("func_write=sys_write")
int func_write(void *ctx)
{
unsigned long long val;
char fmt[] = "sys_write: pmu=%llu\n";
val = perf_event_read(&pmu_map, get_smp_processor_id());
trace_printk(fmt, sizeof(fmt), val);
return 0;
}
SEC("func_write_return=sys_write%return")
int func_write_return(void *ctx)
{
unsigned long long val = 0;
char fmt[] = "sys_write_return: pmu=%llu\n";
val = perf_event_read(&pmu_map, get_smp_processor_id());
trace_printk(fmt, sizeof(fmt), val);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/Normal case:
# echo "" > /sys/kernel/debug/tracing/trace
# perf record -i -e cycles -e './test_bpf_map_2.c/map:pmu_map.event=cycles/' ls /
[SNIP]
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data (7 samples) ]
# cat /sys/kernel/debug/tracing/trace | grep ls
ls-17066 [000] d... 938449.863301: : sys_write: pmu=1157327
ls-17066 [000] dN.. 938449.863342: : sys_write_return: pmu=1225218
ls-17066 [000] d... 938449.863349: : sys_write: pmu=1241922
ls-17066 [000] dN.. 938449.863369: : sys_write_return: pmu=1267445Normal case (system wide):
# echo "" > /sys/kernel/debug/tracing/trace # perf record -i -e cycles -e './test_bpf_map_2.c/map:pmu_map.event=cycles/' -a ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.811 MB perf.data (120 samples) ]
# cat /sys/kernel/debug/tracing/trace | grep -v '18446744073709551594' | grep -v perf | head -n 20
[SNIP]
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
gmain-30828 [002] d... 2740551.068992: : sys_write: pmu=84373
gmain-30828 [002] d... 2740551.068992: : sys_write_return: pmu=87696
gmain-30828 [002] d... 2740551.068996: : sys_write: pmu=100658
gmain-30828 [002] d... 2740551.068997: : sys_write_return: pmu=102572Error case 1:
# perf record -e './test_bpf_map_2.c' ls /
[SNIP]
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.014 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace | grep ls
ls-17115 [007] d... 2724279.665625: : sys_write: pmu=18446744073709551614
ls-17115 [007] dN.. 2724279.665651: : sys_write_return: pmu=18446744073709551614
ls-17115 [007] d... 2724279.665658: : sys_write: pmu=18446744073709551614
ls-17115 [007] dN.. 2724279.665677: : sys_write_return: pmu=18446744073709551614 (18446744073709551614 is 0xfffffffffffffffe (-2))
Error case 2:
# perf record -e cycles -e './test_bpf_map_2.c/map:pmu_map.event=evt/' -a
event syntax error: '..ps:pmu_map.event=evt/'
\___ Event not found for map settingHint: Valid config terms:
map:[<arraymap>].value=[value]
map:[<eventmap>].event=[event][SNIP]
Error case 3:
# ls /proc/2348/task/ 2348 2505 2506 2507 2508 # perf record -i -e cycles -e './test_bpf_map_2.c/map:pmu_map.event=cycles/' -p 2348 ERROR: Apply config to BPF failed: Cannot set event to BPF map in multi-thread tracing
Error case 4:
# perf record -e cycles -e './test_bpf_map_2.c/map:pmu_map.event=cycles/' ls / ERROR: Apply config to BPF failed: Doesn't support inherit event (Hint: use -i to turn off inherit)
Error case 5:
# perf record -i -e raw_syscalls:sys_enter -e './test_bpf_map_2.c/map:pmu_map.event=raw_syscalls:sys_enter/' ls ERROR: Apply config to BPF failed: Can only put raw, hardware and BPF output event into a BPF map
Error case 6:
# perf record -i -e './test_bpf_map_2.c/map:pmu_map.event=123/' ls /
event syntax error: '.._map.event=123/'
\___ Incorrect value type for map
[SNIP]--- tools/perf/util/bpf-loader.c | 163 +++++++++++++++++++++++++++++++++++++++-- tools/perf/util/bpf-loader.h | 5 ++ tools/perf/util/evlist.c | 16 ++++ tools/perf/util/evlist.h | 3 + tools/perf/util/parse-events.c | 15 ++-- tools/perf/util/parse-events.h | 1 + 6 files changed, 190 insertions(+), 13 deletions(-)
tools/perf/util/bpf-loader.c +156 -7
@@ -742,6 +742,7 @@ int bpf__foreach_tev(struct bpf_object *obj,enum bpf_map_op_type {BPF_MAP_OP_SET_VALUE , +BPF_MAP_OP_SET_EVSEL , };enum bpf_map_key_type {
@@ -754,6 +755,7 @@ struct bpf_map_op {enum bpf_map_key_type key_type;union {u64 value; +struct perf_evsel *evsel; } v; };
@@ -838,6 +840,24 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)return 0 ; } +static struct bpf_map_op * +bpf_map__add_newop (struct bpf_map *map) +{ +struct bpf_map_op *op; +int err; + + op =bpf_map_op__new (); +if (IS_ERR (op)) +return op; + + err =bpf_map__add_op (map, op); +if (err) { +bpf_map_op__delete (op); +return ERR_PTR (err); + } +return op; +} +static int __bpf_map__config_value (struct bpf_map *map,struct parse_events_term *term)
@@ -876,16 +896,12 @@ __bpf_map__config_value(struct bpf_map *map,return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE ; } - op =bpf_map_op__new (); + op =bpf_map__add_newop (map);if (IS_ERR (op))return PTR_ERR (op); op->op_type =BPF_MAP_OP_SET_VALUE ; op->v.value = term->val.num; - - err =bpf_map__add_op (map, op); -if (err) -bpf_map_op__delete (op); -return err; +return 0 ; } static int
@@ -899,13 +915,75 @@ bpf_map__config_value(struct bpf_map *map, }if (term->type_val !=PARSE_EVENTS__TERM_TYPE_NUM ) { -pr_debug ("ERROR: wrong value type\n" ); +pr_debug ("ERROR: wrong value type for 'value'\n" );return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE ; }return __bpf_map__config_value (map, term); } +static int +__bpf_map__config_event (struct bpf_map *map, +struct parse_events_term *term, +struct perf_evlist *evlist) +{ +struct perf_evsel *evsel; +struct bpf_map_def def; +struct bpf_map_op *op; +const char *map_name; +int err; + + map_name =bpf_map__get_name (map); + evsel =perf_evlist__find_evsel_by_str (evlist, term->val.str); +if (!evsel) { +pr_debug ("Event (for '%s') '%s' doesn't exist\n" , + map_name, term->val.str); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT ; + } + + err =bpf_map__get_def (map, &def); +if (err) { +pr_debug ("Unable to get map definition from '%s'\n" , + map_name); +return err; + } + +/* +* No need to check key_size and value_size: +* kernel has already checked them. +*/ +if (def.type !=BPF_MAP_TYPE_PERF_EVENT_ARRAY ) { +pr_debug ("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n" , + map_name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE ; + } + + op =bpf_map__add_newop (map); +if (IS_ERR (op)) +return PTR_ERR (op); + op->op_type =BPF_MAP_OP_SET_EVSEL ; + op->v.evsel = evsel; +return 0 ; +} + +static int +bpf_map__config_event (struct bpf_map *map, +struct parse_events_term *term, +struct perf_evlist *evlist) +{ +if (!term->err_val) { +pr_debug ("Config value not set\n" ); +return -BPF_LOADER_ERRNO__OBJCONF_CONF ; + } + +if (term->type_val !=PARSE_EVENTS__TERM_TYPE_STR ) { +pr_debug ("ERROR: wrong value type for 'event'\n" ); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE ; + } + +return __bpf_map__config_event (map, term, evlist); +} +struct bpf_obj_config__map_func {const char *config_opt; int (*config_func)(struct bpf_map *, struct parse_events_term *,
@@ -914,6 +992,7 @@ struct bpf_obj_config__map_func {struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = { {"value" , bpf_map__config_value}, + {"event" , bpf_map__config_event}, }; static int
@@ -1057,6 +1136,7 @@ bpf_map_config_foreach_key(struct bpf_map *map,list_for_each_entry (op, &priv->ops_list, list) {switch (def.type) {case BPF_MAP_TYPE_ARRAY : +case BPF_MAP_TYPE_PERF_EVENT_ARRAY :switch (op->key_type) {case BPF_MAP_KEY_ALL : err = foreach_key_array_all(func, arg, name,
@@ -1115,6 +1195,60 @@ apply_config_value_for_key(int map_fd, void *pkey, }static int +apply_config_evsel_for_key (const char *name,int map_fd,void *pkey, +struct perf_evsel *evsel) +{ +struct xyarray *xy = evsel->fd; +struct perf_event_attr *attr; +unsigned int key, events; +bool check_pass = false; +int *evt_fd; +int err; + +if (!xy) { +pr_debug ("ERROR: evsel not ready for map %s\n" , name); +return -BPF_LOADER_ERRNO__INTERNAL ; + } + +if (xy->row_size / xy->entry_size !=1 ) { +pr_debug ("ERROR: Dimension of target event is incorrect for map %s\n" , + name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM ; + } + + attr = &evsel->attr; +if (attr->inherit) { +pr_debug ("ERROR: Can't put inherit event into map %s\n" , name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH ; + } + +if (attr->type ==PERF_TYPE_RAW ) + check_pass = true; +if (attr->type ==PERF_TYPE_HARDWARE ) + check_pass = true; +if (attr->type ==PERF_TYPE_SOFTWARE && + attr->config ==PERF_COUNT_SW_BPF_OUTPUT ) + check_pass = true; +if (!check_pass) { +pr_debug ("ERROR: Event type is wrong for map %s\n" , name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE ; + } + + events = xy->entries / (xy->row_size / xy->entry_size); + key = *((unsigned int *)pkey); +if (key >= events) { +pr_debug ("ERROR: there is no event %d for map %s\n" , + key, name); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE ; + } + evt_fd =xyarray__entry (xy, key,0 ); + err =bpf_map_update_elem (map_fd, pkey, evt_fd,BPF_ANY ); +if (err && errno) + err = -errno; +return err; +} + +static int apply_obj_config_map_for_key(const char *name, int map_fd, struct bpf_map_def *pdef __maybe_unused, struct bpf_map_op *op,
@@ -1128,6 +1262,10 @@ apply_obj_config_map_for_key(const char *name, int map_fd, pdef->value_size, op->v.value);break ; +case BPF_MAP_OP_SET_EVSEL : + err =apply_config_evsel_for_key (name, map_fd, pkey, + op->v.evsel); +break ;default :pr_debug ("ERROR: unknown value type for '%s'\n" , name); err = -BPF_LOADER_ERRNO__INTERNAL ;
@@ -1193,6 +1331,11 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type", [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size", [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size", + [ERRCODE_OFFSET(OBJCONF_MAP_NOEVT)] = "Event not found for map setting", + [ERRCODE_OFFSET(OBJCONF_MAP_MAPSIZE)] = "Invalid map size for event setting", + [ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large", + [ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event", + [ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map", }; static int
@@ -1329,6 +1472,12 @@ int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,int bpf__strerror_apply_obj_config (int err,char *buf,size_t size) {bpf__strerror_head (err, buf, size); +bpf__strerror_entry (BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM , +"Cannot set event to BPF map in multi-thread tracing" ); +bpf__strerror_entry (BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH , +"%s (Hint: use -i to turn off inherit)" , emsg); +bpf__strerror_entry (BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE , +"Can only put raw, hardware and BPF output event into a BPF map" );bpf__strerror_end (buf, size);return 0 ; }
tools/perf/util/bpf-loader.h +5 -0
@@ -33,6 +33,11 @@ enum bpf_loader_errno {BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE ,/* Incorrect map type */ BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE ,/* Incorrect map key size */ BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE ,/* Incorrect map value size */ +BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT ,/* Event not found for map setting */ +BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE ,/* Invalid map size for event setting */ +BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM ,/* Event dimension too large */ +BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH ,/* Doesn't support inherit event */ +BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE ,/* Wrong event type for map */ __BPF_LOADER_ERRNO__END, };
tools/perf/util/evlist.c +16 -0
@@ -1741,3 +1741,19 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist, tracking_evsel->tracking = true; } + +struct perf_evsel * +perf_evlist__find_evsel_by_str (struct perf_evlist *evlist, +const char *str) +{ +struct perf_evsel *evsel; + +evlist__for_each (evlist, evsel) { +if (!evsel->name) +continue ; +if (strcmp (str, evsel->name) ==0 ) +return evsel; + } + +return NULL ; +}
tools/perf/util/evlist.h +3 -0
@@ -294,4 +294,7 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist, struct perf_evsel *tracking_evsel);void perf_event_attr__set_max_precise_ip (struct perf_event_attr *attr); + +struct perf_evsel * +perf_evlist__find_evsel_by_str (struct perf_evlist *evlist,const char *str);#endif /* __PERF_EVLIST_H */
tools/perf/util/parse-events.c +9 -6
@@ -697,14 +697,16 @@ parse_events_config_bpf(struct parse_events_evlist *data,return -EINVAL ; } - err =bpf__config_obj (obj, term,NULL , &error_pos); + err =bpf__config_obj (obj, term, data->evlist, &error_pos);if (err) { -bpf__strerror_config_obj (obj, term,NULL , +bpf__strerror_config_obj (obj, term, data->evlist, &error_pos, err, errbuf,sizeof (errbuf)); data->error->help =strdup ( -"Hint:\tValid config term:\n" +"Hint:\tValid config terms:\n" " \tmap:[<arraymap>].value=[value]\n" +" \tmap:[<eventmap>].event=[event]\n" +"\n" " \t(add -v to see detail)" ); data->error->str =strdup (errbuf);if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE )
@@ -1530,9 +1532,10 @@ int parse_events(struct perf_evlist *evlist, const char *str, struct parse_events_error *err) {struct parse_events_evlist data = { - .list =LIST_HEAD_INIT (data.list), - .idx = evlist->nr_entries, - .error = err, + .list =LIST_HEAD_INIT (data.list), + .idx = evlist->nr_entries, + .error = err, + .evlist = evlist, };int ret;
tools/perf/util/parse-events.h +1 -0
@@ -99,6 +99,7 @@ struct parse_events_evlist {int idx;int nr_groups;struct parse_events_error *error; +struct perf_evlist *evlist; };struct parse_events_terms {
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 12/15 perf tools: Support setting different slots in a BPF map separately
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch introduces basic facilities to support config different slots in a BPF map one by one.
array.nr_ranges and array.ranges are introduced into 'struct parse_events_term', where ranges is an array of indices range (start, length) which will be configured by this config term. nr_ranges is the size of the array. The array is passed to 'struct bpf_map_priv'. To indicate the new type of configuration, BPF_MAP_KEY_RANGES is added as a new key type. bpf_map_config_foreach_key() is extended to iterate over those indices instead of all possible keys.
Code in this commit will be enabled by following commit which enables the indices syntax for array configuration.
--- tools/perf/util/bpf-loader.c | 128 ++++++++++++++++++++++++++++++++++++++--- tools/perf/util/bpf-loader.h | 1 + tools/perf/util/parse-events.c | 7 +++ tools/perf/util/parse-events.h | 10 ++++ 4 files changed, 137 insertions(+), 9 deletions(-)
tools/perf/util/bpf-loader.c +119 -9
@@ -17,6 +17,7 @@#include "llvm-utils.h" #include "probe-event.h" #include "probe-finder.h" // for MAX_PROBES +#include "parse-events.h" #include "llvm-utils.h" #define DEFINE_PRINT_FN (name, level) \
@@ -747,6 +748,7 @@ enum bpf_map_op_type {enum bpf_map_key_type {BPF_MAP_KEY_ALL , +BPF_MAP_KEY_RANGES , };struct bpf_map_op {
@@ -754,6 +756,9 @@ struct bpf_map_op {enum bpf_map_op_type op_type;enum bpf_map_key_type key_type;union { +struct parse_events_array array; + } k; +union {u64 value;struct perf_evsel *evsel; } v;
@@ -768,6 +773,8 @@ bpf_map_op__delete(struct bpf_map_op *op) {if (!list_empty (&op->list))list_del (&op->list); +if (op->key_type ==BPF_MAP_KEY_RANGES ) +parse_events__clear_array (&op->k.array);free (op); }
@@ -792,10 +799,33 @@ bpf_map_priv__clear(struct bpf_map *map __maybe_unused,free (priv); } +static int +bpf_map_op_setkey (struct bpf_map_op *op,struct parse_events_term *term) +{ + op->key_type =BPF_MAP_KEY_ALL ; +if (!term) +return 0 ; + +if (term->array.nr_ranges) { +size_t memsz = term->array.nr_ranges * +sizeof (op->k.array.ranges[0 ]); + + op->k.array.ranges =memdup (term->array.ranges, memsz); +if (!op->k.array.ranges) { +pr_debug ("No enough memory to alloc indices for map\n" ); +return -ENOMEM ; + } + op->key_type =BPF_MAP_KEY_RANGES ; + op->k.array.nr_ranges = term->array.nr_ranges; + } +return 0 ; +} +static struct bpf_map_op * -bpf_map_op__new (void ) +bpf_map_op__new (struct parse_events_term *term) {struct bpf_map_op *op; +int err; op =zalloc (sizeof (*op));if (!op) {
@@ -804,7 +834,11 @@ bpf_map_op__new(void) }INIT_LIST_HEAD (&op->list); - op->key_type =BPF_MAP_KEY_ALL ; + err =bpf_map_op_setkey (op, term); +if (err) { +free (op); +return ERR_PTR (err); + }return op; }
@@ -841,12 +875,12 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) }static struct bpf_map_op * -bpf_map__add_newop (struct bpf_map *map) +bpf_map__add_newop (struct bpf_map *map,struct parse_events_term *term) {struct bpf_map_op *op;int err; - op =bpf_map_op__new (); + op =bpf_map_op__new (term);if (IS_ERR (op))return op;
@@ -896,7 +930,7 @@ __bpf_map__config_value(struct bpf_map *map,return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE ; } - op =bpf_map__add_newop (map); + op =bpf_map__add_newop (map, term);if (IS_ERR (op))return PTR_ERR (op); op->op_type =BPF_MAP_OP_SET_VALUE ;
@@ -958,7 +992,7 @@ __bpf_map__config_event(struct bpf_map *map,return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE ; } - op =bpf_map__add_newop (map); + op =bpf_map__add_newop (map, term);if (IS_ERR (op))return PTR_ERR (op); op->op_type =BPF_MAP_OP_SET_EVSEL ;
@@ -996,6 +1030,44 @@ struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = { };static int +config_map_indices_range_check (struct parse_events_term *term, +struct bpf_map *map, +const char *map_name) +{ +struct parse_events_array *array = &term->array; +struct bpf_map_def def; +unsigned int i; +int err; + +if (!array->nr_ranges) +return 0 ; +if (!array->ranges) { +pr_debug ("ERROR: map %s: array->nr_ranges is %d but range array is NULL\n" , + map_name, (int )array->nr_ranges); +return -BPF_LOADER_ERRNO__INTERNAL ; + } + + err =bpf_map__get_def (map, &def); +if (err) { +pr_debug ("ERROR: Unable to get map definition from '%s'\n" , + map_name); +return -BPF_LOADER_ERRNO__INTERNAL ; + } + +for (i =0 ; i < array->nr_ranges; i++) { +unsigned int start = array->ranges[i].start; +size_t length = array->ranges[i].length; +unsigned int idx = start + length -1 ; + +if (idx >= def.max_entries) { +pr_debug ("ERROR: index %d too large\n" , idx); +return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG ; + } + } +return 0 ; +} + +static int bpf__obj_config_map(struct bpf_object *obj, struct parse_events_term *term, struct perf_evlist *evlist,
@@ -1030,7 +1102,12 @@ bpf__obj_config_map(struct bpf_object *obj, goto out; } - *key_scan_pos += map_opt - map_name; + *key_scan_pos +=strlen (map_opt); + err =config_map_indices_range_check (term, map, map_name); +if (err) + goto out; + *key_scan_pos -=strlen (map_opt); +for (i =0 ; i <ARRAY_SIZE (bpf_obj_config__map_funcs); i++) {struct bpf_obj_config__map_func *func = &bpf_obj_config__map_funcs[i];
@@ -1100,6 +1177,33 @@ foreach_key_array_all(map_config_func_t func, }static int +foreach_key_array_ranges (map_config_func_t func,void *arg, +const char *name,int map_fd, +struct bpf_map_def *pdef, +struct bpf_map_op *op) +{ +unsigned int i, j; +int err; + +for (i =0 ; i < op->k.array.nr_ranges; i++) { +unsigned int start = op->k.array.ranges[i].start; +size_t length = op->k.array.ranges[i].length; + +for (j =0 ; j < length; j++) { +unsigned int idx = start + j; + + err =func (name, map_fd, pdef, op, &idx, arg); +if (err) { +pr_debug ("ERROR: failed to insert value to %s[%u]\n" , + name, idx); +return err; + } + } + } +return 0 ; +} + +static int bpf_map_config_foreach_key (struct bpf_map *map,map_config_func_t func,void *arg)
@@ -1141,14 +1245,19 @@ bpf_map_config_foreach_key(struct bpf_map *map,case BPF_MAP_KEY_ALL : err =foreach_key_array_all (func, arg, name, map_fd, &def, op); -if (err) -return err; +break ; +case BPF_MAP_KEY_RANGES : + err =foreach_key_array_ranges (func, arg, name, + map_fd, &def, + op);break ;default :pr_debug ("ERROR: keytype for map '%s' invalid\n" , name);return -BPF_LOADER_ERRNO__INTERNAL ; } +if (err) +return err;break ;default :pr_debug ("ERROR: type of '%s' incorrect\n" , name);
@@ -1336,6 +1445,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large", [ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event", [ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map", + [ERRCODE_OFFSET(OBJCONF_MAP_IDX2BIG)] = "Index too large", }; static int
tools/perf/util/bpf-loader.h +1 -0
@@ -38,6 +38,7 @@ enum bpf_loader_errno {BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM ,/* Event dimension too large */ BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH ,/* Doesn't support inherit event */ BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE ,/* Wrong event type for map */ +BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG ,/* Index too large */ __BPF_LOADER_ERRNO__END, };
tools/perf/util/parse-events.c +7 -0
@@ -2211,6 +2211,8 @@ void parse_events_terms__purge(struct list_head *terms)struct parse_events_term *term, *h;list_for_each_entry_safe (term, h, terms, list) { +if (term->array.nr_ranges) +free (term->array.ranges);list_del_init (&term->list);free (term); }
@@ -2224,6 +2226,11 @@ void parse_events_terms__delete(struct list_head *terms)free (terms); } +void parse_events__clear_array (struct parse_events_array *a) +{ +free (a->ranges); +} +void parse_events_evlist_error (struct parse_events_evlist *data,int idx,const char *str) {
tools/perf/util/parse-events.h +10 -0
@@ -72,8 +72,17 @@ enum { __PARSE_EVENTS__TERM_TYPE_NR, }; +struct parse_events_array { +size_t nr_ranges; +struct { +unsigned int start; +size_t length; + } *ranges; +}; +struct parse_events_term {char *config; +struct parse_events_array array;union {char *str;u64 num;
@@ -120,6 +129,7 @@ int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term);void parse_events_terms__delete (struct list_head *terms);void parse_events_terms__purge (struct list_head *terms); +void parse_events__clear_array (struct parse_events_array *a);int parse_events__modifier_event (struct list_head *list,char *str,bool add);int parse_events__modifier_group (struct list_head *list,char *event_mod);int parse_events_name (struct list_head *list,char *name);
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 13/15 perf tools: Enable indices setting syntax for BPF map
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch introduces a new syntax to perf event parser:
# perf record -e './test_bpf_map_3.c/map:channel.value[0,1,2,3...5]=101/' usleep 2
By utilizing the basic facilities in bpf-loader.c which allow setting different slots in a BPF map separately, the newly introduced syntax allows perf to control specific elements in a BPF map.
Test result:
# cat ./test_bpf_map_3.c
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)BPF_FUNC_map_lookup_elem;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(unsigned char),
.max_entries = 100,
};
SEC("func=hrtimer_nanosleep rqtp->tv_nsec")
int func(void *ctx, int err, long nsec)
{
char fmt[] = "%ld\n";
long usec = nsec * 0x10624dd3 >> 38; // nsec / 1000
int key = (int)usec;
unsigned char *pval = map_lookup_elem(&channel, &key); if (!pval)
return 0;
trace_printk(fmt, sizeof(fmt), (unsigned char)*pval); return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE; /************************* END ***************************/
Normal case:
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e './test_bpf_map_3.c/map:channel.value[0,1,2,3...5]=101/' usleep 2
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace | grep usleep
usleep-405 [004] d... 2745423.547822: : 101
# ./perf record -e './test_bpf_map_3.c/map:channel.value[0...9,20...29]=102,map:channel.value[10...19]=103/' usleep 3
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# ./perf record -e './test_bpf_map_3.c/map:channel.value[0...9,20...29]=102,map:channel.value[10...19]=103/' usleep 15
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace | grep usleep
usleep-405 [004] d... 2745423.547822: : 101
usleep-655 [006] d... 2745434.122814: : 102
usleep-904 [006] d... 2745439.916264: : 103
# ./perf record -e './test_bpf_map_3.c/map:channel.value[all]=104/' usleep 99
# cat /sys/kernel/debug/tracing/trace | grep usleep
usleep-405 [004] d... 2745423.547822: : 101
usleep-655 [006] d... 2745434.122814: : 102
usleep-904 [006] d... 2745439.916264: : 103
usleep-1537 [003] d... 2745538.053737: : 104Error case:
# ./perf record -e './test_bpf_map_3.c/map:channel.value[10...1000]=104/' usleep 99
event syntax error: '..annel.value[10...1000]=104/'
\___ Index too large
Hint: Valid config terms:
map:[<arraymap>].value<indices>=[value]
map:[<eventmap>].event<indices>=[event] where <indices> is something like [0,3...5] or [all] (add -v to see detail)
Run 'perf list' for a list of valid events
Usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
--- tools/perf/util/parse-events.c | 5 ++- tools/perf/util/parse-events.l | 13 ++++++- tools/perf/util/parse-events.y | 85 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-)
tools/perf/util/parse-events.c +3 -2
@@ -704,9 +704,10 @@ parse_events_config_bpf(struct parse_events_evlist *data, sizeof(errbuf)); data->error->help =strdup ("Hint:\tValid config terms:\n" -" \tmap:[<arraymap>].value=[value]\n" -" \tmap:[<eventmap>].event=[event]\n" +" \tmap:[<arraymap>].value<indices>=[value]\n" +" \tmap:[<eventmap>].event<indices>=[event]\n" "\n" +" \twhere <indices> is something like [0,3...5] or [all]\n" " \t(add -v to see detail)" ); data->error->str =strdup (errbuf);if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE )
tools/perf/util/parse-events.l +12 -1
@@ -9,8 +9,8 @@ %{ #include <errno.h> #include "../perf.h" -#include "parse-events-bison.h" #include "parse-events.h" +#include "parse-events-bison.h" char *parse_events_get_text(yyscan_t yyscanner); YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
@@ -111,6 +111,7 @@ do { \ %x mem %s config %x event +%x array group [^,{}/]*[{][^}]*[}][^,{}/]* event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
@@ -176,6 +177,14 @@ modifier_bp [rwx]{1,3} } +<array>{ +"]" { BEGIN(config); return ']'; } +{num_dec} { return value(yyscanner, 10); } +{num_hex} { return value(yyscanner, 16); } +, { return ','; } +"\.\.\." { return PE_ARRAY_RANGE; } +} + <config>{ /* * Please update config_term_names when new static term is added.
@@ -195,6 +204,8 @@ no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); } , { return ','; } "/" { BEGIN(INITIAL); return '/'; } {name_minus} { return str(yyscanner, PE_NAME); } +\[all\] { return PE_ARRAY_ALL; } +"[" { BEGIN(array); return '['; } } <mem>{
tools/perf/util/parse-events.y +85 -0
109 lines not on this page · raw message
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 14/15 perf tools: Apply tracepoint event definition options to BPF script
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Users can pass options to tracepoints defined in the BPF script. For example:
# perf record -e ./test.c/no-inherit/ bash # dd if=/dev/zero of=/dev/null count=10000 # exit [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.022 MB perf.data (139 samples) ]
(no-inherit works, only the sys_read issued by bash are captured, at
least 10000 sys_read issued by dd are skipped.)
test.c:
#define SEC(NAME) __attribute__((section(NAME), used))
SEC("func=sys_read")
int bpf_func__sys_read(void *ctx)
{
return 1;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;no-inherit is applied to the kprobe event defined in test.c.
--- tools/perf/tests/bpf.c | 2 +- tools/perf/util/parse-events.c | 56 +++++++++++++++++++++++++++++++++++++----- tools/perf/util/parse-events.h | 3 ++- 3 files changed, 53 insertions(+), 8 deletions(-)
tools/perf/tests/bpf.c +1 -1
@@ -112,7 +112,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void), parse_evlist.error = &parse_error;INIT_LIST_HEAD (&parse_evlist.list); - err =parse_events_load_bpf_obj (&parse_evlist, &parse_evlist.list, obj); + err =parse_events_load_bpf_obj (&parse_evlist, &parse_evlist.list, obj,NULL );if (err ||list_empty (&parse_evlist.list)) {pr_debug ("Failed to add events selected by BPF\n" );return TEST_FAIL ;
tools/perf/util/parse-events.c +50 -6
98 lines not on this page · raw message
tools/perf/util/parse-events.h +2 -1
@@ -146,7 +146,8 @@ int parse_events_load_bpf(struct parse_events_evlist *data,struct bpf_object ;int parse_events_load_bpf_obj (struct parse_events_evlist *data,struct list_head *list, -struct bpf_object *obj); +struct bpf_object *obj, +struct list_head *head_config); int parse_events_add_numeric(struct parse_events_evlist *data, struct list_head *list, u32 type, u64 config,
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 15/15 perf tools: Introduce bpf-output event
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Commit a43eec304259 ("bpf: introduce bpf_perf_event_output() helper") adds a helper to enable a BPF program to output data to a perf ring buffer through a new type of perf event, PERF_COUNT_SW_BPF_OUTPUT. This patch enables perf to create events of that type. Now a perf user can use the following cmdline to receive output data from BPF programs:
# perf record -a -e bpf-output/no-inherit,name=evt/ \
-e ./test_bpf_output.c/map:channel.event=evt/ ls /
# perf script
perf 1560 [004] 347747.086295: evt: ffffffff811fd201 sys_write ...
perf 1560 [004] 347747.086300: evt: ffffffff811fd201 sys_write ...
perf 1560 [004] 347747.086315: evt: ffffffff811fd201 sys_write ...
...Test result:
# cat test_bpf_output.c
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
}; #define SEC(NAME) __attribute__((section(NAME), used))
static u64 (*ktime_get_ns)(void) =
(void *)BPF_FUNC_ktime_get_ns;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
static int (*get_smp_processor_id)(void) =
(void *)BPF_FUNC_get_smp_processor_id;
static int (*perf_event_output)(void *, struct bpf_map_def *, int, void *, unsigned long) =
(void *)BPF_FUNC_perf_event_output;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = __NR_CPUS__,
};
SEC("func_write=sys_write")
int func_write(void *ctx)
{
struct {
u64 ktime;
int cpuid;
} __attribute__((packed)) output_data; char error_data[] = "Error: failed to output: %d\n";
output_data.cpuid = get_smp_processor_id(); output_data.ktime = ktime_get_ns();
int err = perf_event_output(ctx, &channel, get_smp_processor_id(),
&output_data, sizeof(output_data));
if (err)
trace_printk(error_data, sizeof(error_data), err);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE; /************************ END ***************************/
# perf record -a -e bpf-output/no-inherit,name=evt/ \
-e ./test_bpf_output.c/map:channel.event=evt/ ls /
# perf script | grep ls
ls 2242 [003] 347851.557563: evt: ffffffff811fd201 sys_write ...
ls 2242 [003] 347851.557571: evt: ffffffff811fd201 sys_write ...--- tools/perf/util/bpf-loader.c | 5 ++--- tools/perf/util/evsel.c | 5 +++++ tools/perf/util/evsel.h | 8 ++++++++ tools/perf/util/parse-events.l | 1 + 4 files changed, 16 insertions(+), 3 deletions(-)
tools/perf/util/bpf-loader.c +2 -3
@@ -1331,13 +1331,12 @@ apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH ; } +if (perf_evsel__is_bpf_output (evsel)) + check_pass = true;if (attr->type ==PERF_TYPE_RAW ) check_pass = true;if (attr->type ==PERF_TYPE_HARDWARE ) check_pass = true; -if (attr->type ==PERF_TYPE_SOFTWARE && - attr->config ==PERF_COUNT_SW_BPF_OUTPUT ) - check_pass = true;if (!check_pass) {pr_debug ("ERROR: Event type is wrong for map %s\n" , name);return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE ;
tools/perf/util/evsel.c +5 -0
@@ -225,6 +225,11 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)if (evsel !=NULL )perf_evsel__init (evsel, attr, idx); +if (perf_evsel__is_bpf_output (evsel)) { + evsel->attr.sample_type |=PERF_SAMPLE_RAW ; + evsel->attr.sample_period =1 ; + } +return evsel; }
tools/perf/util/evsel.h +8 -0
@@ -364,6 +364,14 @@ static inline bool perf_evsel__is_function_event(struct perf_evsel *evsel)#undef FUNCTION_EVENT } +static inline bool perf_evsel__is_bpf_output (struct perf_evsel *evsel) +{ +struct perf_event_attr *attr = &evsel->attr; + +return (attr->config ==PERF_COUNT_SW_BPF_OUTPUT ) && + (attr->type ==PERF_TYPE_SOFTWARE ); +} +struct perf_attr_details {bool freq;bool verbose;
tools/perf/util/parse-events.l +1 -0
@@ -248,6 +248,7 @@ cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } +bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); } /* * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
Signature · 1 line
2.5.0
Quoted 64 lines · Arnaldo Carvalho de Melo
Hi Ingo,
Please consider pulling,
- Arnaldo
The following changes since commit
91e48b7df15196b8ce01f40455219d3ed7889988:Merge tag 'perf-core-for-mingo' of
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linuxinto perf/core (2016-02-20 11:52:16 +0100)are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.gittags/perf-core-for-mingofor you to fetch changes up to
03e0a7df3efd959e40cd7ff40b1fabddc234ec5a:perf tools: Introduce bpf-output event (2016-02-22 14:37:21 -0300)
----------------------------------------------------------------
perf/core improvements and fixes:New features:
- Add API to set values of map entries in a BPF object, be it individual map slots or ranges (Wang Nan)
- Introduce support for the 'bpf-output' event (Wang Nan)
- Add glue to read perf events in a BPF program (Wang Nan)
Fixes:
- Sort key fixes: Alignment for srcline, file, trace; fix segfault for dynamic, trace events related sort keys (Namyung Kim)
Build fixes:
- Remove duplicate typedef
config_term_func_tdefinition, fixing the build on older systems (Arnaldo Carvalho de Melo)Signed-off-by Arnaldo Carvalho de Melo <[email protected]>----------------------------------------------------------------
Arnaldo Carvalho de Melo (2):
perf tools: Fix build on older systems
perf tools: Remove duplicate typedefconfig_term_func_tdefinitionNamhyung Kim (5):
perf tools: Fix segfault on dynamic entries perf tools: Update srcline/file if needed
perf tools: Fix alignment on some sort keys perf tools: Fix column width setting on 'trace' sort key perf tools: Fix assertion failure on dynamic entryWang Nan (8):
perf bpf: Add API to set values to map entries in a bpf object perf tools: Enable BPF object configure syntax
perf record: Apply config to BPF objects before recording perf tools: Enable passing event to BPF object
perf tools: Support setting different slots in a BPF map separately perf tools: Enable indices setting syntax for BPF map perf tools: Apply tracepoint event definition options to BPF script perf tools: Introduce bpf-output event
Quoted 16 lines
tools/perf/builtin-record.c | 11 + tools/perf/tests/bpf.c | 2 +- tools/perf/ui/hist.c | 3 + tools/perf/util/bpf-loader.c | 718 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 59 ++++ tools/perf/util/evlist.c | 16 + tools/perf/util/evlist.h | 3 + tools/perf/util/evsel.c | 5 + tools/perf/util/evsel.h | 8 + tools/perf/util/hist.c | 3 + tools/perf/util/parse-events.c | 130 +++++++- tools/perf/util/parse-events.h | 17 +- tools/perf/util/parse-events.l | 16 +- tools/perf/util/parse-events.y | 95 +++++- tools/perf/util/sort.c | 90 +++--- 15 files changed, 1112 insertions(+), 64 deletions(-)
Pulled, thanks a lot Arnaldo!
Ingo