perf ftrace: Add option for function filtering
authorNamhyung Kim <namhyung@kernel.org>
Sun, 18 Jun 2017 14:23:01 +0000 (23:23 +0900)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 20 Jun 2017 01:05:53 +0000 (22:05 -0300)
The -T/--trace-funcs and -N/--notrace-funcs options are to specify
functions to enable/disable tracing dynamically.

The -G/--graph-funcs and -g/--nograph-funcs options are to set filters
for function graph tracer.

For example, to trace fault handling functions only:

  $ sudo perf ftrace -T *fault hello
   0)               |  __do_page_fault() {
   0)               |    handle_mm_fault() {
   0)   2.117 us    |      __handle_mm_fault();
   0)   3.627 us    |    }
   0)   7.811 us    |  }
   0)               |  __do_page_fault() {
   0)               |    handle_mm_fault() {
   0)   2.014 us    |      __handle_mm_fault();
   0)   2.424 us    |    }
   0)   2.951 us    |  }
   ...

To trace all functions executed in __do_page_fault:

  $ sudo perf ftrace -G __do_page_fault hello
   2)               |  __do_page_fault() {
   3)   0.060 us    |    down_read_trylock();
   3)               |    find_vma() {
   3)   0.075 us    |      vmacache_find();
   3)   0.053 us    |      vmacache_update();
   3)   1.246 us    |    }
   3)               |    handle_mm_fault() {
   3)   0.063 us    |      __rcu_read_lock();
   3)   0.056 us    |      mem_cgroup_from_task();
   3)   0.057 us    |      __rcu_read_unlock();
   3)               |      __handle_mm_fault() {
   3)               |        filemap_map_pages() {
   3)   0.058 us    |          __rcu_read_lock();
   3)               |          alloc_set_pte() {
   ...

But don't want to show details in handle_mm_fault:

  $ sudo perf ftrace -G __do_page_fault -g handle_mm_fault hello
   3)               |  __do_page_fault() {
   3)   0.049 us    |    down_read_trylock();
   3)               |    find_vma() {
   3)   0.048 us    |      vmacache_find();
   3)   0.041 us    |      vmacache_update();
   3)   0.680 us    |    }
   3)   0.036 us    |    up_read();
   3)   4.547 us    |  } /* __do_page_fault */
   ...

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: kernel-team@lge.com
Link: http://lkml.kernel.org/r/20170618142302.25390-3-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-ftrace.txt
tools/perf/builtin-ftrace.c

index 6e6a8b22c8594f42e9727c81a8e4b4adc8301e43..78d6126ca4856df21015a11a3183742ccb39920a 100644 (file)
@@ -48,6 +48,36 @@ OPTIONS
        Ranges of CPUs are specified with -: 0-2.
        Default is to trace on all online CPUs.
 
+-T::
+--trace-funcs=::
+       Only trace functions given by the argument.  Multiple functions
+       can be given by using this option more than once.  The function
+       argument also can be a glob pattern.  It will be passed to
+       'set_ftrace_filter' in tracefs.
+
+-N::
+--notrace-funcs=::
+       Do not trace functions given by the argument.  Like -T option,
+       this can be used more than once to specify multiple functions
+       (or glob patterns).  It will be passed to 'set_ftrace_notrace'
+       in tracefs.
+
+-G::
+--graph-funcs=::
+       Set graph filter on the given function (or a glob pattern).
+       This is useful for the function_graph tracer only and enables
+       tracing for functions executed from the given function.
+       This can be used more than once to specify multiple functions.
+       It will be passed to 'set_graph_function' in tracefs.
+
+-g::
+--nograph-funcs=::
+       Set graph notrace filter on the given function (or a glob pattern).
+       Like -G option, this is useful for the function_graph tracer only
+       and disables tracing for function executed from the given function.
+       This can be used more than once to specify multiple functions.
+       It will be passed to 'set_graph_notrace' in tracefs.
+
 
 SEE ALSO
 --------
index 982b98ee639edd3f0b199782d89f097cb253b8bc..3285375ce3c234fee480e3e1ce3675fd23d8666e 100644 (file)
 #define DEFAULT_TRACER  "function_graph"
 
 struct perf_ftrace {
-       struct perf_evlist *evlist;
-       struct target target;
-       const char *tracer;
+       struct perf_evlist      *evlist;
+       struct target           target;
+       const char              *tracer;
+       struct list_head        filters;
+       struct list_head        notrace;
+       struct list_head        graph_funcs;
+       struct list_head        nograph_funcs;
+};
+
+struct filter_entry {
+       struct list_head        list;
+       char                    name[];
 };
 
 static bool done;
@@ -104,6 +113,7 @@ static int append_tracing_file(const char *name, const char *val)
 }
 
 static int reset_tracing_cpu(void);
+static void reset_tracing_filters(void);
 
 static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
 {
@@ -119,6 +129,7 @@ static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
        if (reset_tracing_cpu() < 0)
                return -1;
 
+       reset_tracing_filters();
        return 0;
 }
 
@@ -184,6 +195,48 @@ static int reset_tracing_cpu(void)
        return ret;
 }
 
+static int __set_tracing_filter(const char *filter_file, struct list_head *funcs)
+{
+       struct filter_entry *pos;
+
+       list_for_each_entry(pos, funcs, list) {
+               if (append_tracing_file(filter_file, pos->name) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int set_tracing_filters(struct perf_ftrace *ftrace)
+{
+       int ret;
+
+       ret = __set_tracing_filter("set_ftrace_filter", &ftrace->filters);
+       if (ret < 0)
+               return ret;
+
+       ret = __set_tracing_filter("set_ftrace_notrace", &ftrace->notrace);
+       if (ret < 0)
+               return ret;
+
+       ret = __set_tracing_filter("set_graph_function", &ftrace->graph_funcs);
+       if (ret < 0)
+               return ret;
+
+       /* old kernels do not have this filter */
+       __set_tracing_filter("set_graph_notrace", &ftrace->nograph_funcs);
+
+       return ret;
+}
+
+static void reset_tracing_filters(void)
+{
+       write_tracing_file("set_ftrace_filter", " ");
+       write_tracing_file("set_ftrace_notrace", " ");
+       write_tracing_file("set_graph_function", " ");
+       write_tracing_file("set_graph_notrace", " ");
+}
+
 static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
 {
        char *trace_file;
@@ -226,6 +279,11 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
                goto out_reset;
        }
 
+       if (set_tracing_filters(ftrace) < 0) {
+               pr_err("failed to set tracing filters\n");
+               goto out_reset;
+       }
+
        if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
                pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
                goto out_reset;
@@ -310,6 +368,32 @@ static int perf_ftrace_config(const char *var, const char *value, void *cb)
        return -1;
 }
 
+static int parse_filter_func(const struct option *opt, const char *str,
+                            int unset __maybe_unused)
+{
+       struct list_head *head = opt->value;
+       struct filter_entry *entry;
+
+       entry = malloc(sizeof(*entry) + strlen(str) + 1);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       strcpy(entry->name, str);
+       list_add_tail(&entry->list, head);
+
+       return 0;
+}
+
+static void delete_filter_func(struct list_head *head)
+{
+       struct filter_entry *pos, *tmp;
+
+       list_for_each_entry_safe(pos, tmp, head, list) {
+               list_del(&pos->list);
+               free(pos);
+       }
+}
+
 int cmd_ftrace(int argc, const char **argv)
 {
        int ret;
@@ -333,9 +417,22 @@ int cmd_ftrace(int argc, const char **argv)
                    "system-wide collection from all CPUs"),
        OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu",
                    "list of cpus to monitor"),
+       OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func",
+                    "trace given functions only", parse_filter_func),
+       OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func",
+                    "do not trace given functions", parse_filter_func),
+       OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func",
+                    "Set graph filter on given functions", parse_filter_func),
+       OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func",
+                    "Set nograph filter on given functions", parse_filter_func),
        OPT_END()
        };
 
+       INIT_LIST_HEAD(&ftrace.filters);
+       INIT_LIST_HEAD(&ftrace.notrace);
+       INIT_LIST_HEAD(&ftrace.graph_funcs);
+       INIT_LIST_HEAD(&ftrace.nograph_funcs);
+
        ret = perf_config(perf_ftrace_config, &ftrace);
        if (ret < 0)
                return -1;
@@ -351,12 +448,14 @@ int cmd_ftrace(int argc, const char **argv)
 
                target__strerror(&ftrace.target, ret, errbuf, 512);
                pr_err("%s\n", errbuf);
-               return -EINVAL;
+               goto out_delete_filters;
        }
 
        ftrace.evlist = perf_evlist__new();
-       if (ftrace.evlist == NULL)
-               return -ENOMEM;
+       if (ftrace.evlist == NULL) {
+               ret = -ENOMEM;
+               goto out_delete_filters;
+       }
 
        ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target);
        if (ret < 0)
@@ -367,5 +466,11 @@ int cmd_ftrace(int argc, const char **argv)
 out_delete_evlist:
        perf_evlist__delete(ftrace.evlist);
 
+out_delete_filters:
+       delete_filter_func(&ftrace.filters);
+       delete_filter_func(&ftrace.notrace);
+       delete_filter_func(&ftrace.graph_funcs);
+       delete_filter_func(&ftrace.nograph_funcs);
+
        return ret;
 }