perf dlfilter: Add a test for resolve_address()
Adrian Hunter
reviewerPATCH 1/3 perf dlfilter: Add a test for resolve_address()
Extend the "dlfilter C API" test to test perf_dlfilter_fns.resolve_address(). The test currently fails, but passes after a subsequent patch.
--- tools/perf/dlfilters/dlfilter-test-api-v0.c | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
tools/perf/dlfilters/dlfilter-test-api-v0.c +25 -1
@@ -254,6 +254,30 @@ static int check_addr_al(void *ctx)return 0 ; } +static int check_address_al (void *ctx,const struct perf_dlfilter_sample *sample) +{ +struct perf_dlfilter_al address_al; +const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip (ctx); +if (!al) +return test_fail ("resolve_ip() failed" ); + + address_al.size =sizeof (address_al); +if (perf_dlfilter_fns.resolve_address (ctx, sample->ip, &address_al)) +return test_fail ("resolve_address() failed" ); + +CHECK (address_al.sym && al->sym); +CHECK (!strcmp (address_al.sym, al->sym)); +CHECK (address_al.addr == al->addr); +CHECK (address_al.sym_start == al->sym_start); +CHECK (address_al.sym_end == al->sym_end); +CHECK (address_al.dso && al->dso); +CHECK (!strcmp (address_al.dso, al->dso)); + +return 0 ; +} +static int check_attr (void *ctx) {struct perf_event_attr *attr = perf_dlfilter_fns.attr (ctx);
@@ -290,7 +314,7 @@ static int do_checks(void *data, const struct perf_dlfilter_sample *sample, voidif (early && !d->do_early)return 0 ; -if (check_al (ctx) ||check_addr_al (ctx)) +if (check_al (ctx) ||check_addr_al (ctx) ||check_address_al (ctx, sample))return -1 ;if (early)
Signature · 1 line
2.34.1
Adrian Hunter
reviewerPATCH 2/3 perf dlfilter: Initialize addr_location before passing it to thread__find_symbol_fb()
From: Arnaldo Carvalho de Melo <[email protected]>
As thread__find_symbol_fb() will end up calling thread__find_map() and it in turn will call these on uninitialized memory:
maps__zput(al->maps);
map__zput(al->map);
thread__zput(al->thread);--- tools/perf/util/dlfilter.c | 1 + 1 file changed, 1 insertion(+)
tools/perf/util/dlfilter.c +1 -0
@@ -166,6 +166,7 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfif (!thread)return -1 ; +addr_location__init (&al);thread__find_symbol_fb (thread, d->sample->cpumode, address, &al);al_to_d_al (&al, &d_al);
Signature · 1 line
2.34.1
Adrian Hunter
reviewerRe: [PATCH 2/3] perf dlfilter: Initialize addr_location before passing it to thread__find_symbol_fb()
Quoted 1 line · Adrian Hunter
From: Arnaldo Carvalho de Melo <[email protected]>
Or should that and below be [email protected]
Quoted 36 lines
As
thread__find_symbol_fb()will end up callingthread__find_map()and it in turn will call these on uninitialized memory:maps__zput(al->maps); map__zput(al->map); thread__zput(al->thread);Fixes 0dd5041c9a0e ("perf addr_location: Add init/exit/copy functions")Cc Adrian Hunter <[email protected]>Cc Aneesh Kumar K.V <[email protected]>Cc Athira Rajeev <[email protected]>Cc Disha Goel <[email protected]>Cc Ian Rogers <[email protected]>Cc Jiri Olsa <[email protected]>Cc Kajol Jain <[email protected]>Cc Madhavan Srinivasan <[email protected]>Cc Namhyung Kim <[email protected]>Signed-off-by Arnaldo Carvalho de Melo <[email protected]>--- tools/perf/util/dlfilter.c | 1 + 1 file changed, 1 insertion(+)tools/perf/util/dlfilter.c +1
-0@@ -166,6 +166,7 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfif (!thread)return -1 ; +addr_location__init (&al);thread__find_symbol_fb (thread, d->sample->cpumode, address, &al);al_to_d_al (&al, &d_al);
Adrian Hunter
reviewerPATCH 3/3 perf dlfilter: Add al_cleanup()
In reply to Adrian HunterAdd perf_dlfilter_fns.al_cleanup() to do addr_location__exit() on data passed via perf_dlfilter_fns.resolve_address().
Add dlfilter-test-api-v2 to the "dlfilter C API" test to test it.
Update documentation, clarifying that data returned by APIs should not be dereferenced after filter_event() and filter_event_early() return.
--- tools/perf/Documentation/perf-dlfilter.txt | 22 +- tools/perf/Makefile.perf | 2 +- tools/perf/dlfilters/dlfilter-test-api-v2.c | 377 ++++++++++++++++++++ tools/perf/include/perf/perf_dlfilter.h | 11 +- tools/perf/tests/dlfilter-test.c | 38 +- tools/perf/util/dlfilter.c | 29 ++ 6 files changed, 464 insertions(+), 15 deletions(-) create mode 100644 tools/perf/dlfilters/dlfilter-test-api-v2.c
tools/perf/Documentation/perf-dlfilter.txt +20 -2
@@ -64,6 +64,12 @@ internal filtering. If implemented, 'filter_description' should return a one-line description of the filter, and optionally a longer description. +Do not assume the 'sample' argument is valid (dereferenceable) +after 'filter_event' and 'filter_event_early' return. + +Do not assume data referenced by pointers in struct perf_dlfilter_sample +is valid (dereferenceable) after 'filter_event' and 'filter_event_early' return. + The perf_dlfilter_sample structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -150,7 +156,8 @@ struct perf_dlfilter_fns { const char *(*srcline)(void *ctx, __u32 *line_number); struct perf_event_attr *(*attr)(void *ctx); __s32 (*object_code)(void *ctx, __u64 ip, void *buf, __u32 len); - void *(*reserved[120])(void *); + void (*al_cleanup)(void *ctx, struct perf_dlfilter_al *al); + void *(*reserved[119])(void *); }; ----
@@ -161,7 +168,8 @@ struct perf_dlfilter_fns { 'args' returns arguments from --dlarg options. 'resolve_address' provides information about 'address'. al->size must be set -before calling. Returns 0 on success, -1 otherwise. +before calling. Returns 0 on success, -1 otherwise. Call al_cleanup() (if present, +see below) when 'al' data is no longer needed. 'insn' returns instruction bytes and length.
@@ -171,6 +179,12 @@ before calling. Returns 0 on success, -1 otherwise. 'object_code' reads object code and returns the number of bytes read. +'al_cleanup' must be called (if present, so check perf_dlfilter_fns.al_cleanup != NULL) +after resolve_address() to free any associated resources. + +Do not assume pointers obtained via perf_dlfilter_fns are valid (dereferenceable) +after 'filter_event' and 'filter_event_early' return. + The perf_dlfilter_al structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -197,9 +211,13 @@ struct perf_dlfilter_al { /* Below members are only populated by resolve_ip() */ __u8 filtered; /* true if this sample event will be filtered out */ const char *comm; + void *priv; /* Private data. Do not change */ }; ---- +Do not assume data referenced by pointers in struct perf_dlfilter_al +is valid (dereferenceable) after 'filter_event' and 'filter_event_early' return. + perf_dlfilter_sample flags ~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/perf/Makefile.perf +1 -1
@@ -381,7 +381,7 @@ ifndef NO_JVMTI PROGRAMS += $(OUTPUT)$(LIBJVMTI) endif -DLFILTERS := dlfilter-test-api-v0.so dlfilter-show-cycles.so +DLFILTERS := dlfilter-test-api-v0.so dlfilter-test-api-v2.so dlfilter-show-cycles.so DLFILTERS := $(patsubst %,$(OUTPUT)dlfilters/%,$(DLFILTERS)) # what 'all' will build and 'install' will install, in perfexecdir
tools/perf/dlfilters/dlfilter-test-api-v2.c new file +377 -0
377 lines not on this page · raw message
tools/perf/include/perf/perf_dlfilter.h +9 -2
@@ -91,6 +91,7 @@ struct perf_dlfilter_al {/* Below members are only populated by resolve_ip() */ __u8 filtered;/* True if this sample event will be filtered out */ const char *comm; +void *priv;/* Private data. Do not change */ };struct perf_dlfilter_fns {
@@ -102,7 +103,8 @@ struct perf_dlfilter_fns {char **(*args)(void *ctx,int *dlargc);/* * Return information about address (al->size must be set before -* calling). Returns 0 on success, -1 otherwise. +* calling). Returns 0 on success, -1 otherwise. Call al_cleanup() +* when 'al' data is no longer needed. */ __s32 (*resolve_address)(void *ctx,__u64 address,struct perf_dlfilter_al *al);/* Return instruction bytes and length */
@@ -113,8 +115,13 @@ struct perf_dlfilter_fns {struct perf_event_attr *(*attr)(void *ctx);/* Read object code, return numbers of bytes read */ __s32 (*object_code)(void *ctx,__u64 ip,void *buf,__u32 len); +/* +* If present (i.e. must check al_cleanup != NULL), call after +* resolve_address() to free any associated resources. +*/ +void (*al_cleanup)(void *ctx,struct perf_dlfilter_al *al);/* Reserved */ -void *(*reserved[120 ])(void *); +void *(*reserved[119 ])(void *); };/*
tools/perf/tests/dlfilter-test.c +28 -10
@@ -1,7 +1,7 @@// SPDX-License-Identifier: GPL-2.0 /* * Test dlfilter C API. A perf.data file is synthesized and then processed -* by perf script with a dlfilter named dlfilter-test-api-v0.so. Also a C file +* by perf script with dlfilters named dlfilter-test-api-v*.so. Also a C file * is compiled to provide a dso to match the synthesized perf.data file. */
@@ -37,6 +37,8 @@#define MAP_START 0x400000 +#define DLFILTER_TEST_NAME_MAX 128 +struct test_data {struct perf_tool tool;struct machine *machine;
@@ -45,6 +47,8 @@ struct test_data {u64 bar;u64 ip;u64 addr; +char name[DLFILTER_TEST_NAME_MAX ]; +char desc[DLFILTER_TEST_NAME_MAX ];char perf[PATH_MAX ];char perf_data_file_name[PATH_MAX ];char c_file_name[PATH_MAX ];
@@ -215,7 +219,7 @@ static int write_prog(char *file_name)return err ?-1 :0 ; } -static int get_dlfilters_path (char *buf,size_t sz) +static int get_dlfilters_path (const char *name,char *buf,size_t sz) {char perf[PATH_MAX ];char path[PATH_MAX ];
@@ -224,12 +228,12 @@ static int get_dlfilters_path(char *buf, size_t sz)perf_exe (perf,sizeof (perf)); perf_path =dirname (perf); -snprintf (path,sizeof (path),"%s/dlfilters/dlfilter-test-api-v0.so" , perf_path); +snprintf (path,sizeof (path),"%s/dlfilters/%s" , perf_path, name);if (access (path,R_OK )) { exec_path =get_argv_exec_path ();if (!exec_path)return -1 ; -snprintf (path,sizeof (path),"%s/dlfilters/dlfilter-test-api-v0.so" , exec_path); +snprintf (path,sizeof (path),"%s/dlfilters/%s" , exec_path, name);free (exec_path);if (access (path,R_OK ))return -1 ;
@@ -244,9 +248,9 @@ static int check_filter_desc(struct test_data *td)char *desc =NULL ;int ret; -if (get_filter_desc (td->dlfilters,"dlfilter-test-api-v0.so" , &desc, &long_desc) && +if (get_filter_desc (td->dlfilters, td->name, &desc, &long_desc) && long_desc && !strcmp (long_desc,"Filter used by the 'dlfilter C API' perf test" ) && - desc && !strcmp (desc,"dlfilter to test v0 C API" )) + desc && !strcmp (desc, td->desc)) ret =0 ;else ret =-1 ;
@@ -284,7 +288,7 @@ static int get_ip_addr(struct test_data *td)static int do_run_perf_script (struct test_data *td,int do_early) {return system_cmd ("%s script -i %s " -"--dlfilter %s/dlfilter-test-api-v0.so " +"--dlfilter %s/%s " "--dlarg first " "--dlarg %d " "--dlarg %" PRIu64 " "
@@ -292,7 +296,7 @@ static int do_run_perf_script(struct test_data *td, int do_early)"--dlarg %d " "--dlarg last" , td->perf, td->perf_data_file_name, td->dlfilters, - verbose, td->ip, td->addr, do_early); + td->name, verbose, td->ip, td->addr, do_early); }static int run_perf_script (struct test_data *td)
@@ -321,7 +325,7 @@ static int test__dlfilter_test(struct test_data *td)u64 id =99 ;int err; -if (get_dlfilters_path (td->dlfilters,PATH_MAX )) +if (get_dlfilters_path (td->name, td->dlfilters,PATH_MAX ))return test_result ("dlfilters not found" ,TEST_SKIP );if (check_filter_desc (td))
@@ -399,14 +403,18 @@ static void test_data__free(struct test_data *td) } } -static int test__dlfilter (struct test_suite *test__maybe_unused ,int subtest__maybe_unused ) +static int test__dlfilter_ver (int ver) {struct test_data td = {.fd =-1 };int pid =getpid ();int err; +pr_debug ("\n-- Testing version %d API --\n" , ver); +perf_exe (td.perf,sizeof (td.perf)); +snprintf (td.name,sizeof (td.name),"dlfilter-test-api-v%d.so" , ver); +snprintf (td.desc,sizeof (td.desc),"dlfilter to test v%d C API" , ver);snprintf (td.perf_data_file_name,PATH_MAX ,"/tmp/dlfilter-test-%u-perf-data" , pid);snprintf (td.c_file_name,PATH_MAX ,"/tmp/dlfilter-test-%u-prog.c" , pid);snprintf (td.prog_file_name,PATH_MAX ,"/tmp/dlfilter-test-%u-prog" , pid);
@@ -416,4 +424,14 @@ static int test__dlfilter(struct test_suite *test __maybe_unused, int subtest __return err; } +static int test__dlfilter (struct test_suite *test__maybe_unused ,int subtest__maybe_unused ) +{ +int err =test__dlfilter_ver (0 ); + +if (err) +return err; +/* No test for version 1 */ +return test__dlfilter_ver (2 ); +} +DEFINE_SUITE ("dlfilter C API" , dlfilter);
tools/perf/util/dlfilter.c +29 -0
@@ -10,6 +10,8 @@#include <subcmd/exec-cmd.h> #include <linux/zalloc.h> #include <linux/build_bug.h> +#include <linux/kernel.h> +#include <linux/string.h> #include "debug.h" #include "event.h"
@@ -63,6 +65,7 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al) d_al->addr = al->addr; d_al->comm =NULL ; d_al->filtered =0 ; + d_al->priv =NULL ; }static struct addr_location *get_al (struct dlfilter *d)
@@ -151,6 +154,11 @@ static char **dlfilter__args(void *ctx, int *dlargc)return d->dlargv; } +static bool has_priv (struct perf_dlfilter_al *d_al_p) +{ +return d_al_p->size >= offsetof(struct perf_dlfilter_al , priv) +sizeof (d_al_p->priv); +} +static __s32 dlfilter__resolve_address (void *ctx,__u64 address,struct perf_dlfilter_al *d_al_p) {struct dlfilter *d = (struct dlfilter *)ctx;
@@ -177,9 +185,29 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfmemcpy (d_al_p, &d_al,min ((size_t )sz,sizeof (d_al))); d_al_p->size = sz; +if (has_priv (d_al_p)) + d_al_p->priv =memdup (&al,sizeof (al)); +return 0 ; } +static void dlfilter__al_cleanup (void *ctx__maybe_unused ,struct perf_dlfilter_al *d_al_p) +{ +struct addr_location *al; + +/* Ensure backward compatibility */ +if (!has_priv (d_al_p) || !d_al_p->priv) +return ; + + al = d_al_p->priv; + + d_al_p->priv =NULL ; + +addr_location__exit (al); + +free (al); +} +static const __u8 *dlfilter__insn (void *ctx,__u32 *len) {struct dlfilter *d = (struct dlfilter *)ctx;
@@ -297,6 +325,7 @@ static const struct perf_dlfilter_fns perf_dlfilter_fns = { .resolve_addr = dlfilter__resolve_addr, .args = dlfilter__args, .resolve_address = dlfilter__resolve_address, + .al_cleanup = dlfilter__al_cleanup, .insn = dlfilter__insn, .srcline = dlfilter__srcline, .attr = dlfilter__attr,
Signature · 1 line
2.34.1
Arnaldo Carvalho de Melo
maintainerEm Mon, Jul 31, 2023 at 12:18:55PM +0300, Adrian Hunter escreveu:
Quoted 3 lines
Extend the "dlfilter C API" test to test
perf_dlfilter_fns.resolve_address(). The test currently fails, but passes after a subsequent patch.
Ian,
I think this is ok now, can you please take a look and perhaps
provide an Acked-by or Reviewed-by?
- Arnaldo
Quoted 52 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/dlfilters/dlfilter-test-api-v0.c | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)tools/perf/dlfilters/dlfilter-test-api-v0.c +25
-1@@ -254,6 +254,30 @@ static int check_addr_al(void *ctx)return 0 ; } +static int check_address_al (void *ctx,const struct perf_dlfilter_sample *sample) +{ +struct perf_dlfilter_al address_al; +const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip (ctx); +if (!al) +return test_fail ("resolve_ip() failed" ); + + address_al.size =sizeof (address_al); +if (perf_dlfilter_fns.resolve_address (ctx, sample->ip, &address_al)) +return test_fail ("resolve_address() failed" ); + +CHECK (address_al.sym && al->sym); +CHECK (!strcmp (address_al.sym, al->sym)); +CHECK (address_al.addr == al->addr); +CHECK (address_al.sym_start == al->sym_start); +CHECK (address_al.sym_end == al->sym_end); +CHECK (address_al.dso && al->dso); +CHECK (!strcmp (address_al.dso, al->dso)); + +return 0 ; +} +static int check_attr (void *ctx) {struct perf_event_attr *attr = perf_dlfilter_fns.attr (ctx);@@ -290,7 +314,7 @@ static int do_checks(void *data, const struct perf_dlfilter_sample *sample, voidif (early && !d->do_early)return 0 ; -if (check_al (ctx) ||check_addr_al (ctx)) +if (check_al (ctx) ||check_addr_al (ctx) ||check_address_al (ctx, sample))return -1 ;if (early)Signature · 1 line
2.34.1
Signature · 1 line
- Arnaldo
Quoted 10 lines · Arnaldo Carvalho de Melo
Em Mon, Jul 31, 2023 at 12:18:55PM +0300, Adrian Hunter escreveu:
Quoted 3 lines
Extend the "dlfilter C API" test to test
perf_dlfilter_fns.resolve_address(). The test currently fails, but passes after a subsequent patch.Ian,
I think this is ok now, can you please take a look and perhaps
provide an Acked-by or Reviewed-by?
Sorry, found this in my backlog. I'm a little confused over which patch series is current but they all lgtm.
Thanks,
Ian
Quoted 58 lines
- Arnaldo
Quoted 50 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/dlfilters/dlfilter-test-api-v0.c | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)tools/perf/dlfilters/dlfilter-test-api-v0.c +25
-2@@ -254,6 +254,30 @@ static int check_addr_al(void *ctx)return 0 ; } +static int check_address_al (void *ctx,const struct perf_dlfilter_sample *sample) +{ +struct perf_dlfilter_al address_al; +const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip (ctx); +if (!al) +return test_fail ("resolve_ip() failed" ); + + address_al.size =sizeof (address_al); +if (perf_dlfilter_fns.resolve_address (ctx, sample->ip, &address_al)) +return test_fail ("resolve_address() failed" ); + +CHECK (address_al.sym && al->sym); +CHECK (!strcmp (address_al.sym, al->sym)); +CHECK (address_al.addr == al->addr); +CHECK (address_al.sym_start == al->sym_start); +CHECK (address_al.sym_end == al->sym_end); +CHECK (address_al.dso && al->dso); +CHECK (!strcmp (address_al.dso, al->dso)); + +return 0 ; +} +static int check_attr (void *ctx) {struct perf_event_attr *attr = perf_dlfilter_fns.attr (ctx);@@ -290,7 +314,7 @@ static int do_checks(void *data, const struct perf_dlfilter_sample *sample, voidif (early && !d->do_early)return 0 ; -if (check_al (ctx) ||check_addr_al (ctx)) +if (check_al (ctx) ||check_addr_al (ctx) ||check_address_al (ctx, sample))return -1 ;if (early) --Quoted 2 lines
2.34.1
--
- Arnaldo
Em Mon, Aug 14, 2023 at 10:28:29AM -0700, Ian Rogers escreveu:
Quoted 9 lines · Arnaldo Carvalho de Melo
Quoted 8 lines
Em Mon, Jul 31, 2023 at 12:18:55PM +0300, Adrian Hunter escreveu:
Quoted 3 lines
Extend the "dlfilter C API" test to test
perf_dlfilter_fns.resolve_address(). The test currently fails, but passes after a subsequent patch.Ian,
Quoted 7 lines
Quoted 2 lines
I think this is ok now, can you please take a look and perhaps
provide an Acked-by or Reviewed-by?Sorry, found this in my backlog. I'm a little confused over which patch series is current but they all lgtm.
Reviewed-by Ian Rogers <[email protected]>
Thanks, applied.
- Arnaldo
Quoted 61 lines
Thanks,
IanQuoted 58 lines
- Arnaldo
Quoted 50 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/dlfilters/dlfilter-test-api-v0.c | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)tools/perf/dlfilters/dlfilter-test-api-v0.c +25
-2@@ -254,6 +254,30 @@ static int check_addr_al(void *ctx)return 0 ; } +static int check_address_al (void *ctx,const struct perf_dlfilter_sample *sample) +{ +struct perf_dlfilter_al address_al; +const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip (ctx); +if (!al) +return test_fail ("resolve_ip() failed" ); + + address_al.size =sizeof (address_al); +if (perf_dlfilter_fns.resolve_address (ctx, sample->ip, &address_al)) +return test_fail ("resolve_address() failed" ); + +CHECK (address_al.sym && al->sym); +CHECK (!strcmp (address_al.sym, al->sym)); +CHECK (address_al.addr == al->addr); +CHECK (address_al.sym_start == al->sym_start); +CHECK (address_al.sym_end == al->sym_end); +CHECK (address_al.dso && al->dso); +CHECK (!strcmp (address_al.dso, al->dso)); + +return 0 ; +} +static int check_attr (void *ctx) {struct perf_event_attr *attr = perf_dlfilter_fns.attr (ctx);@@ -290,7 +314,7 @@ static int do_checks(void *data, const struct perf_dlfilter_sample *sample, voidif (early && !d->do_early)return 0 ; -if (check_al (ctx) ||check_addr_al (ctx)) +if (check_al (ctx) ||check_addr_al (ctx) ||check_address_al (ctx, sample))return -1 ;if (early) --Quoted 2 lines
2.34.1
--
- Arnaldo
Signature · 1 line
- Arnaldo