diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-04-16 05:09:57 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-04-16 05:09:57 -0400 |
commit | 9243ae5b28d02dc7d71a4f00c981ef6feaede3f1 (patch) | |
tree | c6cc841dcb256d0ea77b0ab56b05fe0b344e4aab /tools | |
parent | 0b22cd276cec21107d9d69453fa58abba73e71df (diff) | |
parent | f3e459d16a8493b617ccf2a940330279679e0291 (diff) |
Merge tag 'perf-core-for-mingo-20160415' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements from Arnaldo Carvalho de Melo:
User visible changes:
- Wire the callchain unwinding "max-stack" now to 'perf script --max-stack',
allowing to limit the depth of callchains, possibly reducing processing
time (Arnaldo Carvalho de Melo)
- Ditto for 'perf trace --max-stack' (Arnaldo Carvalho de Melo)
- Introduce a --min-stack filter for 'perf trace', to show syscalls that
had a userspace callchain leading to it at least min-stack deep (Arnaldo Carvalho de Melo)
- Make 'perf trace' work with multiple threads and the --duration filter,
i.e. do not print the start of an interrupted syscall followed by ...
to print interrupts from other threads, as we need to wait the sys_exit
syscall tracepoint to calculate the duration, duh. (Arnaldo Carvalho de Melo)
System wide --duration now works as expected:
[root@jouet ~]# trace --duration 100
152.393 (145.147 ms): Timer/24358 futex(uaddr: 0x7f5ed98e56cc, op: WAIT_BITSET|PRIV|CLKRT, val: 7055125, utime: 0x7f5ecdbfec30, val3: 4294967295) = -1 ETIMEDOUT Connection timed out
152.438 (145.040 ms): firefox/24321 poll(ufds: 0x7f5ec388b460, nfds: 6, timeout_msecs: 4294967295) = 1
358.580 (158.279 ms): Xorg/2025 select(n: 512, inp: 0x83a8e0, tvp: 0x7ffdcbb63610) = 0 Timeout
358.687 (148.285 ms): gnome-terminal/2711 poll(ufds: 0x55b7e6811ad0, nfds: 15, timeout_msecs: 249) = 1
370.150 (169.569 ms): gnome-shell/2287 poll(ufds: 0x55e623d65490, nfds: 86, timeout_msecs: 4294967295) = 1
- Now 'perf trace's --max-stack and --min-stack will automatically set
"--call-graph dwarf", if --call-graph is not present on the command line:
[root@jouet ~]# perf trace -e nanosleep --max-stack 3 usleep 1
0.299 ( 0.057 ms): usleep/29658 nanosleep(rqtp: 0x7fff80f3b230) = 0
__nanosleep+0x10 (/usr/lib64/libc-2.22.so)
usleep+0x34 (/usr/lib64/libc-2.22.so)
main+0x1eb (/usr/bin/usleep)
[root@jouet ~]#
- Bump 'perf trace --mmap-pages' for root when using callchains and not
specifying --mmap-pages explicitely (Arnaldo Carvalho de Melo)
Build fixes:
- The python binding object had missing symbols, to some refactoring
to fix that (Arnaldo Carvalho de Melo)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
24 files changed, 565 insertions, 443 deletions
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 22ef3933342a..4fc44c75263f 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -259,6 +259,16 @@ include::itrace.txt[] | |||
259 | --full-source-path:: | 259 | --full-source-path:: |
260 | Show the full path for source files for srcline output. | 260 | Show the full path for source files for srcline output. |
261 | 261 | ||
262 | --max-stack:: | ||
263 | Set the stack depth limit when parsing the callchain, anything | ||
264 | beyond the specified depth will be ignored. This is a trade-off | ||
265 | between information loss and faster processing especially for | ||
266 | workloads that can have a very long callchain stack. | ||
267 | Note that when using the --itrace option the synthesized callchain size | ||
268 | will override this value if the synthesized callchain size is bigger. | ||
269 | |||
270 | Default: 127 | ||
271 | |||
262 | --ns:: | 272 | --ns:: |
263 | Use 9 decimal places when displaying time (i.e. show the nanoseconds) | 273 | Use 9 decimal places when displaying time (i.e. show the nanoseconds) |
264 | 274 | ||
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 1bbcf305d233..c075c002eaa4 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -123,12 +123,35 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
123 | man pages for details. The ones that are most useful in 'perf trace' | 123 | man pages for details. The ones that are most useful in 'perf trace' |
124 | are 'dwarf' and 'lbr', where available, try: 'perf trace --call-graph dwarf'. | 124 | are 'dwarf' and 'lbr', where available, try: 'perf trace --call-graph dwarf'. |
125 | 125 | ||
126 | Using this will, for the root user, bump the value of --mmap-pages to 4 | ||
127 | times the maximum for non-root users, based on the kernel.perf_event_mlock_kb | ||
128 | sysctl. This is done only if the user doesn't specify a --mmap-pages value. | ||
129 | |||
126 | --kernel-syscall-graph:: | 130 | --kernel-syscall-graph:: |
127 | Show the kernel callchains on the syscall exit path. | 131 | Show the kernel callchains on the syscall exit path. |
128 | 132 | ||
129 | --event:: | 133 | --event:: |
130 | Trace other events, see 'perf list' for a complete list. | 134 | Trace other events, see 'perf list' for a complete list. |
131 | 135 | ||
136 | --max-stack:: | ||
137 | Set the stack depth limit when parsing the callchain, anything | ||
138 | beyond the specified depth will be ignored. Note that at this point | ||
139 | this is just about the presentation part, i.e. the kernel is still | ||
140 | not limiting, the overhead of callchains needs to be set via the | ||
141 | knobs in --call-graph dwarf. | ||
142 | |||
143 | Implies '--call-graph dwarf' when --call-graph not present on the | ||
144 | command line, on systems where DWARF unwinding was built in. | ||
145 | |||
146 | Default: 127 | ||
147 | |||
148 | --min-stack:: | ||
149 | Set the stack depth limit when parsing the callchain, anything | ||
150 | below the specified depth will be ignored. Disabled by default. | ||
151 | |||
152 | Implies '--call-graph dwarf' when --call-graph not present on the | ||
153 | command line, on systems where DWARF unwinding was built in. | ||
154 | |||
132 | --proc-map-timeout:: | 155 | --proc-map-timeout:: |
133 | When processing pre-existing threads /proc/XXX/mmap, it may take a long time, | 156 | When processing pre-existing threads /proc/XXX/mmap, it may take a long time, |
134 | because the file may be huge. A time out is needed in such cases. | 157 | because the file may be huge. A time out is needed in such cases. |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index c9cb3be47cff..58adfee230de 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -375,7 +375,7 @@ static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample) | |||
375 | } | 375 | } |
376 | 376 | ||
377 | al.thread = machine__findnew_thread(machine, sample->pid, sample->tid); | 377 | al.thread = machine__findnew_thread(machine, sample->pid, sample->tid); |
378 | sample__resolve_callchain(sample, NULL, evsel, &al, 16); | 378 | sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, 16); |
379 | 379 | ||
380 | callchain_cursor_commit(&callchain_cursor); | 380 | callchain_cursor_commit(&callchain_cursor); |
381 | while (true) { | 381 | while (true) { |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 3239a6ec9d23..5b4758a08a49 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -930,45 +930,50 @@ out_delete_session: | |||
930 | return status; | 930 | return status; |
931 | } | 931 | } |
932 | 932 | ||
933 | static void callchain_debug(void) | 933 | static void callchain_debug(struct callchain_param *callchain) |
934 | { | 934 | { |
935 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" }; | 935 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" }; |
936 | 936 | ||
937 | pr_debug("callchain: type %s\n", str[callchain_param.record_mode]); | 937 | pr_debug("callchain: type %s\n", str[callchain->record_mode]); |
938 | 938 | ||
939 | if (callchain_param.record_mode == CALLCHAIN_DWARF) | 939 | if (callchain->record_mode == CALLCHAIN_DWARF) |
940 | pr_debug("callchain: stack dump size %d\n", | 940 | pr_debug("callchain: stack dump size %d\n", |
941 | callchain_param.dump_size); | 941 | callchain->dump_size); |
942 | } | 942 | } |
943 | 943 | ||
944 | int record_parse_callchain_opt(const struct option *opt, | 944 | int record_opts__parse_callchain(struct record_opts *record, |
945 | const char *arg, | 945 | struct callchain_param *callchain, |
946 | int unset) | 946 | const char *arg, bool unset) |
947 | { | 947 | { |
948 | int ret; | 948 | int ret; |
949 | struct record_opts *record = (struct record_opts *)opt->value; | ||
950 | |||
951 | record->callgraph_set = true; | 949 | record->callgraph_set = true; |
952 | callchain_param.enabled = !unset; | 950 | callchain->enabled = !unset; |
953 | 951 | ||
954 | /* --no-call-graph */ | 952 | /* --no-call-graph */ |
955 | if (unset) { | 953 | if (unset) { |
956 | callchain_param.record_mode = CALLCHAIN_NONE; | 954 | callchain->record_mode = CALLCHAIN_NONE; |
957 | pr_debug("callchain: disabled\n"); | 955 | pr_debug("callchain: disabled\n"); |
958 | return 0; | 956 | return 0; |
959 | } | 957 | } |
960 | 958 | ||
961 | ret = parse_callchain_record_opt(arg, &callchain_param); | 959 | ret = parse_callchain_record_opt(arg, callchain); |
962 | if (!ret) { | 960 | if (!ret) { |
963 | /* Enable data address sampling for DWARF unwind. */ | 961 | /* Enable data address sampling for DWARF unwind. */ |
964 | if (callchain_param.record_mode == CALLCHAIN_DWARF) | 962 | if (callchain->record_mode == CALLCHAIN_DWARF) |
965 | record->sample_address = true; | 963 | record->sample_address = true; |
966 | callchain_debug(); | 964 | callchain_debug(callchain); |
967 | } | 965 | } |
968 | 966 | ||
969 | return ret; | 967 | return ret; |
970 | } | 968 | } |
971 | 969 | ||
970 | int record_parse_callchain_opt(const struct option *opt, | ||
971 | const char *arg, | ||
972 | int unset) | ||
973 | { | ||
974 | return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset); | ||
975 | } | ||
976 | |||
972 | int record_callchain_opt(const struct option *opt, | 977 | int record_callchain_opt(const struct option *opt, |
973 | const char *arg __maybe_unused, | 978 | const char *arg __maybe_unused, |
974 | int unset __maybe_unused) | 979 | int unset __maybe_unused) |
@@ -981,7 +986,7 @@ int record_callchain_opt(const struct option *opt, | |||
981 | if (callchain_param.record_mode == CALLCHAIN_NONE) | 986 | if (callchain_param.record_mode == CALLCHAIN_NONE) |
982 | callchain_param.record_mode = CALLCHAIN_FP; | 987 | callchain_param.record_mode = CALLCHAIN_FP; |
983 | 988 | ||
984 | callchain_debug(); | 989 | callchain_debug(&callchain_param); |
985 | return 0; | 990 | return 0; |
986 | } | 991 | } |
987 | 992 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 838c0bc38105..0e93282b405e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "util/thread_map.h" | 22 | #include "util/thread_map.h" |
23 | #include "util/stat.h" | 23 | #include "util/stat.h" |
24 | #include <linux/bitmap.h> | 24 | #include <linux/bitmap.h> |
25 | #include <linux/stringify.h> | ||
25 | #include "asm/bug.h" | 26 | #include "asm/bug.h" |
26 | #include "util/mem-events.h" | 27 | #include "util/mem-events.h" |
27 | 28 | ||
@@ -569,18 +570,23 @@ static void print_sample_bts(struct perf_sample *sample, | |||
569 | /* print branch_from information */ | 570 | /* print branch_from information */ |
570 | if (PRINT_FIELD(IP)) { | 571 | if (PRINT_FIELD(IP)) { |
571 | unsigned int print_opts = output[attr->type].print_ip_opts; | 572 | unsigned int print_opts = output[attr->type].print_ip_opts; |
573 | struct callchain_cursor *cursor = NULL, cursor_callchain; | ||
572 | 574 | ||
573 | if (symbol_conf.use_callchain && sample->callchain) { | 575 | if (symbol_conf.use_callchain && sample->callchain && |
574 | printf("\n"); | 576 | thread__resolve_callchain(al->thread, &cursor_callchain, evsel, |
575 | } else { | 577 | sample, NULL, NULL, scripting_max_stack) == 0) |
576 | printf(" "); | 578 | cursor = &cursor_callchain; |
579 | |||
580 | if (cursor == NULL) { | ||
581 | putchar(' '); | ||
577 | if (print_opts & EVSEL__PRINT_SRCLINE) { | 582 | if (print_opts & EVSEL__PRINT_SRCLINE) { |
578 | print_srcline_last = true; | 583 | print_srcline_last = true; |
579 | print_opts &= ~EVSEL__PRINT_SRCLINE; | 584 | print_opts &= ~EVSEL__PRINT_SRCLINE; |
580 | } | 585 | } |
581 | } | 586 | } else |
582 | perf_evsel__fprintf_sym(evsel, sample, al, 0, print_opts, | 587 | putchar('\n'); |
583 | scripting_max_stack, stdout); | 588 | |
589 | sample__fprintf_sym(sample, al, 0, print_opts, cursor, stdout); | ||
584 | } | 590 | } |
585 | 591 | ||
586 | /* print branch_to information */ | 592 | /* print branch_to information */ |
@@ -783,14 +789,15 @@ static void process_event(struct perf_script *script, | |||
783 | printf("%16" PRIu64, sample->weight); | 789 | printf("%16" PRIu64, sample->weight); |
784 | 790 | ||
785 | if (PRINT_FIELD(IP)) { | 791 | if (PRINT_FIELD(IP)) { |
786 | if (!symbol_conf.use_callchain) | 792 | struct callchain_cursor *cursor = NULL, cursor_callchain; |
787 | printf(" "); | 793 | |
788 | else | 794 | if (symbol_conf.use_callchain && |
789 | printf("\n"); | 795 | thread__resolve_callchain(al->thread, &cursor_callchain, evsel, |
796 | sample, NULL, NULL, scripting_max_stack) == 0) | ||
797 | cursor = &cursor_callchain; | ||
790 | 798 | ||
791 | perf_evsel__fprintf_sym(evsel, sample, al, 0, | 799 | putchar(cursor ? '\n' : ' '); |
792 | output[attr->type].print_ip_opts, | 800 | sample__fprintf_sym(sample, al, 0, output[attr->type].print_ip_opts, cursor, stdout); |
793 | scripting_max_stack, stdout); | ||
794 | } | 801 | } |
795 | 802 | ||
796 | if (PRINT_FIELD(IREGS)) | 803 | if (PRINT_FIELD(IREGS)) |
@@ -2021,6 +2028,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2021 | "only consider symbols in these pids"), | 2028 | "only consider symbols in these pids"), |
2022 | OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", | 2029 | OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", |
2023 | "only consider symbols in these tids"), | 2030 | "only consider symbols in these tids"), |
2031 | OPT_UINTEGER(0, "max-stack", &scripting_max_stack, | ||
2032 | "Set the maximum stack depth when parsing the callchain, " | ||
2033 | "anything beyond the specified depth will be ignored. " | ||
2034 | "Default: " __stringify(PERF_MAX_STACK_DEPTH)), | ||
2024 | OPT_BOOLEAN('I', "show-info", &show_full_info, | 2035 | OPT_BOOLEAN('I', "show-info", &show_full_info, |
2025 | "display extended information from perf.data file"), | 2036 | "display extended information from perf.data file"), |
2026 | OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, | 2037 | OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8e090a785c5e..026ec0c749b0 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -46,23 +46,12 @@ | |||
46 | #include <linux/audit.h> | 46 | #include <linux/audit.h> |
47 | #include <sys/ptrace.h> | 47 | #include <sys/ptrace.h> |
48 | #include <linux/random.h> | 48 | #include <linux/random.h> |
49 | #include <linux/stringify.h> | ||
49 | 50 | ||
50 | #ifndef O_CLOEXEC | 51 | #ifndef O_CLOEXEC |
51 | # define O_CLOEXEC 02000000 | 52 | # define O_CLOEXEC 02000000 |
52 | #endif | 53 | #endif |
53 | 54 | ||
54 | #ifndef SOCK_DCCP | ||
55 | # define SOCK_DCCP 6 | ||
56 | #endif | ||
57 | |||
58 | #ifndef SOCK_CLOEXEC | ||
59 | # define SOCK_CLOEXEC 02000000 | ||
60 | #endif | ||
61 | |||
62 | #ifndef SOCK_NONBLOCK | ||
63 | # define SOCK_NONBLOCK 00004000 | ||
64 | #endif | ||
65 | |||
66 | #ifndef MSG_CMSG_CLOEXEC | 55 | #ifndef MSG_CMSG_CLOEXEC |
67 | # define MSG_CMSG_CLOEXEC 0x40000000 | 56 | # define MSG_CMSG_CLOEXEC 0x40000000 |
68 | #endif | 57 | #endif |
@@ -118,6 +107,8 @@ struct trace { | |||
118 | u64 vfs_getname, | 107 | u64 vfs_getname, |
119 | proc_getname; | 108 | proc_getname; |
120 | } stats; | 109 | } stats; |
110 | unsigned int max_stack; | ||
111 | unsigned int min_stack; | ||
121 | bool not_ev_qualifier; | 112 | bool not_ev_qualifier; |
122 | bool live; | 113 | bool live; |
123 | bool full_time; | 114 | bool full_time; |
@@ -538,53 +529,6 @@ static const char *socket_families[] = { | |||
538 | }; | 529 | }; |
539 | static DEFINE_STRARRAY(socket_families); | 530 | static DEFINE_STRARRAY(socket_families); |
540 | 531 | ||
541 | #ifndef SOCK_TYPE_MASK | ||
542 | #define SOCK_TYPE_MASK 0xf | ||
543 | #endif | ||
544 | |||
545 | static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, | ||
546 | struct syscall_arg *arg) | ||
547 | { | ||
548 | size_t printed; | ||
549 | int type = arg->val, | ||
550 | flags = type & ~SOCK_TYPE_MASK; | ||
551 | |||
552 | type &= SOCK_TYPE_MASK; | ||
553 | /* | ||
554 | * Can't use a strarray, MIPS may override for ABI reasons. | ||
555 | */ | ||
556 | switch (type) { | ||
557 | #define P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break; | ||
558 | P_SK_TYPE(STREAM); | ||
559 | P_SK_TYPE(DGRAM); | ||
560 | P_SK_TYPE(RAW); | ||
561 | P_SK_TYPE(RDM); | ||
562 | P_SK_TYPE(SEQPACKET); | ||
563 | P_SK_TYPE(DCCP); | ||
564 | P_SK_TYPE(PACKET); | ||
565 | #undef P_SK_TYPE | ||
566 | default: | ||
567 | printed = scnprintf(bf, size, "%#x", type); | ||
568 | } | ||
569 | |||
570 | #define P_SK_FLAG(n) \ | ||
571 | if (flags & SOCK_##n) { \ | ||
572 | printed += scnprintf(bf + printed, size - printed, "|%s", #n); \ | ||
573 | flags &= ~SOCK_##n; \ | ||
574 | } | ||
575 | |||
576 | P_SK_FLAG(CLOEXEC); | ||
577 | P_SK_FLAG(NONBLOCK); | ||
578 | #undef P_SK_FLAG | ||
579 | |||
580 | if (flags) | ||
581 | printed += scnprintf(bf + printed, size - printed, "|%#x", flags); | ||
582 | |||
583 | return printed; | ||
584 | } | ||
585 | |||
586 | #define SCA_SK_TYPE syscall_arg__scnprintf_socket_type | ||
587 | |||
588 | #ifndef MSG_PROBE | 532 | #ifndef MSG_PROBE |
589 | #define MSG_PROBE 0x10 | 533 | #define MSG_PROBE 0x10 |
590 | #endif | 534 | #endif |
@@ -951,6 +895,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, | |||
951 | #include "trace/beauty/mmap.c" | 895 | #include "trace/beauty/mmap.c" |
952 | #include "trace/beauty/mode_t.c" | 896 | #include "trace/beauty/mode_t.c" |
953 | #include "trace/beauty/sched_policy.c" | 897 | #include "trace/beauty/sched_policy.c" |
898 | #include "trace/beauty/socket_type.c" | ||
954 | #include "trace/beauty/waitid_options.c" | 899 | #include "trace/beauty/waitid_options.c" |
955 | 900 | ||
956 | static struct syscall_fmt { | 901 | static struct syscall_fmt { |
@@ -1905,7 +1850,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1905 | goto out_put; | 1850 | goto out_put; |
1906 | } | 1851 | } |
1907 | 1852 | ||
1908 | if (!trace->summary_only) | 1853 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) |
1909 | trace__printf_interrupted_entry(trace, sample); | 1854 | trace__printf_interrupted_entry(trace, sample); |
1910 | 1855 | ||
1911 | ttrace->entry_time = sample->time; | 1856 | ttrace->entry_time = sample->time; |
@@ -1916,7 +1861,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1916 | args, trace, thread); | 1861 | args, trace, thread); |
1917 | 1862 | ||
1918 | if (sc->is_exit) { | 1863 | if (sc->is_exit) { |
1919 | if (!trace->duration_filter && !trace->summary_only) { | 1864 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) { |
1920 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); | 1865 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
1921 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); | 1866 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
1922 | } | 1867 | } |
@@ -1936,26 +1881,27 @@ out_put: | |||
1936 | return err; | 1881 | return err; |
1937 | } | 1882 | } |
1938 | 1883 | ||
1939 | static int trace__fprintf_callchain(struct trace *trace, struct perf_evsel *evsel, | 1884 | static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evsel, |
1940 | struct perf_sample *sample) | 1885 | struct perf_sample *sample, |
1886 | struct callchain_cursor *cursor) | ||
1941 | { | 1887 | { |
1942 | struct addr_location al; | 1888 | struct addr_location al; |
1889 | |||
1890 | if (machine__resolve(trace->host, &al, sample) < 0 || | ||
1891 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, trace->max_stack)) | ||
1892 | return -1; | ||
1893 | |||
1894 | return 0; | ||
1895 | } | ||
1896 | |||
1897 | static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample) | ||
1898 | { | ||
1943 | /* TODO: user-configurable print_opts */ | 1899 | /* TODO: user-configurable print_opts */ |
1944 | const unsigned int print_opts = EVSEL__PRINT_SYM | | 1900 | const unsigned int print_opts = EVSEL__PRINT_SYM | |
1945 | EVSEL__PRINT_DSO | | 1901 | EVSEL__PRINT_DSO | |
1946 | EVSEL__PRINT_UNKNOWN_AS_ADDR; | 1902 | EVSEL__PRINT_UNKNOWN_AS_ADDR; |
1947 | 1903 | ||
1948 | if (sample->callchain == NULL) | 1904 | return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output); |
1949 | return 0; | ||
1950 | |||
1951 | if (machine__resolve(trace->host, &al, sample) < 0) { | ||
1952 | pr_err("Problem processing %s callchain, skipping...\n", | ||
1953 | perf_evsel__name(evsel)); | ||
1954 | return 0; | ||
1955 | } | ||
1956 | |||
1957 | return perf_evsel__fprintf_callchain(evsel, sample, &al, 38, print_opts, | ||
1958 | scripting_max_stack, trace->output); | ||
1959 | } | 1905 | } |
1960 | 1906 | ||
1961 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | 1907 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, |
@@ -1965,7 +1911,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1965 | long ret; | 1911 | long ret; |
1966 | u64 duration = 0; | 1912 | u64 duration = 0; |
1967 | struct thread *thread; | 1913 | struct thread *thread; |
1968 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; | 1914 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0; |
1969 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | 1915 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
1970 | struct thread_trace *ttrace; | 1916 | struct thread_trace *ttrace; |
1971 | 1917 | ||
@@ -1997,6 +1943,15 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1997 | } else if (trace->duration_filter) | 1943 | } else if (trace->duration_filter) |
1998 | goto out; | 1944 | goto out; |
1999 | 1945 | ||
1946 | if (sample->callchain) { | ||
1947 | callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor); | ||
1948 | if (callchain_ret == 0) { | ||
1949 | if (callchain_cursor.nr < trace->min_stack) | ||
1950 | goto out; | ||
1951 | callchain_ret = 1; | ||
1952 | } | ||
1953 | } | ||
1954 | |||
2000 | if (trace->summary_only) | 1955 | if (trace->summary_only) |
2001 | goto out; | 1956 | goto out; |
2002 | 1957 | ||
@@ -2037,7 +1992,10 @@ signed_print: | |||
2037 | 1992 | ||
2038 | fputc('\n', trace->output); | 1993 | fputc('\n', trace->output); |
2039 | 1994 | ||
2040 | trace__fprintf_callchain(trace, evsel, sample); | 1995 | if (callchain_ret > 0) |
1996 | trace__fprintf_callchain(trace, sample); | ||
1997 | else if (callchain_ret < 0) | ||
1998 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); | ||
2041 | out: | 1999 | out: |
2042 | ttrace->entry_pending = false; | 2000 | ttrace->entry_pending = false; |
2043 | err = 0; | 2001 | err = 0; |
@@ -2186,7 +2144,10 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
2186 | 2144 | ||
2187 | fprintf(trace->output, ")\n"); | 2145 | fprintf(trace->output, ")\n"); |
2188 | 2146 | ||
2189 | trace__fprintf_callchain(trace, evsel, sample); | 2147 | if (sample->callchain) { |
2148 | if (trace__resolve_callchain(trace, evsel, sample, &callchain_cursor) == 0) | ||
2149 | trace__fprintf_callchain(trace, sample); | ||
2150 | } | ||
2190 | 2151 | ||
2191 | return 0; | 2152 | return 0; |
2192 | } | 2153 | } |
@@ -3086,6 +3047,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
3086 | .show_comm = true, | 3047 | .show_comm = true, |
3087 | .trace_syscalls = true, | 3048 | .trace_syscalls = true, |
3088 | .kernel_syscallchains = false, | 3049 | .kernel_syscallchains = false, |
3050 | .max_stack = UINT_MAX, | ||
3089 | }; | 3051 | }; |
3090 | const char *output_name = NULL; | 3052 | const char *output_name = NULL; |
3091 | const char *ev_qualifier_str = NULL; | 3053 | const char *ev_qualifier_str = NULL; |
@@ -3136,10 +3098,19 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
3136 | &record_parse_callchain_opt), | 3098 | &record_parse_callchain_opt), |
3137 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, | 3099 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, |
3138 | "Show the kernel callchains on the syscall exit path"), | 3100 | "Show the kernel callchains on the syscall exit path"), |
3101 | OPT_UINTEGER(0, "min-stack", &trace.min_stack, | ||
3102 | "Set the minimum stack depth when parsing the callchain, " | ||
3103 | "anything below the specified depth will be ignored."), | ||
3104 | OPT_UINTEGER(0, "max-stack", &trace.max_stack, | ||
3105 | "Set the maximum stack depth when parsing the callchain, " | ||
3106 | "anything beyond the specified depth will be ignored. " | ||
3107 | "Default: " __stringify(PERF_MAX_STACK_DEPTH)), | ||
3139 | OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, | 3108 | OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, |
3140 | "per thread proc mmap processing timeout in ms"), | 3109 | "per thread proc mmap processing timeout in ms"), |
3141 | OPT_END() | 3110 | OPT_END() |
3142 | }; | 3111 | }; |
3112 | bool max_stack_user_set = true; | ||
3113 | bool mmap_pages_user_set = true; | ||
3143 | const char * const trace_subcommands[] = { "record", NULL }; | 3114 | const char * const trace_subcommands[] = { "record", NULL }; |
3144 | int err; | 3115 | int err; |
3145 | char bf[BUFSIZ]; | 3116 | char bf[BUFSIZ]; |
@@ -3173,8 +3144,25 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
3173 | trace.opts.sample_time = true; | 3144 | trace.opts.sample_time = true; |
3174 | } | 3145 | } |
3175 | 3146 | ||
3176 | if (trace.opts.callgraph_set) | 3147 | if (trace.opts.mmap_pages == UINT_MAX) |
3148 | mmap_pages_user_set = false; | ||
3149 | |||
3150 | if (trace.max_stack == UINT_MAX) { | ||
3151 | trace.max_stack = PERF_MAX_STACK_DEPTH; | ||
3152 | max_stack_user_set = false; | ||
3153 | } | ||
3154 | |||
3155 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
3156 | if ((trace.min_stack || max_stack_user_set) && !trace.opts.callgraph_set) | ||
3157 | record_opts__parse_callchain(&trace.opts, &callchain_param, "dwarf", false); | ||
3158 | #endif | ||
3159 | |||
3160 | if (trace.opts.callgraph_set) { | ||
3161 | if (!mmap_pages_user_set && geteuid() == 0) | ||
3162 | trace.opts.mmap_pages = perf_event_mlock_kb_in_pages() * 4; | ||
3163 | |||
3177 | symbol_conf.use_callchain = true; | 3164 | symbol_conf.use_callchain = true; |
3165 | } | ||
3178 | 3166 | ||
3179 | if (trace.evlist->nr_entries > 0) | 3167 | if (trace.evlist->nr_entries > 0) |
3180 | evlist__set_evsel_handler(trace.evlist, trace__event_handler); | 3168 | evlist__set_evsel_handler(trace.evlist, trace__event_handler); |
diff --git a/tools/perf/trace/beauty/socket_type.c b/tools/perf/trace/beauty/socket_type.c new file mode 100644 index 000000000000..0a5ce818131c --- /dev/null +++ b/tools/perf/trace/beauty/socket_type.c | |||
@@ -0,0 +1,60 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <sys/socket.h> | ||
3 | |||
4 | #ifndef SOCK_DCCP | ||
5 | # define SOCK_DCCP 6 | ||
6 | #endif | ||
7 | |||
8 | #ifndef SOCK_CLOEXEC | ||
9 | # define SOCK_CLOEXEC 02000000 | ||
10 | #endif | ||
11 | |||
12 | #ifndef SOCK_NONBLOCK | ||
13 | # define SOCK_NONBLOCK 00004000 | ||
14 | #endif | ||
15 | |||
16 | #ifndef SOCK_TYPE_MASK | ||
17 | #define SOCK_TYPE_MASK 0xf | ||
18 | #endif | ||
19 | |||
20 | static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct syscall_arg *arg) | ||
21 | { | ||
22 | size_t printed; | ||
23 | int type = arg->val, | ||
24 | flags = type & ~SOCK_TYPE_MASK; | ||
25 | |||
26 | type &= SOCK_TYPE_MASK; | ||
27 | /* | ||
28 | * Can't use a strarray, MIPS may override for ABI reasons. | ||
29 | */ | ||
30 | switch (type) { | ||
31 | #define P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break; | ||
32 | P_SK_TYPE(STREAM); | ||
33 | P_SK_TYPE(DGRAM); | ||
34 | P_SK_TYPE(RAW); | ||
35 | P_SK_TYPE(RDM); | ||
36 | P_SK_TYPE(SEQPACKET); | ||
37 | P_SK_TYPE(DCCP); | ||
38 | P_SK_TYPE(PACKET); | ||
39 | #undef P_SK_TYPE | ||
40 | default: | ||
41 | printed = scnprintf(bf, size, "%#x", type); | ||
42 | } | ||
43 | |||
44 | #define P_SK_FLAG(n) \ | ||
45 | if (flags & SOCK_##n) { \ | ||
46 | printed += scnprintf(bf + printed, size - printed, "|%s", #n); \ | ||
47 | flags &= ~SOCK_##n; \ | ||
48 | } | ||
49 | |||
50 | P_SK_FLAG(CLOEXEC); | ||
51 | P_SK_FLAG(NONBLOCK); | ||
52 | #undef P_SK_FLAG | ||
53 | |||
54 | if (flags) | ||
55 | printed += scnprintf(bf + printed, size - printed, "|%#x", flags); | ||
56 | |||
57 | return printed; | ||
58 | } | ||
59 | |||
60 | #define SCA_SK_TYPE syscall_arg__scnprintf_socket_type | ||
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index ea4ac03c1ec8..85a9ab62e23f 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -8,6 +8,7 @@ libperf-y += env.o | |||
8 | libperf-y += event.o | 8 | libperf-y += event.o |
9 | libperf-y += evlist.o | 9 | libperf-y += evlist.o |
10 | libperf-y += evsel.o | 10 | libperf-y += evsel.o |
11 | libperf-y += evsel_fprintf.o | ||
11 | libperf-y += find_bit.o | 12 | libperf-y += find_bit.o |
12 | libperf-y += kallsyms.o | 13 | libperf-y += kallsyms.o |
13 | libperf-y += levenshtein.o | 14 | libperf-y += levenshtein.o |
@@ -29,6 +30,7 @@ libperf-y += usage.o | |||
29 | libperf-y += wrapper.o | 30 | libperf-y += wrapper.o |
30 | libperf-y += dso.o | 31 | libperf-y += dso.o |
31 | libperf-y += symbol.o | 32 | libperf-y += symbol.o |
33 | libperf-y += symbol_fprintf.o | ||
32 | libperf-y += color.o | 34 | libperf-y += color.o |
33 | libperf-y += header.o | 35 | libperf-y += header.o |
34 | libperf-y += callchain.o | 36 | libperf-y += callchain.o |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 24b4bd0d7754..2b4ceaf058bb 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -788,7 +788,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor, | |||
788 | return 0; | 788 | return 0; |
789 | } | 789 | } |
790 | 790 | ||
791 | int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, | 791 | int sample__resolve_callchain(struct perf_sample *sample, |
792 | struct callchain_cursor *cursor, struct symbol **parent, | ||
792 | struct perf_evsel *evsel, struct addr_location *al, | 793 | struct perf_evsel *evsel, struct addr_location *al, |
793 | int max_stack) | 794 | int max_stack) |
794 | { | 795 | { |
@@ -797,7 +798,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent | |||
797 | 798 | ||
798 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || | 799 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || |
799 | sort__has_parent) { | 800 | sort__has_parent) { |
800 | return thread__resolve_callchain(al->thread, evsel, sample, | 801 | return thread__resolve_callchain(al->thread, cursor, evsel, sample, |
801 | parent, al, max_stack); | 802 | parent, al, max_stack); |
802 | } | 803 | } |
803 | return 0; | 804 | return 0; |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index d2a9e694810c..65e2a4f7cb4e 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -212,7 +212,14 @@ struct hist_entry; | |||
212 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | 212 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); |
213 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); | 213 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); |
214 | 214 | ||
215 | int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, | 215 | struct record_opts; |
216 | |||
217 | int record_opts__parse_callchain(struct record_opts *record, | ||
218 | struct callchain_param *callchain, | ||
219 | const char *arg, bool unset); | ||
220 | |||
221 | int sample__resolve_callchain(struct perf_sample *sample, | ||
222 | struct callchain_cursor *cursor, struct symbol **parent, | ||
216 | struct perf_evsel *evsel, struct addr_location *al, | 223 | struct perf_evsel *evsel, struct addr_location *al, |
217 | int max_stack); | 224 | int max_stack); |
218 | int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); | 225 | int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 4c9f510ae18d..6fb5725821de 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -986,26 +986,34 @@ out_unmap: | |||
986 | return -1; | 986 | return -1; |
987 | } | 987 | } |
988 | 988 | ||
989 | static size_t perf_evlist__mmap_size(unsigned long pages) | 989 | unsigned long perf_event_mlock_kb_in_pages(void) |
990 | { | 990 | { |
991 | if (pages == UINT_MAX) { | 991 | unsigned long pages; |
992 | int max; | 992 | int max; |
993 | 993 | ||
994 | if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { | 994 | if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { |
995 | /* | 995 | /* |
996 | * Pick a once upon a time good value, i.e. things look | 996 | * Pick a once upon a time good value, i.e. things look |
997 | * strange since we can't read a sysctl value, but lets not | 997 | * strange since we can't read a sysctl value, but lets not |
998 | * die yet... | 998 | * die yet... |
999 | */ | 999 | */ |
1000 | max = 512; | 1000 | max = 512; |
1001 | } else { | 1001 | } else { |
1002 | max -= (page_size / 1024); | 1002 | max -= (page_size / 1024); |
1003 | } | 1003 | } |
1004 | |||
1005 | pages = (max * 1024) / page_size; | ||
1006 | if (!is_power_of_2(pages)) | ||
1007 | pages = rounddown_pow_of_two(pages); | ||
1004 | 1008 | ||
1005 | pages = (max * 1024) / page_size; | 1009 | return pages; |
1006 | if (!is_power_of_2(pages)) | 1010 | } |
1007 | pages = rounddown_pow_of_two(pages); | 1011 | |
1008 | } else if (!is_power_of_2(pages)) | 1012 | static size_t perf_evlist__mmap_size(unsigned long pages) |
1013 | { | ||
1014 | if (pages == UINT_MAX) | ||
1015 | pages = perf_event_mlock_kb_in_pages(); | ||
1016 | else if (!is_power_of_2(pages)) | ||
1009 | return 0; | 1017 | return 0; |
1010 | 1018 | ||
1011 | return (pages + 1) * page_size; | 1019 | return (pages + 1) * page_size; |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index da46423998e8..208897a646ca 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -158,6 +158,8 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, | |||
158 | const char *str, | 158 | const char *str, |
159 | int unset); | 159 | int unset); |
160 | 160 | ||
161 | unsigned long perf_event_mlock_kb_in_pages(void); | ||
162 | |||
161 | int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages, | 163 | int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages, |
162 | bool overwrite, unsigned int auxtrace_pages, | 164 | bool overwrite, unsigned int auxtrace_pages, |
163 | bool auxtrace_overwrite); | 165 | bool auxtrace_overwrite); |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 6e86598682be..545bb3f0b2b0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -2254,226 +2254,6 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | |||
2254 | return 0; | 2254 | return 0; |
2255 | } | 2255 | } |
2256 | 2256 | ||
2257 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) | ||
2258 | { | ||
2259 | va_list args; | ||
2260 | int ret = 0; | ||
2261 | |||
2262 | if (!*first) { | ||
2263 | ret += fprintf(fp, ","); | ||
2264 | } else { | ||
2265 | ret += fprintf(fp, ":"); | ||
2266 | *first = false; | ||
2267 | } | ||
2268 | |||
2269 | va_start(args, fmt); | ||
2270 | ret += vfprintf(fp, fmt, args); | ||
2271 | va_end(args); | ||
2272 | return ret; | ||
2273 | } | ||
2274 | |||
2275 | static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv) | ||
2276 | { | ||
2277 | return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val); | ||
2278 | } | ||
2279 | |||
2280 | int perf_evsel__fprintf(struct perf_evsel *evsel, | ||
2281 | struct perf_attr_details *details, FILE *fp) | ||
2282 | { | ||
2283 | bool first = true; | ||
2284 | int printed = 0; | ||
2285 | |||
2286 | if (details->event_group) { | ||
2287 | struct perf_evsel *pos; | ||
2288 | |||
2289 | if (!perf_evsel__is_group_leader(evsel)) | ||
2290 | return 0; | ||
2291 | |||
2292 | if (evsel->nr_members > 1) | ||
2293 | printed += fprintf(fp, "%s{", evsel->group_name ?: ""); | ||
2294 | |||
2295 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
2296 | for_each_group_member(pos, evsel) | ||
2297 | printed += fprintf(fp, ",%s", perf_evsel__name(pos)); | ||
2298 | |||
2299 | if (evsel->nr_members > 1) | ||
2300 | printed += fprintf(fp, "}"); | ||
2301 | goto out; | ||
2302 | } | ||
2303 | |||
2304 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
2305 | |||
2306 | if (details->verbose) { | ||
2307 | printed += perf_event_attr__fprintf(fp, &evsel->attr, | ||
2308 | __print_attr__fprintf, &first); | ||
2309 | } else if (details->freq) { | ||
2310 | const char *term = "sample_freq"; | ||
2311 | |||
2312 | if (!evsel->attr.freq) | ||
2313 | term = "sample_period"; | ||
2314 | |||
2315 | printed += comma_fprintf(fp, &first, " %s=%" PRIu64, | ||
2316 | term, (u64)evsel->attr.sample_freq); | ||
2317 | } | ||
2318 | |||
2319 | if (details->trace_fields) { | ||
2320 | struct format_field *field; | ||
2321 | |||
2322 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { | ||
2323 | printed += comma_fprintf(fp, &first, " (not a tracepoint)"); | ||
2324 | goto out; | ||
2325 | } | ||
2326 | |||
2327 | field = evsel->tp_format->format.fields; | ||
2328 | if (field == NULL) { | ||
2329 | printed += comma_fprintf(fp, &first, " (no trace field)"); | ||
2330 | goto out; | ||
2331 | } | ||
2332 | |||
2333 | printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); | ||
2334 | |||
2335 | field = field->next; | ||
2336 | while (field) { | ||
2337 | printed += comma_fprintf(fp, &first, "%s", field->name); | ||
2338 | field = field->next; | ||
2339 | } | ||
2340 | } | ||
2341 | out: | ||
2342 | fputc('\n', fp); | ||
2343 | return ++printed; | ||
2344 | } | ||
2345 | |||
2346 | int perf_evsel__fprintf_callchain(struct perf_evsel *evsel, struct perf_sample *sample, | ||
2347 | struct addr_location *al, int left_alignment, | ||
2348 | unsigned int print_opts, unsigned int stack_depth, | ||
2349 | FILE *fp) | ||
2350 | { | ||
2351 | int printed = 0; | ||
2352 | struct callchain_cursor_node *node; | ||
2353 | int print_ip = print_opts & EVSEL__PRINT_IP; | ||
2354 | int print_sym = print_opts & EVSEL__PRINT_SYM; | ||
2355 | int print_dso = print_opts & EVSEL__PRINT_DSO; | ||
2356 | int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; | ||
2357 | int print_oneline = print_opts & EVSEL__PRINT_ONELINE; | ||
2358 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; | ||
2359 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; | ||
2360 | char s = print_oneline ? ' ' : '\t'; | ||
2361 | |||
2362 | if (sample->callchain) { | ||
2363 | struct addr_location node_al; | ||
2364 | |||
2365 | if (thread__resolve_callchain(al->thread, evsel, | ||
2366 | sample, NULL, NULL, | ||
2367 | stack_depth) != 0) { | ||
2368 | if (verbose) | ||
2369 | error("Failed to resolve callchain. Skipping\n"); | ||
2370 | return printed; | ||
2371 | } | ||
2372 | callchain_cursor_commit(&callchain_cursor); | ||
2373 | |||
2374 | if (print_symoffset) | ||
2375 | node_al = *al; | ||
2376 | |||
2377 | while (stack_depth) { | ||
2378 | u64 addr = 0; | ||
2379 | |||
2380 | node = callchain_cursor_current(&callchain_cursor); | ||
2381 | if (!node) | ||
2382 | break; | ||
2383 | |||
2384 | if (node->sym && node->sym->ignore) | ||
2385 | goto next; | ||
2386 | |||
2387 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | ||
2388 | |||
2389 | if (print_ip) | ||
2390 | printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); | ||
2391 | |||
2392 | if (node->map) | ||
2393 | addr = node->map->map_ip(node->map, node->ip); | ||
2394 | |||
2395 | if (print_sym) { | ||
2396 | printed += fprintf(fp, " "); | ||
2397 | node_al.addr = addr; | ||
2398 | node_al.map = node->map; | ||
2399 | |||
2400 | if (print_symoffset) { | ||
2401 | printed += __symbol__fprintf_symname_offs(node->sym, &node_al, | ||
2402 | print_unknown_as_addr, fp); | ||
2403 | } else { | ||
2404 | printed += __symbol__fprintf_symname(node->sym, &node_al, | ||
2405 | print_unknown_as_addr, fp); | ||
2406 | } | ||
2407 | } | ||
2408 | |||
2409 | if (print_dso) { | ||
2410 | printed += fprintf(fp, " ("); | ||
2411 | printed += map__fprintf_dsoname(node->map, fp); | ||
2412 | printed += fprintf(fp, ")"); | ||
2413 | } | ||
2414 | |||
2415 | if (print_srcline) | ||
2416 | printed += map__fprintf_srcline(node->map, addr, "\n ", fp); | ||
2417 | |||
2418 | if (!print_oneline) | ||
2419 | printed += fprintf(fp, "\n"); | ||
2420 | |||
2421 | stack_depth--; | ||
2422 | next: | ||
2423 | callchain_cursor_advance(&callchain_cursor); | ||
2424 | } | ||
2425 | } | ||
2426 | |||
2427 | return printed; | ||
2428 | } | ||
2429 | |||
2430 | int perf_evsel__fprintf_sym(struct perf_evsel *evsel, struct perf_sample *sample, | ||
2431 | struct addr_location *al, int left_alignment, | ||
2432 | unsigned int print_opts, unsigned int stack_depth, | ||
2433 | FILE *fp) | ||
2434 | { | ||
2435 | int printed = 0; | ||
2436 | int print_ip = print_opts & EVSEL__PRINT_IP; | ||
2437 | int print_sym = print_opts & EVSEL__PRINT_SYM; | ||
2438 | int print_dso = print_opts & EVSEL__PRINT_DSO; | ||
2439 | int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; | ||
2440 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; | ||
2441 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; | ||
2442 | |||
2443 | if (symbol_conf.use_callchain && sample->callchain) { | ||
2444 | printed += perf_evsel__fprintf_callchain(evsel, sample, al, left_alignment, | ||
2445 | print_opts, stack_depth, fp); | ||
2446 | } else if (!(al->sym && al->sym->ignore)) { | ||
2447 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | ||
2448 | |||
2449 | if (print_ip) | ||
2450 | printed += fprintf(fp, "%16" PRIx64, sample->ip); | ||
2451 | |||
2452 | if (print_sym) { | ||
2453 | printed += fprintf(fp, " "); | ||
2454 | if (print_symoffset) { | ||
2455 | printed += __symbol__fprintf_symname_offs(al->sym, al, | ||
2456 | print_unknown_as_addr, fp); | ||
2457 | } else { | ||
2458 | printed += __symbol__fprintf_symname(al->sym, al, | ||
2459 | print_unknown_as_addr, fp); | ||
2460 | } | ||
2461 | } | ||
2462 | |||
2463 | if (print_dso) { | ||
2464 | printed += fprintf(fp, " ("); | ||
2465 | printed += map__fprintf_dsoname(al->map, fp); | ||
2466 | printed += fprintf(fp, ")"); | ||
2467 | } | ||
2468 | |||
2469 | if (print_srcline) | ||
2470 | printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp); | ||
2471 | } | ||
2472 | |||
2473 | return printed; | ||
2474 | } | ||
2475 | |||
2476 | |||
2477 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | 2257 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, |
2478 | char *msg, size_t msgsize) | 2258 | char *msg, size_t msgsize) |
2479 | { | 2259 | { |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 36edd3c91d5c..b993218744d4 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -395,16 +395,15 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
395 | #define EVSEL__PRINT_SRCLINE (1<<5) | 395 | #define EVSEL__PRINT_SRCLINE (1<<5) |
396 | #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) | 396 | #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) |
397 | 397 | ||
398 | int perf_evsel__fprintf_callchain(struct perf_evsel *evsel, | 398 | struct callchain_cursor; |
399 | struct perf_sample *sample, | 399 | |
400 | struct addr_location *al, int left_alignment, | 400 | int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, |
401 | unsigned int print_opts, | 401 | unsigned int print_opts, |
402 | unsigned int stack_depth, FILE *fp); | 402 | struct callchain_cursor *cursor, FILE *fp); |
403 | 403 | ||
404 | int perf_evsel__fprintf_sym(struct perf_evsel *evsel, struct perf_sample *sample, | 404 | int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, |
405 | struct addr_location *al, int left_alignment, | 405 | int left_alignment, unsigned int print_opts, |
406 | unsigned int print_opts, unsigned int stack_depth, | 406 | struct callchain_cursor *cursor, FILE *fp); |
407 | FILE *fp); | ||
408 | 407 | ||
409 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | 408 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, |
410 | char *msg, size_t msgsize); | 409 | char *msg, size_t msgsize); |
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c new file mode 100644 index 000000000000..3674e77ad640 --- /dev/null +++ b/tools/perf/util/evsel_fprintf.c | |||
@@ -0,0 +1,212 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdbool.h> | ||
3 | #include <traceevent/event-parse.h> | ||
4 | #include "evsel.h" | ||
5 | #include "callchain.h" | ||
6 | #include "map.h" | ||
7 | #include "symbol.h" | ||
8 | |||
9 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) | ||
10 | { | ||
11 | va_list args; | ||
12 | int ret = 0; | ||
13 | |||
14 | if (!*first) { | ||
15 | ret += fprintf(fp, ","); | ||
16 | } else { | ||
17 | ret += fprintf(fp, ":"); | ||
18 | *first = false; | ||
19 | } | ||
20 | |||
21 | va_start(args, fmt); | ||
22 | ret += vfprintf(fp, fmt, args); | ||
23 | va_end(args); | ||
24 | return ret; | ||
25 | } | ||
26 | |||
27 | static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv) | ||
28 | { | ||
29 | return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val); | ||
30 | } | ||
31 | |||
32 | int perf_evsel__fprintf(struct perf_evsel *evsel, | ||
33 | struct perf_attr_details *details, FILE *fp) | ||
34 | { | ||
35 | bool first = true; | ||
36 | int printed = 0; | ||
37 | |||
38 | if (details->event_group) { | ||
39 | struct perf_evsel *pos; | ||
40 | |||
41 | if (!perf_evsel__is_group_leader(evsel)) | ||
42 | return 0; | ||
43 | |||
44 | if (evsel->nr_members > 1) | ||
45 | printed += fprintf(fp, "%s{", evsel->group_name ?: ""); | ||
46 | |||
47 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
48 | for_each_group_member(pos, evsel) | ||
49 | printed += fprintf(fp, ",%s", perf_evsel__name(pos)); | ||
50 | |||
51 | if (evsel->nr_members > 1) | ||
52 | printed += fprintf(fp, "}"); | ||
53 | goto out; | ||
54 | } | ||
55 | |||
56 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
57 | |||
58 | if (details->verbose) { | ||
59 | printed += perf_event_attr__fprintf(fp, &evsel->attr, | ||
60 | __print_attr__fprintf, &first); | ||
61 | } else if (details->freq) { | ||
62 | const char *term = "sample_freq"; | ||
63 | |||
64 | if (!evsel->attr.freq) | ||
65 | term = "sample_period"; | ||
66 | |||
67 | printed += comma_fprintf(fp, &first, " %s=%" PRIu64, | ||
68 | term, (u64)evsel->attr.sample_freq); | ||
69 | } | ||
70 | |||
71 | if (details->trace_fields) { | ||
72 | struct format_field *field; | ||
73 | |||
74 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { | ||
75 | printed += comma_fprintf(fp, &first, " (not a tracepoint)"); | ||
76 | goto out; | ||
77 | } | ||
78 | |||
79 | field = evsel->tp_format->format.fields; | ||
80 | if (field == NULL) { | ||
81 | printed += comma_fprintf(fp, &first, " (no trace field)"); | ||
82 | goto out; | ||
83 | } | ||
84 | |||
85 | printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); | ||
86 | |||
87 | field = field->next; | ||
88 | while (field) { | ||
89 | printed += comma_fprintf(fp, &first, "%s", field->name); | ||
90 | field = field->next; | ||
91 | } | ||
92 | } | ||
93 | out: | ||
94 | fputc('\n', fp); | ||
95 | return ++printed; | ||
96 | } | ||
97 | |||
98 | int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | ||
99 | unsigned int print_opts, struct callchain_cursor *cursor, | ||
100 | FILE *fp) | ||
101 | { | ||
102 | int printed = 0; | ||
103 | struct callchain_cursor_node *node; | ||
104 | int print_ip = print_opts & EVSEL__PRINT_IP; | ||
105 | int print_sym = print_opts & EVSEL__PRINT_SYM; | ||
106 | int print_dso = print_opts & EVSEL__PRINT_DSO; | ||
107 | int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; | ||
108 | int print_oneline = print_opts & EVSEL__PRINT_ONELINE; | ||
109 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; | ||
110 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; | ||
111 | char s = print_oneline ? ' ' : '\t'; | ||
112 | |||
113 | if (sample->callchain) { | ||
114 | struct addr_location node_al; | ||
115 | |||
116 | callchain_cursor_commit(cursor); | ||
117 | |||
118 | while (1) { | ||
119 | u64 addr = 0; | ||
120 | |||
121 | node = callchain_cursor_current(cursor); | ||
122 | if (!node) | ||
123 | break; | ||
124 | |||
125 | if (node->sym && node->sym->ignore) | ||
126 | goto next; | ||
127 | |||
128 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | ||
129 | |||
130 | if (print_ip) | ||
131 | printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); | ||
132 | |||
133 | if (node->map) | ||
134 | addr = node->map->map_ip(node->map, node->ip); | ||
135 | |||
136 | if (print_sym) { | ||
137 | printed += fprintf(fp, " "); | ||
138 | node_al.addr = addr; | ||
139 | node_al.map = node->map; | ||
140 | |||
141 | if (print_symoffset) { | ||
142 | printed += __symbol__fprintf_symname_offs(node->sym, &node_al, | ||
143 | print_unknown_as_addr, fp); | ||
144 | } else { | ||
145 | printed += __symbol__fprintf_symname(node->sym, &node_al, | ||
146 | print_unknown_as_addr, fp); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | if (print_dso) { | ||
151 | printed += fprintf(fp, " ("); | ||
152 | printed += map__fprintf_dsoname(node->map, fp); | ||
153 | printed += fprintf(fp, ")"); | ||
154 | } | ||
155 | |||
156 | if (print_srcline) | ||
157 | printed += map__fprintf_srcline(node->map, addr, "\n ", fp); | ||
158 | |||
159 | if (!print_oneline) | ||
160 | printed += fprintf(fp, "\n"); | ||
161 | next: | ||
162 | callchain_cursor_advance(cursor); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | return printed; | ||
167 | } | ||
168 | |||
169 | int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, | ||
170 | int left_alignment, unsigned int print_opts, | ||
171 | struct callchain_cursor *cursor, FILE *fp) | ||
172 | { | ||
173 | int printed = 0; | ||
174 | int print_ip = print_opts & EVSEL__PRINT_IP; | ||
175 | int print_sym = print_opts & EVSEL__PRINT_SYM; | ||
176 | int print_dso = print_opts & EVSEL__PRINT_DSO; | ||
177 | int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; | ||
178 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; | ||
179 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; | ||
180 | |||
181 | if (cursor != NULL) { | ||
182 | printed += sample__fprintf_callchain(sample, left_alignment, | ||
183 | print_opts, cursor, fp); | ||
184 | } else if (!(al->sym && al->sym->ignore)) { | ||
185 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | ||
186 | |||
187 | if (print_ip) | ||
188 | printed += fprintf(fp, "%16" PRIx64, sample->ip); | ||
189 | |||
190 | if (print_sym) { | ||
191 | printed += fprintf(fp, " "); | ||
192 | if (print_symoffset) { | ||
193 | printed += __symbol__fprintf_symname_offs(al->sym, al, | ||
194 | print_unknown_as_addr, fp); | ||
195 | } else { | ||
196 | printed += __symbol__fprintf_symname(al->sym, al, | ||
197 | print_unknown_as_addr, fp); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | if (print_dso) { | ||
202 | printed += fprintf(fp, " ("); | ||
203 | printed += map__fprintf_dsoname(al->map, fp); | ||
204 | printed += fprintf(fp, ")"); | ||
205 | } | ||
206 | |||
207 | if (print_srcline) | ||
208 | printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp); | ||
209 | } | ||
210 | |||
211 | return printed; | ||
212 | } | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 3d34c57dfbe2..991a351a8a41 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -953,7 +953,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, | |||
953 | { | 953 | { |
954 | int err, err2; | 954 | int err, err2; |
955 | 955 | ||
956 | err = sample__resolve_callchain(iter->sample, &iter->parent, | 956 | err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent, |
957 | iter->evsel, al, max_stack_depth); | 957 | iter->evsel, al, max_stack_depth); |
958 | if (err) | 958 | if (err) |
959 | return err; | 959 | return err; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 80b9b6a87990..0c4dabc69932 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1599,6 +1599,7 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, | |||
1599 | } | 1599 | } |
1600 | 1600 | ||
1601 | static int add_callchain_ip(struct thread *thread, | 1601 | static int add_callchain_ip(struct thread *thread, |
1602 | struct callchain_cursor *cursor, | ||
1602 | struct symbol **parent, | 1603 | struct symbol **parent, |
1603 | struct addr_location *root_al, | 1604 | struct addr_location *root_al, |
1604 | u8 *cpumode, | 1605 | u8 *cpumode, |
@@ -1630,7 +1631,7 @@ static int add_callchain_ip(struct thread *thread, | |||
1630 | * It seems the callchain is corrupted. | 1631 | * It seems the callchain is corrupted. |
1631 | * Discard all. | 1632 | * Discard all. |
1632 | */ | 1633 | */ |
1633 | callchain_cursor_reset(&callchain_cursor); | 1634 | callchain_cursor_reset(cursor); |
1634 | return 1; | 1635 | return 1; |
1635 | } | 1636 | } |
1636 | return 0; | 1637 | return 0; |
@@ -1648,13 +1649,13 @@ static int add_callchain_ip(struct thread *thread, | |||
1648 | /* Treat this symbol as the root, | 1649 | /* Treat this symbol as the root, |
1649 | forgetting its callees. */ | 1650 | forgetting its callees. */ |
1650 | *root_al = al; | 1651 | *root_al = al; |
1651 | callchain_cursor_reset(&callchain_cursor); | 1652 | callchain_cursor_reset(cursor); |
1652 | } | 1653 | } |
1653 | } | 1654 | } |
1654 | 1655 | ||
1655 | if (symbol_conf.hide_unresolved && al.sym == NULL) | 1656 | if (symbol_conf.hide_unresolved && al.sym == NULL) |
1656 | return 0; | 1657 | return 0; |
1657 | return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym); | 1658 | return callchain_cursor_append(cursor, al.addr, al.map, al.sym); |
1658 | } | 1659 | } |
1659 | 1660 | ||
1660 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | 1661 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, |
@@ -1724,6 +1725,7 @@ static int remove_loops(struct branch_entry *l, int nr) | |||
1724 | * negative error code on other errors. | 1725 | * negative error code on other errors. |
1725 | */ | 1726 | */ |
1726 | static int resolve_lbr_callchain_sample(struct thread *thread, | 1727 | static int resolve_lbr_callchain_sample(struct thread *thread, |
1728 | struct callchain_cursor *cursor, | ||
1727 | struct perf_sample *sample, | 1729 | struct perf_sample *sample, |
1728 | struct symbol **parent, | 1730 | struct symbol **parent, |
1729 | struct addr_location *root_al, | 1731 | struct addr_location *root_al, |
@@ -1778,7 +1780,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1778 | ip = lbr_stack->entries[0].to; | 1780 | ip = lbr_stack->entries[0].to; |
1779 | } | 1781 | } |
1780 | 1782 | ||
1781 | err = add_callchain_ip(thread, parent, root_al, &cpumode, ip); | 1783 | err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip); |
1782 | if (err) | 1784 | if (err) |
1783 | return (err < 0) ? err : 0; | 1785 | return (err < 0) ? err : 0; |
1784 | } | 1786 | } |
@@ -1789,6 +1791,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1789 | } | 1791 | } |
1790 | 1792 | ||
1791 | static int thread__resolve_callchain_sample(struct thread *thread, | 1793 | static int thread__resolve_callchain_sample(struct thread *thread, |
1794 | struct callchain_cursor *cursor, | ||
1792 | struct perf_evsel *evsel, | 1795 | struct perf_evsel *evsel, |
1793 | struct perf_sample *sample, | 1796 | struct perf_sample *sample, |
1794 | struct symbol **parent, | 1797 | struct symbol **parent, |
@@ -1803,10 +1806,10 @@ static int thread__resolve_callchain_sample(struct thread *thread, | |||
1803 | int skip_idx = -1; | 1806 | int skip_idx = -1; |
1804 | int first_call = 0; | 1807 | int first_call = 0; |
1805 | 1808 | ||
1806 | callchain_cursor_reset(&callchain_cursor); | 1809 | callchain_cursor_reset(cursor); |
1807 | 1810 | ||
1808 | if (has_branch_callstack(evsel)) { | 1811 | if (has_branch_callstack(evsel)) { |
1809 | err = resolve_lbr_callchain_sample(thread, sample, parent, | 1812 | err = resolve_lbr_callchain_sample(thread, cursor, sample, parent, |
1810 | root_al, max_stack); | 1813 | root_al, max_stack); |
1811 | if (err) | 1814 | if (err) |
1812 | return (err < 0) ? err : 0; | 1815 | return (err < 0) ? err : 0; |
@@ -1863,10 +1866,10 @@ static int thread__resolve_callchain_sample(struct thread *thread, | |||
1863 | nr = remove_loops(be, nr); | 1866 | nr = remove_loops(be, nr); |
1864 | 1867 | ||
1865 | for (i = 0; i < nr; i++) { | 1868 | for (i = 0; i < nr; i++) { |
1866 | err = add_callchain_ip(thread, parent, root_al, | 1869 | err = add_callchain_ip(thread, cursor, parent, root_al, |
1867 | NULL, be[i].to); | 1870 | NULL, be[i].to); |
1868 | if (!err) | 1871 | if (!err) |
1869 | err = add_callchain_ip(thread, parent, root_al, | 1872 | err = add_callchain_ip(thread, cursor, parent, root_al, |
1870 | NULL, be[i].from); | 1873 | NULL, be[i].from); |
1871 | if (err == -EINVAL) | 1874 | if (err == -EINVAL) |
1872 | break; | 1875 | break; |
@@ -1896,7 +1899,7 @@ check_calls: | |||
1896 | #endif | 1899 | #endif |
1897 | ip = chain->ips[j]; | 1900 | ip = chain->ips[j]; |
1898 | 1901 | ||
1899 | err = add_callchain_ip(thread, parent, root_al, &cpumode, ip); | 1902 | err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip); |
1900 | 1903 | ||
1901 | if (err) | 1904 | if (err) |
1902 | return (err < 0) ? err : 0; | 1905 | return (err < 0) ? err : 0; |
@@ -1916,13 +1919,14 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
1916 | } | 1919 | } |
1917 | 1920 | ||
1918 | int thread__resolve_callchain(struct thread *thread, | 1921 | int thread__resolve_callchain(struct thread *thread, |
1922 | struct callchain_cursor *cursor, | ||
1919 | struct perf_evsel *evsel, | 1923 | struct perf_evsel *evsel, |
1920 | struct perf_sample *sample, | 1924 | struct perf_sample *sample, |
1921 | struct symbol **parent, | 1925 | struct symbol **parent, |
1922 | struct addr_location *root_al, | 1926 | struct addr_location *root_al, |
1923 | int max_stack) | 1927 | int max_stack) |
1924 | { | 1928 | { |
1925 | int ret = thread__resolve_callchain_sample(thread, evsel, | 1929 | int ret = thread__resolve_callchain_sample(thread, cursor, evsel, |
1926 | sample, parent, | 1930 | sample, parent, |
1927 | root_al, max_stack); | 1931 | root_al, max_stack); |
1928 | if (ret) | 1932 | if (ret) |
@@ -1938,7 +1942,7 @@ int thread__resolve_callchain(struct thread *thread, | |||
1938 | (!sample->user_stack.size)) | 1942 | (!sample->user_stack.size)) |
1939 | return 0; | 1943 | return 0; |
1940 | 1944 | ||
1941 | return unwind__get_entries(unwind_entry, &callchain_cursor, | 1945 | return unwind__get_entries(unwind_entry, cursor, |
1942 | thread, sample, max_stack); | 1946 | thread, sample, max_stack); |
1943 | 1947 | ||
1944 | } | 1948 | } |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 8499db281158..382873bdc563 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -141,7 +141,11 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | |||
141 | struct addr_location *al); | 141 | struct addr_location *al); |
142 | struct mem_info *sample__resolve_mem(struct perf_sample *sample, | 142 | struct mem_info *sample__resolve_mem(struct perf_sample *sample, |
143 | struct addr_location *al); | 143 | struct addr_location *al); |
144 | |||
145 | struct callchain_cursor; | ||
146 | |||
144 | int thread__resolve_callchain(struct thread *thread, | 147 | int thread__resolve_callchain(struct thread *thread, |
148 | struct callchain_cursor *cursor, | ||
145 | struct perf_evsel *evsel, | 149 | struct perf_evsel *evsel, |
146 | struct perf_sample *sample, | 150 | struct perf_sample *sample, |
147 | struct symbol **parent, | 151 | struct symbol **parent, |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 8162ba0e2e57..36c6862119e3 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -23,3 +23,4 @@ util/strlist.c | |||
23 | util/trace-event.c | 23 | util/trace-event.c |
24 | ../lib/rbtree.c | 24 | ../lib/rbtree.c |
25 | util/string.c | 25 | util/string.c |
26 | util/symbol_fprintf.c | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 35ed00a600fb..ae1cebc307c5 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -263,7 +263,7 @@ static SV *perl_process_callchain(struct perf_sample *sample, | |||
263 | if (!symbol_conf.use_callchain || !sample->callchain) | 263 | if (!symbol_conf.use_callchain || !sample->callchain) |
264 | goto exit; | 264 | goto exit; |
265 | 265 | ||
266 | if (thread__resolve_callchain(al->thread, evsel, | 266 | if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel, |
267 | sample, NULL, NULL, | 267 | sample, NULL, NULL, |
268 | PERF_MAX_STACK_DEPTH) != 0) { | 268 | PERF_MAX_STACK_DEPTH) != 0) { |
269 | pr_err("Failed to resolve callchain. Skipping\n"); | 269 | pr_err("Failed to resolve callchain. Skipping\n"); |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index fbd05242b4e5..525eb49e7ba6 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -323,7 +323,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample, | |||
323 | if (!symbol_conf.use_callchain || !sample->callchain) | 323 | if (!symbol_conf.use_callchain || !sample->callchain) |
324 | goto exit; | 324 | goto exit; |
325 | 325 | ||
326 | if (thread__resolve_callchain(al->thread, evsel, | 326 | if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel, |
327 | sample, NULL, NULL, | 327 | sample, NULL, NULL, |
328 | scripting_max_stack) != 0) { | 328 | scripting_max_stack) != 0) { |
329 | pr_err("Failed to resolve callchain. Skipping\n"); | 329 | pr_err("Failed to resolve callchain. Skipping\n"); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index bb162ee433c6..a36823c3b7c0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -255,57 +255,6 @@ void symbol__delete(struct symbol *sym) | |||
255 | free(((void *)sym) - symbol_conf.priv_size); | 255 | free(((void *)sym) - symbol_conf.priv_size); |
256 | } | 256 | } |
257 | 257 | ||
258 | size_t symbol__fprintf(struct symbol *sym, FILE *fp) | ||
259 | { | ||
260 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", | ||
261 | sym->start, sym->end, | ||
262 | sym->binding == STB_GLOBAL ? 'g' : | ||
263 | sym->binding == STB_LOCAL ? 'l' : 'w', | ||
264 | sym->name); | ||
265 | } | ||
266 | |||
267 | size_t __symbol__fprintf_symname_offs(const struct symbol *sym, | ||
268 | const struct addr_location *al, | ||
269 | bool unknown_as_addr, FILE *fp) | ||
270 | { | ||
271 | unsigned long offset; | ||
272 | size_t length; | ||
273 | |||
274 | if (sym && sym->name) { | ||
275 | length = fprintf(fp, "%s", sym->name); | ||
276 | if (al) { | ||
277 | if (al->addr < sym->end) | ||
278 | offset = al->addr - sym->start; | ||
279 | else | ||
280 | offset = al->addr - al->map->start - sym->start; | ||
281 | length += fprintf(fp, "+0x%lx", offset); | ||
282 | } | ||
283 | return length; | ||
284 | } else if (al && unknown_as_addr) | ||
285 | return fprintf(fp, "[%#" PRIx64 "]", al->addr); | ||
286 | else | ||
287 | return fprintf(fp, "[unknown]"); | ||
288 | } | ||
289 | |||
290 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | ||
291 | const struct addr_location *al, | ||
292 | FILE *fp) | ||
293 | { | ||
294 | return __symbol__fprintf_symname_offs(sym, al, false, fp); | ||
295 | } | ||
296 | |||
297 | size_t __symbol__fprintf_symname(const struct symbol *sym, | ||
298 | const struct addr_location *al, | ||
299 | bool unknown_as_addr, FILE *fp) | ||
300 | { | ||
301 | return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, fp); | ||
302 | } | ||
303 | |||
304 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | ||
305 | { | ||
306 | return __symbol__fprintf_symname_offs(sym, NULL, false, fp); | ||
307 | } | ||
308 | |||
309 | void symbols__delete(struct rb_root *symbols) | 258 | void symbols__delete(struct rb_root *symbols) |
310 | { | 259 | { |
311 | struct symbol *pos; | 260 | struct symbol *pos; |
@@ -381,11 +330,6 @@ static struct symbol *symbols__next(struct symbol *sym) | |||
381 | return NULL; | 330 | return NULL; |
382 | } | 331 | } |
383 | 332 | ||
384 | struct symbol_name_rb_node { | ||
385 | struct rb_node rb_node; | ||
386 | struct symbol sym; | ||
387 | }; | ||
388 | |||
389 | static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym) | 333 | static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym) |
390 | { | 334 | { |
391 | struct rb_node **p = &symbols->rb_node; | 335 | struct rb_node **p = &symbols->rb_node; |
@@ -514,21 +458,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type) | |||
514 | &dso->symbols[type]); | 458 | &dso->symbols[type]); |
515 | } | 459 | } |
516 | 460 | ||
517 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | ||
518 | enum map_type type, FILE *fp) | ||
519 | { | ||
520 | size_t ret = 0; | ||
521 | struct rb_node *nd; | ||
522 | struct symbol_name_rb_node *pos; | ||
523 | |||
524 | for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { | ||
525 | pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); | ||
526 | fprintf(fp, "%s\n", pos->sym.name); | ||
527 | } | ||
528 | |||
529 | return ret; | ||
530 | } | ||
531 | |||
532 | int modules__parse(const char *filename, void *arg, | 461 | int modules__parse(const char *filename, void *arg, |
533 | int (*process_module)(void *arg, const char *name, | 462 | int (*process_module)(void *arg, const char *name, |
534 | u64 start)) | 463 | u64 start)) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index e2562568418d..1da7b101bc7f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -140,6 +140,11 @@ struct symbol_conf { | |||
140 | 140 | ||
141 | extern struct symbol_conf symbol_conf; | 141 | extern struct symbol_conf symbol_conf; |
142 | 142 | ||
143 | struct symbol_name_rb_node { | ||
144 | struct rb_node rb_node; | ||
145 | struct symbol sym; | ||
146 | }; | ||
147 | |||
143 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) | 148 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) |
144 | { | 149 | { |
145 | return path__join(bf, size, symbol_conf.symfs, path); | 150 | return path__join(bf, size, symbol_conf.symfs, path); |
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c new file mode 100644 index 000000000000..a680bdaa65dc --- /dev/null +++ b/tools/perf/util/symbol_fprintf.c | |||
@@ -0,0 +1,71 @@ | |||
1 | #include <elf.h> | ||
2 | #include <inttypes.h> | ||
3 | #include <stdio.h> | ||
4 | |||
5 | #include "symbol.h" | ||
6 | |||
7 | size_t symbol__fprintf(struct symbol *sym, FILE *fp) | ||
8 | { | ||
9 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", | ||
10 | sym->start, sym->end, | ||
11 | sym->binding == STB_GLOBAL ? 'g' : | ||
12 | sym->binding == STB_LOCAL ? 'l' : 'w', | ||
13 | sym->name); | ||
14 | } | ||
15 | |||
16 | size_t __symbol__fprintf_symname_offs(const struct symbol *sym, | ||
17 | const struct addr_location *al, | ||
18 | bool unknown_as_addr, FILE *fp) | ||
19 | { | ||
20 | unsigned long offset; | ||
21 | size_t length; | ||
22 | |||
23 | if (sym && sym->name) { | ||
24 | length = fprintf(fp, "%s", sym->name); | ||
25 | if (al) { | ||
26 | if (al->addr < sym->end) | ||
27 | offset = al->addr - sym->start; | ||
28 | else | ||
29 | offset = al->addr - al->map->start - sym->start; | ||
30 | length += fprintf(fp, "+0x%lx", offset); | ||
31 | } | ||
32 | return length; | ||
33 | } else if (al && unknown_as_addr) | ||
34 | return fprintf(fp, "[%#" PRIx64 "]", al->addr); | ||
35 | else | ||
36 | return fprintf(fp, "[unknown]"); | ||
37 | } | ||
38 | |||
39 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | ||
40 | const struct addr_location *al, | ||
41 | FILE *fp) | ||
42 | { | ||
43 | return __symbol__fprintf_symname_offs(sym, al, false, fp); | ||
44 | } | ||
45 | |||
46 | size_t __symbol__fprintf_symname(const struct symbol *sym, | ||
47 | const struct addr_location *al, | ||
48 | bool unknown_as_addr, FILE *fp) | ||
49 | { | ||
50 | return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, fp); | ||
51 | } | ||
52 | |||
53 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | ||
54 | { | ||
55 | return __symbol__fprintf_symname_offs(sym, NULL, false, fp); | ||
56 | } | ||
57 | |||
58 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | ||
59 | enum map_type type, FILE *fp) | ||
60 | { | ||
61 | size_t ret = 0; | ||
62 | struct rb_node *nd; | ||
63 | struct symbol_name_rb_node *pos; | ||
64 | |||
65 | for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { | ||
66 | pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); | ||
67 | fprintf(fp, "%s\n", pos->sym.name); | ||
68 | } | ||
69 | |||
70 | return ret; | ||
71 | } | ||