diff --git a/cmd-info.c b/cmd-info.c index 7e2cba834..fe504d743 100644 --- a/cmd-info.c +++ b/cmd-info.c @@ -788,6 +788,37 @@ static int read_record_date(void *arg) return 0; } +static int fill_pattern_type(void *arg) +{ + struct fill_handler_arg *fha = arg; + + dprintf(fha->fd, "pattern_type:%s\n", + get_filter_pattern(fha->opts->patt_type)); + + return 0; +} + +static int read_pattern_type(void *arg) +{ + struct ftrace_file_handle *handle = arg; + struct uftrace_info *info = &handle->info; + char buf[4096]; + size_t len; + + if (fgets(buf, sizeof(buf), handle->fp) == NULL) + return -1; + + if (strncmp(buf, "pattern_type:", 13)) + return -1; + + len = strlen(&buf[13]); + if (buf[13 + len - 1] == '\n') + buf[13 + len - 1] = '\0'; + + info->patt_type = parse_filter_pattern(&buf[13]); + return 0; +} + struct uftrace_info_handler { enum uftrace_info_bits bit; int (*handler)(void *arg); @@ -818,6 +849,7 @@ void fill_uftrace_info(uint64_t *info_mask, int fd, struct opts *opts, int statu { LOADINFO, fill_loadinfo }, { ARG_SPEC, fill_arg_spec }, { RECORD_DATE, fill_record_date }, + { PATTERN_TYPE, fill_pattern_type }, }; for (i = 0; i < ARRAY_SIZE(fill_handlers); i++) { @@ -856,6 +888,7 @@ int read_uftrace_info(uint64_t info_mask, struct ftrace_file_handle *handle) { LOADINFO, read_loadinfo }, { ARG_SPEC, read_arg_spec }, { RECORD_DATE, read_record_date }, + { PATTERN_TYPE, read_pattern_type }, }; memset(&handle->info, 0, sizeof(handle->info)); @@ -1003,6 +1036,9 @@ int command_info(int argc, char *argv[], struct opts *opts) pr_out(fmt, "auto-args", "true"); } + if (handle.hdr.info_mask & (1UL << PATTERN_TYPE)) + pr_out(fmt, "pattern", get_filter_pattern(handle.info.patt_type)); + if (handle.hdr.info_mask & (1UL << EXIT_STATUS)) { int status = handle.info.exit_status; diff --git a/cmd-record.c b/cmd-record.c index 16939c1e4..93b0eb220 100644 --- a/cmd-record.c +++ b/cmd-record.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "uftrace.h" #include "libmcount/mcount.h" @@ -243,6 +242,9 @@ static void setup_child_environ(struct opts *opts, int pfd) if (opts->script_file) setenv("UFTRACE_SCRIPT", opts->script_file, 1); + if (opts->patt_type != PATT_REGEX) + setenv("UFTRACE_PATTERN", get_filter_pattern(opts->patt_type), 1); + if (opts->lib_path) snprintf(buf, sizeof(buf), "%s/libmcount/", opts->lib_path); else @@ -1470,7 +1472,8 @@ static void check_binary(struct opts *opts) close(fd); } -static bool check_linux_perf_event(char *events) +static bool check_linux_schedule_event(char *events, + enum uftrace_pattern_type ptype) { struct strv strv = STRV_INIT; char *evt; @@ -1483,10 +1486,17 @@ static bool check_linux_perf_event(char *events) strv_split(&strv, events, ";"); strv_for_each(&strv, evt, i) { - if (fnmatch(evt, "linux:schedule", 0) == 0) { + struct uftrace_pattern patt; + + init_filter_pattern(ptype, &patt, evt); + + if (match_filter_pattern(&patt, "linux:schedule")) found = true; + + free_filter_pattern(&patt); + + if (found) break; - } } strv_free(&strv); @@ -1856,7 +1866,8 @@ int command_record(int argc, char *argv[], struct opts *opts) check_binary(opts); - has_perf_event = check_linux_perf_event(opts->event); + has_perf_event = check_linux_schedule_event(opts->event, + opts->patt_type); fflush(stdout); diff --git a/cmd-replay.c b/cmd-replay.c index 420c1ba32..fd4756788 100644 --- a/cmd-replay.c +++ b/cmd-replay.c @@ -939,8 +939,12 @@ static bool skip_sys_exit(struct opts *opts, struct ftrace_task_handle *task) ip = task->func_stack[0].addr; sym = find_symtabs(&task->h->sessions.first->symtabs, ip); + if (sym == NULL) + return false; - if (sym && !strncmp(sym->name, "sys_exit", 8)) + if (!strncmp(sym->name, "sys_exit", 8)) + return true; + if (!strcmp(sym->name, "do_syscall_64")) return true; return false; diff --git a/cmd-script.c b/cmd-script.c index e369cc96a..cef675e14 100644 --- a/cmd-script.c +++ b/cmd-script.c @@ -169,7 +169,7 @@ int command_script(int argc, char *argv[], struct opts *opts) fstack_setup_filters(opts, &handle); /* initialize script */ - if (script_init(opts->script_file) < 0) + if (script_init(opts->script_file, opts->patt_type) < 0) return -1; while (read_rstack(&handle, &task) == 0 && !uftrace_done) { diff --git a/doc/uftrace-dump.md b/doc/uftrace-dump.md index eba54c2fb..41210fb07 100644 --- a/doc/uftrace-dump.md +++ b/doc/uftrace-dump.md @@ -65,6 +65,9 @@ OPTIONS \--demangle=*TYPE* : Use demangled C++ symbol names for filters, triggers, arguments and/or return values. Possible values are "full", "simple" and "no". Default is "simple" which ignores function arguments and template parameters. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + EXAMPLE ======= diff --git a/doc/uftrace-graph.md b/doc/uftrace-graph.md index 99b43711a..42f9f0768 100644 --- a/doc/uftrace-graph.md +++ b/doc/uftrace-graph.md @@ -64,6 +64,9 @@ OPTIONS \--demangle=*TYPE* : Use demangled C++ symbol names for filters, triggers, arguments and/or return values. Possible values are "full", "simple" and "no". Default is "simple" which ignores function arguments and template parameters. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + EXAMPLES ======== diff --git a/doc/uftrace-live.md b/doc/uftrace-live.md index 51824b6db..e7d33ac9b 100644 --- a/doc/uftrace-live.md +++ b/doc/uftrace-live.md @@ -139,6 +139,9 @@ OPTIONS \--libname : Show library name along with function name. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + FILTERS ======= diff --git a/doc/uftrace-record.md b/doc/uftrace-record.md index a033cf903..9db1a5ded 100644 --- a/doc/uftrace-record.md +++ b/doc/uftrace-record.md @@ -108,6 +108,9 @@ OPTIONS -S *SCRIPT_PATH*, \--script=*SCRIPT_PATH* : Add a script to do addtional work at the entry and exit of function. The type of script is detected by the postfix such as '.py' for python. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + FILTERS ======= @@ -203,7 +206,7 @@ You can also set triggers on filtered functions. See *TRIGGERS* section below f When kernel function tracing is enabled, you can also set the filters on kernel functions by marking the symbol with the `@kernel` modifier. The following example will show all user functions and the (kernel) page fault handler. - $ sudo uftrace -k -F '*page_fault@kernel' ./abc + $ sudo uftrace -k -F '.*page_fault@kernel' ./abc # DURATION TID FUNCTION [14721] | main() { 7.713 us [14721] | __do_page_fault(); diff --git a/doc/uftrace-replay.md b/doc/uftrace-replay.md index 336544a45..cccdc0157 100644 --- a/doc/uftrace-replay.md +++ b/doc/uftrace-replay.md @@ -65,7 +65,7 @@ OPTIONS : Do not show comments of returned functions. -k, \--kernel -: Trace kernel functions (and events) as well as user functions (and events). This options has no meaning and so is deprecated now. It will always show kernel functions and events if exists. If you want to hide kernel functions, please use `-N .@kernel` to filter out all kernel functions. +: Trace kernel functions (and events) as well as user functions (and events). This options has no meaning and so is deprecated now. It will always show kernel functions and events if exists. If you want to hide kernel functions, please use `-N .@kernel` to filter out all kernel functions (for the regex match). \--kernel-full : Show all kernel functions and events occurred outside of user functions. This option is the inverse of `--kernel-skip-out`. @@ -82,6 +82,9 @@ OPTIONS \--libname : Show libname name along with function name. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + FILTERS ======= diff --git a/doc/uftrace-report.md b/doc/uftrace-report.md index 4c55ef294..fe558f20a 100644 --- a/doc/uftrace-report.md +++ b/doc/uftrace-report.md @@ -76,6 +76,9 @@ OPTIONS \--demangle=*TYPE* : Use demangled C++ symbol names for filters, triggers, arguments and/or return values. Possible values are "full", "simple" and "no". Default is "simple" which ignores function arguments and template parameters. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + EXAMPLE ======= diff --git a/doc/uftrace-script.md b/doc/uftrace-script.md index e4dcdd0d1..5ec7c3871 100644 --- a/doc/uftrace-script.md +++ b/doc/uftrace-script.md @@ -46,6 +46,9 @@ OPTIONS \--record COMMAND [*command-options*] : Record a new trace before running a given script. +--match=*TYPE* +: Use pattern match using TYPE. Possible types are `regex` and `glob`. Default is `regex`. + EXAMPLES ======== @@ -117,7 +120,7 @@ The below is another example that shows the different output compared to previou The python script above can be modified to do more output customization. -The python script can have an optional "UFTRACE_FUNCS" list which can have name (or regex pattern) of functions to run the script. If it exists, only matched functions will run the script. For example, if you add following lines to the script, it will run only for functions with a single letter name. +The python script can have an optional "UFTRACE_FUNCS" list which can have name (or pattern depending on the --match option) of functions to run the script. If it exists, only matched functions will run the script. For example, if you add following lines to the script, it will run only for functions with a single letter name. $ echo 'UFTRACE_FUNCS = [ "^.$" ]' >> replay.py $ uftrace script -S replay.py diff --git a/libmcount/dynamic.c b/libmcount/dynamic.c index ce9721d21..f07f88148 100644 --- a/libmcount/dynamic.c +++ b/libmcount/dynamic.c @@ -1,6 +1,5 @@ #include #include -#include /* This should be defined before #include "utils.h" */ #define PR_FMT "dynamic" @@ -92,7 +91,8 @@ static int prepare_dynamic_update(void) return ret; } -static int do_dynamic_update(struct symtabs *symtabs, char *patch_funcs) +static int do_dynamic_update(struct symtabs *symtabs, char *patch_funcs, + enum uftrace_pattern_type ptype) { char *name, *nopatched_name = NULL; struct symtab *symtab = &symtabs->symtab; @@ -105,26 +105,17 @@ static int do_dynamic_update(struct symtabs *symtabs, char *patch_funcs) strv_split(&funcs, patch_funcs, ";"); strv_for_each(&funcs, name, j) { - bool is_regex; bool found = false; - regex_t re; unsigned i; struct sym *sym; + struct uftrace_pattern patt; - is_regex = strpbrk(name, REGEX_CHARS); - if (is_regex) { - if (regcomp(&re, name, REG_NOSUB | REG_EXTENDED)) { - pr_dbg("regex pattern failed: %s\n", name); - strv_free(&funcs); - return -1; - } - } + init_filter_pattern(ptype, &patt, name); for (i = 0; i < symtab->nr_sym; i++) { sym = &symtab->sym[i]; - if ((is_regex && regexec(&re, sym->name, 0, NULL, 0)) || - (!is_regex && strcmp(name, sym->name))) + if (!match_filter_pattern(&patt, sym->name)) continue; found = true; @@ -147,14 +138,14 @@ static int do_dynamic_update(struct symtabs *symtabs, char *patch_funcs) if (!found) stats.nomatch++; - if (is_regex) - regfree(&re); + free_filter_pattern(&patt); } - if (stats.failed || stats.skipped || stats.nomatch) + if (stats.failed || stats.skipped || stats.nomatch) { pr_out("%s cannot be patched dynamically\n", (stats.failed + stats.skipped + stats.nomatch) > 1 ? "some functions" : nopatched_name); + } strv_free(&funcs); return 0; @@ -184,7 +175,8 @@ static float calc_percent(int n, int total) return 100.0 * n / total; } -int mcount_dynamic_update(struct symtabs *symtabs, char *patch_funcs) +int mcount_dynamic_update(struct symtabs *symtabs, char *patch_funcs, + enum uftrace_pattern_type ptype) { int ret = 0; int success; @@ -194,7 +186,7 @@ int mcount_dynamic_update(struct symtabs *symtabs, char *patch_funcs) return -1; } - ret = do_dynamic_update(symtabs, patch_funcs); + ret = do_dynamic_update(symtabs, patch_funcs, ptype); success = stats.total - stats.failed - stats.skipped; pr_dbg("dynamic update stats:\n"); diff --git a/libmcount/event.c b/libmcount/event.c index 207270884..cd6f7f9b4 100644 --- a/libmcount/event.c +++ b/libmcount/event.c @@ -5,7 +5,6 @@ #include #include #include -#include /* This should be defined before #include "utils.h" */ #define PR_FMT "event" @@ -15,6 +14,7 @@ #include "libmcount/internal.h" #include "utils/utils.h" #include "utils/list.h" +#include "utils/filter.h" #define SDT_SECT ".note.stapsdt" #define SDT_NAME "stapsdt" @@ -22,8 +22,8 @@ struct event_spec { struct list_head list; - char *provider; - char *event; + struct uftrace_pattern provider; + struct uftrace_pattern event; }; struct stapsdt { @@ -126,9 +126,9 @@ static int search_sdt_event(struct dl_phdr_info *info, size_t sz, void *data) } list_for_each_entry(spec, spec_list, list) { - if (fnmatch(spec->provider, vendor, 0) != 0) + if (!match_filter_pattern(&spec->provider, vendor)) continue; - if (fnmatch(spec->event, event, 0) != 0) + if (!match_filter_pattern(&spec->event, event)) continue; break; } @@ -161,7 +161,8 @@ static int search_sdt_event(struct dl_phdr_info *info, size_t sz, void *data) goto out; } -int mcount_setup_events(char *dirname, char *event_str) +int mcount_setup_events(char *dirname, char *event_str, + enum uftrace_pattern_type ptype) { int ret = 0; FILE *fp; @@ -187,8 +188,9 @@ int mcount_setup_events(char *dirname, char *event_str) continue; es = xmalloc(sizeof(*es)); - es->provider = spec; - es->event = sep; + + init_filter_pattern(ptype, &es->provider, spec); + init_filter_pattern(ptype, &es->event, sep); list_add_tail(&es->list, &specs); } else { @@ -200,6 +202,9 @@ int mcount_setup_events(char *dirname, char *event_str) list_for_each_entry_safe(es, tmp, &specs, list) { list_del(&es->list); + + free_filter_pattern(&es->provider); + free_filter_pattern(&es->event); free(es); } strv_free(&strv); diff --git a/libmcount/internal.h b/libmcount/internal.h index 5902530e6..dfc23d92f 100644 --- a/libmcount/internal.h +++ b/libmcount/internal.h @@ -139,7 +139,7 @@ static inline bool mcount_should_stop(void) } #ifdef DISABLE_MCOUNT_FILTER -static inline void mcount_filter_init(void) {} +static inline void mcount_filter_init(enum uftrace_pattern_type ptype) {} static inline void mcount_filter_setup(struct mcount_thread_data *mtdp) {} static inline void mcount_filter_release(struct mcount_thread_data *mtdp) {} #endif /* DISABLE_MCOUNT_FILTER */ @@ -321,7 +321,8 @@ struct mcount_dynamic_info { void *arch; }; -int mcount_dynamic_update(struct symtabs *symtabs, char *patch_funcs); +int mcount_dynamic_update(struct symtabs *symtabs, char *patch_funcs, + enum uftrace_pattern_type ptype); /* these should be implemented for each architecture */ int mcount_setup_trampoline(struct mcount_dynamic_info *adi); @@ -339,7 +340,8 @@ struct mcount_event_info { struct list_head list; }; -int mcount_setup_events(char *dirname, char *event_str); +int mcount_setup_events(char *dirname, char *event_str, + enum uftrace_pattern_type ptype); struct mcount_event_info * mcount_lookup_event(unsigned long addr); int mcount_save_event(struct mcount_event_info *mei); void mcount_finish_events(void); diff --git a/libmcount/mcount.c b/libmcount/mcount.c index 47861c14b..0b4f2431e 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -101,7 +101,7 @@ static void prepare_pmu_trigger(struct rb_root *root) } } -static void mcount_filter_init(void) +static void mcount_filter_init(enum uftrace_pattern_type ptype) { char *filter_str = getenv("UFTRACE_FILTER"); char *trigger_str = getenv("UFTRACE_TRIGGER"); @@ -118,17 +118,19 @@ static void mcount_filter_init(void) setup_auto_args(); uftrace_setup_filter(filter_str, &symtabs, &mcount_triggers, - &mcount_filter_mode, false); + &mcount_filter_mode, false, ptype); uftrace_setup_trigger(trigger_str, &symtabs, &mcount_triggers, - &mcount_filter_mode, false); - uftrace_setup_argument(argument_str, &symtabs, &mcount_triggers, false); - uftrace_setup_retval(retval_str, &symtabs, &mcount_triggers, false); + &mcount_filter_mode, false, ptype); + uftrace_setup_argument(argument_str, &symtabs, &mcount_triggers, + false, ptype); + uftrace_setup_retval(retval_str, &symtabs, &mcount_triggers, + false, ptype); if (autoargs_str) { uftrace_setup_argument(get_auto_argspec_str(), &symtabs, - &mcount_triggers, true); + &mcount_triggers, true, ptype); uftrace_setup_retval(get_auto_retspec_str(), &symtabs, - &mcount_triggers, true); + &mcount_triggers, true, ptype); } if (getenv("UFTRACE_DEPTH")) @@ -1164,8 +1166,10 @@ static void mcount_startup(void) char *patch_str; char *event_str; char *dirname; + char *pattern_str; struct stat statbuf; bool nest_libcall; + enum uftrace_pattern_type patt_type = PATT_REGEX; if (!(mcount_global_flags & MCOUNT_GFL_SETUP) || mtd.recursion_guard) return; @@ -1191,6 +1195,7 @@ static void mcount_startup(void) event_str = getenv("UFTRACE_EVENT"); script_str = getenv("UFTRACE_SCRIPT"); nest_libcall = !!getenv("UFTRACE_NEST_LIBCALL"); + pattern_str = getenv("UFTRACE_PATTERN"); page_size_in_kb = getpagesize() / KB; @@ -1251,7 +1256,10 @@ static void mcount_startup(void) set_kernel_base(&symtabs, mcount_session_name()); load_symtabs(&symtabs, NULL, mcount_exename); - mcount_filter_init(); + if (pattern_str) + patt_type = parse_filter_pattern(pattern_str); + + mcount_filter_init(patt_type); if (maxstack_str) mcount_rstack_max = strtol(maxstack_str, NULL, 0); @@ -1260,10 +1268,10 @@ static void mcount_startup(void) mcount_threshold = strtoull(threshold_str, NULL, 0); if (patch_str) - mcount_dynamic_update(&symtabs, patch_str); + mcount_dynamic_update(&symtabs, patch_str, patt_type); if (event_str) - mcount_setup_events(dirname, event_str); + mcount_setup_events(dirname, event_str, patt_type); if (plthook_str) mcount_setup_plthook(mcount_exename, nest_libcall); @@ -1277,7 +1285,7 @@ static void mcount_startup(void) /* initialize script binding */ if (SCRIPT_ENABLED && script_str) - if (script_init(script_str) < 0) + if (script_init(script_str, patt_type) < 0) script_str = NULL; compiler_barrier(); diff --git a/libmcount/wrap.c b/libmcount/wrap.c index cb816c584..d3c5d1720 100644 --- a/libmcount/wrap.c +++ b/libmcount/wrap.c @@ -147,7 +147,7 @@ static char ** collect_uftrace_envp(void) ENV(COLOR), ENV(THRESHOLD), ENV(DEMANGLE), ENV(PLTHOOK), ENV(PATCH), ENV(EVENT), ENV(SCRIPT), ENV(NEST_LIBCALL), ENV(DEBUG_DOMAIN), ENV(LIST_EVENT), ENV(DIR), - ENV(KERNEL_PID_UPDATE), + ENV(KERNEL_PID_UPDATE), ENV(PATTERN), /* not uftrace-specific, but necessary to run */ "LD_PRELOAD", "LD_LIBRARY_PATH", }; diff --git a/tests/t097_dump_basic.py b/tests/t097_dump_basic.py index c78f066c9..094523d92 100644 --- a/tests/t097_dump_basic.py +++ b/tests/t097_dump_basic.py @@ -14,7 +14,7 @@ def __init__(self): uftrace file header: endian = 1 (little) uftrace file header: class = 2 (64 bit) uftrace file header: features = 0x363 (PLTHOOK | TASK_SESSION | SYM_REL_ADDR | MAX_STACK | PERF_EVENT | AUTO_ARGS) -uftrace file header: info = 0xbff +uftrace file header: info = 0x1bff reading 5231.dat 58348.873430946 5231: [entry] __monstartup(4004d0) depth: 0 diff --git a/tests/t098_dump_tid.py b/tests/t098_dump_tid.py index 9d6222f68..10417052a 100644 --- a/tests/t098_dump_tid.py +++ b/tests/t098_dump_tid.py @@ -14,7 +14,7 @@ def __init__(self): uftrace file header: endian = 1 (little) uftrace file header: class = 2 (64 bit) uftrace file header: features = 0x363 (PLTHOOK | TASK_SESSION | SYM_REL_ADDR | MAX_STACK | PERF_EVENT | AUTO_ARGS) -uftrace file header: info = 0xbff +uftrace file header: info = 0x1bff reading 5186.dat 58071.916834908 5186: [entry] main(400590) depth: 0 diff --git a/tests/t099_dump_filter.py b/tests/t099_dump_filter.py index 12f488a16..2061c2f2b 100644 --- a/tests/t099_dump_filter.py +++ b/tests/t099_dump_filter.py @@ -14,7 +14,7 @@ def __init__(self): uftrace file header: endian = 1 (little) uftrace file header: class = 2 (64 bit) uftrace file header: features = 0x363 (PLTHOOK | TASK_SESSION | SYM_REL_ADDR | MAX_STACK | PERF_EVENT | AUTO_ARGS) -uftrace file header: info = 0xbff +uftrace file header: info = 0x1bff reading 5231.dat 58348.873444506 5231: [entry] main(400512) depth: 0 diff --git a/tests/t100_dump_depth.py b/tests/t100_dump_depth.py index ab81d8a13..0550f6560 100644 --- a/tests/t100_dump_depth.py +++ b/tests/t100_dump_depth.py @@ -14,7 +14,7 @@ def __init__(self): uftrace file header: endian = 1 (little) uftrace file header: class = 2 (64 bit) uftrace file header: features = 0x363 (PLTHOOK | TASK_SESSION | SYM_REL_ADDR | MAX_STACK | PERF_EVENT | AUTO_ARGS) -uftrace file header: info = 0xbff +uftrace file header: info = 0x1bff reading 5231.dat 58348.873444506 5231: [entry] main(400512) depth: 0 diff --git a/tests/t147_event_sdt.py b/tests/t147_event_sdt.py index 32d232e53..2f9c0737f 100644 --- a/tests/t147_event_sdt.py +++ b/tests/t147_event_sdt.py @@ -16,4 +16,4 @@ def __init__(self): """) def runcmd(self): - return '%s -E uftrace:* %s' % (TestBase.uftrace_cmd, 't-' + self.name) + return '%s -E uftrace:* --match glob %s' % (TestBase.uftrace_cmd, 't-' + self.name) diff --git a/tests/t148_event_kernel.py b/tests/t148_event_kernel.py index c20313027..65cd3f373 100644 --- a/tests/t148_event_kernel.py +++ b/tests/t148_event_kernel.py @@ -56,6 +56,6 @@ def sort(self, output, ignored=''): return '\n'.join(result) def runcmd(self): - arg = '-E sched_switch@kernel' + arg = '-E sched:sched_switch@kernel' name = 't-' + self.name return '%s %s %s' % (TestBase.uftrace_cmd, arg, name) diff --git a/tests/t197_filter_glob.py b/tests/t197_filter_glob.py new file mode 100644 index 000000000..f8a5553c3 --- /dev/null +++ b/tests/t197_filter_glob.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +from runtest import TestBase + +class TestCase(TestBase): + def __init__(self): + TestBase.__init__(self, 'allocfree', """ +# DURATION TID FUNCTION + [11583] | alloc1() { + [11583] | alloc2() { + [11583] | alloc3() { + [11583] | alloc4() { + [11583] | alloc5() { + 1.873 us [11583] | malloc(); + 2.909 us [11583] | } /* alloc5 */ + 3.652 us [11583] | } /* alloc4 */ + 4.239 us [11583] | } /* alloc3 */ + 5.016 us [11583] | } /* alloc2 */ + 104.119 us [11583] | } /* alloc1 */ +""", sort='simple') + + def runcmd(self): + return '%s -F "alloc*" %s' % (TestBase.uftrace_cmd, 't-allocfree') diff --git a/uftrace.c b/uftrace.c index 35657dfc3..06035b10c 100644 --- a/uftrace.c +++ b/uftrace.c @@ -95,6 +95,7 @@ enum options { OPT_record, OPT_auto_args, OPT_libname, + OPT_match_type, }; static struct argp_option uftrace_options[] = { @@ -166,6 +167,7 @@ static struct argp_option uftrace_options[] = { { "record", OPT_record, 0, 0, "Record a new trace data before running command" }, { "auto-args", OPT_auto_args, 0, 0, "Show arguments and return value of known functions" }, { "libname", OPT_libname, 0, 0, "Show libname name with symbol name" }, + { "match", OPT_match_type, "TYPE", 0, "Support pattern match: regex, glob (default: regex)" }, { "help", 'h', 0, 0, "Give this help list" }, { 0 } }; @@ -713,6 +715,14 @@ static error_t parse_option(int key, char *arg, struct argp_state *state) opts->libname = true; break; + case OPT_match_type: + opts->patt_type = parse_filter_pattern(arg); + if (opts->patt_type == PATT_NONE) { + pr_use("invalid match pattern: %s (ignoring...)\n", arg); + opts->patt_type = PATT_REGEX; + } + break; + case ARGP_KEY_ARG: if (state->arg_num) { /* @@ -942,6 +952,7 @@ int main(int argc, char *argv[]) .fields = NULL, .sort_column = 2, .event_skip_out = true, + .patt_type = PATT_REGEX, }; struct argp argp = { .options = uftrace_options, diff --git a/uftrace.h b/uftrace.h index 66a89abfe..b9f10bae5 100644 --- a/uftrace.h +++ b/uftrace.h @@ -11,7 +11,7 @@ #include "utils/list.h" #include "utils/symbol.h" #include "utils/perf.h" - +#include "utils/filter.h" #define UFTRACE_MAGIC_LEN 8 #define UFTRACE_MAGIC_STR "Ftrace!" @@ -84,6 +84,7 @@ enum uftrace_info_bits { LOADINFO, ARG_SPEC, RECORD_DATE, + PATTERN_TYPE, }; struct uftrace_info { @@ -120,6 +121,7 @@ struct uftrace_info { float load1; float load5; float load15; + enum uftrace_pattern_type patt_type; }; enum { @@ -244,6 +246,7 @@ struct opts { bool auto_args; bool libname; struct uftrace_time_range range; + enum uftrace_pattern_type patt_type; }; static inline bool opts_has_filter(struct opts *opts) diff --git a/utils/data-file.c b/utils/data-file.c index 6c2ef972d..a2473907d 100644 --- a/utils/data-file.c +++ b/utils/data-file.c @@ -467,11 +467,11 @@ int open_data_file(struct opts *opts, struct ftrace_file_handle *handle) } setup_fstack_args(handle->info.argspec, handle->info.retspec, - handle, false); + handle, false, handle->info.patt_type); if (handle->info.auto_args_enabled) { setup_fstack_args(handle->info.autoarg, handle->info.autoret, - handle, true); + handle, true, handle->info.patt_type); } } diff --git a/utils/filter.c b/utils/filter.c index 3cfde041b..4479d7ba3 100644 --- a/utils/filter.c +++ b/utils/filter.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -296,13 +297,88 @@ static int add_filter(struct rb_root *root, struct uftrace_filter *filter, return 1; } +struct { + enum uftrace_pattern_type type; + const char *name; +} filter_patterns[] = { + { PATT_SIMPLE, "simple" }, + { PATT_REGEX, "regex" }, + { PATT_GLOB, "glob" }, +}; + +void init_filter_pattern(enum uftrace_pattern_type type, + struct uftrace_pattern *p, char *str) +{ + if (strpbrk(str, REGEX_CHARS) == NULL) + type = PATT_SIMPLE; + + p->type = type; + p->patt = xstrdup(str); + + if (type == PATT_REGEX) { + if (regcomp(&p->re, str, REG_NOSUB | REG_EXTENDED)) { + pr_dbg("regex pattern failed: %s\n", str); + p->type = PATT_SIMPLE; + } + } +} + +bool match_filter_pattern(struct uftrace_pattern *p, char *name) +{ + switch (p->type) { + case PATT_SIMPLE: + return !strcmp(p->patt, name); + case PATT_REGEX: + return !regexec(&p->re, name, 0, NULL, 0); + case PATT_GLOB: + return !fnmatch(p->patt, name, 0); + default: + return false; + } +} + +void free_filter_pattern(struct uftrace_pattern *p) +{ + free(p->patt); + p->patt = NULL; + + if (p->type == PATT_REGEX) + regfree(&p->re); + + p->type = PATT_NONE; +} + +enum uftrace_pattern_type parse_filter_pattern(const char *str) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(filter_patterns); i++) { + if (!strcmp(str, filter_patterns[i].name)) + return filter_patterns[i].type; + } + + return PATT_NONE; +} + +const char * get_filter_pattern(enum uftrace_pattern_type ptype) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(filter_patterns); i++) { + if (filter_patterns[i].type == ptype) + return filter_patterns[i].name; + } + + return "none"; +} + static int add_exact_filter(struct rb_root *root, struct symtab *symtab, - char *filter_str, struct uftrace_trigger *tr) + struct uftrace_pattern *p, struct uftrace_trigger *tr) { struct uftrace_filter filter; struct sym *sym; - sym = find_symname(symtab, filter_str); + sym = find_symname(symtab, p->patt); if (sym == NULL) return 0; @@ -313,24 +389,19 @@ static int add_exact_filter(struct rb_root *root, struct symtab *symtab, return add_filter(root, &filter, tr, true); } -static int add_regex_filter(struct rb_root *root, struct symtab *symtab, - char *filter_str, struct uftrace_trigger *tr) +static int add_pattern_filter(struct rb_root *root, struct symtab *symtab, + struct uftrace_pattern *patt, + struct uftrace_trigger *tr) { struct uftrace_filter filter; struct sym *sym; - regex_t re; unsigned i; int ret = 0; - if (regcomp(&re, filter_str, REG_NOSUB | REG_EXTENDED)) { - pr_dbg("regex pattern failed: %s\n", filter_str); - return 0; - } - for (i = 0; i < symtab->nr_sym; i++) { sym = &symtab->sym[i]; - if (regexec(&re, sym->name, 0, NULL, 0)) + if (!match_filter_pattern(patt, sym->name)) continue; filter.name = sym->name; @@ -340,7 +411,6 @@ static int add_regex_filter(struct rb_root *root, struct symtab *symtab, ret += add_filter(root, &filter, tr, false); } - regfree(&re); return ret; } @@ -811,18 +881,19 @@ int setup_trigger_action(char *str, struct uftrace_trigger *tr, } static int add_trigger_entry(struct rb_root *root, struct symtab *symtab, - char *name, bool is_regex, + struct uftrace_pattern *patt, struct uftrace_trigger *tr) { - if (is_regex) - return add_regex_filter(root, symtab, name, tr); + if (patt->type == PATT_SIMPLE) + return add_exact_filter(root, symtab, patt, tr); else - return add_exact_filter(root, symtab, name, tr); + return add_pattern_filter(root, symtab, patt, tr); } static void setup_trigger(char *filter_str, struct symtabs *symtabs, struct rb_root *root, unsigned long flags, - enum filter_mode *fmode, bool allow_kernel) + enum filter_mode *fmode, bool allow_kernel, + enum uftrace_pattern_type ptype) { struct strv filters = STRV_INIT; char *name; @@ -843,7 +914,9 @@ static void setup_trigger(char *filter_str, struct symtabs *symtabs, char *module = NULL; struct uftrace_arg_spec *arg; struct uftrace_mmap *map; - bool is_regex; + struct uftrace_pattern patt = { + .type = PATT_NONE, + }; if (setup_trigger_action(name, &tr, &module, flags) < 0) goto next; @@ -861,7 +934,7 @@ static void setup_trigger(char *filter_str, struct symtabs *symtabs, tr.fmode = FILTER_MODE_IN; } - is_regex = strpbrk(name, REGEX_CHARS); + init_filter_pattern(ptype, &patt, name); if (module) { map = find_map_by_name(symtabs, module); @@ -877,37 +950,37 @@ static void setup_trigger(char *filter_str, struct symtabs *symtabs, if (!strncmp(module, basename(symtabs->filename), strlen(module))) { ret += add_trigger_entry(root, &symtabs->symtab, - name, is_regex, &tr); + &patt, &tr); ret += add_trigger_entry(root, &symtabs->dsymtab, - name, is_regex, &tr); + &patt, &tr); } else if (!strcasecmp(module, "PLT")) { ret = add_trigger_entry(root, &symtabs->dsymtab, - name, is_regex, &tr); + &patt, &tr); } else if (!strcasecmp(module, "kernel")) { ret = add_trigger_entry(root, get_kernel_symtab(), - name, is_regex, &tr); + &patt, &tr); } else { ret = add_trigger_entry(root, &map->symtab, - name, is_regex, &tr); + &patt, &tr); } free(module); } else { /* check main executable's symtab first */ - ret += add_trigger_entry(root, &symtabs->symtab, name, - is_regex, &tr); - ret += add_trigger_entry(root, &symtabs->dsymtab, name, - is_regex, &tr); + ret += add_trigger_entry(root, &symtabs->symtab, + &patt, &tr); + ret += add_trigger_entry(root, &symtabs->dsymtab, + &patt, &tr); /* and then find all module's symtabs */ map = symtabs->maps; while (map) { ret += add_trigger_entry(root, &map->symtab, - name, is_regex, &tr); + &patt, &tr); map = map->next; } } @@ -919,6 +992,8 @@ static void setup_trigger(char *filter_str, struct symtabs *symtabs, *fmode = FILTER_MODE_OUT; } next: + free_filter_pattern(&patt); + while (!list_empty(&args)) { arg = list_first_entry(&args, typeof(*arg), list); list_del(&arg->list); @@ -940,13 +1015,15 @@ static void setup_trigger(char *filter_str, struct symtabs *symtabs, * @root - root of resulting rbtree * @mode - filter mode: opt-in (-F) or opt-out (-N) * @allow_kernel - allow filtering on kernel function + * @patt_type - filter match pattern (regex or glob) */ void uftrace_setup_filter(char *filter_str, struct symtabs *symtabs, struct rb_root *root, enum filter_mode *mode, - bool allow_kernel) + bool allow_kernel, + enum uftrace_pattern_type patt_type) { setup_trigger(filter_str, symtabs, root, TRIGGER_FL_FILTER, mode, - allow_kernel); + allow_kernel, patt_type); } /** @@ -956,12 +1033,15 @@ void uftrace_setup_filter(char *filter_str, struct symtabs *symtabs, * @root - root of resulting rbtree * @mode - filter mode: opt-in (-F) or opt-out (-N) * @allow_kernel - allow filtering on kernel function + * @patt_type - filter match pattern (regex or glob) */ void uftrace_setup_trigger(char *trigger_str, struct symtabs *symtabs, struct rb_root *root, enum filter_mode *mode, - bool allow_kernel) + bool allow_kernel, + enum uftrace_pattern_type patt_type) { - setup_trigger(trigger_str, symtabs, root, 0, mode, allow_kernel); + setup_trigger(trigger_str, symtabs, root, 0, mode, allow_kernel, + patt_type); } /** @@ -970,16 +1050,18 @@ void uftrace_setup_trigger(char *trigger_str, struct symtabs *symtabs, * @symtabs - symbol tables to find symbol address * @root - root of resulting rbtree * @auto_args - whether current arguments are auto-spec + * @patt_type - filter match pattern (regex or glob) */ void uftrace_setup_argument(char *args_str, struct symtabs *symtabs, - struct rb_root *root, bool auto_args) + struct rb_root *root, bool auto_args, + enum uftrace_pattern_type patt_type) { unsigned long flags = TRIGGER_FL_ARGUMENT; if (auto_args) flags |= TRIGGER_FL_AUTO_ARGS; - setup_trigger(args_str, symtabs, root, flags, NULL, false); + setup_trigger(args_str, symtabs, root, flags, NULL, false, patt_type); } /** @@ -988,16 +1070,18 @@ void uftrace_setup_argument(char *args_str, struct symtabs *symtabs, * @symtabs - symbol tables to find symbol address * @root - root of resulting rbtree * @auto_args - whether current retvals are auto-spec + * @patt_type - filter match pattern (regex or glob) */ void uftrace_setup_retval(char *retval_str, struct symtabs *symtabs, - struct rb_root *root, bool auto_args) + struct rb_root *root, bool auto_args, + enum uftrace_pattern_type patt_type) { unsigned long flags = TRIGGER_FL_RETVAL; if (auto_args) flags |= TRIGGER_FL_AUTO_ARGS; - setup_trigger(retval_str, symtabs, root, flags, NULL, false); + setup_trigger(retval_str, symtabs, root, flags, NULL, false, patt_type); } /** @@ -1091,7 +1175,7 @@ static void filter_test_load_symtabs(struct symtabs *stabs) stabs->loaded = true; } -TEST_CASE(filter_setup_exact) +TEST_CASE(filter_setup_simple) { struct symtabs stabs = { .loaded = false, @@ -1099,11 +1183,12 @@ TEST_CASE(filter_setup_exact) struct rb_root root = RB_ROOT; struct rb_node *node; struct uftrace_filter *filter; + enum uftrace_pattern_type ptype = PATT_SIMPLE; filter_test_load_symtabs(&stabs); /* test1: simple method */ - uftrace_setup_filter("foo::bar", &stabs, &root, NULL, false); + uftrace_setup_filter("foo::bar", &stabs, &root, NULL, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); node = rb_first(&root); @@ -1116,7 +1201,7 @@ TEST_CASE(filter_setup_exact) TEST_EQ(RB_EMPTY_ROOT(&root), true); /* test2: destructor */ - uftrace_setup_filter("foo::~foo", &stabs, &root, NULL, false); + uftrace_setup_filter("foo::~foo", &stabs, &root, NULL, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); node = rb_first(&root); @@ -1129,7 +1214,7 @@ TEST_CASE(filter_setup_exact) TEST_EQ(RB_EMPTY_ROOT(&root), true); /* test3: unknown symbol */ - uftrace_setup_filter("invalid_name", &stabs, &root, NULL, false); + uftrace_setup_filter("invalid_name", &stabs, &root, NULL, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), true); return TEST_OK; @@ -1143,10 +1228,56 @@ TEST_CASE(filter_setup_regex) struct rb_root root = RB_ROOT; struct rb_node *node; struct uftrace_filter *filter; + enum uftrace_pattern_type ptype = PATT_REGEX; + + filter_test_load_symtabs(&stabs); + + uftrace_setup_filter("^foo::b", &stabs, &root, NULL, false, ptype); + TEST_EQ(RB_EMPTY_ROOT(&root), false); + + node = rb_first(&root); + filter = rb_entry(node, struct uftrace_filter, node); + TEST_STREQ(filter->name, "foo::bar"); + TEST_EQ(filter->start, 0x2000UL); + TEST_EQ(filter->end, 0x2000UL + 0x1000UL); + + node = rb_next(node); + filter = rb_entry(node, struct uftrace_filter, node); + TEST_STREQ(filter->name, "foo::baz1"); + TEST_EQ(filter->start, 0x3000UL); + TEST_EQ(filter->end, 0x3000UL + 0x1000UL); + + node = rb_next(node); + filter = rb_entry(node, struct uftrace_filter, node); + TEST_STREQ(filter->name, "foo::baz2"); + TEST_EQ(filter->start, 0x4000UL); + TEST_EQ(filter->end, 0x4000UL + 0x1000UL); + + node = rb_next(node); + filter = rb_entry(node, struct uftrace_filter, node); + TEST_STREQ(filter->name, "foo::baz3"); + TEST_EQ(filter->start, 0x5000UL); + TEST_EQ(filter->end, 0x5000UL + 0x1000UL); + + uftrace_cleanup_filter(&root); + TEST_EQ(RB_EMPTY_ROOT(&root), true); + + return TEST_OK; +} + +TEST_CASE(filter_setup_glob) +{ + struct symtabs stabs = { + .loaded = false, + };; + struct rb_root root = RB_ROOT; + struct rb_node *node; + struct uftrace_filter *filter; + enum uftrace_pattern_type ptype = PATT_GLOB; filter_test_load_symtabs(&stabs); - uftrace_setup_filter("foo::b.*", &stabs, &root, NULL, false); + uftrace_setup_filter("foo::b*", &stabs, &root, NULL, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); node = rb_first(&root); @@ -1188,14 +1319,15 @@ TEST_CASE(filter_setup_notrace) struct rb_node *node; struct uftrace_filter *filter; enum filter_mode fmode; + enum uftrace_pattern_type ptype = PATT_GLOB; filter_test_load_symtabs(&stabs); - uftrace_setup_filter("foo::.*", &stabs, &root, &fmode, false); + uftrace_setup_filter("foo::*", &stabs, &root, &fmode, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); TEST_EQ(fmode, FILTER_MODE_IN); - uftrace_setup_filter("!foo::foo", &stabs, &root, &fmode, false); + uftrace_setup_filter("!foo::foo", &stabs, &root, &fmode, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); TEST_EQ(fmode, FILTER_MODE_IN); /* overall filter mode doesn't change */ @@ -1227,10 +1359,11 @@ TEST_CASE(filter_match) struct uftrace_filter *filter; enum filter_mode fmode; struct uftrace_trigger tr; + enum uftrace_pattern_type ptype = PATT_REGEX; filter_test_load_symtabs(&stabs); - uftrace_setup_filter("foo::foo", &stabs, &root, &fmode, false); + uftrace_setup_filter("foo::foo", &stabs, &root, &fmode, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); TEST_EQ(fmode, FILTER_MODE_IN); @@ -1267,10 +1400,12 @@ TEST_CASE(trigger_setup_actions) struct rb_node *node; struct uftrace_filter *filter; struct uftrace_trigger tr; + enum uftrace_pattern_type ptype = PATT_REGEX; filter_test_load_symtabs(&stabs); - uftrace_setup_trigger("foo::bar@depth=2", &stabs, &root, NULL, false); + uftrace_setup_trigger("foo::bar@depth=2", &stabs, &root, + NULL, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); memset(&tr, 0, sizeof(tr)); @@ -1278,17 +1413,20 @@ TEST_CASE(trigger_setup_actions) TEST_EQ(tr.flags, TRIGGER_FL_DEPTH); TEST_EQ(tr.depth, 2); - uftrace_setup_trigger("foo::bar@backtrace", &stabs, &root, NULL, false); + uftrace_setup_trigger("foo::bar@backtrace", &stabs, &root, + NULL, false, ptype); memset(&tr, 0, sizeof(tr)); TEST_NE(uftrace_match_filter(0x2500, &root, &tr), NULL); TEST_EQ(tr.flags, TRIGGER_FL_DEPTH | TRIGGER_FL_BACKTRACE); - uftrace_setup_trigger("foo::baz1@traceon", &stabs, &root, NULL, false); + uftrace_setup_trigger("foo::baz1@traceon", &stabs, &root, + NULL, false, ptype); memset(&tr, 0, sizeof(tr)); TEST_NE(uftrace_match_filter(0x3000, &root, &tr), NULL); TEST_EQ(tr.flags, TRIGGER_FL_TRACE_ON); - uftrace_setup_trigger("foo::baz3@trace_off,depth=1", &stabs, &root, NULL, false); + uftrace_setup_trigger("foo::baz3@trace_off,depth=1", &stabs, &root, + NULL, false, ptype); memset(&tr, 0, sizeof(tr)); TEST_NE(uftrace_match_filter(0x5000, &root, &tr), NULL); TEST_EQ(tr.flags, TRIGGER_FL_TRACE_OFF | TRIGGER_FL_DEPTH); @@ -1310,10 +1448,12 @@ TEST_CASE(trigger_setup_filters) struct uftrace_filter *filter; struct uftrace_trigger tr; enum filter_mode fmode; + enum uftrace_pattern_type ptype = PATT_REGEX; filter_test_load_symtabs(&stabs); - uftrace_setup_trigger("foo::bar@depth=2,notrace", &stabs, &root, &fmode, false); + uftrace_setup_trigger("foo::bar@depth=2,notrace", &stabs, &root, + &fmode, false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); TEST_EQ(fmode, FILTER_MODE_OUT); @@ -1323,7 +1463,8 @@ TEST_CASE(trigger_setup_filters) TEST_EQ(tr.depth, 2); TEST_EQ(tr.fmode, FILTER_MODE_OUT); - uftrace_setup_filter("foo::baz1", &stabs, &root, &fmode, false); + uftrace_setup_filter("foo::baz1", &stabs, &root, + &fmode, false, ptype); TEST_EQ(fmode, FILTER_MODE_IN); memset(&tr, 0, sizeof(tr)); @@ -1331,7 +1472,8 @@ TEST_CASE(trigger_setup_filters) TEST_EQ(tr.flags, TRIGGER_FL_FILTER); TEST_EQ(tr.fmode, FILTER_MODE_IN); - uftrace_setup_trigger("foo::baz2@notrace", &stabs, &root, &fmode, false); + uftrace_setup_trigger("foo::baz2@notrace", &stabs, &root, + &fmode, false, ptype); TEST_EQ(fmode, FILTER_MODE_IN); memset(&tr, 0, sizeof(tr)); @@ -1355,11 +1497,13 @@ TEST_CASE(trigger_setup_args) struct uftrace_filter *filter; struct uftrace_trigger tr; struct uftrace_arg_spec *spec; + enum uftrace_pattern_type ptype = PATT_REGEX; int count; filter_test_load_symtabs(&stabs); - uftrace_setup_argument("foo::bar@arg1", &stabs, &root, false); + uftrace_setup_argument("foo::bar@arg1", &stabs, &root, + false, ptype); TEST_EQ(RB_EMPTY_ROOT(&root), false); memset(&tr, 0, sizeof(tr)); @@ -1367,7 +1511,8 @@ TEST_CASE(trigger_setup_args) TEST_EQ(tr.flags, TRIGGER_FL_ARGUMENT); TEST_NE(tr.pargs, NULL); - uftrace_setup_trigger("foo::bar@arg2/s", &stabs, &root, NULL, false); + uftrace_setup_trigger("foo::bar@arg2/s", &stabs, &root, + NULL, false, ptype); memset(&tr, 0, sizeof(tr)); TEST_NE(uftrace_match_filter(0x2500, &root, &tr), NULL); TEST_EQ(tr.flags, TRIGGER_FL_ARGUMENT); @@ -1390,7 +1535,7 @@ TEST_CASE(trigger_setup_args) TEST_EQ(count, 2); uftrace_setup_argument("foo::baz1@arg1/i32,arg2/x64,fparg1/32,fparg2", - &stabs, &root, false); + &stabs, &root, false, ptype); memset(&tr, 0, sizeof(tr)); TEST_NE(uftrace_match_filter(0x3999, &root, &tr), NULL); TEST_EQ(tr.flags, TRIGGER_FL_ARGUMENT); @@ -1432,7 +1577,7 @@ TEST_CASE(trigger_setup_args) /* FIXME: this test will fail on non-x86 architecture */ uftrace_setup_trigger("foo::baz2@arg1/c,arg2/x32%rdi,arg3%stack+4,retval/f64", - &stabs, &root, NULL, false); + &stabs, &root, NULL, false, ptype); memset(&tr, 0, sizeof(tr)); TEST_NE(uftrace_match_filter(0x4000, &root, &tr), NULL); TEST_EQ(tr.flags, TRIGGER_FL_ARGUMENT | TRIGGER_FL_RETVAL); diff --git a/utils/filter.h b/utils/filter.h index 5c38690eb..b3f938642 100644 --- a/utils/filter.h +++ b/utils/filter.h @@ -2,6 +2,7 @@ #define UFTRACE_FILTER_H #include +#include #include "rbtree.h" #include "list.h" @@ -110,6 +111,19 @@ struct uftrace_filter { struct uftrace_trigger trigger; }; +enum uftrace_pattern_type { + PATT_NONE, + PATT_SIMPLE, + PATT_REGEX, + PATT_GLOB, +}; + +struct uftrace_pattern { + enum uftrace_pattern_type type; + char *patt; + regex_t re; +}; + /* please see man proc(5) for /proc/[pid]/statm */ struct uftrace_proc_statm { uint64_t vmsize; /* total program size in KB */ @@ -143,20 +157,29 @@ struct symtabs; void uftrace_setup_filter(char *filter_str, struct symtabs *symtabs, struct rb_root *root, enum filter_mode *mode, - bool allow_kernel); + bool allow_kernel, enum uftrace_pattern_type ptype); void uftrace_setup_trigger(char *trigger_str, struct symtabs *symtabs, struct rb_root *root, enum filter_mode *mode, - bool allow_kernel); + bool allow_kernel, enum uftrace_pattern_type ptype); void uftrace_setup_argument(char *trigger_str, struct symtabs *symtabs, - struct rb_root *root, bool auto_args); + struct rb_root *root, bool auto_args, + enum uftrace_pattern_type ptype); void uftrace_setup_retval(char *trigger_str, struct symtabs *symtabs, - struct rb_root *root, bool auto_args); + struct rb_root *root, bool auto_args, + enum uftrace_pattern_type ptype); struct uftrace_filter *uftrace_match_filter(uint64_t ip, struct rb_root *root, struct uftrace_trigger *tr); void uftrace_cleanup_filter(struct rb_root *root); void uftrace_print_filter(struct rb_root *root); +void init_filter_pattern(enum uftrace_pattern_type type, + struct uftrace_pattern *p, char *str); +bool match_filter_pattern(struct uftrace_pattern *p, char *name); +void free_filter_pattern(struct uftrace_pattern *p); +enum uftrace_pattern_type parse_filter_pattern(const char *str); +const char * get_filter_pattern(enum uftrace_pattern_type ptype); + char * uftrace_clear_kernel(char *filter_str); void setup_auto_args(void); diff --git a/utils/fstack.c b/utils/fstack.c index 8cb59f40b..f98ede453 100644 --- a/utils/fstack.c +++ b/utils/fstack.c @@ -207,21 +207,26 @@ static void setup_task_filter(char *tid_filter, struct ftrace_file_handle *handl free(filter_tids); } +struct filter_data { + char *str; + enum uftrace_pattern_type patt_type; +}; + static int setup_filters(struct uftrace_session *s, void *arg) { - char *filter_str = arg; + struct filter_data *filter = arg; - uftrace_setup_filter(filter_str, &s->symtabs, &s->filters, - &fstack_filter_mode, true); + uftrace_setup_filter(filter->str, &s->symtabs, &s->filters, + &fstack_filter_mode, true, filter->patt_type); return 0; } static int setup_trigger(struct uftrace_session *s, void *arg) { - char *trigger_str = arg; + struct filter_data *trigger = arg; - uftrace_setup_trigger(trigger_str, &s->symtabs, &s->filters, - &fstack_filter_mode, true); + uftrace_setup_trigger(trigger->str, &s->symtabs, &s->filters, + &fstack_filter_mode, true, trigger->patt_type); return 0; } @@ -242,6 +247,7 @@ static int count_filters(struct uftrace_session *s, void *arg) * @handle - handle for uftrace data * @filter_str - CSV of filter symbol names * @trigger_str - CSV of trigger definitions + * @patt_type - filter match pattern (regex or glob) * * This function sets up the symbol filters and triggers using following syntax: * filter_strs = filter | filter ";" filter_strs @@ -250,13 +256,18 @@ static int count_filters(struct uftrace_session *s, void *arg) * trigger_def = "depth=" NUM | "backtrace" */ static int setup_fstack_filters(struct ftrace_file_handle *handle, - char *filter_str, char *trigger_str) + char *filter_str, char *trigger_str, + enum uftrace_pattern_type patt_type) { int count = 0; struct uftrace_session_link *sessions = &handle->sessions; + struct filter_data data = { + .patt_type = patt_type, + }; if (filter_str) { - walk_sessions(sessions, setup_filters, filter_str); + data.str = filter_str; + walk_sessions(sessions, setup_filters, &data); walk_sessions(sessions, count_filters, &count); if (count == 0) @@ -268,7 +279,8 @@ static int setup_fstack_filters(struct ftrace_file_handle *handle, if (trigger_str) { int prev = count; - walk_sessions(sessions, setup_trigger, trigger_str); + data.str = trigger_str; + walk_sessions(sessions, setup_trigger, &data); walk_sessions(sessions, count_filters, &count); if (prev == count) @@ -298,7 +310,7 @@ static int build_fixup_filter(struct uftrace_session *s, void *arg) for (i = 0; i < ARRAY_SIZE(fixup_syms); i++) { uftrace_setup_trigger((char *)fixup_syms[i], &s->symtabs, - &s->fixups, NULL, false); + &s->fixups, NULL, false, PATT_SIMPLE); } return 0; } @@ -318,6 +330,7 @@ static void fstack_prepare_fixup(struct ftrace_file_handle *handle) struct spec_data { char *str; bool auto_args; + enum uftrace_pattern_type patt_type; }; static int build_arg_spec(struct uftrace_session *s, void *arg) @@ -326,7 +339,7 @@ static int build_arg_spec(struct uftrace_session *s, void *arg) if (spec->str) uftrace_setup_argument(spec->str, &s->symtabs, &s->filters, - spec->auto_args); + spec->auto_args, spec->patt_type); return 0; } @@ -337,7 +350,7 @@ static int build_ret_spec(struct uftrace_session *s, void *arg) if (spec->str) uftrace_setup_retval(spec->str, &s->symtabs, &s->filters, - spec->auto_args); + spec->auto_args, spec->patt_type); return 0; } @@ -347,16 +360,19 @@ static int build_ret_spec(struct uftrace_session *s, void *arg) * @argspec: spec string describes function arguments * @retspec: spec string describes function return values * @handle: handle for uftrace data - * @auto_args - whether current spec is auto-spec + * @auto_args: whether current spec is auto-spec + * @patt_type: filter match pattern (regex or glob) * * This functions sets up argument and return value information * provided by user at the time of recording. */ void setup_fstack_args(char *argspec, char *retspec, - struct ftrace_file_handle *handle, bool auto_args) + struct ftrace_file_handle *handle, bool auto_args, + enum uftrace_pattern_type patt_type) { struct spec_data spec = { .auto_args = auto_args, + .patt_type = patt_type, }; if (argspec == NULL && retspec == NULL && !auto_args) @@ -387,7 +403,8 @@ void setup_fstack_args(char *argspec, char *retspec, int fstack_setup_filters(struct opts *opts, struct ftrace_file_handle *handle) { if (opts->filter || opts->trigger) { - if (setup_fstack_filters(handle, opts->filter, opts->trigger) < 0) { + if (setup_fstack_filters(handle, opts->filter, opts->trigger, + opts->patt_type) < 0) { pr_use("failed to set filter or trigger: %s%s%s\n", opts->filter ?: "", (opts->filter && opts->trigger) ? " or " : "", diff --git a/utils/fstack.h b/utils/fstack.h index 731efd8dc..3d3cc1c94 100644 --- a/utils/fstack.h +++ b/utils/fstack.h @@ -5,10 +5,10 @@ #include #include -#include "../uftrace.h" +#include "uftrace.h" +#include "utils/filter.h" struct sym; -struct uftrace_trigger; enum fstack_flag { FSTACK_FL_FILTERED = (1U << 0), @@ -126,7 +126,8 @@ static inline bool is_kernel_record(struct ftrace_task_handle *task, } void setup_fstack_args(char *argspec, char *retspec, - struct ftrace_file_handle *handle, bool auto_args); + struct ftrace_file_handle *handle, bool auto_args, + enum uftrace_pattern_type patt_type); int fstack_setup_filters(struct opts *opts, struct ftrace_file_handle *handle); int fstack_entry(struct ftrace_task_handle *task, diff --git a/utils/kernel.c b/utils/kernel.c index d5e8bef54..0afe88738 100644 --- a/utils/kernel.c +++ b/utils/kernel.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -255,13 +254,44 @@ static int set_tracing_options(struct uftrace_kernel_writer *kernel) return 0; } +static void add_single_filter(struct list_head *head, char *name) +{ + struct kfilter *kfilter; + + kfilter = xmalloc(sizeof(*kfilter) + strlen(name) + 1); + strcpy(kfilter->name, name); + list_add(&kfilter->list, head); +} + +static void add_pattern_filter(struct list_head *head, + struct uftrace_pattern *patt) +{ + char *filename; + FILE *fp; + char buf[1024]; + + filename = get_tracing_file("available_filter_functions"); + fp = fopen(filename, "r"); + if (fp == NULL) + pr_err("failed to open 'tracing/available_filter_functions' file"); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* it's ok to have a trailing '\n' */ + if (match_filter_pattern(patt, buf)) + add_single_filter(head, buf); + } + + fclose(fp); + put_tracing_file(filename); +} + static void build_kernel_filter(struct uftrace_kernel_writer *kernel, char *filter_str, + enum uftrace_pattern_type ptype, struct list_head *filters, struct list_head *notrace) { struct list_head *head; - struct kfilter *kfilter; struct strv strv = STRV_INIT; char *pos, *name; int j; @@ -272,6 +302,8 @@ static void build_kernel_filter(struct uftrace_kernel_writer *kernel, strv_split(&strv, filter_str, ";"); strv_for_each(&strv, name, j) { + struct uftrace_pattern patt; + pos = strstr(name, "@kernel"); if (pos == NULL) continue; @@ -284,9 +316,14 @@ static void build_kernel_filter(struct uftrace_kernel_writer *kernel, else head = filters; - kfilter = xmalloc(sizeof(*kfilter) + strlen(name) + 1); - strcpy(kfilter->name, name); - list_add(&kfilter->list, head); + init_filter_pattern(ptype, &patt, name); + + if (patt.type == PATT_SIMPLE) + add_single_filter(head, name); + else + add_pattern_filter(head, &patt); + + free_filter_pattern(&patt); } strv_free(&strv); } @@ -320,7 +357,8 @@ static void add_single_event(struct list_head *events, char *name) list_add_tail(&kevent->list, events); } -static void add_glob_event(struct list_head *events, char *name) +static void add_pattern_event(struct list_head *events, + struct uftrace_pattern *patt) { char *filename; FILE *fp; @@ -333,7 +371,7 @@ static void add_glob_event(struct list_head *events, char *name) while (fgets(buf, sizeof(buf), fp) != NULL) { /* it's ok to have a trailing '\n' */ - if (fnmatch(name, buf, 0) == 0) + if (match_filter_pattern(patt, buf)) add_single_event(events, buf); } @@ -342,7 +380,8 @@ static void add_glob_event(struct list_head *events, char *name) } static void build_kernel_event(struct uftrace_kernel_writer *kernel, - char *event_str, struct list_head *events) + char *event_str, enum uftrace_pattern_type ptype, + struct list_head *events) { struct strv strv = STRV_INIT; char *pos, *name; @@ -354,15 +393,21 @@ static void build_kernel_event(struct uftrace_kernel_writer *kernel, strv_split(&strv, event_str, ";"); strv_for_each(&strv, name, j) { + struct uftrace_pattern patt; + pos = strstr(name, "@kernel"); if (pos == NULL) continue; *pos = '\0'; - if (strpbrk(name, "*?[]{}")) - add_glob_event(events, name); - else + init_filter_pattern(ptype, &patt, name); + + if (patt.type == PATT_SIMPLE) add_single_event(events, name); + else + add_pattern_event(events, &patt); + + free_filter_pattern(&patt); } strv_free(&strv); } @@ -481,11 +526,12 @@ int setup_kernel_tracing(struct uftrace_kernel_writer *kernel, struct opts *opts INIT_LIST_HEAD(&kernel->nopatch); INIT_LIST_HEAD(&kernel->events); - build_kernel_filter(kernel, opts->filter, + build_kernel_filter(kernel, opts->filter, opts->patt_type, &kernel->filters, &kernel->notrace); - build_kernel_filter(kernel, opts->patch, + build_kernel_filter(kernel, opts->patch, opts->patt_type, &kernel->patches, &kernel->nopatch); - build_kernel_event(kernel, opts->event, &kernel->events); + build_kernel_event(kernel, opts->event, opts->patt_type, + &kernel->events); if (opts->kernel) kernel->tracer = KERNEL_GRAPH_TRACER; @@ -506,6 +552,7 @@ int setup_kernel_tracing(struct uftrace_kernel_writer *kernel, struct opts *opts * If an user wants to see them, give --kernel-full option. */ build_kernel_filter(kernel, "!sys_clock_gettime@kernel", + opts->patt_type, &kernel->filters, &kernel->notrace); } diff --git a/utils/script-python.c b/utils/script-python.c index 3f272a898..fd713aef8 100644 --- a/utils/script-python.c +++ b/utils/script-python.c @@ -533,7 +533,8 @@ int python_atfork_prepare(void) return 0; } -int script_init_for_python(char *py_pathname) +int script_init_for_python(char *py_pathname, + enum uftrace_pattern_type ptype) { pr_dbg("%s(\"%s\")\n", __func__, py_pathname); @@ -607,7 +608,7 @@ int script_init_for_python(char *py_pathname) for (i = 0; i < len; i++) { PyObject *func = __PyList_GetItem(filter_list, i); - script_add_filter(__PyString_AsString(func)); + script_add_filter(__PyString_AsString(func), ptype); } } diff --git a/utils/script-python.h b/utils/script-python.h index 20884df29..f17048d17 100644 --- a/utils/script-python.h +++ b/utils/script-python.h @@ -8,12 +8,15 @@ #ifndef UFTRACE_SCRIPT_PYTHON_H #define UFTRACE_SCRIPT_PYTHON_H +#include "utils/filter.h" + #ifdef HAVE_LIBPYTHON2 #include #define SCRIPT_ENABLED 1 -int script_init_for_python(char *py_pathname); +int script_init_for_python(char *py_pathname, + enum uftrace_pattern_type ptype); void script_finish_for_python(void); @@ -22,7 +25,8 @@ void script_finish_for_python(void); /* Do nothing if libpython2.7.so is not installed. */ #define SCRIPT_ENABLED 0 -static inline int script_init_for_python(char *py_pathname) +static inline int script_init_for_python(char *py_pathname, + enum uftrace_pattern_type ptype) { return -1; } diff --git a/utils/script.c b/utils/script.c index 6fbf01699..ef12021de 100644 --- a/utils/script.c +++ b/utils/script.c @@ -11,7 +11,6 @@ #define PR_DOMAIN DBG_SCRIPT #include -#include #include "utils/script.h" #include "utils/filter.h" #include "utils/list.h" @@ -32,9 +31,7 @@ script_atfork_prepare_t script_atfork_prepare; struct script_filter_item { struct list_head list; - char *name; - bool is_regex; - regex_t re; + struct uftrace_pattern patt; }; static LIST_HEAD(filters); @@ -53,7 +50,7 @@ static enum script_type_t get_script_type(const char *str) return SCRIPT_UNKNOWN; } -void script_add_filter(char *func) +void script_add_filter(char *func, enum uftrace_pattern_type ptype) { struct script_filter_item *item; @@ -62,13 +59,10 @@ void script_add_filter(char *func) item = xmalloc(sizeof(*item)); - item->name = xstrdup(func); - item->is_regex = strpbrk(func, REGEX_CHARS); - if (item->is_regex) - regcomp(&item->re, item->name, REG_EXTENDED); + init_filter_pattern(ptype, &item->patt, func); - pr_dbg2("add script filter: %s (%s)\n", item->name, - item->is_regex ? "regex" : "simple"); + pr_dbg2("add script filter: %s (%s)\n", func, + get_filter_pattern(item->patt.type)); list_add_tail(&item->list, &filters); } @@ -83,11 +77,7 @@ int script_match_filter(char *func) return 1; list_for_each_entry(item, &filters, list) { - if (item->is_regex) { - if (!regexec(&item->re, func, 0, NULL, 0)) - return 1; - } - else if (!strcmp(item->name, func)) + if (match_filter_pattern(&item->patt, func)) return 1; } return 0; @@ -98,14 +88,12 @@ void script_finish_filter(void) struct script_filter_item *item, *tmp; list_for_each_entry_safe(item, tmp, &filters, list) { - if (item->is_regex) - regfree(&item->re); - free(item->name); + free_filter_pattern(&item->patt); free(item); } } -int script_init(char *script_pathname) +int script_init(char *script_pathname, enum uftrace_pattern_type ptype) { pr_dbg2("%s(\"%s\")\n", __func__, script_pathname); if (access(script_pathname, F_OK) < 0) { @@ -116,7 +104,7 @@ int script_init(char *script_pathname) script_lang = get_script_type(script_pathname); switch (script_lang) { case SCRIPT_PYTHON: - if (script_init_for_python(script_pathname) < 0) { + if (script_init_for_python(script_pathname, ptype) < 0) { pr_dbg("failed to init python scripting\n"); script_pathname = NULL; } diff --git a/utils/script.h b/utils/script.h index 9905840db..8ca980aba 100644 --- a/utils/script.h +++ b/utils/script.h @@ -44,10 +44,10 @@ extern script_uftrace_exit_t script_uftrace_exit; extern script_uftrace_end_t script_uftrace_end; extern script_atfork_prepare_t script_atfork_prepare; -int script_init(char *script_pathname); +int script_init(char *script_pathname, enum uftrace_pattern_type ptype); void script_finish(void); -void script_add_filter(char *func); +void script_add_filter(char *func, enum uftrace_pattern_type ptype); int script_match_filter(char *func); void script_finish_filter(void);