aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Olsa <jolsa@redhat.com>2012-08-07 09:20:47 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2012-08-11 14:07:18 -0400
commit26d330226b9cf6208daae9b0b3697980c8fb51d8 (patch)
tree344329470da50b84cbff987a3547da279d4d9c05
parent71ad0f5e4e361c8bca864c7d09d14b64af6bc2fc (diff)
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: 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>
-rw-r--r--tools/perf/builtin-record.c114
-rw-r--r--tools/perf/perf.h9
-rw-r--r--tools/perf/util/evsel.c13
3 files changed, 132 insertions, 4 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4db6e1ba54e3..22dd05d3680c 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -31,6 +31,15 @@
31#include <sched.h> 31#include <sched.h>
32#include <sys/mman.h> 32#include <sys/mman.h>
33 33
34#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
35
36#ifdef NO_LIBUNWIND_SUPPORT
37static char callchain_help[] = CALLCHAIN_HELP "[fp]";
38#else
39static unsigned long default_stack_dump_size = 8192;
40static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
41#endif
42
34enum write_mode_t { 43enum write_mode_t {
35 WRITE_FORCE, 44 WRITE_FORCE,
36 WRITE_APPEND 45 WRITE_APPEND
@@ -732,6 +741,106 @@ error:
732 return ret; 741 return ret;
733} 742}
734 743
744#ifndef NO_LIBUNWIND_SUPPORT
745static int get_stack_size(char *str, unsigned long *_size)
746{
747 char *endptr;
748 unsigned long size;
749 unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
750
751 size = strtoul(str, &endptr, 0);
752
753 do {
754 if (*endptr)
755 break;
756
757 size = round_up(size, sizeof(u64));
758 if (!size || size > max_size)
759 break;
760
761 *_size = size;
762 return 0;
763
764 } while (0);
765
766 pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
767 max_size, str);
768 return -1;
769}
770#endif /* !NO_LIBUNWIND_SUPPORT */
771
772static int
773parse_callchain_opt(const struct option *opt __used, const char *arg,
774 int unset)
775{
776 struct perf_record *rec = (struct perf_record *)opt->value;
777 char *tok, *name, *saveptr = NULL;
778 char *buf;
779 int ret = -1;
780
781 /* --no-call-graph */
782 if (unset)
783 return 0;
784
785 /* We specified default option if none is provided. */
786 BUG_ON(!arg);
787
788 /* We need buffer that we know we can write to. */
789 buf = malloc(strlen(arg) + 1);
790 if (!buf)
791 return -ENOMEM;
792
793 strcpy(buf, arg);
794
795 tok = strtok_r((char *)buf, ",", &saveptr);
796 name = tok ? : (char *)buf;
797
798 do {
799 /* Framepointer style */
800 if (!strncmp(name, "fp", sizeof("fp"))) {
801 if (!strtok_r(NULL, ",", &saveptr)) {
802 rec->opts.call_graph = CALLCHAIN_FP;
803 ret = 0;
804 } else
805 pr_err("callchain: No more arguments "
806 "needed for -g fp\n");
807 break;
808
809#ifndef NO_LIBUNWIND_SUPPORT
810 /* Dwarf style */
811 } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
812 ret = 0;
813 rec->opts.call_graph = CALLCHAIN_DWARF;
814 rec->opts.stack_dump_size = default_stack_dump_size;
815
816 tok = strtok_r(NULL, ",", &saveptr);
817 if (tok) {
818 unsigned long size = 0;
819
820 ret = get_stack_size(tok, &size);
821 rec->opts.stack_dump_size = size;
822 }
823
824 if (!ret)
825 pr_debug("callchain: stack dump size %d\n",
826 rec->opts.stack_dump_size);
827#endif /* !NO_LIBUNWIND_SUPPORT */
828 } else {
829 pr_err("callchain: Unknown -g option "
830 "value: %s\n", arg);
831 break;
832 }
833
834 } while (0);
835
836 free(buf);
837
838 if (!ret)
839 pr_debug("callchain: type %d\n", rec->opts.call_graph);
840
841 return ret;
842}
843
735static const char * const record_usage[] = { 844static const char * const record_usage[] = {
736 "perf record [<options>] [<command>]", 845 "perf record [<options>] [<command>]",
737 "perf record [<options>] -- <command> [<options>]", 846 "perf record [<options>] -- <command> [<options>]",
@@ -803,8 +912,9 @@ const struct option record_options[] = {
803 "number of mmap data pages"), 912 "number of mmap data pages"),
804 OPT_BOOLEAN(0, "group", &record.opts.group, 913 OPT_BOOLEAN(0, "group", &record.opts.group,
805 "put the counters into a counter group"), 914 "put the counters into a counter group"),
806 OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph, 915 OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]",
807 "do call-graph (stack chain/backtrace) recording"), 916 callchain_help, &parse_callchain_opt,
917 "fp"),
808 OPT_INCR('v', "verbose", &verbose, 918 OPT_INCR('v', "verbose", &verbose,
809 "be more verbose (show counter open errors, etc)"), 919 "be more verbose (show counter open errors, etc)"),
810 OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), 920 OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index f960ccb2edc6..87f4ec6d1f36 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -209,9 +209,15 @@ void pthread__unblock_sigwinch(void);
209 209
210#include "util/target.h" 210#include "util/target.h"
211 211
212enum perf_call_graph_mode {
213 CALLCHAIN_NONE,
214 CALLCHAIN_FP,
215 CALLCHAIN_DWARF
216};
217
212struct perf_record_opts { 218struct perf_record_opts {
213 struct perf_target target; 219 struct perf_target target;
214 bool call_graph; 220 int call_graph;
215 bool group; 221 bool group;
216 bool inherit_stat; 222 bool inherit_stat;
217 bool no_delay; 223 bool no_delay;
@@ -230,6 +236,7 @@ struct perf_record_opts {
230 u64 branch_stack; 236 u64 branch_stack;
231 u64 default_interval; 237 u64 default_interval;
232 u64 user_interval; 238 u64 user_interval;
239 u16 stack_dump_size;
233}; 240};
234 241
235#endif 242#endif
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index a2da682db819..9c54e8fc2dfc 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -17,6 +17,8 @@
17#include "thread_map.h" 17#include "thread_map.h"
18#include "target.h" 18#include "target.h"
19#include "../../../include/linux/hw_breakpoint.h" 19#include "../../../include/linux/hw_breakpoint.h"
20#include "../../include/linux/perf_event.h"
21#include "perf_regs.h"
20 22
21#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) 23#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
22#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) 24#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 attr->mmap_data = track; 370 attr->mmap_data = track;
369 } 371 }
370 372
371 if (opts->call_graph) 373 if (opts->call_graph) {
372 attr->sample_type |= PERF_SAMPLE_CALLCHAIN; 374 attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
373 375
376 if (opts->call_graph == CALLCHAIN_DWARF) {
377 attr->sample_type |= PERF_SAMPLE_REGS_USER |
378 PERF_SAMPLE_STACK_USER;
379 attr->sample_regs_user = PERF_REGS_MASK;
380 attr->sample_stack_user = opts->stack_dump_size;
381 attr->exclude_callchain_user = 1;
382 }
383 }
384
374 if (perf_target__has_cpu(&opts->target)) 385 if (perf_target__has_cpu(&opts->target))
375 attr->sample_type |= PERF_SAMPLE_CPU; 386 attr->sample_type |= PERF_SAMPLE_CPU;
376 387