aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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