Commit 6810fc915f7a89d8134edb3996dbbf8eac386c26
Committed by
Arnaldo Carvalho de Melo
1 parent
a2f2804a71
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
perf trace: Add option to analyze events in a file versus live
Allows capture of raw_syscall:* events and analyzed at a later time. v2: change -i option from inherit to input name for consistency with other perf commands Signed-off-by: David Ahern <dsahern@gmail.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1377750593-48046-3-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Showing 2 changed files with 100 additions and 2 deletions Inline Diff
tools/perf/Documentation/perf-trace.txt
1 | perf-trace(1) | 1 | perf-trace(1) |
2 | ============= | 2 | ============= |
3 | 3 | ||
4 | NAME | 4 | NAME |
5 | ---- | 5 | ---- |
6 | perf-trace - strace inspired tool | 6 | perf-trace - strace inspired tool |
7 | 7 | ||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf trace' | 11 | 'perf trace' |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command will show the events associated with the target, initially | 15 | This command will show the events associated with the target, initially |
16 | syscalls, but other system events like pagefaults, task lifetime events, | 16 | syscalls, but other system events like pagefaults, task lifetime events, |
17 | scheduling events, etc. | 17 | scheduling events, etc. |
18 | 18 | ||
19 | Initially this is a live mode only tool, but eventually will work with | 19 | Initially this is a live mode only tool, but eventually will work with |
20 | perf.data files like the other tools, allowing a detached 'record' from | 20 | perf.data files like the other tools, allowing a detached 'record' from |
21 | analysis phases. | 21 | analysis phases. |
22 | 22 | ||
23 | OPTIONS | 23 | OPTIONS |
24 | ------- | 24 | ------- |
25 | 25 | ||
26 | -a:: | 26 | -a:: |
27 | --all-cpus:: | 27 | --all-cpus:: |
28 | System-wide collection from all CPUs. | 28 | System-wide collection from all CPUs. |
29 | 29 | ||
30 | -e:: | 30 | -e:: |
31 | --expr:: | 31 | --expr:: |
32 | List of events to show, currently only syscall names. | 32 | List of events to show, currently only syscall names. |
33 | Prefixing with ! shows all syscalls but the ones specified. You may | 33 | Prefixing with ! shows all syscalls but the ones specified. You may |
34 | need to escape it. | 34 | need to escape it. |
35 | 35 | ||
36 | -o:: | 36 | -o:: |
37 | --output=:: | 37 | --output=:: |
38 | Output file name. | 38 | Output file name. |
39 | 39 | ||
40 | -p:: | 40 | -p:: |
41 | --pid=:: | 41 | --pid=:: |
42 | Record events on existing process ID (comma separated list). | 42 | Record events on existing process ID (comma separated list). |
43 | 43 | ||
44 | -t:: | 44 | -t:: |
45 | --tid=:: | 45 | --tid=:: |
46 | Record events on existing thread ID (comma separated list). | 46 | Record events on existing thread ID (comma separated list). |
47 | 47 | ||
48 | -u:: | 48 | -u:: |
49 | --uid=:: | 49 | --uid=:: |
50 | Record events in threads owned by uid. Name or number. | 50 | Record events in threads owned by uid. Name or number. |
51 | 51 | ||
52 | -v:: | 52 | -v:: |
53 | --verbose=:: | 53 | --verbose=:: |
54 | Verbosity level. | 54 | Verbosity level. |
55 | 55 | ||
56 | -i:: | 56 | -i:: |
57 | --no-inherit:: | 57 | --no-inherit:: |
58 | Child tasks do not inherit counters. | 58 | Child tasks do not inherit counters. |
59 | 59 | ||
60 | -m:: | 60 | -m:: |
61 | --mmap-pages=:: | 61 | --mmap-pages=:: |
62 | Number of mmap data pages. Must be a power of two. | 62 | Number of mmap data pages. Must be a power of two. |
63 | 63 | ||
64 | -C:: | 64 | -C:: |
65 | --cpu:: | 65 | --cpu:: |
66 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a | 66 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a |
67 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | 67 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. |
68 | In per-thread mode with inheritance mode on (default), Events are captured only when | 68 | In per-thread mode with inheritance mode on (default), Events are captured only when |
69 | the thread executes on the designated CPUs. Default is to monitor all CPUs. | 69 | the thread executes on the designated CPUs. Default is to monitor all CPUs. |
70 | 70 | ||
71 | --duration: | 71 | --duration: |
72 | Show only events that had a duration greater than N.M ms. | 72 | Show only events that had a duration greater than N.M ms. |
73 | 73 | ||
74 | --sched: | 74 | --sched: |
75 | Accrue thread runtime and provide a summary at the end of the session. | 75 | Accrue thread runtime and provide a summary at the end of the session. |
76 | 76 | ||
77 | -i | ||
78 | --input | ||
79 | Process events from a given perf data file. | ||
80 | |||
77 | SEE ALSO | 81 | SEE ALSO |
78 | -------- | 82 | -------- |
79 | linkperf:perf-record[1], linkperf:perf-script[1] | 83 | linkperf:perf-record[1], linkperf:perf-script[1] |
80 | 84 |
tools/perf/builtin-trace.c
1 | #include <traceevent/event-parse.h> | 1 | #include <traceevent/event-parse.h> |
2 | #include "builtin.h" | 2 | #include "builtin.h" |
3 | #include "util/color.h" | 3 | #include "util/color.h" |
4 | #include "util/debug.h" | 4 | #include "util/debug.h" |
5 | #include "util/evlist.h" | 5 | #include "util/evlist.h" |
6 | #include "util/machine.h" | 6 | #include "util/machine.h" |
7 | #include "util/session.h" | ||
7 | #include "util/thread.h" | 8 | #include "util/thread.h" |
8 | #include "util/parse-options.h" | 9 | #include "util/parse-options.h" |
9 | #include "util/strlist.h" | 10 | #include "util/strlist.h" |
10 | #include "util/thread_map.h" | 11 | #include "util/thread_map.h" |
11 | 12 | ||
12 | #include <libaudit.h> | 13 | #include <libaudit.h> |
13 | #include <stdlib.h> | 14 | #include <stdlib.h> |
14 | #include <sys/mman.h> | 15 | #include <sys/mman.h> |
15 | 16 | ||
16 | static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, unsigned long arg) | 17 | static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, unsigned long arg) |
17 | { | 18 | { |
18 | return scnprintf(bf, size, "%#lx", arg); | 19 | return scnprintf(bf, size, "%#lx", arg); |
19 | } | 20 | } |
20 | 21 | ||
21 | #define SCA_HEX syscall_arg__scnprintf_hex | 22 | #define SCA_HEX syscall_arg__scnprintf_hex |
22 | 23 | ||
23 | static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, unsigned long arg) | 24 | static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, unsigned long arg) |
24 | { | 25 | { |
25 | int printed = 0, prot = arg; | 26 | int printed = 0, prot = arg; |
26 | 27 | ||
27 | if (prot == PROT_NONE) | 28 | if (prot == PROT_NONE) |
28 | return scnprintf(bf, size, "NONE"); | 29 | return scnprintf(bf, size, "NONE"); |
29 | #define P_MMAP_PROT(n) \ | 30 | #define P_MMAP_PROT(n) \ |
30 | if (prot & PROT_##n) { \ | 31 | if (prot & PROT_##n) { \ |
31 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | 32 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ |
32 | prot &= ~PROT_##n; \ | 33 | prot &= ~PROT_##n; \ |
33 | } | 34 | } |
34 | 35 | ||
35 | P_MMAP_PROT(EXEC); | 36 | P_MMAP_PROT(EXEC); |
36 | P_MMAP_PROT(READ); | 37 | P_MMAP_PROT(READ); |
37 | P_MMAP_PROT(WRITE); | 38 | P_MMAP_PROT(WRITE); |
38 | #ifdef PROT_SEM | 39 | #ifdef PROT_SEM |
39 | P_MMAP_PROT(SEM); | 40 | P_MMAP_PROT(SEM); |
40 | #endif | 41 | #endif |
41 | P_MMAP_PROT(GROWSDOWN); | 42 | P_MMAP_PROT(GROWSDOWN); |
42 | P_MMAP_PROT(GROWSUP); | 43 | P_MMAP_PROT(GROWSUP); |
43 | #undef P_MMAP_PROT | 44 | #undef P_MMAP_PROT |
44 | 45 | ||
45 | if (prot) | 46 | if (prot) |
46 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", prot); | 47 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", prot); |
47 | 48 | ||
48 | return printed; | 49 | return printed; |
49 | } | 50 | } |
50 | 51 | ||
51 | #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot | 52 | #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot |
52 | 53 | ||
53 | static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned long arg) | 54 | static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned long arg) |
54 | { | 55 | { |
55 | int printed = 0, flags = arg; | 56 | int printed = 0, flags = arg; |
56 | 57 | ||
57 | #define P_MMAP_FLAG(n) \ | 58 | #define P_MMAP_FLAG(n) \ |
58 | if (flags & MAP_##n) { \ | 59 | if (flags & MAP_##n) { \ |
59 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | 60 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ |
60 | flags &= ~MAP_##n; \ | 61 | flags &= ~MAP_##n; \ |
61 | } | 62 | } |
62 | 63 | ||
63 | P_MMAP_FLAG(SHARED); | 64 | P_MMAP_FLAG(SHARED); |
64 | P_MMAP_FLAG(PRIVATE); | 65 | P_MMAP_FLAG(PRIVATE); |
65 | P_MMAP_FLAG(32BIT); | 66 | P_MMAP_FLAG(32BIT); |
66 | P_MMAP_FLAG(ANONYMOUS); | 67 | P_MMAP_FLAG(ANONYMOUS); |
67 | P_MMAP_FLAG(DENYWRITE); | 68 | P_MMAP_FLAG(DENYWRITE); |
68 | P_MMAP_FLAG(EXECUTABLE); | 69 | P_MMAP_FLAG(EXECUTABLE); |
69 | P_MMAP_FLAG(FILE); | 70 | P_MMAP_FLAG(FILE); |
70 | P_MMAP_FLAG(FIXED); | 71 | P_MMAP_FLAG(FIXED); |
71 | P_MMAP_FLAG(GROWSDOWN); | 72 | P_MMAP_FLAG(GROWSDOWN); |
72 | P_MMAP_FLAG(HUGETLB); | 73 | P_MMAP_FLAG(HUGETLB); |
73 | P_MMAP_FLAG(LOCKED); | 74 | P_MMAP_FLAG(LOCKED); |
74 | P_MMAP_FLAG(NONBLOCK); | 75 | P_MMAP_FLAG(NONBLOCK); |
75 | P_MMAP_FLAG(NORESERVE); | 76 | P_MMAP_FLAG(NORESERVE); |
76 | P_MMAP_FLAG(POPULATE); | 77 | P_MMAP_FLAG(POPULATE); |
77 | P_MMAP_FLAG(STACK); | 78 | P_MMAP_FLAG(STACK); |
78 | #ifdef MAP_UNINITIALIZED | 79 | #ifdef MAP_UNINITIALIZED |
79 | P_MMAP_FLAG(UNINITIALIZED); | 80 | P_MMAP_FLAG(UNINITIALIZED); |
80 | #endif | 81 | #endif |
81 | #undef P_MMAP_FLAG | 82 | #undef P_MMAP_FLAG |
82 | 83 | ||
83 | if (flags) | 84 | if (flags) |
84 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | 85 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); |
85 | 86 | ||
86 | return printed; | 87 | return printed; |
87 | } | 88 | } |
88 | 89 | ||
89 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags | 90 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags |
90 | 91 | ||
91 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, unsigned long arg) | 92 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, unsigned long arg) |
92 | { | 93 | { |
93 | int behavior = arg; | 94 | int behavior = arg; |
94 | 95 | ||
95 | switch (behavior) { | 96 | switch (behavior) { |
96 | #define P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n) | 97 | #define P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n) |
97 | P_MADV_BHV(NORMAL); | 98 | P_MADV_BHV(NORMAL); |
98 | P_MADV_BHV(RANDOM); | 99 | P_MADV_BHV(RANDOM); |
99 | P_MADV_BHV(SEQUENTIAL); | 100 | P_MADV_BHV(SEQUENTIAL); |
100 | P_MADV_BHV(WILLNEED); | 101 | P_MADV_BHV(WILLNEED); |
101 | P_MADV_BHV(DONTNEED); | 102 | P_MADV_BHV(DONTNEED); |
102 | P_MADV_BHV(REMOVE); | 103 | P_MADV_BHV(REMOVE); |
103 | P_MADV_BHV(DONTFORK); | 104 | P_MADV_BHV(DONTFORK); |
104 | P_MADV_BHV(DOFORK); | 105 | P_MADV_BHV(DOFORK); |
105 | P_MADV_BHV(HWPOISON); | 106 | P_MADV_BHV(HWPOISON); |
106 | #ifdef MADV_SOFT_OFFLINE | 107 | #ifdef MADV_SOFT_OFFLINE |
107 | P_MADV_BHV(SOFT_OFFLINE); | 108 | P_MADV_BHV(SOFT_OFFLINE); |
108 | #endif | 109 | #endif |
109 | P_MADV_BHV(MERGEABLE); | 110 | P_MADV_BHV(MERGEABLE); |
110 | P_MADV_BHV(UNMERGEABLE); | 111 | P_MADV_BHV(UNMERGEABLE); |
111 | P_MADV_BHV(HUGEPAGE); | 112 | P_MADV_BHV(HUGEPAGE); |
112 | P_MADV_BHV(NOHUGEPAGE); | 113 | P_MADV_BHV(NOHUGEPAGE); |
113 | #ifdef MADV_DONTDUMP | 114 | #ifdef MADV_DONTDUMP |
114 | P_MADV_BHV(DONTDUMP); | 115 | P_MADV_BHV(DONTDUMP); |
115 | #endif | 116 | #endif |
116 | #ifdef MADV_DODUMP | 117 | #ifdef MADV_DODUMP |
117 | P_MADV_BHV(DODUMP); | 118 | P_MADV_BHV(DODUMP); |
118 | #endif | 119 | #endif |
119 | #undef P_MADV_PHV | 120 | #undef P_MADV_PHV |
120 | default: break; | 121 | default: break; |
121 | } | 122 | } |
122 | 123 | ||
123 | return scnprintf(bf, size, "%#x", behavior); | 124 | return scnprintf(bf, size, "%#x", behavior); |
124 | } | 125 | } |
125 | 126 | ||
126 | #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior | 127 | #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior |
127 | 128 | ||
128 | static struct syscall_fmt { | 129 | static struct syscall_fmt { |
129 | const char *name; | 130 | const char *name; |
130 | const char *alias; | 131 | const char *alias; |
131 | size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg); | 132 | size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg); |
132 | bool errmsg; | 133 | bool errmsg; |
133 | bool timeout; | 134 | bool timeout; |
134 | bool hexret; | 135 | bool hexret; |
135 | } syscall_fmts[] = { | 136 | } syscall_fmts[] = { |
136 | { .name = "access", .errmsg = true, }, | 137 | { .name = "access", .errmsg = true, }, |
137 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, | 138 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, |
138 | { .name = "brk", .hexret = true, | 139 | { .name = "brk", .hexret = true, |
139 | .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, | 140 | .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, |
140 | { .name = "mmap", .hexret = true, }, | 141 | { .name = "mmap", .hexret = true, }, |
141 | { .name = "connect", .errmsg = true, }, | 142 | { .name = "connect", .errmsg = true, }, |
142 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, | 143 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, |
143 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, | 144 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, |
144 | { .name = "futex", .errmsg = true, }, | 145 | { .name = "futex", .errmsg = true, }, |
145 | { .name = "ioctl", .errmsg = true, | 146 | { .name = "ioctl", .errmsg = true, |
146 | .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, | 147 | .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, |
147 | { .name = "lstat", .errmsg = true, .alias = "newlstat", }, | 148 | { .name = "lstat", .errmsg = true, .alias = "newlstat", }, |
148 | { .name = "madvise", .errmsg = true, | 149 | { .name = "madvise", .errmsg = true, |
149 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | 150 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ |
150 | [2] = SCA_MADV_BHV, /* behavior */ }, }, | 151 | [2] = SCA_MADV_BHV, /* behavior */ }, }, |
151 | { .name = "mmap", .hexret = true, | 152 | { .name = "mmap", .hexret = true, |
152 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | 153 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ |
153 | [2] = SCA_MMAP_PROT, /* prot */ | 154 | [2] = SCA_MMAP_PROT, /* prot */ |
154 | [3] = SCA_MMAP_FLAGS, /* flags */ }, }, | 155 | [3] = SCA_MMAP_FLAGS, /* flags */ }, }, |
155 | { .name = "mprotect", .errmsg = true, | 156 | { .name = "mprotect", .errmsg = true, |
156 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | 157 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ |
157 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | 158 | [2] = SCA_MMAP_PROT, /* prot */ }, }, |
158 | { .name = "mremap", .hexret = true, | 159 | { .name = "mremap", .hexret = true, |
159 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | 160 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ |
160 | [4] = SCA_HEX, /* new_addr */ }, }, | 161 | [4] = SCA_HEX, /* new_addr */ }, }, |
161 | { .name = "munmap", .errmsg = true, | 162 | { .name = "munmap", .errmsg = true, |
162 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | 163 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, |
163 | { .name = "open", .errmsg = true, }, | 164 | { .name = "open", .errmsg = true, }, |
164 | { .name = "poll", .errmsg = true, .timeout = true, }, | 165 | { .name = "poll", .errmsg = true, .timeout = true, }, |
165 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 166 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
166 | { .name = "pread", .errmsg = true, .alias = "pread64", }, | 167 | { .name = "pread", .errmsg = true, .alias = "pread64", }, |
167 | { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, | 168 | { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, |
168 | { .name = "read", .errmsg = true, }, | 169 | { .name = "read", .errmsg = true, }, |
169 | { .name = "recvfrom", .errmsg = true, }, | 170 | { .name = "recvfrom", .errmsg = true, }, |
170 | { .name = "select", .errmsg = true, .timeout = true, }, | 171 | { .name = "select", .errmsg = true, .timeout = true, }, |
171 | { .name = "socket", .errmsg = true, }, | 172 | { .name = "socket", .errmsg = true, }, |
172 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 173 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
173 | { .name = "uname", .errmsg = true, .alias = "newuname", }, | 174 | { .name = "uname", .errmsg = true, .alias = "newuname", }, |
174 | }; | 175 | }; |
175 | 176 | ||
176 | static int syscall_fmt__cmp(const void *name, const void *fmtp) | 177 | static int syscall_fmt__cmp(const void *name, const void *fmtp) |
177 | { | 178 | { |
178 | const struct syscall_fmt *fmt = fmtp; | 179 | const struct syscall_fmt *fmt = fmtp; |
179 | return strcmp(name, fmt->name); | 180 | return strcmp(name, fmt->name); |
180 | } | 181 | } |
181 | 182 | ||
182 | static struct syscall_fmt *syscall_fmt__find(const char *name) | 183 | static struct syscall_fmt *syscall_fmt__find(const char *name) |
183 | { | 184 | { |
184 | const int nmemb = ARRAY_SIZE(syscall_fmts); | 185 | const int nmemb = ARRAY_SIZE(syscall_fmts); |
185 | return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); | 186 | return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); |
186 | } | 187 | } |
187 | 188 | ||
188 | struct syscall { | 189 | struct syscall { |
189 | struct event_format *tp_format; | 190 | struct event_format *tp_format; |
190 | const char *name; | 191 | const char *name; |
191 | bool filtered; | 192 | bool filtered; |
192 | struct syscall_fmt *fmt; | 193 | struct syscall_fmt *fmt; |
193 | size_t (**arg_scnprintf)(char *bf, size_t size, unsigned long arg); | 194 | size_t (**arg_scnprintf)(char *bf, size_t size, unsigned long arg); |
194 | }; | 195 | }; |
195 | 196 | ||
196 | static size_t fprintf_duration(unsigned long t, FILE *fp) | 197 | static size_t fprintf_duration(unsigned long t, FILE *fp) |
197 | { | 198 | { |
198 | double duration = (double)t / NSEC_PER_MSEC; | 199 | double duration = (double)t / NSEC_PER_MSEC; |
199 | size_t printed = fprintf(fp, "("); | 200 | size_t printed = fprintf(fp, "("); |
200 | 201 | ||
201 | if (duration >= 1.0) | 202 | if (duration >= 1.0) |
202 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); | 203 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); |
203 | else if (duration >= 0.01) | 204 | else if (duration >= 0.01) |
204 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | 205 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); |
205 | else | 206 | else |
206 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | 207 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); |
207 | return printed + fprintf(fp, "): "); | 208 | return printed + fprintf(fp, "): "); |
208 | } | 209 | } |
209 | 210 | ||
210 | struct thread_trace { | 211 | struct thread_trace { |
211 | u64 entry_time; | 212 | u64 entry_time; |
212 | u64 exit_time; | 213 | u64 exit_time; |
213 | bool entry_pending; | 214 | bool entry_pending; |
214 | unsigned long nr_events; | 215 | unsigned long nr_events; |
215 | char *entry_str; | 216 | char *entry_str; |
216 | double runtime_ms; | 217 | double runtime_ms; |
217 | }; | 218 | }; |
218 | 219 | ||
219 | static struct thread_trace *thread_trace__new(void) | 220 | static struct thread_trace *thread_trace__new(void) |
220 | { | 221 | { |
221 | return zalloc(sizeof(struct thread_trace)); | 222 | return zalloc(sizeof(struct thread_trace)); |
222 | } | 223 | } |
223 | 224 | ||
224 | static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) | 225 | static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) |
225 | { | 226 | { |
226 | struct thread_trace *ttrace; | 227 | struct thread_trace *ttrace; |
227 | 228 | ||
228 | if (thread == NULL) | 229 | if (thread == NULL) |
229 | goto fail; | 230 | goto fail; |
230 | 231 | ||
231 | if (thread->priv == NULL) | 232 | if (thread->priv == NULL) |
232 | thread->priv = thread_trace__new(); | 233 | thread->priv = thread_trace__new(); |
233 | 234 | ||
234 | if (thread->priv == NULL) | 235 | if (thread->priv == NULL) |
235 | goto fail; | 236 | goto fail; |
236 | 237 | ||
237 | ttrace = thread->priv; | 238 | ttrace = thread->priv; |
238 | ++ttrace->nr_events; | 239 | ++ttrace->nr_events; |
239 | 240 | ||
240 | return ttrace; | 241 | return ttrace; |
241 | fail: | 242 | fail: |
242 | color_fprintf(fp, PERF_COLOR_RED, | 243 | color_fprintf(fp, PERF_COLOR_RED, |
243 | "WARNING: not enough memory, dropping samples!\n"); | 244 | "WARNING: not enough memory, dropping samples!\n"); |
244 | return NULL; | 245 | return NULL; |
245 | } | 246 | } |
246 | 247 | ||
247 | struct trace { | 248 | struct trace { |
248 | struct perf_tool tool; | 249 | struct perf_tool tool; |
249 | int audit_machine; | 250 | int audit_machine; |
250 | struct { | 251 | struct { |
251 | int max; | 252 | int max; |
252 | struct syscall *table; | 253 | struct syscall *table; |
253 | } syscalls; | 254 | } syscalls; |
254 | struct perf_record_opts opts; | 255 | struct perf_record_opts opts; |
255 | struct machine host; | 256 | struct machine host; |
256 | u64 base_time; | 257 | u64 base_time; |
257 | FILE *output; | 258 | FILE *output; |
258 | unsigned long nr_events; | 259 | unsigned long nr_events; |
259 | struct strlist *ev_qualifier; | 260 | struct strlist *ev_qualifier; |
260 | bool not_ev_qualifier; | 261 | bool not_ev_qualifier; |
261 | bool sched; | 262 | bool sched; |
262 | bool multiple_threads; | 263 | bool multiple_threads; |
263 | double duration_filter; | 264 | double duration_filter; |
264 | double runtime_ms; | 265 | double runtime_ms; |
265 | }; | 266 | }; |
266 | 267 | ||
267 | static bool trace__filter_duration(struct trace *trace, double t) | 268 | static bool trace__filter_duration(struct trace *trace, double t) |
268 | { | 269 | { |
269 | return t < (trace->duration_filter * NSEC_PER_MSEC); | 270 | return t < (trace->duration_filter * NSEC_PER_MSEC); |
270 | } | 271 | } |
271 | 272 | ||
272 | static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) | 273 | static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) |
273 | { | 274 | { |
274 | double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; | 275 | double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; |
275 | 276 | ||
276 | return fprintf(fp, "%10.3f ", ts); | 277 | return fprintf(fp, "%10.3f ", ts); |
277 | } | 278 | } |
278 | 279 | ||
279 | static bool done = false; | 280 | static bool done = false; |
280 | 281 | ||
281 | static void sig_handler(int sig __maybe_unused) | 282 | static void sig_handler(int sig __maybe_unused) |
282 | { | 283 | { |
283 | done = true; | 284 | done = true; |
284 | } | 285 | } |
285 | 286 | ||
286 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | 287 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, |
287 | u64 duration, u64 tstamp, FILE *fp) | 288 | u64 duration, u64 tstamp, FILE *fp) |
288 | { | 289 | { |
289 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | 290 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); |
290 | printed += fprintf_duration(duration, fp); | 291 | printed += fprintf_duration(duration, fp); |
291 | 292 | ||
292 | if (trace->multiple_threads) | 293 | if (trace->multiple_threads) |
293 | printed += fprintf(fp, "%d ", thread->tid); | 294 | printed += fprintf(fp, "%d ", thread->tid); |
294 | 295 | ||
295 | return printed; | 296 | return printed; |
296 | } | 297 | } |
297 | 298 | ||
298 | static int trace__process_event(struct trace *trace, struct machine *machine, | 299 | static int trace__process_event(struct trace *trace, struct machine *machine, |
299 | union perf_event *event) | 300 | union perf_event *event) |
300 | { | 301 | { |
301 | int ret = 0; | 302 | int ret = 0; |
302 | 303 | ||
303 | switch (event->header.type) { | 304 | switch (event->header.type) { |
304 | case PERF_RECORD_LOST: | 305 | case PERF_RECORD_LOST: |
305 | color_fprintf(trace->output, PERF_COLOR_RED, | 306 | color_fprintf(trace->output, PERF_COLOR_RED, |
306 | "LOST %" PRIu64 " events!\n", event->lost.lost); | 307 | "LOST %" PRIu64 " events!\n", event->lost.lost); |
307 | ret = machine__process_lost_event(machine, event); | 308 | ret = machine__process_lost_event(machine, event); |
308 | default: | 309 | default: |
309 | ret = machine__process_event(machine, event); | 310 | ret = machine__process_event(machine, event); |
310 | break; | 311 | break; |
311 | } | 312 | } |
312 | 313 | ||
313 | return ret; | 314 | return ret; |
314 | } | 315 | } |
315 | 316 | ||
316 | static int trace__tool_process(struct perf_tool *tool, | 317 | static int trace__tool_process(struct perf_tool *tool, |
317 | union perf_event *event, | 318 | union perf_event *event, |
318 | struct perf_sample *sample __maybe_unused, | 319 | struct perf_sample *sample __maybe_unused, |
319 | struct machine *machine) | 320 | struct machine *machine) |
320 | { | 321 | { |
321 | struct trace *trace = container_of(tool, struct trace, tool); | 322 | struct trace *trace = container_of(tool, struct trace, tool); |
322 | return trace__process_event(trace, machine, event); | 323 | return trace__process_event(trace, machine, event); |
323 | } | 324 | } |
324 | 325 | ||
325 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | 326 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
326 | { | 327 | { |
327 | int err = symbol__init(); | 328 | int err = symbol__init(); |
328 | 329 | ||
329 | if (err) | 330 | if (err) |
330 | return err; | 331 | return err; |
331 | 332 | ||
332 | machine__init(&trace->host, "", HOST_KERNEL_ID); | 333 | machine__init(&trace->host, "", HOST_KERNEL_ID); |
333 | machine__create_kernel_maps(&trace->host); | 334 | machine__create_kernel_maps(&trace->host); |
334 | 335 | ||
335 | if (perf_target__has_task(&trace->opts.target)) { | 336 | if (perf_target__has_task(&trace->opts.target)) { |
336 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, | 337 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, |
337 | trace__tool_process, | 338 | trace__tool_process, |
338 | &trace->host); | 339 | &trace->host); |
339 | } else { | 340 | } else { |
340 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, | 341 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, |
341 | &trace->host); | 342 | &trace->host); |
342 | } | 343 | } |
343 | 344 | ||
344 | if (err) | 345 | if (err) |
345 | symbol__exit(); | 346 | symbol__exit(); |
346 | 347 | ||
347 | return err; | 348 | return err; |
348 | } | 349 | } |
349 | 350 | ||
350 | static int syscall__set_arg_fmts(struct syscall *sc) | 351 | static int syscall__set_arg_fmts(struct syscall *sc) |
351 | { | 352 | { |
352 | struct format_field *field; | 353 | struct format_field *field; |
353 | int idx = 0; | 354 | int idx = 0; |
354 | 355 | ||
355 | sc->arg_scnprintf = calloc(sc->tp_format->format.nr_fields - 1, sizeof(void *)); | 356 | sc->arg_scnprintf = calloc(sc->tp_format->format.nr_fields - 1, sizeof(void *)); |
356 | if (sc->arg_scnprintf == NULL) | 357 | if (sc->arg_scnprintf == NULL) |
357 | return -1; | 358 | return -1; |
358 | 359 | ||
359 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 360 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
360 | if (sc->fmt && sc->fmt->arg_scnprintf[idx]) | 361 | if (sc->fmt && sc->fmt->arg_scnprintf[idx]) |
361 | sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; | 362 | sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; |
362 | else if (field->flags & FIELD_IS_POINTER) | 363 | else if (field->flags & FIELD_IS_POINTER) |
363 | sc->arg_scnprintf[idx] = syscall_arg__scnprintf_hex; | 364 | sc->arg_scnprintf[idx] = syscall_arg__scnprintf_hex; |
364 | ++idx; | 365 | ++idx; |
365 | } | 366 | } |
366 | 367 | ||
367 | return 0; | 368 | return 0; |
368 | } | 369 | } |
369 | 370 | ||
370 | static int trace__read_syscall_info(struct trace *trace, int id) | 371 | static int trace__read_syscall_info(struct trace *trace, int id) |
371 | { | 372 | { |
372 | char tp_name[128]; | 373 | char tp_name[128]; |
373 | struct syscall *sc; | 374 | struct syscall *sc; |
374 | const char *name = audit_syscall_to_name(id, trace->audit_machine); | 375 | const char *name = audit_syscall_to_name(id, trace->audit_machine); |
375 | 376 | ||
376 | if (name == NULL) | 377 | if (name == NULL) |
377 | return -1; | 378 | return -1; |
378 | 379 | ||
379 | if (id > trace->syscalls.max) { | 380 | if (id > trace->syscalls.max) { |
380 | struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); | 381 | struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); |
381 | 382 | ||
382 | if (nsyscalls == NULL) | 383 | if (nsyscalls == NULL) |
383 | return -1; | 384 | return -1; |
384 | 385 | ||
385 | if (trace->syscalls.max != -1) { | 386 | if (trace->syscalls.max != -1) { |
386 | memset(nsyscalls + trace->syscalls.max + 1, 0, | 387 | memset(nsyscalls + trace->syscalls.max + 1, 0, |
387 | (id - trace->syscalls.max) * sizeof(*sc)); | 388 | (id - trace->syscalls.max) * sizeof(*sc)); |
388 | } else { | 389 | } else { |
389 | memset(nsyscalls, 0, (id + 1) * sizeof(*sc)); | 390 | memset(nsyscalls, 0, (id + 1) * sizeof(*sc)); |
390 | } | 391 | } |
391 | 392 | ||
392 | trace->syscalls.table = nsyscalls; | 393 | trace->syscalls.table = nsyscalls; |
393 | trace->syscalls.max = id; | 394 | trace->syscalls.max = id; |
394 | } | 395 | } |
395 | 396 | ||
396 | sc = trace->syscalls.table + id; | 397 | sc = trace->syscalls.table + id; |
397 | sc->name = name; | 398 | sc->name = name; |
398 | 399 | ||
399 | if (trace->ev_qualifier) { | 400 | if (trace->ev_qualifier) { |
400 | bool in = strlist__find(trace->ev_qualifier, name) != NULL; | 401 | bool in = strlist__find(trace->ev_qualifier, name) != NULL; |
401 | 402 | ||
402 | if (!(in ^ trace->not_ev_qualifier)) { | 403 | if (!(in ^ trace->not_ev_qualifier)) { |
403 | sc->filtered = true; | 404 | sc->filtered = true; |
404 | /* | 405 | /* |
405 | * No need to do read tracepoint information since this will be | 406 | * No need to do read tracepoint information since this will be |
406 | * filtered out. | 407 | * filtered out. |
407 | */ | 408 | */ |
408 | return 0; | 409 | return 0; |
409 | } | 410 | } |
410 | } | 411 | } |
411 | 412 | ||
412 | sc->fmt = syscall_fmt__find(sc->name); | 413 | sc->fmt = syscall_fmt__find(sc->name); |
413 | 414 | ||
414 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); | 415 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); |
415 | sc->tp_format = event_format__new("syscalls", tp_name); | 416 | sc->tp_format = event_format__new("syscalls", tp_name); |
416 | 417 | ||
417 | if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) { | 418 | if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) { |
418 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias); | 419 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias); |
419 | sc->tp_format = event_format__new("syscalls", tp_name); | 420 | sc->tp_format = event_format__new("syscalls", tp_name); |
420 | } | 421 | } |
421 | 422 | ||
422 | if (sc->tp_format == NULL) | 423 | if (sc->tp_format == NULL) |
423 | return -1; | 424 | return -1; |
424 | 425 | ||
425 | return syscall__set_arg_fmts(sc); | 426 | return syscall__set_arg_fmts(sc); |
426 | } | 427 | } |
427 | 428 | ||
428 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | 429 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
429 | unsigned long *args) | 430 | unsigned long *args) |
430 | { | 431 | { |
431 | int i = 0; | 432 | int i = 0; |
432 | size_t printed = 0; | 433 | size_t printed = 0; |
433 | 434 | ||
434 | if (sc->tp_format != NULL) { | 435 | if (sc->tp_format != NULL) { |
435 | struct format_field *field; | 436 | struct format_field *field; |
436 | 437 | ||
437 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 438 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
438 | printed += scnprintf(bf + printed, size - printed, | 439 | printed += scnprintf(bf + printed, size - printed, |
439 | "%s%s: ", printed ? ", " : "", field->name); | 440 | "%s%s: ", printed ? ", " : "", field->name); |
440 | 441 | ||
441 | if (sc->arg_scnprintf && sc->arg_scnprintf[i]) | 442 | if (sc->arg_scnprintf && sc->arg_scnprintf[i]) |
442 | printed += sc->arg_scnprintf[i](bf + printed, size - printed, args[i]); | 443 | printed += sc->arg_scnprintf[i](bf + printed, size - printed, args[i]); |
443 | else | 444 | else |
444 | printed += scnprintf(bf + printed, size - printed, | 445 | printed += scnprintf(bf + printed, size - printed, |
445 | "%ld", args[i]); | 446 | "%ld", args[i]); |
446 | ++i; | 447 | ++i; |
447 | } | 448 | } |
448 | } else { | 449 | } else { |
449 | while (i < 6) { | 450 | while (i < 6) { |
450 | printed += scnprintf(bf + printed, size - printed, | 451 | printed += scnprintf(bf + printed, size - printed, |
451 | "%sarg%d: %ld", | 452 | "%sarg%d: %ld", |
452 | printed ? ", " : "", i, args[i]); | 453 | printed ? ", " : "", i, args[i]); |
453 | ++i; | 454 | ++i; |
454 | } | 455 | } |
455 | } | 456 | } |
456 | 457 | ||
457 | return printed; | 458 | return printed; |
458 | } | 459 | } |
459 | 460 | ||
460 | typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, | 461 | typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, |
461 | struct perf_sample *sample); | 462 | struct perf_sample *sample); |
462 | 463 | ||
463 | static struct syscall *trace__syscall_info(struct trace *trace, | 464 | static struct syscall *trace__syscall_info(struct trace *trace, |
464 | struct perf_evsel *evsel, | 465 | struct perf_evsel *evsel, |
465 | struct perf_sample *sample) | 466 | struct perf_sample *sample) |
466 | { | 467 | { |
467 | int id = perf_evsel__intval(evsel, sample, "id"); | 468 | int id = perf_evsel__intval(evsel, sample, "id"); |
468 | 469 | ||
469 | if (id < 0) { | 470 | if (id < 0) { |
470 | 471 | ||
471 | /* | 472 | /* |
472 | * XXX: Noticed on x86_64, reproduced as far back as 3.0.36, haven't tried | 473 | * XXX: Noticed on x86_64, reproduced as far back as 3.0.36, haven't tried |
473 | * before that, leaving at a higher verbosity level till that is | 474 | * before that, leaving at a higher verbosity level till that is |
474 | * explained. Reproduced with plain ftrace with: | 475 | * explained. Reproduced with plain ftrace with: |
475 | * | 476 | * |
476 | * echo 1 > /t/events/raw_syscalls/sys_exit/enable | 477 | * echo 1 > /t/events/raw_syscalls/sys_exit/enable |
477 | * grep "NR -1 " /t/trace_pipe | 478 | * grep "NR -1 " /t/trace_pipe |
478 | * | 479 | * |
479 | * After generating some load on the machine. | 480 | * After generating some load on the machine. |
480 | */ | 481 | */ |
481 | if (verbose > 1) { | 482 | if (verbose > 1) { |
482 | static u64 n; | 483 | static u64 n; |
483 | fprintf(trace->output, "Invalid syscall %d id, skipping (%s, %" PRIu64 ") ...\n", | 484 | fprintf(trace->output, "Invalid syscall %d id, skipping (%s, %" PRIu64 ") ...\n", |
484 | id, perf_evsel__name(evsel), ++n); | 485 | id, perf_evsel__name(evsel), ++n); |
485 | } | 486 | } |
486 | return NULL; | 487 | return NULL; |
487 | } | 488 | } |
488 | 489 | ||
489 | if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && | 490 | if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && |
490 | trace__read_syscall_info(trace, id)) | 491 | trace__read_syscall_info(trace, id)) |
491 | goto out_cant_read; | 492 | goto out_cant_read; |
492 | 493 | ||
493 | if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) | 494 | if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) |
494 | goto out_cant_read; | 495 | goto out_cant_read; |
495 | 496 | ||
496 | return &trace->syscalls.table[id]; | 497 | return &trace->syscalls.table[id]; |
497 | 498 | ||
498 | out_cant_read: | 499 | out_cant_read: |
499 | if (verbose) { | 500 | if (verbose) { |
500 | fprintf(trace->output, "Problems reading syscall %d", id); | 501 | fprintf(trace->output, "Problems reading syscall %d", id); |
501 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | 502 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) |
502 | fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); | 503 | fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); |
503 | fputs(" information\n", trace->output); | 504 | fputs(" information\n", trace->output); |
504 | } | 505 | } |
505 | return NULL; | 506 | return NULL; |
506 | } | 507 | } |
507 | 508 | ||
508 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 509 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
509 | struct perf_sample *sample) | 510 | struct perf_sample *sample) |
510 | { | 511 | { |
511 | char *msg; | 512 | char *msg; |
512 | void *args; | 513 | void *args; |
513 | size_t printed = 0; | 514 | size_t printed = 0; |
514 | struct thread *thread; | 515 | struct thread *thread; |
515 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 516 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
516 | struct thread_trace *ttrace; | 517 | struct thread_trace *ttrace; |
517 | 518 | ||
518 | if (sc == NULL) | 519 | if (sc == NULL) |
519 | return -1; | 520 | return -1; |
520 | 521 | ||
521 | if (sc->filtered) | 522 | if (sc->filtered) |
522 | return 0; | 523 | return 0; |
523 | 524 | ||
524 | thread = machine__findnew_thread(&trace->host, sample->pid, | 525 | thread = machine__findnew_thread(&trace->host, sample->pid, |
525 | sample->tid); | 526 | sample->tid); |
526 | ttrace = thread__trace(thread, trace->output); | 527 | ttrace = thread__trace(thread, trace->output); |
527 | if (ttrace == NULL) | 528 | if (ttrace == NULL) |
528 | return -1; | 529 | return -1; |
529 | 530 | ||
530 | args = perf_evsel__rawptr(evsel, sample, "args"); | 531 | args = perf_evsel__rawptr(evsel, sample, "args"); |
531 | if (args == NULL) { | 532 | if (args == NULL) { |
532 | fprintf(trace->output, "Problems reading syscall arguments\n"); | 533 | fprintf(trace->output, "Problems reading syscall arguments\n"); |
533 | return -1; | 534 | return -1; |
534 | } | 535 | } |
535 | 536 | ||
536 | ttrace = thread->priv; | 537 | ttrace = thread->priv; |
537 | 538 | ||
538 | if (ttrace->entry_str == NULL) { | 539 | if (ttrace->entry_str == NULL) { |
539 | ttrace->entry_str = malloc(1024); | 540 | ttrace->entry_str = malloc(1024); |
540 | if (!ttrace->entry_str) | 541 | if (!ttrace->entry_str) |
541 | return -1; | 542 | return -1; |
542 | } | 543 | } |
543 | 544 | ||
544 | ttrace->entry_time = sample->time; | 545 | ttrace->entry_time = sample->time; |
545 | msg = ttrace->entry_str; | 546 | msg = ttrace->entry_str; |
546 | printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); | 547 | printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); |
547 | 548 | ||
548 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); | 549 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); |
549 | 550 | ||
550 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 551 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { |
551 | if (!trace->duration_filter) { | 552 | if (!trace->duration_filter) { |
552 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); | 553 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
553 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); | 554 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
554 | } | 555 | } |
555 | } else | 556 | } else |
556 | ttrace->entry_pending = true; | 557 | ttrace->entry_pending = true; |
557 | 558 | ||
558 | return 0; | 559 | return 0; |
559 | } | 560 | } |
560 | 561 | ||
561 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | 562 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, |
562 | struct perf_sample *sample) | 563 | struct perf_sample *sample) |
563 | { | 564 | { |
564 | int ret; | 565 | int ret; |
565 | u64 duration = 0; | 566 | u64 duration = 0; |
566 | struct thread *thread; | 567 | struct thread *thread; |
567 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 568 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
568 | struct thread_trace *ttrace; | 569 | struct thread_trace *ttrace; |
569 | 570 | ||
570 | if (sc == NULL) | 571 | if (sc == NULL) |
571 | return -1; | 572 | return -1; |
572 | 573 | ||
573 | if (sc->filtered) | 574 | if (sc->filtered) |
574 | return 0; | 575 | return 0; |
575 | 576 | ||
576 | thread = machine__findnew_thread(&trace->host, sample->pid, | 577 | thread = machine__findnew_thread(&trace->host, sample->pid, |
577 | sample->tid); | 578 | sample->tid); |
578 | ttrace = thread__trace(thread, trace->output); | 579 | ttrace = thread__trace(thread, trace->output); |
579 | if (ttrace == NULL) | 580 | if (ttrace == NULL) |
580 | return -1; | 581 | return -1; |
581 | 582 | ||
582 | ret = perf_evsel__intval(evsel, sample, "ret"); | 583 | ret = perf_evsel__intval(evsel, sample, "ret"); |
583 | 584 | ||
584 | ttrace = thread->priv; | 585 | ttrace = thread->priv; |
585 | 586 | ||
586 | ttrace->exit_time = sample->time; | 587 | ttrace->exit_time = sample->time; |
587 | 588 | ||
588 | if (ttrace->entry_time) { | 589 | if (ttrace->entry_time) { |
589 | duration = sample->time - ttrace->entry_time; | 590 | duration = sample->time - ttrace->entry_time; |
590 | if (trace__filter_duration(trace, duration)) | 591 | if (trace__filter_duration(trace, duration)) |
591 | goto out; | 592 | goto out; |
592 | } else if (trace->duration_filter) | 593 | } else if (trace->duration_filter) |
593 | goto out; | 594 | goto out; |
594 | 595 | ||
595 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); | 596 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); |
596 | 597 | ||
597 | if (ttrace->entry_pending) { | 598 | if (ttrace->entry_pending) { |
598 | fprintf(trace->output, "%-70s", ttrace->entry_str); | 599 | fprintf(trace->output, "%-70s", ttrace->entry_str); |
599 | } else { | 600 | } else { |
600 | fprintf(trace->output, " ... ["); | 601 | fprintf(trace->output, " ... ["); |
601 | color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued"); | 602 | color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued"); |
602 | fprintf(trace->output, "]: %s()", sc->name); | 603 | fprintf(trace->output, "]: %s()", sc->name); |
603 | } | 604 | } |
604 | 605 | ||
605 | if (sc->fmt == NULL) { | 606 | if (sc->fmt == NULL) { |
606 | signed_print: | 607 | signed_print: |
607 | fprintf(trace->output, ") = %d", ret); | 608 | fprintf(trace->output, ") = %d", ret); |
608 | } else if (ret < 0 && sc->fmt->errmsg) { | 609 | } else if (ret < 0 && sc->fmt->errmsg) { |
609 | char bf[256]; | 610 | char bf[256]; |
610 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 611 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
611 | *e = audit_errno_to_name(-ret); | 612 | *e = audit_errno_to_name(-ret); |
612 | 613 | ||
613 | fprintf(trace->output, ") = -1 %s %s", e, emsg); | 614 | fprintf(trace->output, ") = -1 %s %s", e, emsg); |
614 | } else if (ret == 0 && sc->fmt->timeout) | 615 | } else if (ret == 0 && sc->fmt->timeout) |
615 | fprintf(trace->output, ") = 0 Timeout"); | 616 | fprintf(trace->output, ") = 0 Timeout"); |
616 | else if (sc->fmt->hexret) | 617 | else if (sc->fmt->hexret) |
617 | fprintf(trace->output, ") = %#x", ret); | 618 | fprintf(trace->output, ") = %#x", ret); |
618 | else | 619 | else |
619 | goto signed_print; | 620 | goto signed_print; |
620 | 621 | ||
621 | fputc('\n', trace->output); | 622 | fputc('\n', trace->output); |
622 | out: | 623 | out: |
623 | ttrace->entry_pending = false; | 624 | ttrace->entry_pending = false; |
624 | 625 | ||
625 | return 0; | 626 | return 0; |
626 | } | 627 | } |
627 | 628 | ||
628 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, | 629 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, |
629 | struct perf_sample *sample) | 630 | struct perf_sample *sample) |
630 | { | 631 | { |
631 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 632 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
632 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | 633 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; |
633 | struct thread *thread = machine__findnew_thread(&trace->host, | 634 | struct thread *thread = machine__findnew_thread(&trace->host, |
634 | sample->pid, | 635 | sample->pid, |
635 | sample->tid); | 636 | sample->tid); |
636 | struct thread_trace *ttrace = thread__trace(thread, trace->output); | 637 | struct thread_trace *ttrace = thread__trace(thread, trace->output); |
637 | 638 | ||
638 | if (ttrace == NULL) | 639 | if (ttrace == NULL) |
639 | goto out_dump; | 640 | goto out_dump; |
640 | 641 | ||
641 | ttrace->runtime_ms += runtime_ms; | 642 | ttrace->runtime_ms += runtime_ms; |
642 | trace->runtime_ms += runtime_ms; | 643 | trace->runtime_ms += runtime_ms; |
643 | return 0; | 644 | return 0; |
644 | 645 | ||
645 | out_dump: | 646 | out_dump: |
646 | fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | 647 | fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", |
647 | evsel->name, | 648 | evsel->name, |
648 | perf_evsel__strval(evsel, sample, "comm"), | 649 | perf_evsel__strval(evsel, sample, "comm"), |
649 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | 650 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), |
650 | runtime, | 651 | runtime, |
651 | perf_evsel__intval(evsel, sample, "vruntime")); | 652 | perf_evsel__intval(evsel, sample, "vruntime")); |
652 | return 0; | 653 | return 0; |
653 | } | 654 | } |
654 | 655 | ||
656 | static int trace__process_sample(struct perf_tool *tool, | ||
657 | union perf_event *event __maybe_unused, | ||
658 | struct perf_sample *sample, | ||
659 | struct perf_evsel *evsel, | ||
660 | struct machine *machine __maybe_unused) | ||
661 | { | ||
662 | struct trace *trace = container_of(tool, struct trace, tool); | ||
663 | int err = 0; | ||
664 | |||
665 | tracepoint_handler handler = evsel->handler.func; | ||
666 | |||
667 | if (trace->base_time == 0) | ||
668 | trace->base_time = sample->time; | ||
669 | |||
670 | if (handler) | ||
671 | handler(trace, evsel, sample); | ||
672 | |||
673 | return err; | ||
674 | } | ||
675 | |||
676 | static bool | ||
677 | perf_session__has_tp(struct perf_session *session, const char *name) | ||
678 | { | ||
679 | struct perf_evsel *evsel; | ||
680 | |||
681 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name); | ||
682 | |||
683 | return evsel != NULL; | ||
684 | } | ||
685 | |||
655 | static int trace__run(struct trace *trace, int argc, const char **argv) | 686 | static int trace__run(struct trace *trace, int argc, const char **argv) |
656 | { | 687 | { |
657 | struct perf_evlist *evlist = perf_evlist__new(); | 688 | struct perf_evlist *evlist = perf_evlist__new(); |
658 | struct perf_evsel *evsel; | 689 | struct perf_evsel *evsel; |
659 | int err = -1, i; | 690 | int err = -1, i; |
660 | unsigned long before; | 691 | unsigned long before; |
661 | const bool forks = argc > 0; | 692 | const bool forks = argc > 0; |
662 | 693 | ||
663 | if (evlist == NULL) { | 694 | if (evlist == NULL) { |
664 | fprintf(trace->output, "Not enough memory to run!\n"); | 695 | fprintf(trace->output, "Not enough memory to run!\n"); |
665 | goto out; | 696 | goto out; |
666 | } | 697 | } |
667 | 698 | ||
668 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || | 699 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || |
669 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { | 700 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { |
670 | fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); | 701 | fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); |
671 | goto out_delete_evlist; | 702 | goto out_delete_evlist; |
672 | } | 703 | } |
673 | 704 | ||
674 | if (trace->sched && | 705 | if (trace->sched && |
675 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 706 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
676 | trace__sched_stat_runtime)) { | 707 | trace__sched_stat_runtime)) { |
677 | fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); | 708 | fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); |
678 | goto out_delete_evlist; | 709 | goto out_delete_evlist; |
679 | } | 710 | } |
680 | 711 | ||
681 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 712 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
682 | if (err < 0) { | 713 | if (err < 0) { |
683 | fprintf(trace->output, "Problems parsing the target to trace, check your options!\n"); | 714 | fprintf(trace->output, "Problems parsing the target to trace, check your options!\n"); |
684 | goto out_delete_evlist; | 715 | goto out_delete_evlist; |
685 | } | 716 | } |
686 | 717 | ||
687 | err = trace__symbols_init(trace, evlist); | 718 | err = trace__symbols_init(trace, evlist); |
688 | if (err < 0) { | 719 | if (err < 0) { |
689 | fprintf(trace->output, "Problems initializing symbol libraries!\n"); | 720 | fprintf(trace->output, "Problems initializing symbol libraries!\n"); |
690 | goto out_delete_maps; | 721 | goto out_delete_maps; |
691 | } | 722 | } |
692 | 723 | ||
693 | perf_evlist__config(evlist, &trace->opts); | 724 | perf_evlist__config(evlist, &trace->opts); |
694 | 725 | ||
695 | signal(SIGCHLD, sig_handler); | 726 | signal(SIGCHLD, sig_handler); |
696 | signal(SIGINT, sig_handler); | 727 | signal(SIGINT, sig_handler); |
697 | 728 | ||
698 | if (forks) { | 729 | if (forks) { |
699 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, | 730 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, |
700 | argv, false, false); | 731 | argv, false, false); |
701 | if (err < 0) { | 732 | if (err < 0) { |
702 | fprintf(trace->output, "Couldn't run the workload!\n"); | 733 | fprintf(trace->output, "Couldn't run the workload!\n"); |
703 | goto out_delete_maps; | 734 | goto out_delete_maps; |
704 | } | 735 | } |
705 | } | 736 | } |
706 | 737 | ||
707 | err = perf_evlist__open(evlist); | 738 | err = perf_evlist__open(evlist); |
708 | if (err < 0) { | 739 | if (err < 0) { |
709 | fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); | 740 | fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); |
710 | goto out_delete_maps; | 741 | goto out_delete_maps; |
711 | } | 742 | } |
712 | 743 | ||
713 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 744 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
714 | if (err < 0) { | 745 | if (err < 0) { |
715 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); | 746 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); |
716 | goto out_close_evlist; | 747 | goto out_close_evlist; |
717 | } | 748 | } |
718 | 749 | ||
719 | perf_evlist__enable(evlist); | 750 | perf_evlist__enable(evlist); |
720 | 751 | ||
721 | if (forks) | 752 | if (forks) |
722 | perf_evlist__start_workload(evlist); | 753 | perf_evlist__start_workload(evlist); |
723 | 754 | ||
724 | trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1; | 755 | trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1; |
725 | again: | 756 | again: |
726 | before = trace->nr_events; | 757 | before = trace->nr_events; |
727 | 758 | ||
728 | for (i = 0; i < evlist->nr_mmaps; i++) { | 759 | for (i = 0; i < evlist->nr_mmaps; i++) { |
729 | union perf_event *event; | 760 | union perf_event *event; |
730 | 761 | ||
731 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | 762 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { |
732 | const u32 type = event->header.type; | 763 | const u32 type = event->header.type; |
733 | tracepoint_handler handler; | 764 | tracepoint_handler handler; |
734 | struct perf_sample sample; | 765 | struct perf_sample sample; |
735 | 766 | ||
736 | ++trace->nr_events; | 767 | ++trace->nr_events; |
737 | 768 | ||
738 | err = perf_evlist__parse_sample(evlist, event, &sample); | 769 | err = perf_evlist__parse_sample(evlist, event, &sample); |
739 | if (err) { | 770 | if (err) { |
740 | fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); | 771 | fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); |
741 | continue; | 772 | continue; |
742 | } | 773 | } |
743 | 774 | ||
744 | if (trace->base_time == 0) | 775 | if (trace->base_time == 0) |
745 | trace->base_time = sample.time; | 776 | trace->base_time = sample.time; |
746 | 777 | ||
747 | if (type != PERF_RECORD_SAMPLE) { | 778 | if (type != PERF_RECORD_SAMPLE) { |
748 | trace__process_event(trace, &trace->host, event); | 779 | trace__process_event(trace, &trace->host, event); |
749 | continue; | 780 | continue; |
750 | } | 781 | } |
751 | 782 | ||
752 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 783 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
753 | if (evsel == NULL) { | 784 | if (evsel == NULL) { |
754 | fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 785 | fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
755 | continue; | 786 | continue; |
756 | } | 787 | } |
757 | 788 | ||
758 | if (sample.raw_data == NULL) { | 789 | if (sample.raw_data == NULL) { |
759 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 790 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
760 | perf_evsel__name(evsel), sample.tid, | 791 | perf_evsel__name(evsel), sample.tid, |
761 | sample.cpu, sample.raw_size); | 792 | sample.cpu, sample.raw_size); |
762 | continue; | 793 | continue; |
763 | } | 794 | } |
764 | 795 | ||
765 | handler = evsel->handler.func; | 796 | handler = evsel->handler.func; |
766 | handler(trace, evsel, &sample); | 797 | handler(trace, evsel, &sample); |
767 | } | 798 | } |
768 | } | 799 | } |
769 | 800 | ||
770 | if (trace->nr_events == before) { | 801 | if (trace->nr_events == before) { |
771 | if (done) | 802 | if (done) |
772 | goto out_unmap_evlist; | 803 | goto out_unmap_evlist; |
773 | 804 | ||
774 | poll(evlist->pollfd, evlist->nr_fds, -1); | 805 | poll(evlist->pollfd, evlist->nr_fds, -1); |
775 | } | 806 | } |
776 | 807 | ||
777 | if (done) | 808 | if (done) |
778 | perf_evlist__disable(evlist); | 809 | perf_evlist__disable(evlist); |
779 | 810 | ||
780 | goto again; | 811 | goto again; |
781 | 812 | ||
782 | out_unmap_evlist: | 813 | out_unmap_evlist: |
783 | perf_evlist__munmap(evlist); | 814 | perf_evlist__munmap(evlist); |
784 | out_close_evlist: | 815 | out_close_evlist: |
785 | perf_evlist__close(evlist); | 816 | perf_evlist__close(evlist); |
786 | out_delete_maps: | 817 | out_delete_maps: |
787 | perf_evlist__delete_maps(evlist); | 818 | perf_evlist__delete_maps(evlist); |
788 | out_delete_evlist: | 819 | out_delete_evlist: |
789 | perf_evlist__delete(evlist); | 820 | perf_evlist__delete(evlist); |
790 | out: | 821 | out: |
791 | return err; | 822 | return err; |
792 | } | 823 | } |
793 | 824 | ||
825 | static int trace__replay(struct trace *trace) | ||
826 | { | ||
827 | const struct perf_evsel_str_handler handlers[] = { | ||
828 | { "raw_syscalls:sys_enter", trace__sys_enter, }, | ||
829 | { "raw_syscalls:sys_exit", trace__sys_exit, }, | ||
830 | }; | ||
831 | |||
832 | struct perf_session *session; | ||
833 | int err = -1; | ||
834 | |||
835 | trace->tool.sample = trace__process_sample; | ||
836 | trace->tool.mmap = perf_event__process_mmap; | ||
837 | trace->tool.comm = perf_event__process_comm; | ||
838 | trace->tool.exit = perf_event__process_exit; | ||
839 | trace->tool.fork = perf_event__process_fork; | ||
840 | trace->tool.attr = perf_event__process_attr; | ||
841 | trace->tool.tracing_data = perf_event__process_tracing_data; | ||
842 | trace->tool.build_id = perf_event__process_build_id; | ||
843 | |||
844 | trace->tool.ordered_samples = true; | ||
845 | trace->tool.ordering_requires_timestamps = true; | ||
846 | |||
847 | /* add tid to output */ | ||
848 | trace->multiple_threads = true; | ||
849 | |||
850 | if (symbol__init() < 0) | ||
851 | return -1; | ||
852 | |||
853 | session = perf_session__new(input_name, O_RDONLY, 0, false, | ||
854 | &trace->tool); | ||
855 | if (session == NULL) | ||
856 | return -ENOMEM; | ||
857 | |||
858 | err = perf_session__set_tracepoints_handlers(session, handlers); | ||
859 | if (err) | ||
860 | goto out; | ||
861 | |||
862 | if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) { | ||
863 | pr_err("Data file does not have raw_syscalls:sys_enter events\n"); | ||
864 | goto out; | ||
865 | } | ||
866 | |||
867 | if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) { | ||
868 | pr_err("Data file does not have raw_syscalls:sys_exit events\n"); | ||
869 | goto out; | ||
870 | } | ||
871 | |||
872 | setup_pager(); | ||
873 | |||
874 | err = perf_session__process_events(session, &trace->tool); | ||
875 | if (err) | ||
876 | pr_err("Failed to process events, error %d", err); | ||
877 | |||
878 | out: | ||
879 | perf_session__delete(session); | ||
880 | |||
881 | return err; | ||
882 | } | ||
883 | |||
794 | static size_t trace__fprintf_threads_header(FILE *fp) | 884 | static size_t trace__fprintf_threads_header(FILE *fp) |
795 | { | 885 | { |
796 | size_t printed; | 886 | size_t printed; |
797 | 887 | ||
798 | printed = fprintf(fp, "\n _____________________________________________________________________\n"); | 888 | printed = fprintf(fp, "\n _____________________________________________________________________\n"); |
799 | printed += fprintf(fp," __) Summary of events (__\n\n"); | 889 | printed += fprintf(fp," __) Summary of events (__\n\n"); |
800 | printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | 890 | printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); |
801 | printed += fprintf(fp," _____________________________________________________________________\n\n"); | 891 | printed += fprintf(fp," _____________________________________________________________________\n\n"); |
802 | 892 | ||
803 | return printed; | 893 | return printed; |
804 | } | 894 | } |
805 | 895 | ||
806 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) | 896 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) |
807 | { | 897 | { |
808 | size_t printed = trace__fprintf_threads_header(fp); | 898 | size_t printed = trace__fprintf_threads_header(fp); |
809 | struct rb_node *nd; | 899 | struct rb_node *nd; |
810 | 900 | ||
811 | for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { | 901 | for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { |
812 | struct thread *thread = rb_entry(nd, struct thread, rb_node); | 902 | struct thread *thread = rb_entry(nd, struct thread, rb_node); |
813 | struct thread_trace *ttrace = thread->priv; | 903 | struct thread_trace *ttrace = thread->priv; |
814 | const char *color; | 904 | const char *color; |
815 | double ratio; | 905 | double ratio; |
816 | 906 | ||
817 | if (ttrace == NULL) | 907 | if (ttrace == NULL) |
818 | continue; | 908 | continue; |
819 | 909 | ||
820 | ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; | 910 | ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; |
821 | 911 | ||
822 | color = PERF_COLOR_NORMAL; | 912 | color = PERF_COLOR_NORMAL; |
823 | if (ratio > 50.0) | 913 | if (ratio > 50.0) |
824 | color = PERF_COLOR_RED; | 914 | color = PERF_COLOR_RED; |
825 | else if (ratio > 25.0) | 915 | else if (ratio > 25.0) |
826 | color = PERF_COLOR_GREEN; | 916 | color = PERF_COLOR_GREEN; |
827 | else if (ratio > 5.0) | 917 | else if (ratio > 5.0) |
828 | color = PERF_COLOR_YELLOW; | 918 | color = PERF_COLOR_YELLOW; |
829 | 919 | ||
830 | printed += color_fprintf(fp, color, "%20s", thread->comm); | 920 | printed += color_fprintf(fp, color, "%20s", thread->comm); |
831 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); | 921 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); |
832 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | 922 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); |
833 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | 923 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); |
834 | } | 924 | } |
835 | 925 | ||
836 | return printed; | 926 | return printed; |
837 | } | 927 | } |
838 | 928 | ||
839 | static int trace__set_duration(const struct option *opt, const char *str, | 929 | static int trace__set_duration(const struct option *opt, const char *str, |
840 | int unset __maybe_unused) | 930 | int unset __maybe_unused) |
841 | { | 931 | { |
842 | struct trace *trace = opt->value; | 932 | struct trace *trace = opt->value; |
843 | 933 | ||
844 | trace->duration_filter = atof(str); | 934 | trace->duration_filter = atof(str); |
845 | return 0; | 935 | return 0; |
846 | } | 936 | } |
847 | 937 | ||
848 | static int trace__open_output(struct trace *trace, const char *filename) | 938 | static int trace__open_output(struct trace *trace, const char *filename) |
849 | { | 939 | { |
850 | struct stat st; | 940 | struct stat st; |
851 | 941 | ||
852 | if (!stat(filename, &st) && st.st_size) { | 942 | if (!stat(filename, &st) && st.st_size) { |
853 | char oldname[PATH_MAX]; | 943 | char oldname[PATH_MAX]; |
854 | 944 | ||
855 | scnprintf(oldname, sizeof(oldname), "%s.old", filename); | 945 | scnprintf(oldname, sizeof(oldname), "%s.old", filename); |
856 | unlink(oldname); | 946 | unlink(oldname); |
857 | rename(filename, oldname); | 947 | rename(filename, oldname); |
858 | } | 948 | } |
859 | 949 | ||
860 | trace->output = fopen(filename, "w"); | 950 | trace->output = fopen(filename, "w"); |
861 | 951 | ||
862 | return trace->output == NULL ? -errno : 0; | 952 | return trace->output == NULL ? -errno : 0; |
863 | } | 953 | } |
864 | 954 | ||
865 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 955 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
866 | { | 956 | { |
867 | const char * const trace_usage[] = { | 957 | const char * const trace_usage[] = { |
868 | "perf trace [<options>] [<command>]", | 958 | "perf trace [<options>] [<command>]", |
869 | "perf trace [<options>] -- <command> [<options>]", | 959 | "perf trace [<options>] -- <command> [<options>]", |
870 | NULL | 960 | NULL |
871 | }; | 961 | }; |
872 | struct trace trace = { | 962 | struct trace trace = { |
873 | .audit_machine = audit_detect_machine(), | 963 | .audit_machine = audit_detect_machine(), |
874 | .syscalls = { | 964 | .syscalls = { |
875 | . max = -1, | 965 | . max = -1, |
876 | }, | 966 | }, |
877 | .opts = { | 967 | .opts = { |
878 | .target = { | 968 | .target = { |
879 | .uid = UINT_MAX, | 969 | .uid = UINT_MAX, |
880 | .uses_mmap = true, | 970 | .uses_mmap = true, |
881 | }, | 971 | }, |
882 | .user_freq = UINT_MAX, | 972 | .user_freq = UINT_MAX, |
883 | .user_interval = ULLONG_MAX, | 973 | .user_interval = ULLONG_MAX, |
884 | .no_delay = true, | 974 | .no_delay = true, |
885 | .mmap_pages = 1024, | 975 | .mmap_pages = 1024, |
886 | }, | 976 | }, |
887 | .output = stdout, | 977 | .output = stdout, |
888 | }; | 978 | }; |
889 | const char *output_name = NULL; | 979 | const char *output_name = NULL; |
890 | const char *ev_qualifier_str = NULL; | 980 | const char *ev_qualifier_str = NULL; |
891 | const struct option trace_options[] = { | 981 | const struct option trace_options[] = { |
892 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | 982 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", |
893 | "list of events to trace"), | 983 | "list of events to trace"), |
894 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | 984 | OPT_STRING('o', "output", &output_name, "file", "output file name"), |
985 | OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), | ||
895 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", | 986 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", |
896 | "trace events on existing process id"), | 987 | "trace events on existing process id"), |
897 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", | 988 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", |
898 | "trace events on existing thread id"), | 989 | "trace events on existing thread id"), |
899 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, | 990 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, |
900 | "system-wide collection from all CPUs"), | 991 | "system-wide collection from all CPUs"), |
901 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", | 992 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", |
902 | "list of cpus to monitor"), | 993 | "list of cpus to monitor"), |
903 | OPT_BOOLEAN('i', "no-inherit", &trace.opts.no_inherit, | 994 | OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, |
904 | "child tasks do not inherit counters"), | 995 | "child tasks do not inherit counters"), |
905 | OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, | 996 | OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, |
906 | "number of mmap data pages"), | 997 | "number of mmap data pages"), |
907 | OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user", | 998 | OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user", |
908 | "user to profile"), | 999 | "user to profile"), |
909 | OPT_CALLBACK(0, "duration", &trace, "float", | 1000 | OPT_CALLBACK(0, "duration", &trace, "float", |
910 | "show only events with duration > N.M ms", | 1001 | "show only events with duration > N.M ms", |
911 | trace__set_duration), | 1002 | trace__set_duration), |
912 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), | 1003 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), |
913 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | 1004 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
914 | OPT_END() | 1005 | OPT_END() |
915 | }; | 1006 | }; |
916 | int err; | 1007 | int err; |
917 | char bf[BUFSIZ]; | 1008 | char bf[BUFSIZ]; |
918 | 1009 | ||
919 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 1010 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
920 | 1011 | ||
921 | if (output_name != NULL) { | 1012 | if (output_name != NULL) { |
922 | err = trace__open_output(&trace, output_name); | 1013 | err = trace__open_output(&trace, output_name); |
923 | if (err < 0) { | 1014 | if (err < 0) { |
924 | perror("failed to create output file"); | 1015 | perror("failed to create output file"); |
925 | goto out; | 1016 | goto out; |
926 | } | 1017 | } |
927 | } | 1018 | } |
928 | 1019 | ||
929 | if (ev_qualifier_str != NULL) { | 1020 | if (ev_qualifier_str != NULL) { |
930 | const char *s = ev_qualifier_str; | 1021 | const char *s = ev_qualifier_str; |
931 | 1022 | ||
932 | trace.not_ev_qualifier = *s == '!'; | 1023 | trace.not_ev_qualifier = *s == '!'; |
933 | if (trace.not_ev_qualifier) | 1024 | if (trace.not_ev_qualifier) |
934 | ++s; | 1025 | ++s; |
935 | trace.ev_qualifier = strlist__new(true, s); | 1026 | trace.ev_qualifier = strlist__new(true, s); |
936 | if (trace.ev_qualifier == NULL) { | 1027 | if (trace.ev_qualifier == NULL) { |
937 | fputs("Not enough memory to parse event qualifier", | 1028 | fputs("Not enough memory to parse event qualifier", |
938 | trace.output); | 1029 | trace.output); |
939 | err = -ENOMEM; | 1030 | err = -ENOMEM; |
940 | goto out_close; | 1031 | goto out_close; |
941 | } | 1032 | } |
942 | } | 1033 | } |
943 | 1034 | ||
944 | err = perf_target__validate(&trace.opts.target); | 1035 | err = perf_target__validate(&trace.opts.target); |
945 | if (err) { | 1036 | if (err) { |
946 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 1037 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
947 | fprintf(trace.output, "%s", bf); | 1038 | fprintf(trace.output, "%s", bf); |
948 | goto out_close; | 1039 | goto out_close; |
949 | } | 1040 | } |
950 | 1041 | ||
951 | err = perf_target__parse_uid(&trace.opts.target); | 1042 | err = perf_target__parse_uid(&trace.opts.target); |
952 | if (err) { | 1043 | if (err) { |
953 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 1044 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
954 | fprintf(trace.output, "%s", bf); | 1045 | fprintf(trace.output, "%s", bf); |
955 | goto out_close; | 1046 | goto out_close; |
956 | } | 1047 | } |
957 | 1048 | ||
958 | if (!argc && perf_target__none(&trace.opts.target)) | 1049 | if (!argc && perf_target__none(&trace.opts.target)) |
959 | trace.opts.target.system_wide = true; | 1050 | trace.opts.target.system_wide = true; |
960 | 1051 | ||
961 | err = trace__run(&trace, argc, argv); | 1052 | if (input_name) |
1053 | err = trace__replay(&trace); | ||
1054 | else | ||
1055 | err = trace__run(&trace, argc, argv); | ||
962 | 1056 | ||
963 | if (trace.sched && !err) | 1057 | if (trace.sched && !err) |
964 | trace__fprintf_thread_summary(&trace, trace.output); | 1058 | trace__fprintf_thread_summary(&trace, trace.output); |
965 | 1059 | ||
966 | out_close: | 1060 | out_close: |
967 | if (output_name != NULL) | 1061 | if (output_name != NULL) |
968 | fclose(trace.output); | 1062 | fclose(trace.output); |
969 | out: | 1063 | out: |
970 | return err; | 1064 | return err; |
971 | } | 1065 | } |
972 | 1066 |