diff options
-rw-r--r-- | tools/perf/builtin-record.c | 114 | ||||
-rw-r--r-- | tools/perf/perf.h | 9 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 13 |
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 | ||
37 | static char callchain_help[] = CALLCHAIN_HELP "[fp]"; | ||
38 | #else | ||
39 | static unsigned long default_stack_dump_size = 8192; | ||
40 | static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; | ||
41 | #endif | ||
42 | |||
34 | enum write_mode_t { | 43 | enum 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 | ||
745 | static 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 | |||
772 | static int | ||
773 | parse_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 | |||
735 | static const char * const record_usage[] = { | 844 | static 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 | ||
212 | enum perf_call_graph_mode { | ||
213 | CALLCHAIN_NONE, | ||
214 | CALLCHAIN_FP, | ||
215 | CALLCHAIN_DWARF | ||
216 | }; | ||
217 | |||
212 | struct perf_record_opts { | 218 | struct 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 | ||