Commit 26d33022 authored by Jiri Olsa's avatar Jiri Olsa Committed by Arnaldo Carvalho de Melo

perf tools: Support for DWARF mode callchain

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: default avatarJiri Olsa <jolsa@redhat.com>
Original-patch-by: default avatarFrederic 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 71ad0f5e
...@@ -31,6 +31,15 @@ ...@@ -31,6 +31,15 @@
#include <sched.h> #include <sched.h>
#include <sys/mman.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 { enum write_mode_t {
WRITE_FORCE, WRITE_FORCE,
WRITE_APPEND WRITE_APPEND
...@@ -732,6 +741,106 @@ parse_branch_stack(const struct option *opt, const char *str, int unset) ...@@ -732,6 +741,106 @@ parse_branch_stack(const struct option *opt, const char *str, int unset)
return ret; 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[] = { static const char * const record_usage[] = {
"perf record [<options>] [<command>]", "perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]", "perf record [<options>] -- <command> [<options>]",
...@@ -803,8 +912,9 @@ const struct option record_options[] = { ...@@ -803,8 +912,9 @@ const struct option record_options[] = {
"number of mmap data pages"), "number of mmap data pages"),
OPT_BOOLEAN(0, "group", &record.opts.group, OPT_BOOLEAN(0, "group", &record.opts.group,
"put the counters into a counter group"), "put the counters into a counter group"),
OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph, OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]",
"do call-graph (stack chain/backtrace) recording"), callchain_help, &parse_callchain_opt,
"fp"),
OPT_INCR('v', "verbose", &verbose, OPT_INCR('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"), "be more verbose (show counter open errors, etc)"),
OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
......
...@@ -209,9 +209,15 @@ void pthread__unblock_sigwinch(void); ...@@ -209,9 +209,15 @@ void pthread__unblock_sigwinch(void);
#include "util/target.h" #include "util/target.h"
enum perf_call_graph_mode {
CALLCHAIN_NONE,
CALLCHAIN_FP,
CALLCHAIN_DWARF
};
struct perf_record_opts { struct perf_record_opts {
struct perf_target target; struct perf_target target;
bool call_graph; int call_graph;
bool group; bool group;
bool inherit_stat; bool inherit_stat;
bool no_delay; bool no_delay;
...@@ -230,6 +236,7 @@ struct perf_record_opts { ...@@ -230,6 +236,7 @@ struct perf_record_opts {
u64 branch_stack; u64 branch_stack;
u64 default_interval; u64 default_interval;
u64 user_interval; u64 user_interval;
u16 stack_dump_size;
}; };
#endif #endif
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include "thread_map.h" #include "thread_map.h"
#include "target.h" #include "target.h"
#include "../../../include/linux/hw_breakpoint.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 FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) #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, ...@@ -368,9 +370,18 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
attr->mmap_data = track; attr->mmap_data = track;
} }
if (opts->call_graph) if (opts->call_graph) {
attr->sample_type |= PERF_SAMPLE_CALLCHAIN; 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)) if (perf_target__has_cpu(&opts->target))
attr->sample_type |= PERF_SAMPLE_CPU; attr->sample_type |= PERF_SAMPLE_CPU;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment