perf tools: Support for DWARF mode callchain
authorJiri Olsa <jolsa@redhat.com>
Tue, 7 Aug 2012 13:20:47 +0000 (15:20 +0200)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 11 Aug 2012 18:07:18 +0000 (15:07 -0300)
This patch enables perf to use the DWARF unwind code.

It extends the perf record '-g' option with following arguments:
  'fp'           - provides framepointer based user
                   stack backtrace
  'dwarf[,size]' - provides DWARF (libunwind) based user stack
                   backtrace. The size specifies the size of the
                   user stack dump. If omitted it is 8192 by default.

If libunwind is found during the perf build, then the 'dwarf' argument
becomes available for record command. The 'fp' stays as default option
in any case.

Examples: (perf compiled with libunwind)

   perf record -g dwarf ls
      - provides dwarf unwind with 8192 as stack dump size

   perf record -g dwarf,4096 ls
      - provides dwarf unwind with 4096 as stack dump size

   perf record -g -- ls
   perf record -g fp ls
      - provides frame pointer unwind

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Original-patch-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Arun Sharma <asharma@fb.com>
Cc: Benjamin Redelings <benjamin.redelings@nescent.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
Cc: Ulrich Drepper <drepper@gmail.com>
Link: http://lkml.kernel.org/r/1344345647-11536-13-git-send-email-jolsa@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-record.c
tools/perf/perf.h
tools/perf/util/evsel.c

index 4db6e1ba54e30bf780d990a8fb1d7b203e89707f..22dd05d3680ce4e81281e00938cc3a256451ec51 100644 (file)
 #include <sched.h>
 #include <sys/mman.h>
 
+#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
+
+#ifdef NO_LIBUNWIND_SUPPORT
+static char callchain_help[] = CALLCHAIN_HELP "[fp]";
+#else
+static unsigned long default_stack_dump_size = 8192;
+static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
+#endif
+
 enum write_mode_t {
        WRITE_FORCE,
        WRITE_APPEND
@@ -732,6 +741,106 @@ error:
        return ret;
 }
 
+#ifndef NO_LIBUNWIND_SUPPORT
+static int get_stack_size(char *str, unsigned long *_size)
+{
+       char *endptr;
+       unsigned long size;
+       unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
+
+       size = strtoul(str, &endptr, 0);
+
+       do {
+               if (*endptr)
+                       break;
+
+               size = round_up(size, sizeof(u64));
+               if (!size || size > max_size)
+                       break;
+
+               *_size = size;
+               return 0;
+
+       } while (0);
+
+       pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
+              max_size, str);
+       return -1;
+}
+#endif /* !NO_LIBUNWIND_SUPPORT */
+
+static int
+parse_callchain_opt(const struct option *opt __used, const char *arg,
+                   int unset)
+{
+       struct perf_record *rec = (struct perf_record *)opt->value;
+       char *tok, *name, *saveptr = NULL;
+       char *buf;
+       int ret = -1;
+
+       /* --no-call-graph */
+       if (unset)
+               return 0;
+
+       /* We specified default option if none is provided. */
+       BUG_ON(!arg);
+
+       /* We need buffer that we know we can write to. */
+       buf = malloc(strlen(arg) + 1);
+       if (!buf)
+               return -ENOMEM;
+
+       strcpy(buf, arg);
+
+       tok = strtok_r((char *)buf, ",", &saveptr);
+       name = tok ? : (char *)buf;
+
+       do {
+               /* Framepointer style */
+               if (!strncmp(name, "fp", sizeof("fp"))) {
+                       if (!strtok_r(NULL, ",", &saveptr)) {
+                               rec->opts.call_graph = CALLCHAIN_FP;
+                               ret = 0;
+                       } else
+                               pr_err("callchain: No more arguments "
+                                      "needed for -g fp\n");
+                       break;
+
+#ifndef NO_LIBUNWIND_SUPPORT
+               /* Dwarf style */
+               } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
+                       ret = 0;
+                       rec->opts.call_graph = CALLCHAIN_DWARF;
+                       rec->opts.stack_dump_size = default_stack_dump_size;
+
+                       tok = strtok_r(NULL, ",", &saveptr);
+                       if (tok) {
+                               unsigned long size = 0;
+
+                               ret = get_stack_size(tok, &size);
+                               rec->opts.stack_dump_size = size;
+                       }
+
+                       if (!ret)
+                               pr_debug("callchain: stack dump size %d\n",
+                                        rec->opts.stack_dump_size);
+#endif /* !NO_LIBUNWIND_SUPPORT */
+               } else {
+                       pr_err("callchain: Unknown -g option "
+                              "value: %s\n", arg);
+                       break;
+               }
+
+       } while (0);
+
+       free(buf);
+
+       if (!ret)
+               pr_debug("callchain: type %d\n", rec->opts.call_graph);
+
+       return ret;
+}
+
 static const char * const record_usage[] = {
        "perf record [<options>] [<command>]",
        "perf record [<options>] -- <command> [<options>]",
@@ -803,8 +912,9 @@ const struct option record_options[] = {
                     "number of mmap data pages"),
        OPT_BOOLEAN(0, "group", &record.opts.group,
                    "put the counters into a counter group"),
-       OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph,
-                   "do call-graph (stack chain/backtrace) recording"),
+       OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]",
+                            callchain_help, &parse_callchain_opt,
+                            "fp"),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
        OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
index f960ccb2edc6f38f8a7b7a3f0f1b740cbd351c2b..87f4ec6d1f367d76791855c58f25b432d0b769f2 100644 (file)
@@ -209,9 +209,15 @@ void pthread__unblock_sigwinch(void);
 
 #include "util/target.h"
 
+enum perf_call_graph_mode {
+       CALLCHAIN_NONE,
+       CALLCHAIN_FP,
+       CALLCHAIN_DWARF
+};
+
 struct perf_record_opts {
        struct perf_target target;
-       bool         call_graph;
+       int          call_graph;
        bool         group;
        bool         inherit_stat;
        bool         no_delay;
@@ -230,6 +236,7 @@ struct perf_record_opts {
        u64          branch_stack;
        u64          default_interval;
        u64          user_interval;
+       u16          stack_dump_size;
 };
 
 #endif
index a2da682db8198d3c437608b8c2a5a4b54b284718..9c54e8fc2dfceecd2f27a34e0b1204322d0c901d 100644 (file)
@@ -17,6 +17,8 @@
 #include "thread_map.h"
 #include "target.h"
 #include "../../../include/linux/hw_breakpoint.h"
+#include "../../include/linux/perf_event.h"
+#include "perf_regs.h"
 
 #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
 #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
@@ -368,9 +370,18 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
                attr->mmap_data = track;
        }
 
-       if (opts->call_graph)
+       if (opts->call_graph) {
                attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
 
+               if (opts->call_graph == CALLCHAIN_DWARF) {
+                       attr->sample_type |= PERF_SAMPLE_REGS_USER |
+                                            PERF_SAMPLE_STACK_USER;
+                       attr->sample_regs_user = PERF_REGS_MASK;
+                       attr->sample_stack_user = opts->stack_dump_size;
+                       attr->exclude_callchain_user = 1;
+               }
+       }
+
        if (perf_target__has_cpu(&opts->target))
                attr->sample_type       |= PERF_SAMPLE_CPU;