diff options
Diffstat (limited to 'tools/perf/util')
57 files changed, 2002 insertions, 721 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 809b4c50beae..36437527dbb3 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -232,9 +232,16 @@ static int mov__parse(struct ins_operands *ops) | |||
232 | return -1; | 232 | return -1; |
233 | 233 | ||
234 | target = ++s; | 234 | target = ++s; |
235 | comment = strchr(s, '#'); | ||
235 | 236 | ||
236 | while (s[0] != '\0' && !isspace(s[0])) | 237 | if (comment != NULL) |
237 | ++s; | 238 | s = comment - 1; |
239 | else | ||
240 | s = strchr(s, '\0') - 1; | ||
241 | |||
242 | while (s > target && isspace(s[0])) | ||
243 | --s; | ||
244 | s++; | ||
238 | prev = *s; | 245 | prev = *s; |
239 | *s = '\0'; | 246 | *s = '\0'; |
240 | 247 | ||
@@ -244,7 +251,6 @@ static int mov__parse(struct ins_operands *ops) | |||
244 | if (ops->target.raw == NULL) | 251 | if (ops->target.raw == NULL) |
245 | goto out_free_source; | 252 | goto out_free_source; |
246 | 253 | ||
247 | comment = strchr(s, '#'); | ||
248 | if (comment == NULL) | 254 | if (comment == NULL) |
249 | return 0; | 255 | return 0; |
250 | 256 | ||
@@ -899,10 +905,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
899 | struct kcore_extract kce; | 905 | struct kcore_extract kce; |
900 | bool delete_extract = false; | 906 | bool delete_extract = false; |
901 | 907 | ||
902 | if (filename) { | 908 | if (filename) |
903 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 909 | symbol__join_symfs(symfs_filename, filename); |
904 | symbol_conf.symfs, filename); | ||
905 | } | ||
906 | 910 | ||
907 | if (filename == NULL) { | 911 | if (filename == NULL) { |
908 | if (dso->has_build_id) { | 912 | if (dso->has_build_id) { |
@@ -922,8 +926,7 @@ fallback: | |||
922 | * DSO is the same as when 'perf record' ran. | 926 | * DSO is the same as when 'perf record' ran. |
923 | */ | 927 | */ |
924 | filename = (char *)dso->long_name; | 928 | filename = (char *)dso->long_name; |
925 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 929 | symbol__join_symfs(symfs_filename, filename); |
926 | symbol_conf.symfs, filename); | ||
927 | free_filename = false; | 930 | free_filename = false; |
928 | } | 931 | } |
929 | 932 | ||
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 7b176dd02e1a..5cf9e1b5989d 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -22,6 +22,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *); | |||
22 | extern int perf_default_config(const char *, const char *, void *); | 22 | extern int perf_default_config(const char *, const char *, void *); |
23 | extern int perf_config(config_fn_t fn, void *); | 23 | extern int perf_config(config_fn_t fn, void *); |
24 | extern int perf_config_int(const char *, const char *); | 24 | extern int perf_config_int(const char *, const char *); |
25 | extern u64 perf_config_u64(const char *, const char *); | ||
25 | extern int perf_config_bool(const char *, const char *); | 26 | extern int perf_config_bool(const char *, const char *); |
26 | extern int config_error_nonbool(const char *); | 27 | extern int config_error_nonbool(const char *); |
27 | extern const char *perf_config_dirname(const char *, const char *); | 28 | extern const char *perf_config_dirname(const char *, const char *); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 437ee09727e6..c84d3f8dcb75 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -25,77 +25,172 @@ | |||
25 | 25 | ||
26 | __thread struct callchain_cursor callchain_cursor; | 26 | __thread struct callchain_cursor callchain_cursor; |
27 | 27 | ||
28 | int | 28 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
29 | parse_callchain_report_opt(const char *arg) | 29 | static int get_stack_size(const char *str, unsigned long *_size) |
30 | { | 30 | { |
31 | char *tok, *tok2; | ||
32 | char *endptr; | 31 | char *endptr; |
32 | unsigned long size; | ||
33 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
33 | 34 | ||
34 | symbol_conf.use_callchain = true; | 35 | size = strtoul(str, &endptr, 0); |
35 | 36 | ||
36 | if (!arg) | 37 | do { |
38 | if (*endptr) | ||
39 | break; | ||
40 | |||
41 | size = round_up(size, sizeof(u64)); | ||
42 | if (!size || size > max_size) | ||
43 | break; | ||
44 | |||
45 | *_size = size; | ||
37 | return 0; | 46 | return 0; |
38 | 47 | ||
39 | tok = strtok((char *)arg, ","); | 48 | } while (0); |
40 | if (!tok) | ||
41 | return -1; | ||
42 | 49 | ||
43 | /* get the output mode */ | 50 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", |
44 | if (!strncmp(tok, "graph", strlen(arg))) { | 51 | max_size, str); |
45 | callchain_param.mode = CHAIN_GRAPH_ABS; | 52 | return -1; |
53 | } | ||
54 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
46 | 55 | ||
47 | } else if (!strncmp(tok, "flat", strlen(arg))) { | 56 | int parse_callchain_record_opt(const char *arg) |
48 | callchain_param.mode = CHAIN_FLAT; | 57 | { |
49 | } else if (!strncmp(tok, "fractal", strlen(arg))) { | 58 | char *tok, *name, *saveptr = NULL; |
50 | callchain_param.mode = CHAIN_GRAPH_REL; | 59 | char *buf; |
51 | } else if (!strncmp(tok, "none", strlen(arg))) { | 60 | int ret = -1; |
52 | callchain_param.mode = CHAIN_NONE; | 61 | |
53 | symbol_conf.use_callchain = false; | 62 | /* We need buffer that we know we can write to. */ |
54 | return 0; | 63 | buf = malloc(strlen(arg) + 1); |
55 | } else { | 64 | if (!buf) |
56 | return -1; | 65 | return -ENOMEM; |
57 | } | 66 | |
67 | strcpy(buf, arg); | ||
68 | |||
69 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
70 | name = tok ? : (char *)buf; | ||
71 | |||
72 | do { | ||
73 | /* Framepointer style */ | ||
74 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
75 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
76 | callchain_param.record_mode = CALLCHAIN_FP; | ||
77 | ret = 0; | ||
78 | } else | ||
79 | pr_err("callchain: No more arguments " | ||
80 | "needed for -g fp\n"); | ||
81 | break; | ||
58 | 82 | ||
59 | /* get the min percentage */ | 83 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
60 | tok = strtok(NULL, ","); | 84 | /* Dwarf style */ |
61 | if (!tok) | 85 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { |
62 | goto setup; | 86 | const unsigned long default_stack_dump_size = 8192; |
63 | 87 | ||
64 | callchain_param.min_percent = strtod(tok, &endptr); | 88 | ret = 0; |
65 | if (tok == endptr) | 89 | callchain_param.record_mode = CALLCHAIN_DWARF; |
66 | return -1; | 90 | callchain_param.dump_size = default_stack_dump_size; |
67 | 91 | ||
68 | /* get the print limit */ | 92 | tok = strtok_r(NULL, ",", &saveptr); |
69 | tok2 = strtok(NULL, ","); | 93 | if (tok) { |
70 | if (!tok2) | 94 | unsigned long size = 0; |
71 | goto setup; | ||
72 | 95 | ||
73 | if (tok2[0] != 'c') { | 96 | ret = get_stack_size(tok, &size); |
74 | callchain_param.print_limit = strtoul(tok2, &endptr, 0); | 97 | callchain_param.dump_size = size; |
75 | tok2 = strtok(NULL, ","); | 98 | } |
76 | if (!tok2) | 99 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ |
77 | goto setup; | 100 | } else { |
101 | pr_err("callchain: Unknown --call-graph option " | ||
102 | "value: %s\n", arg); | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | } while (0); | ||
107 | |||
108 | free(buf); | ||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | static int parse_callchain_mode(const char *value) | ||
113 | { | ||
114 | if (!strncmp(value, "graph", strlen(value))) { | ||
115 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
116 | return 0; | ||
117 | } | ||
118 | if (!strncmp(value, "flat", strlen(value))) { | ||
119 | callchain_param.mode = CHAIN_FLAT; | ||
120 | return 0; | ||
78 | } | 121 | } |
122 | if (!strncmp(value, "fractal", strlen(value))) { | ||
123 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
124 | return 0; | ||
125 | } | ||
126 | return -1; | ||
127 | } | ||
79 | 128 | ||
80 | /* get the call chain order */ | 129 | static int parse_callchain_order(const char *value) |
81 | if (!strncmp(tok2, "caller", strlen("caller"))) | 130 | { |
131 | if (!strncmp(value, "caller", strlen(value))) { | ||
82 | callchain_param.order = ORDER_CALLER; | 132 | callchain_param.order = ORDER_CALLER; |
83 | else if (!strncmp(tok2, "callee", strlen("callee"))) | 133 | return 0; |
134 | } | ||
135 | if (!strncmp(value, "callee", strlen(value))) { | ||
84 | callchain_param.order = ORDER_CALLEE; | 136 | callchain_param.order = ORDER_CALLEE; |
85 | else | 137 | return 0; |
86 | return -1; | 138 | } |
139 | return -1; | ||
140 | } | ||
87 | 141 | ||
88 | /* Get the sort key */ | 142 | static int parse_callchain_sort_key(const char *value) |
89 | tok2 = strtok(NULL, ","); | 143 | { |
90 | if (!tok2) | 144 | if (!strncmp(value, "function", strlen(value))) { |
91 | goto setup; | ||
92 | if (!strncmp(tok2, "function", strlen("function"))) | ||
93 | callchain_param.key = CCKEY_FUNCTION; | 145 | callchain_param.key = CCKEY_FUNCTION; |
94 | else if (!strncmp(tok2, "address", strlen("address"))) | 146 | return 0; |
147 | } | ||
148 | if (!strncmp(value, "address", strlen(value))) { | ||
95 | callchain_param.key = CCKEY_ADDRESS; | 149 | callchain_param.key = CCKEY_ADDRESS; |
96 | else | 150 | return 0; |
97 | return -1; | 151 | } |
98 | setup: | 152 | return -1; |
153 | } | ||
154 | |||
155 | int | ||
156 | parse_callchain_report_opt(const char *arg) | ||
157 | { | ||
158 | char *tok; | ||
159 | char *endptr; | ||
160 | bool minpcnt_set = false; | ||
161 | |||
162 | symbol_conf.use_callchain = true; | ||
163 | |||
164 | if (!arg) | ||
165 | return 0; | ||
166 | |||
167 | while ((tok = strtok((char *)arg, ",")) != NULL) { | ||
168 | if (!strncmp(tok, "none", strlen(tok))) { | ||
169 | callchain_param.mode = CHAIN_NONE; | ||
170 | symbol_conf.use_callchain = false; | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | if (!parse_callchain_mode(tok) || | ||
175 | !parse_callchain_order(tok) || | ||
176 | !parse_callchain_sort_key(tok)) { | ||
177 | /* parsing ok - move on to the next */ | ||
178 | } else if (!minpcnt_set) { | ||
179 | /* try to get the min percent */ | ||
180 | callchain_param.min_percent = strtod(tok, &endptr); | ||
181 | if (tok == endptr) | ||
182 | return -1; | ||
183 | minpcnt_set = true; | ||
184 | } else { | ||
185 | /* try print limit at last */ | ||
186 | callchain_param.print_limit = strtoul(tok, &endptr, 0); | ||
187 | if (tok == endptr) | ||
188 | return -1; | ||
189 | } | ||
190 | |||
191 | arg = NULL; | ||
192 | } | ||
193 | |||
99 | if (callchain_register_param(&callchain_param) < 0) { | 194 | if (callchain_register_param(&callchain_param) < 0) { |
100 | pr_err("Can't register callchain params\n"); | 195 | pr_err("Can't register callchain params\n"); |
101 | return -1; | 196 | return -1; |
@@ -103,6 +198,47 @@ setup: | |||
103 | return 0; | 198 | return 0; |
104 | } | 199 | } |
105 | 200 | ||
201 | int perf_callchain_config(const char *var, const char *value) | ||
202 | { | ||
203 | char *endptr; | ||
204 | |||
205 | if (prefixcmp(var, "call-graph.")) | ||
206 | return 0; | ||
207 | var += sizeof("call-graph.") - 1; | ||
208 | |||
209 | if (!strcmp(var, "record-mode")) | ||
210 | return parse_callchain_record_opt(value); | ||
211 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
212 | if (!strcmp(var, "dump-size")) { | ||
213 | unsigned long size = 0; | ||
214 | int ret; | ||
215 | |||
216 | ret = get_stack_size(value, &size); | ||
217 | callchain_param.dump_size = size; | ||
218 | |||
219 | return ret; | ||
220 | } | ||
221 | #endif | ||
222 | if (!strcmp(var, "print-type")) | ||
223 | return parse_callchain_mode(value); | ||
224 | if (!strcmp(var, "order")) | ||
225 | return parse_callchain_order(value); | ||
226 | if (!strcmp(var, "sort-key")) | ||
227 | return parse_callchain_sort_key(value); | ||
228 | if (!strcmp(var, "threshold")) { | ||
229 | callchain_param.min_percent = strtod(value, &endptr); | ||
230 | if (value == endptr) | ||
231 | return -1; | ||
232 | } | ||
233 | if (!strcmp(var, "print-limit")) { | ||
234 | callchain_param.print_limit = strtod(value, &endptr); | ||
235 | if (value == endptr) | ||
236 | return -1; | ||
237 | } | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
106 | static void | 242 | static void |
107 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 243 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
108 | enum chain_mode mode) | 244 | enum chain_mode mode) |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index da43619d6173..2a1f5a46543a 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -54,6 +54,9 @@ enum chain_key { | |||
54 | }; | 54 | }; |
55 | 55 | ||
56 | struct callchain_param { | 56 | struct callchain_param { |
57 | bool enabled; | ||
58 | enum perf_call_graph_mode record_mode; | ||
59 | u32 dump_size; | ||
57 | enum chain_mode mode; | 60 | enum chain_mode mode; |
58 | u32 print_limit; | 61 | u32 print_limit; |
59 | double min_percent; | 62 | double min_percent; |
@@ -154,7 +157,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
154 | struct option; | 157 | struct option; |
155 | struct hist_entry; | 158 | struct hist_entry; |
156 | 159 | ||
157 | int record_parse_callchain(const char *arg, struct record_opts *opts); | ||
158 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | 160 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); |
159 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); | 161 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); |
160 | 162 | ||
@@ -166,7 +168,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
166 | bool hide_unresolved); | 168 | bool hide_unresolved); |
167 | 169 | ||
168 | extern const char record_callchain_help[]; | 170 | extern const char record_callchain_help[]; |
171 | int parse_callchain_record_opt(const char *arg); | ||
169 | int parse_callchain_report_opt(const char *arg); | 172 | int parse_callchain_report_opt(const char *arg); |
173 | int perf_callchain_config(const char *var, const char *value); | ||
170 | 174 | ||
171 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | 175 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, |
172 | struct callchain_cursor *src) | 176 | struct callchain_cursor *src) |
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c index c5d05ec17220..47b78b3f0325 100644 --- a/tools/perf/util/cloexec.c +++ b/tools/perf/util/cloexec.c | |||
@@ -1,7 +1,9 @@ | |||
1 | #include <sched.h> | ||
1 | #include "util.h" | 2 | #include "util.h" |
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "cloexec.h" | 4 | #include "cloexec.h" |
4 | #include "asm/bug.h" | 5 | #include "asm/bug.h" |
6 | #include "debug.h" | ||
5 | 7 | ||
6 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; | 8 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; |
7 | 9 | ||
@@ -9,15 +11,30 @@ static int perf_flag_probe(void) | |||
9 | { | 11 | { |
10 | /* use 'safest' configuration as used in perf_evsel__fallback() */ | 12 | /* use 'safest' configuration as used in perf_evsel__fallback() */ |
11 | struct perf_event_attr attr = { | 13 | struct perf_event_attr attr = { |
12 | .type = PERF_COUNT_SW_CPU_CLOCK, | 14 | .type = PERF_TYPE_SOFTWARE, |
13 | .config = PERF_COUNT_SW_CPU_CLOCK, | 15 | .config = PERF_COUNT_SW_CPU_CLOCK, |
16 | .exclude_kernel = 1, | ||
14 | }; | 17 | }; |
15 | int fd; | 18 | int fd; |
16 | int err; | 19 | int err; |
20 | int cpu; | ||
21 | pid_t pid = -1; | ||
22 | char sbuf[STRERR_BUFSIZE]; | ||
17 | 23 | ||
18 | /* check cloexec flag */ | 24 | cpu = sched_getcpu(); |
19 | fd = sys_perf_event_open(&attr, 0, -1, -1, | 25 | if (cpu < 0) |
20 | PERF_FLAG_FD_CLOEXEC); | 26 | cpu = 0; |
27 | |||
28 | while (1) { | ||
29 | /* check cloexec flag */ | ||
30 | fd = sys_perf_event_open(&attr, pid, cpu, -1, | ||
31 | PERF_FLAG_FD_CLOEXEC); | ||
32 | if (fd < 0 && pid == -1 && errno == EACCES) { | ||
33 | pid = 0; | ||
34 | continue; | ||
35 | } | ||
36 | break; | ||
37 | } | ||
21 | err = errno; | 38 | err = errno; |
22 | 39 | ||
23 | if (fd >= 0) { | 40 | if (fd >= 0) { |
@@ -25,17 +42,17 @@ static int perf_flag_probe(void) | |||
25 | return 1; | 42 | return 1; |
26 | } | 43 | } |
27 | 44 | ||
28 | WARN_ONCE(err != EINVAL, | 45 | WARN_ONCE(err != EINVAL && err != EBUSY, |
29 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", | 46 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", |
30 | err, strerror(err)); | 47 | err, strerror_r(err, sbuf, sizeof(sbuf))); |
31 | 48 | ||
32 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ | 49 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ |
33 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | 50 | fd = sys_perf_event_open(&attr, pid, cpu, -1, 0); |
34 | err = errno; | 51 | err = errno; |
35 | 52 | ||
36 | if (WARN_ONCE(fd < 0, | 53 | if (WARN_ONCE(fd < 0 && err != EBUSY, |
37 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", | 54 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", |
38 | err, strerror(err))) | 55 | err, strerror_r(err, sbuf, sizeof(sbuf)))) |
39 | return -1; | 56 | return -1; |
40 | 57 | ||
41 | close(fd); | 58 | close(fd); |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 87b8672eb413..f4654183d391 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -335,3 +335,19 @@ int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
335 | va_end(args); | 335 | va_end(args); |
336 | return value_color_snprintf(bf, size, fmt, percent); | 336 | return value_color_snprintf(bf, size, fmt, percent); |
337 | } | 337 | } |
338 | |||
339 | int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...) | ||
340 | { | ||
341 | va_list args; | ||
342 | int len; | ||
343 | double percent; | ||
344 | const char *color; | ||
345 | |||
346 | va_start(args, fmt); | ||
347 | len = va_arg(args, int); | ||
348 | percent = va_arg(args, double); | ||
349 | va_end(args); | ||
350 | |||
351 | color = get_percent_color(percent); | ||
352 | return color_snprintf(bf, size, color, fmt, len, percent); | ||
353 | } | ||
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 7ff30a62a132..0a594b8a0c26 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
@@ -41,6 +41,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); | |||
41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); | 41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); |
42 | int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); | 42 | int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); |
43 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); | 43 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); |
44 | int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...); | ||
44 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 45 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
45 | const char *get_percent_color(double percent); | 46 | const char *get_percent_color(double percent); |
46 | 47 | ||
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index f9e777629e21..b2bb59df65e1 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c | |||
@@ -74,7 +74,7 @@ static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) | |||
74 | return new; | 74 | return new; |
75 | } | 75 | } |
76 | 76 | ||
77 | struct comm *comm__new(const char *str, u64 timestamp) | 77 | struct comm *comm__new(const char *str, u64 timestamp, bool exec) |
78 | { | 78 | { |
79 | struct comm *comm = zalloc(sizeof(*comm)); | 79 | struct comm *comm = zalloc(sizeof(*comm)); |
80 | 80 | ||
@@ -82,6 +82,7 @@ struct comm *comm__new(const char *str, u64 timestamp) | |||
82 | return NULL; | 82 | return NULL; |
83 | 83 | ||
84 | comm->start = timestamp; | 84 | comm->start = timestamp; |
85 | comm->exec = exec; | ||
85 | 86 | ||
86 | comm->comm_str = comm_str__findnew(str, &comm_str_root); | 87 | comm->comm_str = comm_str__findnew(str, &comm_str_root); |
87 | if (!comm->comm_str) { | 88 | if (!comm->comm_str) { |
@@ -94,7 +95,7 @@ struct comm *comm__new(const char *str, u64 timestamp) | |||
94 | return comm; | 95 | return comm; |
95 | } | 96 | } |
96 | 97 | ||
97 | int comm__override(struct comm *comm, const char *str, u64 timestamp) | 98 | int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec) |
98 | { | 99 | { |
99 | struct comm_str *new, *old = comm->comm_str; | 100 | struct comm_str *new, *old = comm->comm_str; |
100 | 101 | ||
@@ -106,6 +107,8 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp) | |||
106 | comm_str__put(old); | 107 | comm_str__put(old); |
107 | comm->comm_str = new; | 108 | comm->comm_str = new; |
108 | comm->start = timestamp; | 109 | comm->start = timestamp; |
110 | if (exec) | ||
111 | comm->exec = true; | ||
109 | 112 | ||
110 | return 0; | 113 | return 0; |
111 | } | 114 | } |
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index fac5bd51befc..51c10ab257f8 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h | |||
@@ -11,11 +11,13 @@ struct comm { | |||
11 | struct comm_str *comm_str; | 11 | struct comm_str *comm_str; |
12 | u64 start; | 12 | u64 start; |
13 | struct list_head list; | 13 | struct list_head list; |
14 | bool exec; | ||
14 | }; | 15 | }; |
15 | 16 | ||
16 | void comm__free(struct comm *comm); | 17 | void comm__free(struct comm *comm); |
17 | struct comm *comm__new(const char *str, u64 timestamp); | 18 | struct comm *comm__new(const char *str, u64 timestamp, bool exec); |
18 | const char *comm__str(const struct comm *comm); | 19 | const char *comm__str(const struct comm *comm); |
19 | int comm__override(struct comm *comm, const char *str, u64 timestamp); | 20 | int comm__override(struct comm *comm, const char *str, u64 timestamp, |
21 | bool exec); | ||
20 | 22 | ||
21 | #endif /* __PERF_COMM_H */ | 23 | #endif /* __PERF_COMM_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 1e5e2e5af6b1..57ff826f150b 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data) | |||
222 | const unsigned char *bomptr = utf8_bom; | 222 | const unsigned char *bomptr = utf8_bom; |
223 | 223 | ||
224 | for (;;) { | 224 | for (;;) { |
225 | int c = get_next_char(); | 225 | int line, c = get_next_char(); |
226 | |||
226 | if (bomptr && *bomptr) { | 227 | if (bomptr && *bomptr) { |
227 | /* We are at the file beginning; skip UTF8-encoded BOM | 228 | /* We are at the file beginning; skip UTF8-encoded BOM |
228 | * if present. Sane editors won't put this in on their | 229 | * if present. Sane editors won't put this in on their |
@@ -261,8 +262,16 @@ static int perf_parse_file(config_fn_t fn, void *data) | |||
261 | if (!isalpha(c)) | 262 | if (!isalpha(c)) |
262 | break; | 263 | break; |
263 | var[baselen] = tolower(c); | 264 | var[baselen] = tolower(c); |
264 | if (get_value(fn, data, var, baselen+1) < 0) | 265 | |
266 | /* | ||
267 | * The get_value function might or might not reach the '\n', | ||
268 | * so saving the current line number for error reporting. | ||
269 | */ | ||
270 | line = config_linenr; | ||
271 | if (get_value(fn, data, var, baselen+1) < 0) { | ||
272 | config_linenr = line; | ||
265 | break; | 273 | break; |
274 | } | ||
266 | } | 275 | } |
267 | die("bad config file line %d in %s", config_linenr, config_file_name); | 276 | die("bad config file line %d in %s", config_linenr, config_file_name); |
268 | } | 277 | } |
@@ -286,6 +295,21 @@ static int parse_unit_factor(const char *end, unsigned long *val) | |||
286 | return 0; | 295 | return 0; |
287 | } | 296 | } |
288 | 297 | ||
298 | static int perf_parse_llong(const char *value, long long *ret) | ||
299 | { | ||
300 | if (value && *value) { | ||
301 | char *end; | ||
302 | long long val = strtoll(value, &end, 0); | ||
303 | unsigned long factor = 1; | ||
304 | |||
305 | if (!parse_unit_factor(end, &factor)) | ||
306 | return 0; | ||
307 | *ret = val * factor; | ||
308 | return 1; | ||
309 | } | ||
310 | return 0; | ||
311 | } | ||
312 | |||
289 | static int perf_parse_long(const char *value, long *ret) | 313 | static int perf_parse_long(const char *value, long *ret) |
290 | { | 314 | { |
291 | if (value && *value) { | 315 | if (value && *value) { |
@@ -307,6 +331,15 @@ static void die_bad_config(const char *name) | |||
307 | die("bad config value for '%s'", name); | 331 | die("bad config value for '%s'", name); |
308 | } | 332 | } |
309 | 333 | ||
334 | u64 perf_config_u64(const char *name, const char *value) | ||
335 | { | ||
336 | long long ret = 0; | ||
337 | |||
338 | if (!perf_parse_llong(value, &ret)) | ||
339 | die_bad_config(name); | ||
340 | return (u64) ret; | ||
341 | } | ||
342 | |||
310 | int perf_config_int(const char *name, const char *value) | 343 | int perf_config_int(const char *name, const char *value) |
311 | { | 344 | { |
312 | long ret = 0; | 345 | long ret = 0; |
@@ -372,6 +405,9 @@ int perf_default_config(const char *var, const char *value, | |||
372 | if (!prefixcmp(var, "ui.")) | 405 | if (!prefixcmp(var, "ui.")) |
373 | return perf_ui_config(var, value); | 406 | return perf_ui_config(var, value); |
374 | 407 | ||
408 | if (!prefixcmp(var, "call-graph.")) | ||
409 | return perf_callchain_config(var, value); | ||
410 | |||
375 | /* Add other config variables here. */ | 411 | /* Add other config variables here. */ |
376 | return 0; | 412 | return 0; |
377 | } | 413 | } |
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 29d720cf5844..1921942fc2e0 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c | |||
@@ -50,12 +50,14 @@ static int open_file_read(struct perf_data_file *file) | |||
50 | { | 50 | { |
51 | struct stat st; | 51 | struct stat st; |
52 | int fd; | 52 | int fd; |
53 | char sbuf[STRERR_BUFSIZE]; | ||
53 | 54 | ||
54 | fd = open(file->path, O_RDONLY); | 55 | fd = open(file->path, O_RDONLY); |
55 | if (fd < 0) { | 56 | if (fd < 0) { |
56 | int err = errno; | 57 | int err = errno; |
57 | 58 | ||
58 | pr_err("failed to open %s: %s", file->path, strerror(err)); | 59 | pr_err("failed to open %s: %s", file->path, |
60 | strerror_r(err, sbuf, sizeof(sbuf))); | ||
59 | if (err == ENOENT && !strcmp(file->path, "perf.data")) | 61 | if (err == ENOENT && !strcmp(file->path, "perf.data")) |
60 | pr_err(" (try 'perf record' first)"); | 62 | pr_err(" (try 'perf record' first)"); |
61 | pr_err("\n"); | 63 | pr_err("\n"); |
@@ -88,6 +90,7 @@ static int open_file_read(struct perf_data_file *file) | |||
88 | static int open_file_write(struct perf_data_file *file) | 90 | static int open_file_write(struct perf_data_file *file) |
89 | { | 91 | { |
90 | int fd; | 92 | int fd; |
93 | char sbuf[STRERR_BUFSIZE]; | ||
91 | 94 | ||
92 | if (check_backup(file)) | 95 | if (check_backup(file)) |
93 | return -1; | 96 | return -1; |
@@ -95,7 +98,8 @@ static int open_file_write(struct perf_data_file *file) | |||
95 | fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); | 98 | fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); |
96 | 99 | ||
97 | if (fd < 0) | 100 | if (fd < 0) |
98 | pr_err("failed to open %s : %s\n", file->path, strerror(errno)); | 101 | pr_err("failed to open %s : %s\n", file->path, |
102 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
99 | 103 | ||
100 | return fd; | 104 | return fd; |
101 | } | 105 | } |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 71d419362634..ba357f3226c6 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -13,8 +13,12 @@ | |||
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "target.h" | 14 | #include "target.h" |
15 | 15 | ||
16 | #define NSECS_PER_SEC 1000000000ULL | ||
17 | #define NSECS_PER_USEC 1000ULL | ||
18 | |||
16 | int verbose; | 19 | int verbose; |
17 | bool dump_trace = false, quiet = false; | 20 | bool dump_trace = false, quiet = false; |
21 | int debug_ordered_events; | ||
18 | 22 | ||
19 | static int _eprintf(int level, int var, const char *fmt, va_list args) | 23 | static int _eprintf(int level, int var, const char *fmt, va_list args) |
20 | { | 24 | { |
@@ -42,6 +46,35 @@ int eprintf(int level, int var, const char *fmt, ...) | |||
42 | return ret; | 46 | return ret; |
43 | } | 47 | } |
44 | 48 | ||
49 | static int __eprintf_time(u64 t, const char *fmt, va_list args) | ||
50 | { | ||
51 | int ret = 0; | ||
52 | u64 secs, usecs, nsecs = t; | ||
53 | |||
54 | secs = nsecs / NSECS_PER_SEC; | ||
55 | nsecs -= secs * NSECS_PER_SEC; | ||
56 | usecs = nsecs / NSECS_PER_USEC; | ||
57 | |||
58 | ret = fprintf(stderr, "[%13" PRIu64 ".%06" PRIu64 "] ", | ||
59 | secs, usecs); | ||
60 | ret += vfprintf(stderr, fmt, args); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | int eprintf_time(int level, int var, u64 t, const char *fmt, ...) | ||
65 | { | ||
66 | int ret = 0; | ||
67 | va_list args; | ||
68 | |||
69 | if (var >= level) { | ||
70 | va_start(args, fmt); | ||
71 | ret = __eprintf_time(t, fmt, args); | ||
72 | va_end(args); | ||
73 | } | ||
74 | |||
75 | return ret; | ||
76 | } | ||
77 | |||
45 | /* | 78 | /* |
46 | * Overloading libtraceevent standard info print | 79 | * Overloading libtraceevent standard info print |
47 | * function, display with -v in perf. | 80 | * function, display with -v in perf. |
@@ -110,7 +143,8 @@ static struct debug_variable { | |||
110 | const char *name; | 143 | const char *name; |
111 | int *ptr; | 144 | int *ptr; |
112 | } debug_variables[] = { | 145 | } debug_variables[] = { |
113 | { .name = "verbose", .ptr = &verbose }, | 146 | { .name = "verbose", .ptr = &verbose }, |
147 | { .name = "ordered-events", .ptr = &debug_ordered_events}, | ||
114 | { .name = NULL, } | 148 | { .name = NULL, } |
115 | }; | 149 | }; |
116 | 150 | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 89fb6b0f7ab2..be264d6f3b30 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #define __PERF_DEBUG_H | 3 | #define __PERF_DEBUG_H |
4 | 4 | ||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <string.h> | ||
6 | #include "event.h" | 7 | #include "event.h" |
7 | #include "../ui/helpline.h" | 8 | #include "../ui/helpline.h" |
8 | #include "../ui/progress.h" | 9 | #include "../ui/progress.h" |
@@ -10,6 +11,7 @@ | |||
10 | 11 | ||
11 | extern int verbose; | 12 | extern int verbose; |
12 | extern bool quiet, dump_trace; | 13 | extern bool quiet, dump_trace; |
14 | extern int debug_ordered_events; | ||
13 | 15 | ||
14 | #ifndef pr_fmt | 16 | #ifndef pr_fmt |
15 | #define pr_fmt(fmt) fmt | 17 | #define pr_fmt(fmt) fmt |
@@ -29,6 +31,14 @@ extern bool quiet, dump_trace; | |||
29 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | 31 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) |
30 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) | 32 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) |
31 | 33 | ||
34 | #define pr_time_N(n, var, t, fmt, ...) \ | ||
35 | eprintf_time(n, var, t, fmt, ##__VA_ARGS__) | ||
36 | |||
37 | #define pr_oe_time(t, fmt, ...) pr_time_N(1, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__) | ||
38 | #define pr_oe_time2(t, fmt, ...) pr_time_N(2, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__) | ||
39 | |||
40 | #define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */ | ||
41 | |||
32 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 42 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
33 | void trace_event(union perf_event *event); | 43 | void trace_event(union perf_event *event); |
34 | 44 | ||
@@ -38,6 +48,7 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | |||
38 | void pr_stat(const char *fmt, ...); | 48 | void pr_stat(const char *fmt, ...); |
39 | 49 | ||
40 | int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); | 50 | int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); |
51 | int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5))); | ||
41 | 52 | ||
42 | int perf_debug_option(const char *str); | 53 | int perf_debug_option(const char *str); |
43 | 54 | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 90d02c661dd4..0247acfdfaca 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -37,6 +37,7 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
37 | { | 37 | { |
38 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 38 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
39 | int ret = 0; | 39 | int ret = 0; |
40 | size_t len; | ||
40 | 41 | ||
41 | switch (type) { | 42 | switch (type) { |
42 | case DSO_BINARY_TYPE__DEBUGLINK: { | 43 | case DSO_BINARY_TYPE__DEBUGLINK: { |
@@ -60,26 +61,25 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
60 | break; | 61 | break; |
61 | 62 | ||
62 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | 63 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: |
63 | snprintf(filename, size, "%s/usr/lib/debug%s.debug", | 64 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); |
64 | symbol_conf.symfs, dso->long_name); | 65 | snprintf(filename + len, size - len, "%s.debug", dso->long_name); |
65 | break; | 66 | break; |
66 | 67 | ||
67 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | 68 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: |
68 | snprintf(filename, size, "%s/usr/lib/debug%s", | 69 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); |
69 | symbol_conf.symfs, dso->long_name); | 70 | snprintf(filename + len, size - len, "%s", dso->long_name); |
70 | break; | 71 | break; |
71 | 72 | ||
72 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: | 73 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: |
73 | { | 74 | { |
74 | const char *last_slash; | 75 | const char *last_slash; |
75 | size_t len; | ||
76 | size_t dir_size; | 76 | size_t dir_size; |
77 | 77 | ||
78 | last_slash = dso->long_name + dso->long_name_len; | 78 | last_slash = dso->long_name + dso->long_name_len; |
79 | while (last_slash != dso->long_name && *last_slash != '/') | 79 | while (last_slash != dso->long_name && *last_slash != '/') |
80 | last_slash--; | 80 | last_slash--; |
81 | 81 | ||
82 | len = scnprintf(filename, size, "%s", symbol_conf.symfs); | 82 | len = __symbol__join_symfs(filename, size, ""); |
83 | dir_size = last_slash - dso->long_name + 2; | 83 | dir_size = last_slash - dso->long_name + 2; |
84 | if (dir_size > (size - len)) { | 84 | if (dir_size > (size - len)) { |
85 | ret = -1; | 85 | ret = -1; |
@@ -100,26 +100,24 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
100 | build_id__sprintf(dso->build_id, | 100 | build_id__sprintf(dso->build_id, |
101 | sizeof(dso->build_id), | 101 | sizeof(dso->build_id), |
102 | build_id_hex); | 102 | build_id_hex); |
103 | snprintf(filename, size, | 103 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); |
104 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | 104 | snprintf(filename + len, size - len, "%.2s/%s.debug", |
105 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 105 | build_id_hex, build_id_hex + 2); |
106 | break; | 106 | break; |
107 | 107 | ||
108 | case DSO_BINARY_TYPE__VMLINUX: | 108 | case DSO_BINARY_TYPE__VMLINUX: |
109 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | 109 | case DSO_BINARY_TYPE__GUEST_VMLINUX: |
110 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | 110 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: |
111 | snprintf(filename, size, "%s%s", | 111 | __symbol__join_symfs(filename, size, dso->long_name); |
112 | symbol_conf.symfs, dso->long_name); | ||
113 | break; | 112 | break; |
114 | 113 | ||
115 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 114 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
116 | snprintf(filename, size, "%s%s%s", symbol_conf.symfs, | 115 | path__join3(filename, size, symbol_conf.symfs, |
117 | root_dir, dso->long_name); | 116 | root_dir, dso->long_name); |
118 | break; | 117 | break; |
119 | 118 | ||
120 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 119 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
121 | snprintf(filename, size, "%s%s", symbol_conf.symfs, | 120 | __symbol__join_symfs(filename, size, dso->long_name); |
122 | dso->long_name); | ||
123 | break; | 121 | break; |
124 | 122 | ||
125 | case DSO_BINARY_TYPE__KCORE: | 123 | case DSO_BINARY_TYPE__KCORE: |
@@ -164,13 +162,15 @@ static void close_first_dso(void); | |||
164 | static int do_open(char *name) | 162 | static int do_open(char *name) |
165 | { | 163 | { |
166 | int fd; | 164 | int fd; |
165 | char sbuf[STRERR_BUFSIZE]; | ||
167 | 166 | ||
168 | do { | 167 | do { |
169 | fd = open(name, O_RDONLY); | 168 | fd = open(name, O_RDONLY); |
170 | if (fd >= 0) | 169 | if (fd >= 0) |
171 | return fd; | 170 | return fd; |
172 | 171 | ||
173 | pr_debug("dso open failed, mmap: %s\n", strerror(errno)); | 172 | pr_debug("dso open failed, mmap: %s\n", |
173 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
174 | if (!dso__data_open_cnt || errno != EMFILE) | 174 | if (!dso__data_open_cnt || errno != EMFILE) |
175 | break; | 175 | break; |
176 | 176 | ||
@@ -532,10 +532,12 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) | |||
532 | static int data_file_size(struct dso *dso) | 532 | static int data_file_size(struct dso *dso) |
533 | { | 533 | { |
534 | struct stat st; | 534 | struct stat st; |
535 | char sbuf[STRERR_BUFSIZE]; | ||
535 | 536 | ||
536 | if (!dso->data.file_size) { | 537 | if (!dso->data.file_size) { |
537 | if (fstat(dso->data.fd, &st)) { | 538 | if (fstat(dso->data.fd, &st)) { |
538 | pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); | 539 | pr_err("dso mmap failed, fstat: %s\n", |
540 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
539 | return -1; | 541 | return -1; |
540 | } | 542 | } |
541 | dso->data.file_size = st.st_size; | 543 | dso->data.file_size = st.st_size; |
@@ -651,6 +653,65 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | |||
651 | return dso; | 653 | return dso; |
652 | } | 654 | } |
653 | 655 | ||
656 | /* | ||
657 | * Find a matching entry and/or link current entry to RB tree. | ||
658 | * Either one of the dso or name parameter must be non-NULL or the | ||
659 | * function will not work. | ||
660 | */ | ||
661 | static struct dso *dso__findlink_by_longname(struct rb_root *root, | ||
662 | struct dso *dso, const char *name) | ||
663 | { | ||
664 | struct rb_node **p = &root->rb_node; | ||
665 | struct rb_node *parent = NULL; | ||
666 | |||
667 | if (!name) | ||
668 | name = dso->long_name; | ||
669 | /* | ||
670 | * Find node with the matching name | ||
671 | */ | ||
672 | while (*p) { | ||
673 | struct dso *this = rb_entry(*p, struct dso, rb_node); | ||
674 | int rc = strcmp(name, this->long_name); | ||
675 | |||
676 | parent = *p; | ||
677 | if (rc == 0) { | ||
678 | /* | ||
679 | * In case the new DSO is a duplicate of an existing | ||
680 | * one, print an one-time warning & put the new entry | ||
681 | * at the end of the list of duplicates. | ||
682 | */ | ||
683 | if (!dso || (dso == this)) | ||
684 | return this; /* Find matching dso */ | ||
685 | /* | ||
686 | * The core kernel DSOs may have duplicated long name. | ||
687 | * In this case, the short name should be different. | ||
688 | * Comparing the short names to differentiate the DSOs. | ||
689 | */ | ||
690 | rc = strcmp(dso->short_name, this->short_name); | ||
691 | if (rc == 0) { | ||
692 | pr_err("Duplicated dso name: %s\n", name); | ||
693 | return NULL; | ||
694 | } | ||
695 | } | ||
696 | if (rc < 0) | ||
697 | p = &parent->rb_left; | ||
698 | else | ||
699 | p = &parent->rb_right; | ||
700 | } | ||
701 | if (dso) { | ||
702 | /* Add new node and rebalance tree */ | ||
703 | rb_link_node(&dso->rb_node, parent, p); | ||
704 | rb_insert_color(&dso->rb_node, root); | ||
705 | } | ||
706 | return NULL; | ||
707 | } | ||
708 | |||
709 | static inline struct dso * | ||
710 | dso__find_by_longname(const struct rb_root *root, const char *name) | ||
711 | { | ||
712 | return dso__findlink_by_longname((struct rb_root *)root, NULL, name); | ||
713 | } | ||
714 | |||
654 | void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) | 715 | void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) |
655 | { | 716 | { |
656 | if (name == NULL) | 717 | if (name == NULL) |
@@ -753,6 +814,7 @@ struct dso *dso__new(const char *name) | |||
753 | dso->a2l_fails = 1; | 814 | dso->a2l_fails = 1; |
754 | dso->kernel = DSO_TYPE_USER; | 815 | dso->kernel = DSO_TYPE_USER; |
755 | dso->needs_swap = DSO_SWAP__UNSET; | 816 | dso->needs_swap = DSO_SWAP__UNSET; |
817 | RB_CLEAR_NODE(&dso->rb_node); | ||
756 | INIT_LIST_HEAD(&dso->node); | 818 | INIT_LIST_HEAD(&dso->node); |
757 | INIT_LIST_HEAD(&dso->data.open_entry); | 819 | INIT_LIST_HEAD(&dso->data.open_entry); |
758 | } | 820 | } |
@@ -763,6 +825,10 @@ struct dso *dso__new(const char *name) | |||
763 | void dso__delete(struct dso *dso) | 825 | void dso__delete(struct dso *dso) |
764 | { | 826 | { |
765 | int i; | 827 | int i; |
828 | |||
829 | if (!RB_EMPTY_NODE(&dso->rb_node)) | ||
830 | pr_err("DSO %s is still in rbtree when being deleted!\n", | ||
831 | dso->long_name); | ||
766 | for (i = 0; i < MAP__NR_TYPES; ++i) | 832 | for (i = 0; i < MAP__NR_TYPES; ++i) |
767 | symbols__delete(&dso->symbols[i]); | 833 | symbols__delete(&dso->symbols[i]); |
768 | 834 | ||
@@ -849,35 +915,34 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
849 | return have_build_id; | 915 | return have_build_id; |
850 | } | 916 | } |
851 | 917 | ||
852 | void dsos__add(struct list_head *head, struct dso *dso) | 918 | void dsos__add(struct dsos *dsos, struct dso *dso) |
853 | { | 919 | { |
854 | list_add_tail(&dso->node, head); | 920 | list_add_tail(&dso->node, &dsos->head); |
921 | dso__findlink_by_longname(&dsos->root, dso, NULL); | ||
855 | } | 922 | } |
856 | 923 | ||
857 | struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short) | 924 | struct dso *dsos__find(const struct dsos *dsos, const char *name, |
925 | bool cmp_short) | ||
858 | { | 926 | { |
859 | struct dso *pos; | 927 | struct dso *pos; |
860 | 928 | ||
861 | if (cmp_short) { | 929 | if (cmp_short) { |
862 | list_for_each_entry(pos, head, node) | 930 | list_for_each_entry(pos, &dsos->head, node) |
863 | if (strcmp(pos->short_name, name) == 0) | 931 | if (strcmp(pos->short_name, name) == 0) |
864 | return pos; | 932 | return pos; |
865 | return NULL; | 933 | return NULL; |
866 | } | 934 | } |
867 | list_for_each_entry(pos, head, node) | 935 | return dso__find_by_longname(&dsos->root, name); |
868 | if (strcmp(pos->long_name, name) == 0) | ||
869 | return pos; | ||
870 | return NULL; | ||
871 | } | 936 | } |
872 | 937 | ||
873 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | 938 | struct dso *__dsos__findnew(struct dsos *dsos, const char *name) |
874 | { | 939 | { |
875 | struct dso *dso = dsos__find(head, name, false); | 940 | struct dso *dso = dsos__find(dsos, name, false); |
876 | 941 | ||
877 | if (!dso) { | 942 | if (!dso) { |
878 | dso = dso__new(name); | 943 | dso = dso__new(name); |
879 | if (dso != NULL) { | 944 | if (dso != NULL) { |
880 | dsos__add(head, dso); | 945 | dsos__add(dsos, dso); |
881 | dso__set_basename(dso); | 946 | dso__set_basename(dso); |
882 | } | 947 | } |
883 | } | 948 | } |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 5e463c0964d4..acb651acc7fd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -90,8 +90,18 @@ struct dso_cache { | |||
90 | char data[0]; | 90 | char data[0]; |
91 | }; | 91 | }; |
92 | 92 | ||
93 | /* | ||
94 | * DSOs are put into both a list for fast iteration and rbtree for fast | ||
95 | * long name lookup. | ||
96 | */ | ||
97 | struct dsos { | ||
98 | struct list_head head; | ||
99 | struct rb_root root; /* rbtree root sorted by long name */ | ||
100 | }; | ||
101 | |||
93 | struct dso { | 102 | struct dso { |
94 | struct list_head node; | 103 | struct list_head node; |
104 | struct rb_node rb_node; /* rbtree node sorted by long name */ | ||
95 | struct rb_root symbols[MAP__NR_TYPES]; | 105 | struct rb_root symbols[MAP__NR_TYPES]; |
96 | struct rb_root symbol_names[MAP__NR_TYPES]; | 106 | struct rb_root symbol_names[MAP__NR_TYPES]; |
97 | void *a2l; | 107 | void *a2l; |
@@ -224,10 +234,10 @@ struct map *dso__new_map(const char *name); | |||
224 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | 234 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, |
225 | const char *short_name, int dso_type); | 235 | const char *short_name, int dso_type); |
226 | 236 | ||
227 | void dsos__add(struct list_head *head, struct dso *dso); | 237 | void dsos__add(struct dsos *dsos, struct dso *dso); |
228 | struct dso *dsos__find(const struct list_head *head, const char *name, | 238 | struct dso *dsos__find(const struct dsos *dsos, const char *name, |
229 | bool cmp_short); | 239 | bool cmp_short); |
230 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | 240 | struct dso *__dsos__findnew(struct dsos *dsos, const char *name); |
231 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | 241 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); |
232 | 242 | ||
233 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 243 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1398c83d896d..4af6b279e34a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -558,13 +558,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
558 | struct map *map; | 558 | struct map *map; |
559 | struct kmap *kmap; | 559 | struct kmap *kmap; |
560 | int err; | 560 | int err; |
561 | union perf_event *event; | ||
562 | |||
563 | if (machine->vmlinux_maps[0] == NULL) | ||
564 | return -1; | ||
565 | |||
561 | /* | 566 | /* |
562 | * We should get this from /sys/kernel/sections/.text, but till that is | 567 | * We should get this from /sys/kernel/sections/.text, but till that is |
563 | * available use this, and after it is use this as a fallback for older | 568 | * available use this, and after it is use this as a fallback for older |
564 | * kernels. | 569 | * kernels. |
565 | */ | 570 | */ |
566 | union perf_event *event = zalloc((sizeof(event->mmap) + | 571 | event = zalloc((sizeof(event->mmap) + machine->id_hdr_size)); |
567 | machine->id_hdr_size)); | ||
568 | if (event == NULL) { | 572 | if (event == NULL) { |
569 | pr_debug("Not enough memory synthesizing mmap event " | 573 | pr_debug("Not enough memory synthesizing mmap event " |
570 | "for kernel modules\n"); | 574 | "for kernel modules\n"); |
@@ -784,9 +788,9 @@ try_again: | |||
784 | * "[vdso]" dso, but for now lets use the old trick of looking | 788 | * "[vdso]" dso, but for now lets use the old trick of looking |
785 | * in the whole kernel symbol list. | 789 | * in the whole kernel symbol list. |
786 | */ | 790 | */ |
787 | if ((long long)al->addr < 0 && | 791 | if (cpumode == PERF_RECORD_MISC_USER && machine && |
788 | cpumode == PERF_RECORD_MISC_USER && | 792 | mg != &machine->kmaps && |
789 | machine && mg != &machine->kmaps) { | 793 | machine__kernel_ip(machine, al->addr)) { |
790 | mg = &machine->kmaps; | 794 | mg = &machine->kmaps; |
791 | load_map = true; | 795 | load_map = true; |
792 | goto try_again; | 796 | goto try_again; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 94d6976180da..7eb7107731ec 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -156,6 +156,8 @@ struct perf_sample { | |||
156 | u32 cpu; | 156 | u32 cpu; |
157 | u32 raw_size; | 157 | u32 raw_size; |
158 | u64 data_src; | 158 | u64 data_src; |
159 | u32 flags; | ||
160 | u16 insn_len; | ||
159 | void *raw_data; | 161 | void *raw_data; |
160 | struct ip_callchain *callchain; | 162 | struct ip_callchain *callchain; |
161 | struct branch_stack *branch_stack; | 163 | struct branch_stack *branch_stack; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 814e954c1318..3cebc9a8d52e 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include <linux/bitops.h> | 25 | #include <linux/bitops.h> |
26 | #include <linux/hash.h> | 26 | #include <linux/hash.h> |
27 | 27 | ||
28 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx); | ||
29 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx); | ||
30 | |||
28 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 31 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
29 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | 32 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) |
30 | 33 | ||
@@ -37,6 +40,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
37 | INIT_HLIST_HEAD(&evlist->heads[i]); | 40 | INIT_HLIST_HEAD(&evlist->heads[i]); |
38 | INIT_LIST_HEAD(&evlist->entries); | 41 | INIT_LIST_HEAD(&evlist->entries); |
39 | perf_evlist__set_maps(evlist, cpus, threads); | 42 | perf_evlist__set_maps(evlist, cpus, threads); |
43 | fdarray__init(&evlist->pollfd, 64); | ||
40 | evlist->workload.pid = -1; | 44 | evlist->workload.pid = -1; |
41 | } | 45 | } |
42 | 46 | ||
@@ -102,7 +106,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist) | |||
102 | void perf_evlist__exit(struct perf_evlist *evlist) | 106 | void perf_evlist__exit(struct perf_evlist *evlist) |
103 | { | 107 | { |
104 | zfree(&evlist->mmap); | 108 | zfree(&evlist->mmap); |
105 | zfree(&evlist->pollfd); | 109 | fdarray__exit(&evlist->pollfd); |
106 | } | 110 | } |
107 | 111 | ||
108 | void perf_evlist__delete(struct perf_evlist *evlist) | 112 | void perf_evlist__delete(struct perf_evlist *evlist) |
@@ -122,6 +126,7 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | |||
122 | { | 126 | { |
123 | list_add_tail(&entry->node, &evlist->entries); | 127 | list_add_tail(&entry->node, &evlist->entries); |
124 | entry->idx = evlist->nr_entries; | 128 | entry->idx = evlist->nr_entries; |
129 | entry->tracking = !entry->idx; | ||
125 | 130 | ||
126 | if (!evlist->nr_entries++) | 131 | if (!evlist->nr_entries++) |
127 | perf_evlist__set_id_pos(evlist); | 132 | perf_evlist__set_id_pos(evlist); |
@@ -265,17 +270,27 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist, | |||
265 | return 0; | 270 | return 0; |
266 | } | 271 | } |
267 | 272 | ||
273 | static int perf_evlist__nr_threads(struct perf_evlist *evlist, | ||
274 | struct perf_evsel *evsel) | ||
275 | { | ||
276 | if (evsel->system_wide) | ||
277 | return 1; | ||
278 | else | ||
279 | return thread_map__nr(evlist->threads); | ||
280 | } | ||
281 | |||
268 | void perf_evlist__disable(struct perf_evlist *evlist) | 282 | void perf_evlist__disable(struct perf_evlist *evlist) |
269 | { | 283 | { |
270 | int cpu, thread; | 284 | int cpu, thread; |
271 | struct perf_evsel *pos; | 285 | struct perf_evsel *pos; |
272 | int nr_cpus = cpu_map__nr(evlist->cpus); | 286 | int nr_cpus = cpu_map__nr(evlist->cpus); |
273 | int nr_threads = thread_map__nr(evlist->threads); | 287 | int nr_threads; |
274 | 288 | ||
275 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 289 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
276 | evlist__for_each(evlist, pos) { | 290 | evlist__for_each(evlist, pos) { |
277 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 291 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
278 | continue; | 292 | continue; |
293 | nr_threads = perf_evlist__nr_threads(evlist, pos); | ||
279 | for (thread = 0; thread < nr_threads; thread++) | 294 | for (thread = 0; thread < nr_threads; thread++) |
280 | ioctl(FD(pos, cpu, thread), | 295 | ioctl(FD(pos, cpu, thread), |
281 | PERF_EVENT_IOC_DISABLE, 0); | 296 | PERF_EVENT_IOC_DISABLE, 0); |
@@ -288,12 +303,13 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
288 | int cpu, thread; | 303 | int cpu, thread; |
289 | struct perf_evsel *pos; | 304 | struct perf_evsel *pos; |
290 | int nr_cpus = cpu_map__nr(evlist->cpus); | 305 | int nr_cpus = cpu_map__nr(evlist->cpus); |
291 | int nr_threads = thread_map__nr(evlist->threads); | 306 | int nr_threads; |
292 | 307 | ||
293 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 308 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
294 | evlist__for_each(evlist, pos) { | 309 | evlist__for_each(evlist, pos) { |
295 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 310 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
296 | continue; | 311 | continue; |
312 | nr_threads = perf_evlist__nr_threads(evlist, pos); | ||
297 | for (thread = 0; thread < nr_threads; thread++) | 313 | for (thread = 0; thread < nr_threads; thread++) |
298 | ioctl(FD(pos, cpu, thread), | 314 | ioctl(FD(pos, cpu, thread), |
299 | PERF_EVENT_IOC_ENABLE, 0); | 315 | PERF_EVENT_IOC_ENABLE, 0); |
@@ -305,12 +321,14 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
305 | struct perf_evsel *evsel) | 321 | struct perf_evsel *evsel) |
306 | { | 322 | { |
307 | int cpu, thread, err; | 323 | int cpu, thread, err; |
324 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
325 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
308 | 326 | ||
309 | if (!evsel->fd) | 327 | if (!evsel->fd) |
310 | return 0; | 328 | return 0; |
311 | 329 | ||
312 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 330 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
313 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 331 | for (thread = 0; thread < nr_threads; thread++) { |
314 | err = ioctl(FD(evsel, cpu, thread), | 332 | err = ioctl(FD(evsel, cpu, thread), |
315 | PERF_EVENT_IOC_DISABLE, 0); | 333 | PERF_EVENT_IOC_DISABLE, 0); |
316 | if (err) | 334 | if (err) |
@@ -324,12 +342,14 @@ int perf_evlist__enable_event(struct perf_evlist *evlist, | |||
324 | struct perf_evsel *evsel) | 342 | struct perf_evsel *evsel) |
325 | { | 343 | { |
326 | int cpu, thread, err; | 344 | int cpu, thread, err; |
345 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
346 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
327 | 347 | ||
328 | if (!evsel->fd) | 348 | if (!evsel->fd) |
329 | return -EINVAL; | 349 | return -EINVAL; |
330 | 350 | ||
331 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 351 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
332 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 352 | for (thread = 0; thread < nr_threads; thread++) { |
333 | err = ioctl(FD(evsel, cpu, thread), | 353 | err = ioctl(FD(evsel, cpu, thread), |
334 | PERF_EVENT_IOC_ENABLE, 0); | 354 | PERF_EVENT_IOC_ENABLE, 0); |
335 | if (err) | 355 | if (err) |
@@ -339,21 +359,111 @@ int perf_evlist__enable_event(struct perf_evlist *evlist, | |||
339 | return 0; | 359 | return 0; |
340 | } | 360 | } |
341 | 361 | ||
342 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 362 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, |
363 | struct perf_evsel *evsel, int cpu) | ||
364 | { | ||
365 | int thread, err; | ||
366 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
367 | |||
368 | if (!evsel->fd) | ||
369 | return -EINVAL; | ||
370 | |||
371 | for (thread = 0; thread < nr_threads; thread++) { | ||
372 | err = ioctl(FD(evsel, cpu, thread), | ||
373 | PERF_EVENT_IOC_ENABLE, 0); | ||
374 | if (err) | ||
375 | return err; | ||
376 | } | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, | ||
381 | struct perf_evsel *evsel, | ||
382 | int thread) | ||
383 | { | ||
384 | int cpu, err; | ||
385 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
386 | |||
387 | if (!evsel->fd) | ||
388 | return -EINVAL; | ||
389 | |||
390 | for (cpu = 0; cpu < nr_cpus; cpu++) { | ||
391 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); | ||
392 | if (err) | ||
393 | return err; | ||
394 | } | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
399 | struct perf_evsel *evsel, int idx) | ||
400 | { | ||
401 | bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus); | ||
402 | |||
403 | if (per_cpu_mmaps) | ||
404 | return perf_evlist__enable_event_cpu(evlist, evsel, idx); | ||
405 | else | ||
406 | return perf_evlist__enable_event_thread(evlist, evsel, idx); | ||
407 | } | ||
408 | |||
409 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | ||
343 | { | 410 | { |
344 | int nr_cpus = cpu_map__nr(evlist->cpus); | 411 | int nr_cpus = cpu_map__nr(evlist->cpus); |
345 | int nr_threads = thread_map__nr(evlist->threads); | 412 | int nr_threads = thread_map__nr(evlist->threads); |
346 | int nfds = nr_cpus * nr_threads * evlist->nr_entries; | 413 | int nfds = 0; |
347 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 414 | struct perf_evsel *evsel; |
348 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 415 | |
416 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
417 | if (evsel->system_wide) | ||
418 | nfds += nr_cpus; | ||
419 | else | ||
420 | nfds += nr_cpus * nr_threads; | ||
421 | } | ||
422 | |||
423 | if (fdarray__available_entries(&evlist->pollfd) < nfds && | ||
424 | fdarray__grow(&evlist->pollfd, nfds) < 0) | ||
425 | return -ENOMEM; | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx) | ||
431 | { | ||
432 | int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP); | ||
433 | /* | ||
434 | * Save the idx so that when we filter out fds POLLHUP'ed we can | ||
435 | * close the associated evlist->mmap[] entry. | ||
436 | */ | ||
437 | if (pos >= 0) { | ||
438 | evlist->pollfd.priv[pos].idx = idx; | ||
439 | |||
440 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
441 | } | ||
442 | |||
443 | return pos; | ||
444 | } | ||
445 | |||
446 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
447 | { | ||
448 | return __perf_evlist__add_pollfd(evlist, fd, -1); | ||
449 | } | ||
450 | |||
451 | static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd) | ||
452 | { | ||
453 | struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd); | ||
454 | |||
455 | perf_evlist__mmap_put(evlist, fda->priv[fd].idx); | ||
456 | } | ||
457 | |||
458 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) | ||
459 | { | ||
460 | return fdarray__filter(&evlist->pollfd, revents_and_mask, | ||
461 | perf_evlist__munmap_filtered); | ||
349 | } | 462 | } |
350 | 463 | ||
351 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | 464 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout) |
352 | { | 465 | { |
353 | fcntl(fd, F_SETFL, O_NONBLOCK); | 466 | return fdarray__poll(&evlist->pollfd, timeout); |
354 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
355 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
356 | evlist->nr_fds++; | ||
357 | } | 467 | } |
358 | 468 | ||
359 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | 469 | static void perf_evlist__id_hash(struct perf_evlist *evlist, |
@@ -566,14 +676,36 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
566 | return event; | 676 | return event; |
567 | } | 677 | } |
568 | 678 | ||
679 | static bool perf_mmap__empty(struct perf_mmap *md) | ||
680 | { | ||
681 | return perf_mmap__read_head(md) != md->prev; | ||
682 | } | ||
683 | |||
684 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) | ||
685 | { | ||
686 | ++evlist->mmap[idx].refcnt; | ||
687 | } | ||
688 | |||
689 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx) | ||
690 | { | ||
691 | BUG_ON(evlist->mmap[idx].refcnt == 0); | ||
692 | |||
693 | if (--evlist->mmap[idx].refcnt == 0) | ||
694 | __perf_evlist__munmap(evlist, idx); | ||
695 | } | ||
696 | |||
569 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | 697 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) |
570 | { | 698 | { |
699 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
700 | |||
571 | if (!evlist->overwrite) { | 701 | if (!evlist->overwrite) { |
572 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
573 | unsigned int old = md->prev; | 702 | unsigned int old = md->prev; |
574 | 703 | ||
575 | perf_mmap__write_tail(md, old); | 704 | perf_mmap__write_tail(md, old); |
576 | } | 705 | } |
706 | |||
707 | if (md->refcnt == 1 && perf_mmap__empty(md)) | ||
708 | perf_evlist__mmap_put(evlist, idx); | ||
577 | } | 709 | } |
578 | 710 | ||
579 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | 711 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
@@ -581,6 +713,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | |||
581 | if (evlist->mmap[idx].base != NULL) { | 713 | if (evlist->mmap[idx].base != NULL) { |
582 | munmap(evlist->mmap[idx].base, evlist->mmap_len); | 714 | munmap(evlist->mmap[idx].base, evlist->mmap_len); |
583 | evlist->mmap[idx].base = NULL; | 715 | evlist->mmap[idx].base = NULL; |
716 | evlist->mmap[idx].refcnt = 0; | ||
584 | } | 717 | } |
585 | } | 718 | } |
586 | 719 | ||
@@ -614,6 +747,20 @@ struct mmap_params { | |||
614 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | 747 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, |
615 | struct mmap_params *mp, int fd) | 748 | struct mmap_params *mp, int fd) |
616 | { | 749 | { |
750 | /* | ||
751 | * The last one will be done at perf_evlist__mmap_consume(), so that we | ||
752 | * make sure we don't prevent tools from consuming every last event in | ||
753 | * the ring buffer. | ||
754 | * | ||
755 | * I.e. we can get the POLLHUP meaning that the fd doesn't exist | ||
756 | * anymore, but the last events for it are still in the ring buffer, | ||
757 | * waiting to be consumed. | ||
758 | * | ||
759 | * Tools can chose to ignore this at their own discretion, but the | ||
760 | * evlist layer can't just drop it when filtering events in | ||
761 | * perf_evlist__filter_pollfd(). | ||
762 | */ | ||
763 | evlist->mmap[idx].refcnt = 2; | ||
617 | evlist->mmap[idx].prev = 0; | 764 | evlist->mmap[idx].prev = 0; |
618 | evlist->mmap[idx].mask = mp->mask; | 765 | evlist->mmap[idx].mask = mp->mask; |
619 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, | 766 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, |
@@ -625,7 +772,6 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | |||
625 | return -1; | 772 | return -1; |
626 | } | 773 | } |
627 | 774 | ||
628 | perf_evlist__add_pollfd(evlist, fd); | ||
629 | return 0; | 775 | return 0; |
630 | } | 776 | } |
631 | 777 | ||
@@ -636,7 +782,12 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
636 | struct perf_evsel *evsel; | 782 | struct perf_evsel *evsel; |
637 | 783 | ||
638 | evlist__for_each(evlist, evsel) { | 784 | evlist__for_each(evlist, evsel) { |
639 | int fd = FD(evsel, cpu, thread); | 785 | int fd; |
786 | |||
787 | if (evsel->system_wide && thread) | ||
788 | continue; | ||
789 | |||
790 | fd = FD(evsel, cpu, thread); | ||
640 | 791 | ||
641 | if (*output == -1) { | 792 | if (*output == -1) { |
642 | *output = fd; | 793 | *output = fd; |
@@ -645,6 +796,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
645 | } else { | 796 | } else { |
646 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) | 797 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) |
647 | return -1; | 798 | return -1; |
799 | |||
800 | perf_evlist__mmap_get(evlist, idx); | ||
801 | } | ||
802 | |||
803 | if (__perf_evlist__add_pollfd(evlist, fd, idx) < 0) { | ||
804 | perf_evlist__mmap_put(evlist, idx); | ||
805 | return -1; | ||
648 | } | 806 | } |
649 | 807 | ||
650 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 808 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
@@ -804,7 +962,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
804 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 962 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
805 | return -ENOMEM; | 963 | return -ENOMEM; |
806 | 964 | ||
807 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | 965 | if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) |
808 | return -ENOMEM; | 966 | return -ENOMEM; |
809 | 967 | ||
810 | evlist->overwrite = overwrite; | 968 | evlist->overwrite = overwrite; |
@@ -1061,6 +1219,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
1061 | } | 1219 | } |
1062 | 1220 | ||
1063 | if (!evlist->workload.pid) { | 1221 | if (!evlist->workload.pid) { |
1222 | int ret; | ||
1223 | |||
1064 | if (pipe_output) | 1224 | if (pipe_output) |
1065 | dup2(2, 1); | 1225 | dup2(2, 1); |
1066 | 1226 | ||
@@ -1078,8 +1238,22 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
1078 | /* | 1238 | /* |
1079 | * Wait until the parent tells us to go. | 1239 | * Wait until the parent tells us to go. |
1080 | */ | 1240 | */ |
1081 | if (read(go_pipe[0], &bf, 1) == -1) | 1241 | ret = read(go_pipe[0], &bf, 1); |
1082 | perror("unable to read pipe"); | 1242 | /* |
1243 | * The parent will ask for the execvp() to be performed by | ||
1244 | * writing exactly one byte, in workload.cork_fd, usually via | ||
1245 | * perf_evlist__start_workload(). | ||
1246 | * | ||
1247 | * For cancelling the workload without actuallin running it, | ||
1248 | * the parent will just close workload.cork_fd, without writing | ||
1249 | * anything, i.e. read will return zero and we just exit() | ||
1250 | * here. | ||
1251 | */ | ||
1252 | if (ret != 1) { | ||
1253 | if (ret == -1) | ||
1254 | perror("unable to read pipe"); | ||
1255 | exit(ret); | ||
1256 | } | ||
1083 | 1257 | ||
1084 | execvp(argv[0], (char **)argv); | 1258 | execvp(argv[0], (char **)argv); |
1085 | 1259 | ||
@@ -1202,7 +1376,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | |||
1202 | int err, char *buf, size_t size) | 1376 | int err, char *buf, size_t size) |
1203 | { | 1377 | { |
1204 | int printed, value; | 1378 | int printed, value; |
1205 | char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | 1379 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); |
1206 | 1380 | ||
1207 | switch (err) { | 1381 | switch (err) { |
1208 | case EACCES: | 1382 | case EACCES: |
@@ -1250,3 +1424,19 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
1250 | 1424 | ||
1251 | list_splice(&move, &evlist->entries); | 1425 | list_splice(&move, &evlist->entries); |
1252 | } | 1426 | } |
1427 | |||
1428 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
1429 | struct perf_evsel *tracking_evsel) | ||
1430 | { | ||
1431 | struct perf_evsel *evsel; | ||
1432 | |||
1433 | if (tracking_evsel->tracking) | ||
1434 | return; | ||
1435 | |||
1436 | evlist__for_each(evlist, evsel) { | ||
1437 | if (evsel != tracking_evsel) | ||
1438 | evsel->tracking = false; | ||
1439 | } | ||
1440 | |||
1441 | tracking_evsel->tracking = true; | ||
1442 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f5173cd63693..bd312b01e876 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_EVLIST_H 1 | 2 | #define __PERF_EVLIST_H 1 |
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | #include <api/fd/array.h> | ||
5 | #include <stdio.h> | 6 | #include <stdio.h> |
6 | #include "../perf.h" | 7 | #include "../perf.h" |
7 | #include "event.h" | 8 | #include "event.h" |
@@ -17,9 +18,15 @@ struct record_opts; | |||
17 | #define PERF_EVLIST__HLIST_BITS 8 | 18 | #define PERF_EVLIST__HLIST_BITS 8 |
18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | 19 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) |
19 | 20 | ||
21 | /** | ||
22 | * struct perf_mmap - perf's ring buffer mmap details | ||
23 | * | ||
24 | * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this | ||
25 | */ | ||
20 | struct perf_mmap { | 26 | struct perf_mmap { |
21 | void *base; | 27 | void *base; |
22 | int mask; | 28 | int mask; |
29 | int refcnt; | ||
23 | unsigned int prev; | 30 | unsigned int prev; |
24 | char event_copy[PERF_SAMPLE_MAX_SIZE]; | 31 | char event_copy[PERF_SAMPLE_MAX_SIZE]; |
25 | }; | 32 | }; |
@@ -29,7 +36,6 @@ struct perf_evlist { | |||
29 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | 36 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; |
30 | int nr_entries; | 37 | int nr_entries; |
31 | int nr_groups; | 38 | int nr_groups; |
32 | int nr_fds; | ||
33 | int nr_mmaps; | 39 | int nr_mmaps; |
34 | size_t mmap_len; | 40 | size_t mmap_len; |
35 | int id_pos; | 41 | int id_pos; |
@@ -40,8 +46,8 @@ struct perf_evlist { | |||
40 | pid_t pid; | 46 | pid_t pid; |
41 | } workload; | 47 | } workload; |
42 | bool overwrite; | 48 | bool overwrite; |
49 | struct fdarray pollfd; | ||
43 | struct perf_mmap *mmap; | 50 | struct perf_mmap *mmap; |
44 | struct pollfd *pollfd; | ||
45 | struct thread_map *threads; | 51 | struct thread_map *threads; |
46 | struct cpu_map *cpus; | 52 | struct cpu_map *cpus; |
47 | struct perf_evsel *selected; | 53 | struct perf_evsel *selected; |
@@ -82,7 +88,11 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | |||
82 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 88 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
83 | int cpu, int thread, u64 id); | 89 | int cpu, int thread, u64 id); |
84 | 90 | ||
85 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | 91 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); |
92 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); | ||
93 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask); | ||
94 | |||
95 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout); | ||
86 | 96 | ||
87 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | 97 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); |
88 | 98 | ||
@@ -122,6 +132,8 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
122 | struct perf_evsel *evsel); | 132 | struct perf_evsel *evsel); |
123 | int perf_evlist__enable_event(struct perf_evlist *evlist, | 133 | int perf_evlist__enable_event(struct perf_evlist *evlist, |
124 | struct perf_evsel *evsel); | 134 | struct perf_evsel *evsel); |
135 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
136 | struct perf_evsel *evsel, int idx); | ||
125 | 137 | ||
126 | void perf_evlist__set_selected(struct perf_evlist *evlist, | 138 | void perf_evlist__set_selected(struct perf_evlist *evlist, |
127 | struct perf_evsel *evsel); | 139 | struct perf_evsel *evsel); |
@@ -262,4 +274,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
262 | #define evlist__for_each_safe(evlist, tmp, evsel) \ | 274 | #define evlist__for_each_safe(evlist, tmp, evsel) \ |
263 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) | 275 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) |
264 | 276 | ||
277 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
278 | struct perf_evsel *tracking_evsel); | ||
279 | |||
265 | #endif /* __PERF_EVLIST_H */ | 280 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 21a373ebea22..e0868a901c4a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -162,6 +162,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
162 | struct perf_event_attr *attr, int idx) | 162 | struct perf_event_attr *attr, int idx) |
163 | { | 163 | { |
164 | evsel->idx = idx; | 164 | evsel->idx = idx; |
165 | evsel->tracking = !idx; | ||
165 | evsel->attr = *attr; | 166 | evsel->attr = *attr; |
166 | evsel->leader = evsel; | 167 | evsel->leader = evsel; |
167 | evsel->unit = ""; | 168 | evsel->unit = ""; |
@@ -502,20 +503,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | |||
502 | } | 503 | } |
503 | 504 | ||
504 | static void | 505 | static void |
505 | perf_evsel__config_callgraph(struct perf_evsel *evsel, | 506 | perf_evsel__config_callgraph(struct perf_evsel *evsel) |
506 | struct record_opts *opts) | ||
507 | { | 507 | { |
508 | bool function = perf_evsel__is_function_event(evsel); | 508 | bool function = perf_evsel__is_function_event(evsel); |
509 | struct perf_event_attr *attr = &evsel->attr; | 509 | struct perf_event_attr *attr = &evsel->attr; |
510 | 510 | ||
511 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); | 511 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); |
512 | 512 | ||
513 | if (opts->call_graph == CALLCHAIN_DWARF) { | 513 | if (callchain_param.record_mode == CALLCHAIN_DWARF) { |
514 | if (!function) { | 514 | if (!function) { |
515 | perf_evsel__set_sample_bit(evsel, REGS_USER); | 515 | perf_evsel__set_sample_bit(evsel, REGS_USER); |
516 | perf_evsel__set_sample_bit(evsel, STACK_USER); | 516 | perf_evsel__set_sample_bit(evsel, STACK_USER); |
517 | attr->sample_regs_user = PERF_REGS_MASK; | 517 | attr->sample_regs_user = PERF_REGS_MASK; |
518 | attr->sample_stack_user = opts->stack_dump_size; | 518 | attr->sample_stack_user = callchain_param.dump_size; |
519 | attr->exclude_callchain_user = 1; | 519 | attr->exclude_callchain_user = 1; |
520 | } else { | 520 | } else { |
521 | pr_info("Cannot use DWARF unwind for function trace event," | 521 | pr_info("Cannot use DWARF unwind for function trace event," |
@@ -561,7 +561,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
561 | { | 561 | { |
562 | struct perf_evsel *leader = evsel->leader; | 562 | struct perf_evsel *leader = evsel->leader; |
563 | struct perf_event_attr *attr = &evsel->attr; | 563 | struct perf_event_attr *attr = &evsel->attr; |
564 | int track = !evsel->idx; /* only the first counter needs these */ | 564 | int track = evsel->tracking; |
565 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; | 565 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; |
566 | 566 | ||
567 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; | 567 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; |
@@ -624,8 +624,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
624 | attr->mmap_data = track; | 624 | attr->mmap_data = track; |
625 | } | 625 | } |
626 | 626 | ||
627 | if (opts->call_graph_enabled && !evsel->no_aux_samples) | 627 | if (callchain_param.enabled && !evsel->no_aux_samples) |
628 | perf_evsel__config_callgraph(evsel, opts); | 628 | perf_evsel__config_callgraph(evsel); |
629 | 629 | ||
630 | if (target__has_cpu(&opts->target)) | 630 | if (target__has_cpu(&opts->target)) |
631 | perf_evsel__set_sample_bit(evsel, CPU); | 631 | perf_evsel__set_sample_bit(evsel, CPU); |
@@ -633,9 +633,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
633 | if (opts->period) | 633 | if (opts->period) |
634 | perf_evsel__set_sample_bit(evsel, PERIOD); | 634 | perf_evsel__set_sample_bit(evsel, PERIOD); |
635 | 635 | ||
636 | if (!perf_missing_features.sample_id_all && | 636 | /* |
637 | (opts->sample_time || !opts->no_inherit || | 637 | * When the user explicitely disabled time don't force it here. |
638 | target__has_cpu(&opts->target) || per_cpu)) | 638 | */ |
639 | if (opts->sample_time && | ||
640 | (!perf_missing_features.sample_id_all && | ||
641 | (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu))) | ||
639 | perf_evsel__set_sample_bit(evsel, TIME); | 642 | perf_evsel__set_sample_bit(evsel, TIME); |
640 | 643 | ||
641 | if (opts->raw_samples && !evsel->no_aux_samples) { | 644 | if (opts->raw_samples && !evsel->no_aux_samples) { |
@@ -692,6 +695,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
692 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 695 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
693 | { | 696 | { |
694 | int cpu, thread; | 697 | int cpu, thread; |
698 | |||
699 | if (evsel->system_wide) | ||
700 | nthreads = 1; | ||
701 | |||
695 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | 702 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); |
696 | 703 | ||
697 | if (evsel->fd) { | 704 | if (evsel->fd) { |
@@ -710,6 +717,9 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea | |||
710 | { | 717 | { |
711 | int cpu, thread; | 718 | int cpu, thread; |
712 | 719 | ||
720 | if (evsel->system_wide) | ||
721 | nthreads = 1; | ||
722 | |||
713 | for (cpu = 0; cpu < ncpus; cpu++) { | 723 | for (cpu = 0; cpu < ncpus; cpu++) { |
714 | for (thread = 0; thread < nthreads; thread++) { | 724 | for (thread = 0; thread < nthreads; thread++) { |
715 | int fd = FD(evsel, cpu, thread), | 725 | int fd = FD(evsel, cpu, thread), |
@@ -740,6 +750,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
740 | 750 | ||
741 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 751 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
742 | { | 752 | { |
753 | if (evsel->system_wide) | ||
754 | nthreads = 1; | ||
755 | |||
743 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 756 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
744 | if (evsel->sample_id == NULL) | 757 | if (evsel->sample_id == NULL) |
745 | return -ENOMEM; | 758 | return -ENOMEM; |
@@ -784,6 +797,9 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
784 | { | 797 | { |
785 | int cpu, thread; | 798 | int cpu, thread; |
786 | 799 | ||
800 | if (evsel->system_wide) | ||
801 | nthreads = 1; | ||
802 | |||
787 | for (cpu = 0; cpu < ncpus; cpu++) | 803 | for (cpu = 0; cpu < ncpus; cpu++) |
788 | for (thread = 0; thread < nthreads; ++thread) { | 804 | for (thread = 0; thread < nthreads; ++thread) { |
789 | close(FD(evsel, cpu, thread)); | 805 | close(FD(evsel, cpu, thread)); |
@@ -872,6 +888,9 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
872 | int cpu, thread; | 888 | int cpu, thread; |
873 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | 889 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; |
874 | 890 | ||
891 | if (evsel->system_wide) | ||
892 | nthreads = 1; | ||
893 | |||
875 | aggr->val = aggr->ena = aggr->run = 0; | 894 | aggr->val = aggr->ena = aggr->run = 0; |
876 | 895 | ||
877 | for (cpu = 0; cpu < ncpus; cpu++) { | 896 | for (cpu = 0; cpu < ncpus; cpu++) { |
@@ -994,13 +1013,18 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
994 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 1013 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
995 | struct thread_map *threads) | 1014 | struct thread_map *threads) |
996 | { | 1015 | { |
997 | int cpu, thread; | 1016 | int cpu, thread, nthreads; |
998 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; | 1017 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; |
999 | int pid = -1, err; | 1018 | int pid = -1, err; |
1000 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; | 1019 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; |
1001 | 1020 | ||
1021 | if (evsel->system_wide) | ||
1022 | nthreads = 1; | ||
1023 | else | ||
1024 | nthreads = threads->nr; | ||
1025 | |||
1002 | if (evsel->fd == NULL && | 1026 | if (evsel->fd == NULL && |
1003 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 1027 | perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0) |
1004 | return -ENOMEM; | 1028 | return -ENOMEM; |
1005 | 1029 | ||
1006 | if (evsel->cgrp) { | 1030 | if (evsel->cgrp) { |
@@ -1024,10 +1048,10 @@ retry_sample_id: | |||
1024 | 1048 | ||
1025 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1049 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
1026 | 1050 | ||
1027 | for (thread = 0; thread < threads->nr; thread++) { | 1051 | for (thread = 0; thread < nthreads; thread++) { |
1028 | int group_fd; | 1052 | int group_fd; |
1029 | 1053 | ||
1030 | if (!evsel->cgrp) | 1054 | if (!evsel->cgrp && !evsel->system_wide) |
1031 | pid = threads->map[thread]; | 1055 | pid = threads->map[thread]; |
1032 | 1056 | ||
1033 | group_fd = get_group_fd(evsel, cpu, thread); | 1057 | group_fd = get_group_fd(evsel, cpu, thread); |
@@ -1100,7 +1124,7 @@ out_close: | |||
1100 | close(FD(evsel, cpu, thread)); | 1124 | close(FD(evsel, cpu, thread)); |
1101 | FD(evsel, cpu, thread) = -1; | 1125 | FD(evsel, cpu, thread) = -1; |
1102 | } | 1126 | } |
1103 | thread = threads->nr; | 1127 | thread = nthreads; |
1104 | } while (--cpu >= 0); | 1128 | } while (--cpu >= 0); |
1105 | return err; | 1129 | return err; |
1106 | } | 1130 | } |
@@ -2002,6 +2026,8 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
2002 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | 2026 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
2003 | int err, char *msg, size_t size) | 2027 | int err, char *msg, size_t size) |
2004 | { | 2028 | { |
2029 | char sbuf[STRERR_BUFSIZE]; | ||
2030 | |||
2005 | switch (err) { | 2031 | switch (err) { |
2006 | case EPERM: | 2032 | case EPERM: |
2007 | case EACCES: | 2033 | case EACCES: |
@@ -2036,13 +2062,20 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | |||
2036 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); | 2062 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); |
2037 | #endif | 2063 | #endif |
2038 | break; | 2064 | break; |
2065 | case EBUSY: | ||
2066 | if (find_process("oprofiled")) | ||
2067 | return scnprintf(msg, size, | ||
2068 | "The PMU counters are busy/taken by another profiler.\n" | ||
2069 | "We found oprofile daemon running, please stop it and try again."); | ||
2070 | break; | ||
2039 | default: | 2071 | default: |
2040 | break; | 2072 | break; |
2041 | } | 2073 | } |
2042 | 2074 | ||
2043 | return scnprintf(msg, size, | 2075 | return scnprintf(msg, size, |
2044 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n" | 2076 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" |
2045 | "/bin/dmesg may provide additional information.\n" | 2077 | "/bin/dmesg may provide additional information.\n" |
2046 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", | 2078 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", |
2047 | err, strerror(err), perf_evsel__name(evsel)); | 2079 | err, strerror_r(err, sbuf, sizeof(sbuf)), |
2080 | perf_evsel__name(evsel)); | ||
2048 | } | 2081 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index d7f93ce0ebc1..7bc314be6a7b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -85,6 +85,8 @@ struct perf_evsel { | |||
85 | bool needs_swap; | 85 | bool needs_swap; |
86 | bool no_aux_samples; | 86 | bool no_aux_samples; |
87 | bool immediate; | 87 | bool immediate; |
88 | bool system_wide; | ||
89 | bool tracking; | ||
88 | /* parse modifier helper */ | 90 | /* parse modifier helper */ |
89 | int exclude_GH; | 91 | int exclude_GH; |
90 | int nr_members; | 92 | int nr_members; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 158c787ce0c4..ce0de00399da 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -214,11 +214,11 @@ static int machine__hit_all_dsos(struct machine *machine) | |||
214 | { | 214 | { |
215 | int err; | 215 | int err; |
216 | 216 | ||
217 | err = __dsos__hit_all(&machine->kernel_dsos); | 217 | err = __dsos__hit_all(&machine->kernel_dsos.head); |
218 | if (err) | 218 | if (err) |
219 | return err; | 219 | return err; |
220 | 220 | ||
221 | return __dsos__hit_all(&machine->user_dsos); | 221 | return __dsos__hit_all(&machine->user_dsos.head); |
222 | } | 222 | } |
223 | 223 | ||
224 | int dsos__hit_all(struct perf_session *session) | 224 | int dsos__hit_all(struct perf_session *session) |
@@ -288,11 +288,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd) | |||
288 | umisc = PERF_RECORD_MISC_GUEST_USER; | 288 | umisc = PERF_RECORD_MISC_GUEST_USER; |
289 | } | 289 | } |
290 | 290 | ||
291 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine, | 291 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, |
292 | machine->pid, kmisc, fd); | 292 | machine->pid, kmisc, fd); |
293 | if (err == 0) | 293 | if (err == 0) |
294 | err = __dsos__write_buildid_table(&machine->user_dsos, machine, | 294 | err = __dsos__write_buildid_table(&machine->user_dsos.head, |
295 | machine->pid, umisc, fd); | 295 | machine, machine->pid, umisc, |
296 | fd); | ||
296 | return err; | 297 | return err; |
297 | } | 298 | } |
298 | 299 | ||
@@ -455,9 +456,10 @@ static int __dsos__cache_build_ids(struct list_head *head, | |||
455 | 456 | ||
456 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | 457 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) |
457 | { | 458 | { |
458 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine, | 459 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, |
459 | debugdir); | 460 | debugdir); |
460 | ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir); | 461 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, |
462 | debugdir); | ||
461 | return ret; | 463 | return ret; |
462 | } | 464 | } |
463 | 465 | ||
@@ -483,8 +485,10 @@ static int perf_session__cache_build_ids(struct perf_session *session) | |||
483 | 485 | ||
484 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | 486 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) |
485 | { | 487 | { |
486 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); | 488 | bool ret; |
487 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); | 489 | |
490 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
491 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
488 | return ret; | 492 | return ret; |
489 | } | 493 | } |
490 | 494 | ||
@@ -1548,7 +1552,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1548 | struct perf_session *session) | 1552 | struct perf_session *session) |
1549 | { | 1553 | { |
1550 | int err = -1; | 1554 | int err = -1; |
1551 | struct list_head *head; | 1555 | struct dsos *dsos; |
1552 | struct machine *machine; | 1556 | struct machine *machine; |
1553 | u16 misc; | 1557 | u16 misc; |
1554 | struct dso *dso; | 1558 | struct dso *dso; |
@@ -1563,22 +1567,22 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1563 | switch (misc) { | 1567 | switch (misc) { |
1564 | case PERF_RECORD_MISC_KERNEL: | 1568 | case PERF_RECORD_MISC_KERNEL: |
1565 | dso_type = DSO_TYPE_KERNEL; | 1569 | dso_type = DSO_TYPE_KERNEL; |
1566 | head = &machine->kernel_dsos; | 1570 | dsos = &machine->kernel_dsos; |
1567 | break; | 1571 | break; |
1568 | case PERF_RECORD_MISC_GUEST_KERNEL: | 1572 | case PERF_RECORD_MISC_GUEST_KERNEL: |
1569 | dso_type = DSO_TYPE_GUEST_KERNEL; | 1573 | dso_type = DSO_TYPE_GUEST_KERNEL; |
1570 | head = &machine->kernel_dsos; | 1574 | dsos = &machine->kernel_dsos; |
1571 | break; | 1575 | break; |
1572 | case PERF_RECORD_MISC_USER: | 1576 | case PERF_RECORD_MISC_USER: |
1573 | case PERF_RECORD_MISC_GUEST_USER: | 1577 | case PERF_RECORD_MISC_GUEST_USER: |
1574 | dso_type = DSO_TYPE_USER; | 1578 | dso_type = DSO_TYPE_USER; |
1575 | head = &machine->user_dsos; | 1579 | dsos = &machine->user_dsos; |
1576 | break; | 1580 | break; |
1577 | default: | 1581 | default: |
1578 | goto out; | 1582 | goto out; |
1579 | } | 1583 | } |
1580 | 1584 | ||
1581 | dso = __dsos__findnew(head, filename); | 1585 | dso = __dsos__findnew(dsos, filename); |
1582 | if (dso != NULL) { | 1586 | if (dso != NULL) { |
1583 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 1587 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
1584 | 1588 | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 30df6187ee02..86569fa3651d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -277,6 +277,28 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
277 | } | 277 | } |
278 | } | 278 | } |
279 | 279 | ||
280 | void hists__delete_entries(struct hists *hists) | ||
281 | { | ||
282 | struct rb_node *next = rb_first(&hists->entries); | ||
283 | struct hist_entry *n; | ||
284 | |||
285 | while (next) { | ||
286 | n = rb_entry(next, struct hist_entry, rb_node); | ||
287 | next = rb_next(&n->rb_node); | ||
288 | |||
289 | rb_erase(&n->rb_node, &hists->entries); | ||
290 | |||
291 | if (sort__need_collapse) | ||
292 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
293 | |||
294 | --hists->nr_entries; | ||
295 | if (!n->filtered) | ||
296 | --hists->nr_non_filtered_entries; | ||
297 | |||
298 | hist_entry__free(n); | ||
299 | } | ||
300 | } | ||
301 | |||
280 | /* | 302 | /* |
281 | * histogram, sorted on item, collects periods | 303 | * histogram, sorted on item, collects periods |
282 | */ | 304 | */ |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 742f49a85725..8c9c70e18cbb 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -152,6 +152,7 @@ void hists__output_resort(struct hists *hists); | |||
152 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 152 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
153 | 153 | ||
154 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 154 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
155 | void hists__delete_entries(struct hists *hists); | ||
155 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 156 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
156 | 157 | ||
157 | u64 hists__total_period(struct hists *hists); | 158 | u64 hists__total_period(struct hists *hists); |
@@ -192,6 +193,7 @@ struct perf_hpp { | |||
192 | }; | 193 | }; |
193 | 194 | ||
194 | struct perf_hpp_fmt { | 195 | struct perf_hpp_fmt { |
196 | const char *name; | ||
195 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 197 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
196 | struct perf_evsel *evsel); | 198 | struct perf_evsel *evsel); |
197 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 199 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
@@ -207,6 +209,8 @@ struct perf_hpp_fmt { | |||
207 | struct list_head list; | 209 | struct list_head list; |
208 | struct list_head sort_list; | 210 | struct list_head sort_list; |
209 | bool elide; | 211 | bool elide; |
212 | int len; | ||
213 | int user_len; | ||
210 | }; | 214 | }; |
211 | 215 | ||
212 | extern struct list_head perf_hpp__list; | 216 | extern struct list_head perf_hpp__list; |
@@ -261,17 +265,19 @@ static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) | |||
261 | } | 265 | } |
262 | 266 | ||
263 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); | 267 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); |
268 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists); | ||
269 | void perf_hpp__set_user_width(const char *width_list_str); | ||
264 | 270 | ||
265 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); | 271 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); |
266 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); | 272 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); |
267 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); | 273 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); |
268 | 274 | ||
269 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 275 | int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
270 | hpp_field_fn get_field, const char *fmt, | 276 | struct hist_entry *he, hpp_field_fn get_field, |
271 | hpp_snprint_fn print_fn, bool fmt_percent); | 277 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
272 | int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, | 278 | int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
273 | hpp_field_fn get_field, const char *fmt, | 279 | struct hist_entry *he, hpp_field_fn get_field, |
274 | hpp_snprint_fn print_fn, bool fmt_percent); | 280 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
275 | 281 | ||
276 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 282 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
277 | { | 283 | { |
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index 0b5a8cd2ee79..cf1d7913783b 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h | |||
@@ -92,7 +92,6 @@ struct perf_kvm_stat { | |||
92 | u64 lost_events; | 92 | u64 lost_events; |
93 | u64 duration; | 93 | u64 duration; |
94 | 94 | ||
95 | const char *pid_str; | ||
96 | struct intlist *pid_list; | 95 | struct intlist *pid_list; |
97 | 96 | ||
98 | struct rb_root result; | 97 | struct rb_root result; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 16bba9fff2c8..b7d477fbda02 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -17,8 +17,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
17 | { | 17 | { |
18 | map_groups__init(&machine->kmaps); | 18 | map_groups__init(&machine->kmaps); |
19 | RB_CLEAR_NODE(&machine->rb_node); | 19 | RB_CLEAR_NODE(&machine->rb_node); |
20 | INIT_LIST_HEAD(&machine->user_dsos); | 20 | INIT_LIST_HEAD(&machine->user_dsos.head); |
21 | INIT_LIST_HEAD(&machine->kernel_dsos); | 21 | INIT_LIST_HEAD(&machine->kernel_dsos.head); |
22 | 22 | ||
23 | machine->threads = RB_ROOT; | 23 | machine->threads = RB_ROOT; |
24 | INIT_LIST_HEAD(&machine->dead_threads); | 24 | INIT_LIST_HEAD(&machine->dead_threads); |
@@ -31,6 +31,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
31 | 31 | ||
32 | machine->symbol_filter = NULL; | 32 | machine->symbol_filter = NULL; |
33 | machine->id_hdr_size = 0; | 33 | machine->id_hdr_size = 0; |
34 | machine->comm_exec = false; | ||
35 | machine->kernel_start = 0; | ||
34 | 36 | ||
35 | machine->root_dir = strdup(root_dir); | 37 | machine->root_dir = strdup(root_dir); |
36 | if (machine->root_dir == NULL) | 38 | if (machine->root_dir == NULL) |
@@ -70,11 +72,12 @@ out_delete: | |||
70 | return NULL; | 72 | return NULL; |
71 | } | 73 | } |
72 | 74 | ||
73 | static void dsos__delete(struct list_head *dsos) | 75 | static void dsos__delete(struct dsos *dsos) |
74 | { | 76 | { |
75 | struct dso *pos, *n; | 77 | struct dso *pos, *n; |
76 | 78 | ||
77 | list_for_each_entry_safe(pos, n, dsos, node) { | 79 | list_for_each_entry_safe(pos, n, &dsos->head, node) { |
80 | RB_CLEAR_NODE(&pos->rb_node); | ||
78 | list_del(&pos->node); | 81 | list_del(&pos->node); |
79 | dso__delete(pos); | 82 | dso__delete(pos); |
80 | } | 83 | } |
@@ -179,6 +182,19 @@ void machines__set_symbol_filter(struct machines *machines, | |||
179 | } | 182 | } |
180 | } | 183 | } |
181 | 184 | ||
185 | void machines__set_comm_exec(struct machines *machines, bool comm_exec) | ||
186 | { | ||
187 | struct rb_node *nd; | ||
188 | |||
189 | machines->host.comm_exec = comm_exec; | ||
190 | |||
191 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
192 | struct machine *machine = rb_entry(nd, struct machine, rb_node); | ||
193 | |||
194 | machine->comm_exec = comm_exec; | ||
195 | } | ||
196 | } | ||
197 | |||
182 | struct machine *machines__find(struct machines *machines, pid_t pid) | 198 | struct machine *machines__find(struct machines *machines, pid_t pid) |
183 | { | 199 | { |
184 | struct rb_node **p = &machines->guests.rb_node; | 200 | struct rb_node **p = &machines->guests.rb_node; |
@@ -398,17 +414,31 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid, | |||
398 | return __machine__findnew_thread(machine, pid, tid, false); | 414 | return __machine__findnew_thread(machine, pid, tid, false); |
399 | } | 415 | } |
400 | 416 | ||
417 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
418 | struct thread *thread) | ||
419 | { | ||
420 | if (machine->comm_exec) | ||
421 | return thread__exec_comm(thread); | ||
422 | else | ||
423 | return thread__comm(thread); | ||
424 | } | ||
425 | |||
401 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 426 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
402 | struct perf_sample *sample) | 427 | struct perf_sample *sample) |
403 | { | 428 | { |
404 | struct thread *thread = machine__findnew_thread(machine, | 429 | struct thread *thread = machine__findnew_thread(machine, |
405 | event->comm.pid, | 430 | event->comm.pid, |
406 | event->comm.tid); | 431 | event->comm.tid); |
432 | bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC; | ||
433 | |||
434 | if (exec) | ||
435 | machine->comm_exec = true; | ||
407 | 436 | ||
408 | if (dump_trace) | 437 | if (dump_trace) |
409 | perf_event__fprintf_comm(event, stdout); | 438 | perf_event__fprintf_comm(event, stdout); |
410 | 439 | ||
411 | if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { | 440 | if (thread == NULL || |
441 | __thread__set_comm(thread, event->comm.comm, sample->time, exec)) { | ||
412 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 442 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
413 | return -1; | 443 | return -1; |
414 | } | 444 | } |
@@ -448,23 +478,23 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
448 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) | 478 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) |
449 | { | 479 | { |
450 | struct rb_node *nd; | 480 | struct rb_node *nd; |
451 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) + | 481 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) + |
452 | __dsos__fprintf(&machines->host.user_dsos, fp); | 482 | __dsos__fprintf(&machines->host.user_dsos.head, fp); |
453 | 483 | ||
454 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | 484 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { |
455 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 485 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
456 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | 486 | ret += __dsos__fprintf(&pos->kernel_dsos.head, fp); |
457 | ret += __dsos__fprintf(&pos->user_dsos, fp); | 487 | ret += __dsos__fprintf(&pos->user_dsos.head, fp); |
458 | } | 488 | } |
459 | 489 | ||
460 | return ret; | 490 | return ret; |
461 | } | 491 | } |
462 | 492 | ||
463 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | 493 | size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp, |
464 | bool (skip)(struct dso *dso, int parm), int parm) | 494 | bool (skip)(struct dso *dso, int parm), int parm) |
465 | { | 495 | { |
466 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | 496 | return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) + |
467 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | 497 | __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm); |
468 | } | 498 | } |
469 | 499 | ||
470 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | 500 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, |
@@ -565,8 +595,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; | |||
565 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as | 595 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as |
566 | * symbol_name if it's not that important. | 596 | * symbol_name if it's not that important. |
567 | */ | 597 | */ |
568 | static u64 machine__get_kernel_start_addr(struct machine *machine, | 598 | static u64 machine__get_running_kernel_start(struct machine *machine, |
569 | const char **symbol_name) | 599 | const char **symbol_name) |
570 | { | 600 | { |
571 | char filename[PATH_MAX]; | 601 | char filename[PATH_MAX]; |
572 | int i; | 602 | int i; |
@@ -593,7 +623,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine, | |||
593 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | 623 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) |
594 | { | 624 | { |
595 | enum map_type type; | 625 | enum map_type type; |
596 | u64 start = machine__get_kernel_start_addr(machine, NULL); | 626 | u64 start = machine__get_running_kernel_start(machine, NULL); |
597 | 627 | ||
598 | for (type = 0; type < MAP__NR_TYPES; ++type) { | 628 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
599 | struct kmap *kmap; | 629 | struct kmap *kmap; |
@@ -912,7 +942,7 @@ int machine__create_kernel_maps(struct machine *machine) | |||
912 | { | 942 | { |
913 | struct dso *kernel = machine__get_kernel(machine); | 943 | struct dso *kernel = machine__get_kernel(machine); |
914 | const char *name; | 944 | const char *name; |
915 | u64 addr = machine__get_kernel_start_addr(machine, &name); | 945 | u64 addr = machine__get_running_kernel_start(machine, &name); |
916 | if (!addr) | 946 | if (!addr) |
917 | return -1; | 947 | return -1; |
918 | 948 | ||
@@ -965,7 +995,7 @@ static bool machine__uses_kcore(struct machine *machine) | |||
965 | { | 995 | { |
966 | struct dso *dso; | 996 | struct dso *dso; |
967 | 997 | ||
968 | list_for_each_entry(dso, &machine->kernel_dsos, node) { | 998 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { |
969 | if (dso__is_kcore(dso)) | 999 | if (dso__is_kcore(dso)) |
970 | return true; | 1000 | return true; |
971 | } | 1001 | } |
@@ -1285,6 +1315,16 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread, | |||
1285 | 1315 | ||
1286 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, | 1316 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, |
1287 | &al); | 1317 | &al); |
1318 | if (al.map == NULL) { | ||
1319 | /* | ||
1320 | * some shared data regions have execute bit set which puts | ||
1321 | * their mapping in the MAP__FUNCTION type array. | ||
1322 | * Check there as a fallback option before dropping the sample. | ||
1323 | */ | ||
1324 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr, | ||
1325 | &al); | ||
1326 | } | ||
1327 | |||
1288 | ams->addr = addr; | 1328 | ams->addr = addr; |
1289 | ams->al_addr = al.addr; | 1329 | ams->al_addr = al.addr; |
1290 | ams->sym = al.sym; | 1330 | ams->sym = al.sym; |
@@ -1531,3 +1571,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, | |||
1531 | 1571 | ||
1532 | return 0; | 1572 | return 0; |
1533 | } | 1573 | } |
1574 | |||
1575 | int machine__get_kernel_start(struct machine *machine) | ||
1576 | { | ||
1577 | struct map *map = machine__kernel_map(machine, MAP__FUNCTION); | ||
1578 | int err = 0; | ||
1579 | |||
1580 | /* | ||
1581 | * The only addresses above 2^63 are kernel addresses of a 64-bit | ||
1582 | * kernel. Note that addresses are unsigned so that on a 32-bit system | ||
1583 | * all addresses including kernel addresses are less than 2^32. In | ||
1584 | * that case (32-bit system), if the kernel mapping is unknown, all | ||
1585 | * addresses will be assumed to be in user space - see | ||
1586 | * machine__kernel_ip(). | ||
1587 | */ | ||
1588 | machine->kernel_start = 1ULL << 63; | ||
1589 | if (map) { | ||
1590 | err = map__load(map, machine->symbol_filter); | ||
1591 | if (map->start) | ||
1592 | machine->kernel_start = map->start; | ||
1593 | } | ||
1594 | return err; | ||
1595 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index b972824e6294..2b651a7f5d0d 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include "map.h" | 6 | #include "map.h" |
7 | #include "dso.h" | ||
7 | #include "event.h" | 8 | #include "event.h" |
8 | 9 | ||
9 | struct addr_location; | 10 | struct addr_location; |
@@ -26,15 +27,17 @@ struct machine { | |||
26 | struct rb_node rb_node; | 27 | struct rb_node rb_node; |
27 | pid_t pid; | 28 | pid_t pid; |
28 | u16 id_hdr_size; | 29 | u16 id_hdr_size; |
30 | bool comm_exec; | ||
29 | char *root_dir; | 31 | char *root_dir; |
30 | struct rb_root threads; | 32 | struct rb_root threads; |
31 | struct list_head dead_threads; | 33 | struct list_head dead_threads; |
32 | struct thread *last_match; | 34 | struct thread *last_match; |
33 | struct vdso_info *vdso_info; | 35 | struct vdso_info *vdso_info; |
34 | struct list_head user_dsos; | 36 | struct dsos user_dsos; |
35 | struct list_head kernel_dsos; | 37 | struct dsos kernel_dsos; |
36 | struct map_groups kmaps; | 38 | struct map_groups kmaps; |
37 | struct map *vmlinux_maps[MAP__NR_TYPES]; | 39 | struct map *vmlinux_maps[MAP__NR_TYPES]; |
40 | u64 kernel_start; | ||
38 | symbol_filter_t symbol_filter; | 41 | symbol_filter_t symbol_filter; |
39 | pid_t *current_tid; | 42 | pid_t *current_tid; |
40 | }; | 43 | }; |
@@ -45,8 +48,26 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) | |||
45 | return machine->vmlinux_maps[type]; | 48 | return machine->vmlinux_maps[type]; |
46 | } | 49 | } |
47 | 50 | ||
51 | int machine__get_kernel_start(struct machine *machine); | ||
52 | |||
53 | static inline u64 machine__kernel_start(struct machine *machine) | ||
54 | { | ||
55 | if (!machine->kernel_start) | ||
56 | machine__get_kernel_start(machine); | ||
57 | return machine->kernel_start; | ||
58 | } | ||
59 | |||
60 | static inline bool machine__kernel_ip(struct machine *machine, u64 ip) | ||
61 | { | ||
62 | u64 kernel_start = machine__kernel_start(machine); | ||
63 | |||
64 | return ip >= kernel_start; | ||
65 | } | ||
66 | |||
48 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, | 67 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, |
49 | pid_t tid); | 68 | pid_t tid); |
69 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
70 | struct thread *thread); | ||
50 | 71 | ||
51 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 72 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
52 | struct perf_sample *sample); | 73 | struct perf_sample *sample); |
@@ -88,6 +109,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | |||
88 | 109 | ||
89 | void machines__set_symbol_filter(struct machines *machines, | 110 | void machines__set_symbol_filter(struct machines *machines, |
90 | symbol_filter_t symbol_filter); | 111 | symbol_filter_t symbol_filter); |
112 | void machines__set_comm_exec(struct machines *machines, bool comm_exec); | ||
91 | 113 | ||
92 | struct machine *machine__new_host(void); | 114 | struct machine *machine__new_host(void); |
93 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 115 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 31b8905dd863..b7090596ac50 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -31,6 +31,7 @@ static inline int is_anon_memory(const char *filename) | |||
31 | static inline int is_no_dso_memory(const char *filename) | 31 | static inline int is_no_dso_memory(const char *filename) |
32 | { | 32 | { |
33 | return !strncmp(filename, "[stack", 6) || | 33 | return !strncmp(filename, "[stack", 6) || |
34 | !strncmp(filename, "/SYSV",5) || | ||
34 | !strcmp(filename, "[heap]"); | 35 | !strcmp(filename, "[heap]"); |
35 | } | 36 | } |
36 | 37 | ||
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c new file mode 100644 index 000000000000..706ce1a66169 --- /dev/null +++ b/tools/perf/util/ordered-events.c | |||
@@ -0,0 +1,245 @@ | |||
1 | #include <linux/list.h> | ||
2 | #include <linux/compiler.h> | ||
3 | #include "ordered-events.h" | ||
4 | #include "evlist.h" | ||
5 | #include "session.h" | ||
6 | #include "asm/bug.h" | ||
7 | #include "debug.h" | ||
8 | |||
9 | #define pr_N(n, fmt, ...) \ | ||
10 | eprintf(n, debug_ordered_events, fmt, ##__VA_ARGS__) | ||
11 | |||
12 | #define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
13 | |||
14 | static void queue_event(struct ordered_events *oe, struct ordered_event *new) | ||
15 | { | ||
16 | struct ordered_event *last = oe->last; | ||
17 | u64 timestamp = new->timestamp; | ||
18 | struct list_head *p; | ||
19 | |||
20 | ++oe->nr_events; | ||
21 | oe->last = new; | ||
22 | |||
23 | pr_oe_time2(timestamp, "queue_event nr_events %u\n", oe->nr_events); | ||
24 | |||
25 | if (!last) { | ||
26 | list_add(&new->list, &oe->events); | ||
27 | oe->max_timestamp = timestamp; | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | /* | ||
32 | * last event might point to some random place in the list as it's | ||
33 | * the last queued event. We expect that the new event is close to | ||
34 | * this. | ||
35 | */ | ||
36 | if (last->timestamp <= timestamp) { | ||
37 | while (last->timestamp <= timestamp) { | ||
38 | p = last->list.next; | ||
39 | if (p == &oe->events) { | ||
40 | list_add_tail(&new->list, &oe->events); | ||
41 | oe->max_timestamp = timestamp; | ||
42 | return; | ||
43 | } | ||
44 | last = list_entry(p, struct ordered_event, list); | ||
45 | } | ||
46 | list_add_tail(&new->list, &last->list); | ||
47 | } else { | ||
48 | while (last->timestamp > timestamp) { | ||
49 | p = last->list.prev; | ||
50 | if (p == &oe->events) { | ||
51 | list_add(&new->list, &oe->events); | ||
52 | return; | ||
53 | } | ||
54 | last = list_entry(p, struct ordered_event, list); | ||
55 | } | ||
56 | list_add(&new->list, &last->list); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) | ||
61 | static struct ordered_event *alloc_event(struct ordered_events *oe) | ||
62 | { | ||
63 | struct list_head *cache = &oe->cache; | ||
64 | struct ordered_event *new = NULL; | ||
65 | |||
66 | if (!list_empty(cache)) { | ||
67 | new = list_entry(cache->next, struct ordered_event, list); | ||
68 | list_del(&new->list); | ||
69 | } else if (oe->buffer) { | ||
70 | new = oe->buffer + oe->buffer_idx; | ||
71 | if (++oe->buffer_idx == MAX_SAMPLE_BUFFER) | ||
72 | oe->buffer = NULL; | ||
73 | } else if (oe->cur_alloc_size < oe->max_alloc_size) { | ||
74 | size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); | ||
75 | |||
76 | oe->buffer = malloc(size); | ||
77 | if (!oe->buffer) | ||
78 | return NULL; | ||
79 | |||
80 | pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n", | ||
81 | oe->cur_alloc_size, size, oe->max_alloc_size); | ||
82 | |||
83 | oe->cur_alloc_size += size; | ||
84 | list_add(&oe->buffer->list, &oe->to_free); | ||
85 | |||
86 | /* First entry is abused to maintain the to_free list. */ | ||
87 | oe->buffer_idx = 2; | ||
88 | new = oe->buffer + 1; | ||
89 | } else { | ||
90 | pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); | ||
91 | } | ||
92 | |||
93 | return new; | ||
94 | } | ||
95 | |||
96 | struct ordered_event * | ||
97 | ordered_events__new(struct ordered_events *oe, u64 timestamp) | ||
98 | { | ||
99 | struct ordered_event *new; | ||
100 | |||
101 | new = alloc_event(oe); | ||
102 | if (new) { | ||
103 | new->timestamp = timestamp; | ||
104 | queue_event(oe, new); | ||
105 | } | ||
106 | |||
107 | return new; | ||
108 | } | ||
109 | |||
110 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event) | ||
111 | { | ||
112 | list_move(&event->list, &oe->cache); | ||
113 | oe->nr_events--; | ||
114 | } | ||
115 | |||
116 | static int __ordered_events__flush(struct perf_session *s, | ||
117 | struct perf_tool *tool) | ||
118 | { | ||
119 | struct ordered_events *oe = &s->ordered_events; | ||
120 | struct list_head *head = &oe->events; | ||
121 | struct ordered_event *tmp, *iter; | ||
122 | struct perf_sample sample; | ||
123 | u64 limit = oe->next_flush; | ||
124 | u64 last_ts = oe->last ? oe->last->timestamp : 0ULL; | ||
125 | bool show_progress = limit == ULLONG_MAX; | ||
126 | struct ui_progress prog; | ||
127 | int ret; | ||
128 | |||
129 | if (!tool->ordered_events || !limit) | ||
130 | return 0; | ||
131 | |||
132 | if (show_progress) | ||
133 | ui_progress__init(&prog, oe->nr_events, "Processing time ordered events..."); | ||
134 | |||
135 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
136 | if (session_done()) | ||
137 | return 0; | ||
138 | |||
139 | if (iter->timestamp > limit) | ||
140 | break; | ||
141 | |||
142 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
143 | if (ret) | ||
144 | pr_err("Can't parse sample, err = %d\n", ret); | ||
145 | else { | ||
146 | ret = perf_session__deliver_event(s, iter->event, &sample, tool, | ||
147 | iter->file_offset); | ||
148 | if (ret) | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | ordered_events__delete(oe, iter); | ||
153 | oe->last_flush = iter->timestamp; | ||
154 | |||
155 | if (show_progress) | ||
156 | ui_progress__update(&prog, 1); | ||
157 | } | ||
158 | |||
159 | if (list_empty(head)) | ||
160 | oe->last = NULL; | ||
161 | else if (last_ts <= limit) | ||
162 | oe->last = list_entry(head->prev, struct ordered_event, list); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
168 | enum oe_flush how) | ||
169 | { | ||
170 | struct ordered_events *oe = &s->ordered_events; | ||
171 | static const char * const str[] = { | ||
172 | "NONE", | ||
173 | "FINAL", | ||
174 | "ROUND", | ||
175 | "HALF ", | ||
176 | }; | ||
177 | int err; | ||
178 | |||
179 | switch (how) { | ||
180 | case OE_FLUSH__FINAL: | ||
181 | oe->next_flush = ULLONG_MAX; | ||
182 | break; | ||
183 | |||
184 | case OE_FLUSH__HALF: | ||
185 | { | ||
186 | struct ordered_event *first, *last; | ||
187 | struct list_head *head = &oe->events; | ||
188 | |||
189 | first = list_entry(head->next, struct ordered_event, list); | ||
190 | last = oe->last; | ||
191 | |||
192 | /* Warn if we are called before any event got allocated. */ | ||
193 | if (WARN_ONCE(!last || list_empty(head), "empty queue")) | ||
194 | return 0; | ||
195 | |||
196 | oe->next_flush = first->timestamp; | ||
197 | oe->next_flush += (last->timestamp - first->timestamp) / 2; | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | case OE_FLUSH__ROUND: | ||
202 | case OE_FLUSH__NONE: | ||
203 | default: | ||
204 | break; | ||
205 | }; | ||
206 | |||
207 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush PRE %s, nr_events %u\n", | ||
208 | str[how], oe->nr_events); | ||
209 | pr_oe_time(oe->max_timestamp, "max_timestamp\n"); | ||
210 | |||
211 | err = __ordered_events__flush(s, tool); | ||
212 | |||
213 | if (!err) { | ||
214 | if (how == OE_FLUSH__ROUND) | ||
215 | oe->next_flush = oe->max_timestamp; | ||
216 | |||
217 | oe->last_flush_type = how; | ||
218 | } | ||
219 | |||
220 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush POST %s, nr_events %u\n", | ||
221 | str[how], oe->nr_events); | ||
222 | pr_oe_time(oe->last_flush, "last_flush\n"); | ||
223 | |||
224 | return err; | ||
225 | } | ||
226 | |||
227 | void ordered_events__init(struct ordered_events *oe) | ||
228 | { | ||
229 | INIT_LIST_HEAD(&oe->events); | ||
230 | INIT_LIST_HEAD(&oe->cache); | ||
231 | INIT_LIST_HEAD(&oe->to_free); | ||
232 | oe->max_alloc_size = (u64) -1; | ||
233 | oe->cur_alloc_size = 0; | ||
234 | } | ||
235 | |||
236 | void ordered_events__free(struct ordered_events *oe) | ||
237 | { | ||
238 | while (!list_empty(&oe->to_free)) { | ||
239 | struct ordered_event *event; | ||
240 | |||
241 | event = list_entry(oe->to_free.next, struct ordered_event, list); | ||
242 | list_del(&event->list); | ||
243 | free(event); | ||
244 | } | ||
245 | } | ||
diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h new file mode 100644 index 000000000000..3b2f20542a01 --- /dev/null +++ b/tools/perf/util/ordered-events.h | |||
@@ -0,0 +1,51 @@ | |||
1 | #ifndef __ORDERED_EVENTS_H | ||
2 | #define __ORDERED_EVENTS_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include "tool.h" | ||
6 | |||
7 | struct perf_session; | ||
8 | |||
9 | struct ordered_event { | ||
10 | u64 timestamp; | ||
11 | u64 file_offset; | ||
12 | union perf_event *event; | ||
13 | struct list_head list; | ||
14 | }; | ||
15 | |||
16 | enum oe_flush { | ||
17 | OE_FLUSH__NONE, | ||
18 | OE_FLUSH__FINAL, | ||
19 | OE_FLUSH__ROUND, | ||
20 | OE_FLUSH__HALF, | ||
21 | }; | ||
22 | |||
23 | struct ordered_events { | ||
24 | u64 last_flush; | ||
25 | u64 next_flush; | ||
26 | u64 max_timestamp; | ||
27 | u64 max_alloc_size; | ||
28 | u64 cur_alloc_size; | ||
29 | struct list_head events; | ||
30 | struct list_head cache; | ||
31 | struct list_head to_free; | ||
32 | struct ordered_event *buffer; | ||
33 | struct ordered_event *last; | ||
34 | int buffer_idx; | ||
35 | unsigned int nr_events; | ||
36 | enum oe_flush last_flush_type; | ||
37 | }; | ||
38 | |||
39 | struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp); | ||
40 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event); | ||
41 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
42 | enum oe_flush how); | ||
43 | void ordered_events__init(struct ordered_events *oe); | ||
44 | void ordered_events__free(struct ordered_events *oe); | ||
45 | |||
46 | static inline | ||
47 | void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) | ||
48 | { | ||
49 | oe->max_alloc_size = size; | ||
50 | } | ||
51 | #endif /* __ORDERED_EVENTS_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1e15df10a88c..d76aa30cb1fb 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debug.h" | ||
13 | #include <api/fs/debugfs.h> | 14 | #include <api/fs/debugfs.h> |
14 | #include "parse-events-bison.h" | 15 | #include "parse-events-bison.h" |
15 | #define YY_EXTRA_TYPE int | 16 | #define YY_EXTRA_TYPE int |
@@ -633,18 +634,28 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
633 | char *name, struct list_head *head_config) | 634 | char *name, struct list_head *head_config) |
634 | { | 635 | { |
635 | struct perf_event_attr attr; | 636 | struct perf_event_attr attr; |
637 | struct perf_pmu_info info; | ||
636 | struct perf_pmu *pmu; | 638 | struct perf_pmu *pmu; |
637 | struct perf_evsel *evsel; | 639 | struct perf_evsel *evsel; |
638 | const char *unit; | ||
639 | double scale; | ||
640 | 640 | ||
641 | pmu = perf_pmu__find(name); | 641 | pmu = perf_pmu__find(name); |
642 | if (!pmu) | 642 | if (!pmu) |
643 | return -EINVAL; | 643 | return -EINVAL; |
644 | 644 | ||
645 | memset(&attr, 0, sizeof(attr)); | 645 | if (pmu->default_config) { |
646 | memcpy(&attr, pmu->default_config, | ||
647 | sizeof(struct perf_event_attr)); | ||
648 | } else { | ||
649 | memset(&attr, 0, sizeof(attr)); | ||
650 | } | ||
651 | |||
652 | if (!head_config) { | ||
653 | attr.type = pmu->type; | ||
654 | evsel = __add_event(list, idx, &attr, NULL, pmu->cpus); | ||
655 | return evsel ? 0 : -ENOMEM; | ||
656 | } | ||
646 | 657 | ||
647 | if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) | 658 | if (perf_pmu__check_alias(pmu, head_config, &info)) |
648 | return -EINVAL; | 659 | return -EINVAL; |
649 | 660 | ||
650 | /* | 661 | /* |
@@ -659,8 +670,8 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
659 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), | 670 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), |
660 | pmu->cpus); | 671 | pmu->cpus); |
661 | if (evsel) { | 672 | if (evsel) { |
662 | evsel->unit = unit; | 673 | evsel->unit = info.unit; |
663 | evsel->scale = scale; | 674 | evsel->scale = info.scale; |
664 | } | 675 | } |
665 | 676 | ||
666 | return evsel ? 0 : -ENOMEM; | 677 | return evsel ? 0 : -ENOMEM; |
@@ -973,7 +984,7 @@ int parse_filter(const struct option *opt, const char *str, | |||
973 | 984 | ||
974 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 985 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
975 | fprintf(stderr, | 986 | fprintf(stderr, |
976 | "-F option should follow a -e tracepoint option\n"); | 987 | "--filter option should follow a -e tracepoint option\n"); |
977 | return -1; | 988 | return -1; |
978 | } | 989 | } |
979 | 990 | ||
@@ -1006,9 +1017,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
1006 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 1017 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
1007 | char evt_path[MAXPATHLEN]; | 1018 | char evt_path[MAXPATHLEN]; |
1008 | char dir_path[MAXPATHLEN]; | 1019 | char dir_path[MAXPATHLEN]; |
1020 | char sbuf[STRERR_BUFSIZE]; | ||
1009 | 1021 | ||
1010 | if (debugfs_valid_mountpoint(tracing_events_path)) { | 1022 | if (debugfs_valid_mountpoint(tracing_events_path)) { |
1011 | printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); | 1023 | printf(" [ Tracepoints not available: %s ]\n", |
1024 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
1012 | return; | 1025 | return; |
1013 | } | 1026 | } |
1014 | 1027 | ||
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 0bc87ba46bf3..55fab6ad609a 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -210,6 +210,16 @@ PE_NAME '/' event_config '/' | |||
210 | parse_events__free_terms($3); | 210 | parse_events__free_terms($3); |
211 | $$ = list; | 211 | $$ = list; |
212 | } | 212 | } |
213 | | | ||
214 | PE_NAME '/' '/' | ||
215 | { | ||
216 | struct parse_events_evlist *data = _data; | ||
217 | struct list_head *list; | ||
218 | |||
219 | ALLOC_LIST(list); | ||
220 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); | ||
221 | $$ = list; | ||
222 | } | ||
213 | 223 | ||
214 | value_sym: | 224 | value_sym: |
215 | PE_VALUE_SYM_HW | 225 | PE_VALUE_SYM_HW |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 7a811eb61f75..93a41ca96b8e 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -2,6 +2,8 @@ | |||
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <unistd.h> | 3 | #include <unistd.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <stdbool.h> | ||
6 | #include <stdarg.h> | ||
5 | #include <dirent.h> | 7 | #include <dirent.h> |
6 | #include <api/fs/fs.h> | 8 | #include <api/fs/fs.h> |
7 | #include <locale.h> | 9 | #include <locale.h> |
@@ -14,8 +16,8 @@ | |||
14 | 16 | ||
15 | struct perf_pmu_alias { | 17 | struct perf_pmu_alias { |
16 | char *name; | 18 | char *name; |
17 | struct list_head terms; | 19 | struct list_head terms; /* HEAD struct parse_events_term -> list */ |
18 | struct list_head list; | 20 | struct list_head list; /* ELEM */ |
19 | char unit[UNIT_MAX_LEN+1]; | 21 | char unit[UNIT_MAX_LEN+1]; |
20 | double scale; | 22 | double scale; |
21 | }; | 23 | }; |
@@ -208,6 +210,19 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
208 | return 0; | 210 | return 0; |
209 | } | 211 | } |
210 | 212 | ||
213 | static inline bool pmu_alias_info_file(char *name) | ||
214 | { | ||
215 | size_t len; | ||
216 | |||
217 | len = strlen(name); | ||
218 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
219 | return true; | ||
220 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
221 | return true; | ||
222 | |||
223 | return false; | ||
224 | } | ||
225 | |||
211 | /* | 226 | /* |
212 | * Process all the sysfs attributes located under the directory | 227 | * Process all the sysfs attributes located under the directory |
213 | * specified in 'dir' parameter. | 228 | * specified in 'dir' parameter. |
@@ -216,7 +231,6 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
216 | { | 231 | { |
217 | struct dirent *evt_ent; | 232 | struct dirent *evt_ent; |
218 | DIR *event_dir; | 233 | DIR *event_dir; |
219 | size_t len; | ||
220 | int ret = 0; | 234 | int ret = 0; |
221 | 235 | ||
222 | event_dir = opendir(dir); | 236 | event_dir = opendir(dir); |
@@ -232,13 +246,9 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
232 | continue; | 246 | continue; |
233 | 247 | ||
234 | /* | 248 | /* |
235 | * skip .unit and .scale info files | 249 | * skip info files parsed in perf_pmu__new_alias() |
236 | * parsed in perf_pmu__new_alias() | ||
237 | */ | 250 | */ |
238 | len = strlen(name); | 251 | if (pmu_alias_info_file(name)) |
239 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
240 | continue; | ||
241 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
242 | continue; | 252 | continue; |
243 | 253 | ||
244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | 254 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
@@ -387,6 +397,12 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
387 | return cpus; | 397 | return cpus; |
388 | } | 398 | } |
389 | 399 | ||
400 | struct perf_event_attr *__attribute__((weak)) | ||
401 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) | ||
402 | { | ||
403 | return NULL; | ||
404 | } | ||
405 | |||
390 | static struct perf_pmu *pmu_lookup(const char *name) | 406 | static struct perf_pmu *pmu_lookup(const char *name) |
391 | { | 407 | { |
392 | struct perf_pmu *pmu; | 408 | struct perf_pmu *pmu; |
@@ -421,6 +437,9 @@ static struct perf_pmu *pmu_lookup(const char *name) | |||
421 | pmu->name = strdup(name); | 437 | pmu->name = strdup(name); |
422 | pmu->type = type; | 438 | pmu->type = type; |
423 | list_add_tail(&pmu->list, &pmus); | 439 | list_add_tail(&pmu->list, &pmus); |
440 | |||
441 | pmu->default_config = perf_pmu__get_default_config(pmu); | ||
442 | |||
424 | return pmu; | 443 | return pmu; |
425 | } | 444 | } |
426 | 445 | ||
@@ -479,28 +498,24 @@ pmu_find_format(struct list_head *formats, char *name) | |||
479 | } | 498 | } |
480 | 499 | ||
481 | /* | 500 | /* |
482 | * Returns value based on the format definition (format parameter) | 501 | * Sets value based on the format definition (format parameter) |
483 | * and unformated value (value parameter). | 502 | * and unformated value (value parameter). |
484 | * | ||
485 | * TODO maybe optimize a little ;) | ||
486 | */ | 503 | */ |
487 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | 504 | static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, |
505 | bool zero) | ||
488 | { | 506 | { |
489 | unsigned long fbit, vbit; | 507 | unsigned long fbit, vbit; |
490 | __u64 v = 0; | ||
491 | 508 | ||
492 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | 509 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { |
493 | 510 | ||
494 | if (!test_bit(fbit, format)) | 511 | if (!test_bit(fbit, format)) |
495 | continue; | 512 | continue; |
496 | 513 | ||
497 | if (!(value & (1llu << vbit++))) | 514 | if (value & (1llu << vbit++)) |
498 | continue; | 515 | *v |= (1llu << fbit); |
499 | 516 | else if (zero) | |
500 | v |= (1llu << fbit); | 517 | *v &= ~(1llu << fbit); |
501 | } | 518 | } |
502 | |||
503 | return v; | ||
504 | } | 519 | } |
505 | 520 | ||
506 | /* | 521 | /* |
@@ -509,7 +524,8 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) | |||
509 | */ | 524 | */ |
510 | static int pmu_config_term(struct list_head *formats, | 525 | static int pmu_config_term(struct list_head *formats, |
511 | struct perf_event_attr *attr, | 526 | struct perf_event_attr *attr, |
512 | struct parse_events_term *term) | 527 | struct parse_events_term *term, |
528 | bool zero) | ||
513 | { | 529 | { |
514 | struct perf_pmu_format *format; | 530 | struct perf_pmu_format *format; |
515 | __u64 *vp; | 531 | __u64 *vp; |
@@ -548,18 +564,19 @@ static int pmu_config_term(struct list_head *formats, | |||
548 | * non-hardcoded terms, here's the place to translate | 564 | * non-hardcoded terms, here's the place to translate |
549 | * them into value. | 565 | * them into value. |
550 | */ | 566 | */ |
551 | *vp |= pmu_format_value(format->bits, term->val.num); | 567 | pmu_format_value(format->bits, term->val.num, vp, zero); |
552 | return 0; | 568 | return 0; |
553 | } | 569 | } |
554 | 570 | ||
555 | int perf_pmu__config_terms(struct list_head *formats, | 571 | int perf_pmu__config_terms(struct list_head *formats, |
556 | struct perf_event_attr *attr, | 572 | struct perf_event_attr *attr, |
557 | struct list_head *head_terms) | 573 | struct list_head *head_terms, |
574 | bool zero) | ||
558 | { | 575 | { |
559 | struct parse_events_term *term; | 576 | struct parse_events_term *term; |
560 | 577 | ||
561 | list_for_each_entry(term, head_terms, list) | 578 | list_for_each_entry(term, head_terms, list) |
562 | if (pmu_config_term(formats, attr, term)) | 579 | if (pmu_config_term(formats, attr, term, zero)) |
563 | return -EINVAL; | 580 | return -EINVAL; |
564 | 581 | ||
565 | return 0; | 582 | return 0; |
@@ -573,8 +590,10 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
573 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 590 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
574 | struct list_head *head_terms) | 591 | struct list_head *head_terms) |
575 | { | 592 | { |
593 | bool zero = !!pmu->default_config; | ||
594 | |||
576 | attr->type = pmu->type; | 595 | attr->type = pmu->type; |
577 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); | 596 | return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero); |
578 | } | 597 | } |
579 | 598 | ||
580 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | 599 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
@@ -634,7 +653,7 @@ static int check_unit_scale(struct perf_pmu_alias *alias, | |||
634 | * defined for the alias | 653 | * defined for the alias |
635 | */ | 654 | */ |
636 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 655 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
637 | const char **unit, double *scale) | 656 | struct perf_pmu_info *info) |
638 | { | 657 | { |
639 | struct parse_events_term *term, *h; | 658 | struct parse_events_term *term, *h; |
640 | struct perf_pmu_alias *alias; | 659 | struct perf_pmu_alias *alias; |
@@ -644,8 +663,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
644 | * Mark unit and scale as not set | 663 | * Mark unit and scale as not set |
645 | * (different from default values, see below) | 664 | * (different from default values, see below) |
646 | */ | 665 | */ |
647 | *unit = NULL; | 666 | info->unit = NULL; |
648 | *scale = 0.0; | 667 | info->scale = 0.0; |
649 | 668 | ||
650 | list_for_each_entry_safe(term, h, head_terms, list) { | 669 | list_for_each_entry_safe(term, h, head_terms, list) { |
651 | alias = pmu_find_alias(pmu, term); | 670 | alias = pmu_find_alias(pmu, term); |
@@ -655,7 +674,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
655 | if (ret) | 674 | if (ret) |
656 | return ret; | 675 | return ret; |
657 | 676 | ||
658 | ret = check_unit_scale(alias, unit, scale); | 677 | ret = check_unit_scale(alias, &info->unit, &info->scale); |
659 | if (ret) | 678 | if (ret) |
660 | return ret; | 679 | return ret; |
661 | 680 | ||
@@ -668,11 +687,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
668 | * set defaults as for evsel | 687 | * set defaults as for evsel |
669 | * unit cannot left to NULL | 688 | * unit cannot left to NULL |
670 | */ | 689 | */ |
671 | if (*unit == NULL) | 690 | if (info->unit == NULL) |
672 | *unit = ""; | 691 | info->unit = ""; |
673 | 692 | ||
674 | if (*scale == 0.0) | 693 | if (info->scale == 0.0) |
675 | *scale = 1.0; | 694 | info->scale = 1.0; |
676 | 695 | ||
677 | return 0; | 696 | return 0; |
678 | } | 697 | } |
@@ -794,3 +813,39 @@ bool pmu_have_event(const char *pname, const char *name) | |||
794 | } | 813 | } |
795 | return false; | 814 | return false; |
796 | } | 815 | } |
816 | |||
817 | static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) | ||
818 | { | ||
819 | struct stat st; | ||
820 | char path[PATH_MAX]; | ||
821 | const char *sysfs; | ||
822 | |||
823 | sysfs = sysfs__mountpoint(); | ||
824 | if (!sysfs) | ||
825 | return NULL; | ||
826 | |||
827 | snprintf(path, PATH_MAX, | ||
828 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); | ||
829 | |||
830 | if (stat(path, &st) < 0) | ||
831 | return NULL; | ||
832 | |||
833 | return fopen(path, "r"); | ||
834 | } | ||
835 | |||
836 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
837 | ...) | ||
838 | { | ||
839 | va_list args; | ||
840 | FILE *file; | ||
841 | int ret = EOF; | ||
842 | |||
843 | va_start(args, fmt); | ||
844 | file = perf_pmu__open_file(pmu, name); | ||
845 | if (file) { | ||
846 | ret = vfscanf(file, fmt, args); | ||
847 | fclose(file); | ||
848 | } | ||
849 | va_end(args); | ||
850 | return ret; | ||
851 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index c14a543ce1f3..fe90a012c003 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -13,13 +13,21 @@ enum { | |||
13 | 13 | ||
14 | #define PERF_PMU_FORMAT_BITS 64 | 14 | #define PERF_PMU_FORMAT_BITS 64 |
15 | 15 | ||
16 | struct perf_event_attr; | ||
17 | |||
16 | struct perf_pmu { | 18 | struct perf_pmu { |
17 | char *name; | 19 | char *name; |
18 | __u32 type; | 20 | __u32 type; |
21 | struct perf_event_attr *default_config; | ||
19 | struct cpu_map *cpus; | 22 | struct cpu_map *cpus; |
20 | struct list_head format; | 23 | struct list_head format; /* HEAD struct perf_pmu_format -> list */ |
21 | struct list_head aliases; | 24 | struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */ |
22 | struct list_head list; | 25 | struct list_head list; /* ELEM */ |
26 | }; | ||
27 | |||
28 | struct perf_pmu_info { | ||
29 | const char *unit; | ||
30 | double scale; | ||
23 | }; | 31 | }; |
24 | 32 | ||
25 | struct perf_pmu *perf_pmu__find(const char *name); | 33 | struct perf_pmu *perf_pmu__find(const char *name); |
@@ -27,9 +35,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
27 | struct list_head *head_terms); | 35 | struct list_head *head_terms); |
28 | int perf_pmu__config_terms(struct list_head *formats, | 36 | int perf_pmu__config_terms(struct list_head *formats, |
29 | struct perf_event_attr *attr, | 37 | struct perf_event_attr *attr, |
30 | struct list_head *head_terms); | 38 | struct list_head *head_terms, |
39 | bool zero); | ||
31 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 40 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
32 | const char **unit, double *scale); | 41 | struct perf_pmu_info *info); |
33 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 42 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
34 | struct list_head *head_terms); | 43 | struct list_head *head_terms); |
35 | int perf_pmu_wrap(void); | 44 | int perf_pmu_wrap(void); |
@@ -45,5 +54,11 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | |||
45 | void print_pmu_events(const char *event_glob, bool name_only); | 54 | void print_pmu_events(const char *event_glob, bool name_only); |
46 | bool pmu_have_event(const char *pname, const char *name); | 55 | bool pmu_have_event(const char *pname, const char *name); |
47 | 56 | ||
57 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
58 | ...) __attribute__((format(scanf, 3, 4))); | ||
59 | |||
48 | int perf_pmu__test(void); | 60 | int perf_pmu__test(void); |
61 | |||
62 | struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); | ||
63 | |||
49 | #endif /* __PMU_H */ | 64 | #endif /* __PMU_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 9a0a1839a377..c150ca4343eb 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -79,7 +79,7 @@ static int init_symbol_maps(bool user_only) | |||
79 | int ret; | 79 | int ret; |
80 | 80 | ||
81 | symbol_conf.sort_by_name = true; | 81 | symbol_conf.sort_by_name = true; |
82 | ret = symbol__init(); | 82 | ret = symbol__init(NULL); |
83 | if (ret < 0) { | 83 | if (ret < 0) { |
84 | pr_debug("Failed to init symbol map.\n"); | 84 | pr_debug("Failed to init symbol map.\n"); |
85 | goto out; | 85 | goto out; |
@@ -184,7 +184,8 @@ static struct dso *kernel_get_module_dso(const char *module) | |||
184 | const char *vmlinux_name; | 184 | const char *vmlinux_name; |
185 | 185 | ||
186 | if (module) { | 186 | if (module) { |
187 | list_for_each_entry(dso, &host_machine->kernel_dsos, node) { | 187 | list_for_each_entry(dso, &host_machine->kernel_dsos.head, |
188 | node) { | ||
188 | if (strncmp(dso->short_name + 1, module, | 189 | if (strncmp(dso->short_name + 1, module, |
189 | dso->short_name_len - 2) == 0) | 190 | dso->short_name_len - 2) == 0) |
190 | goto found; | 191 | goto found; |
@@ -258,21 +259,33 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | |||
258 | #ifdef HAVE_DWARF_SUPPORT | 259 | #ifdef HAVE_DWARF_SUPPORT |
259 | 260 | ||
260 | /* Open new debuginfo of given module */ | 261 | /* Open new debuginfo of given module */ |
261 | static struct debuginfo *open_debuginfo(const char *module) | 262 | static struct debuginfo *open_debuginfo(const char *module, bool silent) |
262 | { | 263 | { |
263 | const char *path = module; | 264 | const char *path = module; |
265 | struct debuginfo *ret; | ||
264 | 266 | ||
265 | if (!module || !strchr(module, '/')) { | 267 | if (!module || !strchr(module, '/')) { |
266 | path = kernel_get_module_path(module); | 268 | path = kernel_get_module_path(module); |
267 | if (!path) { | 269 | if (!path) { |
268 | pr_err("Failed to find path of %s module.\n", | 270 | if (!silent) |
269 | module ?: "kernel"); | 271 | pr_err("Failed to find path of %s module.\n", |
272 | module ?: "kernel"); | ||
270 | return NULL; | 273 | return NULL; |
271 | } | 274 | } |
272 | } | 275 | } |
273 | return debuginfo__new(path); | 276 | ret = debuginfo__new(path); |
277 | if (!ret && !silent) { | ||
278 | pr_warning("The %s file has no debug information.\n", path); | ||
279 | if (!module || !strtailcmp(path, ".ko")) | ||
280 | pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, "); | ||
281 | else | ||
282 | pr_warning("Rebuild with -g, "); | ||
283 | pr_warning("or install an appropriate debuginfo package.\n"); | ||
284 | } | ||
285 | return ret; | ||
274 | } | 286 | } |
275 | 287 | ||
288 | |||
276 | static int get_text_start_address(const char *exec, unsigned long *address) | 289 | static int get_text_start_address(const char *exec, unsigned long *address) |
277 | { | 290 | { |
278 | Elf *elf; | 291 | Elf *elf; |
@@ -333,15 +346,13 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, | |||
333 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, | 346 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, |
334 | tp->module ? : "kernel"); | 347 | tp->module ? : "kernel"); |
335 | 348 | ||
336 | dinfo = open_debuginfo(tp->module); | 349 | dinfo = open_debuginfo(tp->module, verbose == 0); |
337 | if (dinfo) { | 350 | if (dinfo) { |
338 | ret = debuginfo__find_probe_point(dinfo, | 351 | ret = debuginfo__find_probe_point(dinfo, |
339 | (unsigned long)addr, pp); | 352 | (unsigned long)addr, pp); |
340 | debuginfo__delete(dinfo); | 353 | debuginfo__delete(dinfo); |
341 | } else { | 354 | } else |
342 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); | ||
343 | ret = -ENOENT; | 355 | ret = -ENOENT; |
344 | } | ||
345 | 356 | ||
346 | if (ret > 0) { | 357 | if (ret > 0) { |
347 | pp->retprobe = tp->retprobe; | 358 | pp->retprobe = tp->retprobe; |
@@ -457,13 +468,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
457 | struct debuginfo *dinfo; | 468 | struct debuginfo *dinfo; |
458 | int ntevs, ret = 0; | 469 | int ntevs, ret = 0; |
459 | 470 | ||
460 | dinfo = open_debuginfo(target); | 471 | dinfo = open_debuginfo(target, !need_dwarf); |
461 | 472 | ||
462 | if (!dinfo) { | 473 | if (!dinfo) { |
463 | if (need_dwarf) { | 474 | if (need_dwarf) |
464 | pr_warning("Failed to open debuginfo file.\n"); | ||
465 | return -ENOENT; | 475 | return -ENOENT; |
466 | } | ||
467 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); | 476 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
468 | return 0; | 477 | return 0; |
469 | } | 478 | } |
@@ -565,7 +574,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir, | |||
565 | 574 | ||
566 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | 575 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
567 | { | 576 | { |
568 | char buf[LINEBUF_SIZE]; | 577 | char buf[LINEBUF_SIZE], sbuf[STRERR_BUFSIZE]; |
569 | const char *color = show_num ? "" : PERF_COLOR_BLUE; | 578 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
570 | const char *prefix = NULL; | 579 | const char *prefix = NULL; |
571 | 580 | ||
@@ -585,7 +594,8 @@ static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | |||
585 | return 1; | 594 | return 1; |
586 | error: | 595 | error: |
587 | if (ferror(fp)) { | 596 | if (ferror(fp)) { |
588 | pr_warning("File read error: %s\n", strerror(errno)); | 597 | pr_warning("File read error: %s\n", |
598 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
589 | return -1; | 599 | return -1; |
590 | } | 600 | } |
591 | return 0; | 601 | return 0; |
@@ -618,13 +628,12 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
618 | FILE *fp; | 628 | FILE *fp; |
619 | int ret; | 629 | int ret; |
620 | char *tmp; | 630 | char *tmp; |
631 | char sbuf[STRERR_BUFSIZE]; | ||
621 | 632 | ||
622 | /* Search a line range */ | 633 | /* Search a line range */ |
623 | dinfo = open_debuginfo(module); | 634 | dinfo = open_debuginfo(module, false); |
624 | if (!dinfo) { | 635 | if (!dinfo) |
625 | pr_warning("Failed to open debuginfo file.\n"); | ||
626 | return -ENOENT; | 636 | return -ENOENT; |
627 | } | ||
628 | 637 | ||
629 | ret = debuginfo__find_line_range(dinfo, lr); | 638 | ret = debuginfo__find_line_range(dinfo, lr); |
630 | debuginfo__delete(dinfo); | 639 | debuginfo__delete(dinfo); |
@@ -656,7 +665,7 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
656 | fp = fopen(lr->path, "r"); | 665 | fp = fopen(lr->path, "r"); |
657 | if (fp == NULL) { | 666 | if (fp == NULL) { |
658 | pr_warning("Failed to open %s: %s\n", lr->path, | 667 | pr_warning("Failed to open %s: %s\n", lr->path, |
659 | strerror(errno)); | 668 | strerror_r(errno, sbuf, sizeof(sbuf))); |
660 | return -errno; | 669 | return -errno; |
661 | } | 670 | } |
662 | /* Skip to starting line number */ | 671 | /* Skip to starting line number */ |
@@ -689,11 +698,11 @@ end: | |||
689 | return ret; | 698 | return ret; |
690 | } | 699 | } |
691 | 700 | ||
692 | int show_line_range(struct line_range *lr, const char *module) | 701 | int show_line_range(struct line_range *lr, const char *module, bool user) |
693 | { | 702 | { |
694 | int ret; | 703 | int ret; |
695 | 704 | ||
696 | ret = init_symbol_maps(false); | 705 | ret = init_symbol_maps(user); |
697 | if (ret < 0) | 706 | if (ret < 0) |
698 | return ret; | 707 | return ret; |
699 | ret = __show_line_range(lr, module); | 708 | ret = __show_line_range(lr, module); |
@@ -768,13 +777,12 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
768 | int i, ret = 0; | 777 | int i, ret = 0; |
769 | struct debuginfo *dinfo; | 778 | struct debuginfo *dinfo; |
770 | 779 | ||
771 | ret = init_symbol_maps(false); | 780 | ret = init_symbol_maps(pevs->uprobes); |
772 | if (ret < 0) | 781 | if (ret < 0) |
773 | return ret; | 782 | return ret; |
774 | 783 | ||
775 | dinfo = open_debuginfo(module); | 784 | dinfo = open_debuginfo(module, false); |
776 | if (!dinfo) { | 785 | if (!dinfo) { |
777 | pr_warning("Failed to open debuginfo file.\n"); | ||
778 | ret = -ENOENT; | 786 | ret = -ENOENT; |
779 | goto out; | 787 | goto out; |
780 | } | 788 | } |
@@ -815,7 +823,8 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
815 | } | 823 | } |
816 | 824 | ||
817 | int show_line_range(struct line_range *lr __maybe_unused, | 825 | int show_line_range(struct line_range *lr __maybe_unused, |
818 | const char *module __maybe_unused) | 826 | const char *module __maybe_unused, |
827 | bool user __maybe_unused) | ||
819 | { | 828 | { |
820 | pr_warning("Debuginfo-analysis is not supported.\n"); | 829 | pr_warning("Debuginfo-analysis is not supported.\n"); |
821 | return -ENOSYS; | 830 | return -ENOSYS; |
@@ -1405,8 +1414,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
1405 | 1414 | ||
1406 | return tmp - buf; | 1415 | return tmp - buf; |
1407 | error: | 1416 | error: |
1408 | pr_debug("Failed to synthesize perf probe argument: %s\n", | 1417 | pr_debug("Failed to synthesize perf probe argument: %d\n", ret); |
1409 | strerror(-ret)); | ||
1410 | return ret; | 1418 | return ret; |
1411 | } | 1419 | } |
1412 | 1420 | ||
@@ -1455,8 +1463,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
1455 | 1463 | ||
1456 | return buf; | 1464 | return buf; |
1457 | error: | 1465 | error: |
1458 | pr_debug("Failed to synthesize perf probe point: %s\n", | 1466 | pr_debug("Failed to synthesize perf probe point: %d\n", ret); |
1459 | strerror(-ret)); | ||
1460 | free(buf); | 1467 | free(buf); |
1461 | return NULL; | 1468 | return NULL; |
1462 | } | 1469 | } |
@@ -1780,10 +1787,11 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) | |||
1780 | memset(tev, 0, sizeof(*tev)); | 1787 | memset(tev, 0, sizeof(*tev)); |
1781 | } | 1788 | } |
1782 | 1789 | ||
1783 | static void print_warn_msg(const char *file, bool is_kprobe) | 1790 | static void print_open_warning(int err, bool is_kprobe) |
1784 | { | 1791 | { |
1792 | char sbuf[STRERR_BUFSIZE]; | ||
1785 | 1793 | ||
1786 | if (errno == ENOENT) { | 1794 | if (err == -ENOENT) { |
1787 | const char *config; | 1795 | const char *config; |
1788 | 1796 | ||
1789 | if (!is_kprobe) | 1797 | if (!is_kprobe) |
@@ -1791,25 +1799,43 @@ static void print_warn_msg(const char *file, bool is_kprobe) | |||
1791 | else | 1799 | else |
1792 | config = "CONFIG_KPROBE_EVENTS"; | 1800 | config = "CONFIG_KPROBE_EVENTS"; |
1793 | 1801 | ||
1794 | pr_warning("%s file does not exist - please rebuild kernel" | 1802 | pr_warning("%cprobe_events file does not exist" |
1795 | " with %s.\n", file, config); | 1803 | " - please rebuild kernel with %s.\n", |
1796 | } else | 1804 | is_kprobe ? 'k' : 'u', config); |
1797 | pr_warning("Failed to open %s file: %s\n", file, | 1805 | } else if (err == -ENOTSUP) |
1798 | strerror(errno)); | 1806 | pr_warning("Debugfs is not mounted.\n"); |
1807 | else | ||
1808 | pr_warning("Failed to open %cprobe_events: %s\n", | ||
1809 | is_kprobe ? 'k' : 'u', | ||
1810 | strerror_r(-err, sbuf, sizeof(sbuf))); | ||
1811 | } | ||
1812 | |||
1813 | static void print_both_open_warning(int kerr, int uerr) | ||
1814 | { | ||
1815 | /* Both kprobes and uprobes are disabled, warn it. */ | ||
1816 | if (kerr == -ENOTSUP && uerr == -ENOTSUP) | ||
1817 | pr_warning("Debugfs is not mounted.\n"); | ||
1818 | else if (kerr == -ENOENT && uerr == -ENOENT) | ||
1819 | pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS " | ||
1820 | "or/and CONFIG_UPROBE_EVENTS.\n"); | ||
1821 | else { | ||
1822 | char sbuf[STRERR_BUFSIZE]; | ||
1823 | pr_warning("Failed to open kprobe events: %s.\n", | ||
1824 | strerror_r(-kerr, sbuf, sizeof(sbuf))); | ||
1825 | pr_warning("Failed to open uprobe events: %s.\n", | ||
1826 | strerror_r(-uerr, sbuf, sizeof(sbuf))); | ||
1827 | } | ||
1799 | } | 1828 | } |
1800 | 1829 | ||
1801 | static int open_probe_events(const char *trace_file, bool readwrite, | 1830 | static int open_probe_events(const char *trace_file, bool readwrite) |
1802 | bool is_kprobe) | ||
1803 | { | 1831 | { |
1804 | char buf[PATH_MAX]; | 1832 | char buf[PATH_MAX]; |
1805 | const char *__debugfs; | 1833 | const char *__debugfs; |
1806 | int ret; | 1834 | int ret; |
1807 | 1835 | ||
1808 | __debugfs = debugfs_find_mountpoint(); | 1836 | __debugfs = debugfs_find_mountpoint(); |
1809 | if (__debugfs == NULL) { | 1837 | if (__debugfs == NULL) |
1810 | pr_warning("Debugfs is not mounted.\n"); | 1838 | return -ENOTSUP; |
1811 | return -ENOENT; | ||
1812 | } | ||
1813 | 1839 | ||
1814 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); | 1840 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); |
1815 | if (ret >= 0) { | 1841 | if (ret >= 0) { |
@@ -1820,19 +1846,19 @@ static int open_probe_events(const char *trace_file, bool readwrite, | |||
1820 | ret = open(buf, O_RDONLY, 0); | 1846 | ret = open(buf, O_RDONLY, 0); |
1821 | 1847 | ||
1822 | if (ret < 0) | 1848 | if (ret < 0) |
1823 | print_warn_msg(buf, is_kprobe); | 1849 | ret = -errno; |
1824 | } | 1850 | } |
1825 | return ret; | 1851 | return ret; |
1826 | } | 1852 | } |
1827 | 1853 | ||
1828 | static int open_kprobe_events(bool readwrite) | 1854 | static int open_kprobe_events(bool readwrite) |
1829 | { | 1855 | { |
1830 | return open_probe_events("tracing/kprobe_events", readwrite, true); | 1856 | return open_probe_events("tracing/kprobe_events", readwrite); |
1831 | } | 1857 | } |
1832 | 1858 | ||
1833 | static int open_uprobe_events(bool readwrite) | 1859 | static int open_uprobe_events(bool readwrite) |
1834 | { | 1860 | { |
1835 | return open_probe_events("tracing/uprobe_events", readwrite, false); | 1861 | return open_probe_events("tracing/uprobe_events", readwrite); |
1836 | } | 1862 | } |
1837 | 1863 | ||
1838 | /* Get raw string list of current kprobe_events or uprobe_events */ | 1864 | /* Get raw string list of current kprobe_events or uprobe_events */ |
@@ -1857,7 +1883,7 @@ static struct strlist *get_probe_trace_command_rawlist(int fd) | |||
1857 | p[idx] = '\0'; | 1883 | p[idx] = '\0'; |
1858 | ret = strlist__add(sl, buf); | 1884 | ret = strlist__add(sl, buf); |
1859 | if (ret < 0) { | 1885 | if (ret < 0) { |
1860 | pr_debug("strlist__add failed: %s\n", strerror(-ret)); | 1886 | pr_debug("strlist__add failed (%d)\n", ret); |
1861 | strlist__delete(sl); | 1887 | strlist__delete(sl); |
1862 | return NULL; | 1888 | return NULL; |
1863 | } | 1889 | } |
@@ -1916,7 +1942,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
1916 | 1942 | ||
1917 | rawlist = get_probe_trace_command_rawlist(fd); | 1943 | rawlist = get_probe_trace_command_rawlist(fd); |
1918 | if (!rawlist) | 1944 | if (!rawlist) |
1919 | return -ENOENT; | 1945 | return -ENOMEM; |
1920 | 1946 | ||
1921 | strlist__for_each(ent, rawlist) { | 1947 | strlist__for_each(ent, rawlist) { |
1922 | ret = parse_probe_trace_command(ent->s, &tev); | 1948 | ret = parse_probe_trace_command(ent->s, &tev); |
@@ -1940,27 +1966,34 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
1940 | /* List up current perf-probe events */ | 1966 | /* List up current perf-probe events */ |
1941 | int show_perf_probe_events(void) | 1967 | int show_perf_probe_events(void) |
1942 | { | 1968 | { |
1943 | int fd, ret; | 1969 | int kp_fd, up_fd, ret; |
1944 | 1970 | ||
1945 | setup_pager(); | 1971 | setup_pager(); |
1946 | fd = open_kprobe_events(false); | ||
1947 | |||
1948 | if (fd < 0) | ||
1949 | return fd; | ||
1950 | 1972 | ||
1951 | ret = init_symbol_maps(false); | 1973 | ret = init_symbol_maps(false); |
1952 | if (ret < 0) | 1974 | if (ret < 0) |
1953 | return ret; | 1975 | return ret; |
1954 | 1976 | ||
1955 | ret = __show_perf_probe_events(fd, true); | 1977 | kp_fd = open_kprobe_events(false); |
1956 | close(fd); | 1978 | if (kp_fd >= 0) { |
1979 | ret = __show_perf_probe_events(kp_fd, true); | ||
1980 | close(kp_fd); | ||
1981 | if (ret < 0) | ||
1982 | goto out; | ||
1983 | } | ||
1957 | 1984 | ||
1958 | fd = open_uprobe_events(false); | 1985 | up_fd = open_uprobe_events(false); |
1959 | if (fd >= 0) { | 1986 | if (kp_fd < 0 && up_fd < 0) { |
1960 | ret = __show_perf_probe_events(fd, false); | 1987 | print_both_open_warning(kp_fd, up_fd); |
1961 | close(fd); | 1988 | ret = kp_fd; |
1989 | goto out; | ||
1962 | } | 1990 | } |
1963 | 1991 | ||
1992 | if (up_fd >= 0) { | ||
1993 | ret = __show_perf_probe_events(up_fd, false); | ||
1994 | close(up_fd); | ||
1995 | } | ||
1996 | out: | ||
1964 | exit_symbol_maps(); | 1997 | exit_symbol_maps(); |
1965 | return ret; | 1998 | return ret; |
1966 | } | 1999 | } |
@@ -1976,6 +2009,8 @@ static struct strlist *get_probe_trace_event_names(int fd, bool include_group) | |||
1976 | 2009 | ||
1977 | memset(&tev, 0, sizeof(tev)); | 2010 | memset(&tev, 0, sizeof(tev)); |
1978 | rawlist = get_probe_trace_command_rawlist(fd); | 2011 | rawlist = get_probe_trace_command_rawlist(fd); |
2012 | if (!rawlist) | ||
2013 | return NULL; | ||
1979 | sl = strlist__new(true, NULL); | 2014 | sl = strlist__new(true, NULL); |
1980 | strlist__for_each(ent, rawlist) { | 2015 | strlist__for_each(ent, rawlist) { |
1981 | ret = parse_probe_trace_command(ent->s, &tev); | 2016 | ret = parse_probe_trace_command(ent->s, &tev); |
@@ -2005,6 +2040,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | |||
2005 | { | 2040 | { |
2006 | int ret = 0; | 2041 | int ret = 0; |
2007 | char *buf = synthesize_probe_trace_command(tev); | 2042 | char *buf = synthesize_probe_trace_command(tev); |
2043 | char sbuf[STRERR_BUFSIZE]; | ||
2008 | 2044 | ||
2009 | if (!buf) { | 2045 | if (!buf) { |
2010 | pr_debug("Failed to synthesize probe trace event.\n"); | 2046 | pr_debug("Failed to synthesize probe trace event.\n"); |
@@ -2016,7 +2052,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | |||
2016 | ret = write(fd, buf, strlen(buf)); | 2052 | ret = write(fd, buf, strlen(buf)); |
2017 | if (ret <= 0) | 2053 | if (ret <= 0) |
2018 | pr_warning("Failed to write event: %s\n", | 2054 | pr_warning("Failed to write event: %s\n", |
2019 | strerror(errno)); | 2055 | strerror_r(errno, sbuf, sizeof(sbuf))); |
2020 | } | 2056 | } |
2021 | free(buf); | 2057 | free(buf); |
2022 | return ret; | 2058 | return ret; |
@@ -2030,7 +2066,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
2030 | /* Try no suffix */ | 2066 | /* Try no suffix */ |
2031 | ret = e_snprintf(buf, len, "%s", base); | 2067 | ret = e_snprintf(buf, len, "%s", base); |
2032 | if (ret < 0) { | 2068 | if (ret < 0) { |
2033 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 2069 | pr_debug("snprintf() failed: %d\n", ret); |
2034 | return ret; | 2070 | return ret; |
2035 | } | 2071 | } |
2036 | if (!strlist__has_entry(namelist, buf)) | 2072 | if (!strlist__has_entry(namelist, buf)) |
@@ -2046,7 +2082,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
2046 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | 2082 | for (i = 1; i < MAX_EVENT_INDEX; i++) { |
2047 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 2083 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
2048 | if (ret < 0) { | 2084 | if (ret < 0) { |
2049 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 2085 | pr_debug("snprintf() failed: %d\n", ret); |
2050 | return ret; | 2086 | return ret; |
2051 | } | 2087 | } |
2052 | if (!strlist__has_entry(namelist, buf)) | 2088 | if (!strlist__has_entry(namelist, buf)) |
@@ -2075,8 +2111,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
2075 | else | 2111 | else |
2076 | fd = open_kprobe_events(true); | 2112 | fd = open_kprobe_events(true); |
2077 | 2113 | ||
2078 | if (fd < 0) | 2114 | if (fd < 0) { |
2115 | print_open_warning(fd, !pev->uprobes); | ||
2079 | return fd; | 2116 | return fd; |
2117 | } | ||
2118 | |||
2080 | /* Get current event names */ | 2119 | /* Get current event names */ |
2081 | namelist = get_probe_trace_event_names(fd, false); | 2120 | namelist = get_probe_trace_event_names(fd, false); |
2082 | if (!namelist) { | 2121 | if (!namelist) { |
@@ -2408,7 +2447,8 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
2408 | printf("Removed event: %s\n", ent->s); | 2447 | printf("Removed event: %s\n", ent->s); |
2409 | return 0; | 2448 | return 0; |
2410 | error: | 2449 | error: |
2411 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); | 2450 | pr_warning("Failed to delete event: %s\n", |
2451 | strerror_r(-ret, buf, sizeof(buf))); | ||
2412 | return ret; | 2452 | return ret; |
2413 | } | 2453 | } |
2414 | 2454 | ||
@@ -2449,15 +2489,18 @@ int del_perf_probe_events(struct strlist *dellist) | |||
2449 | 2489 | ||
2450 | /* Get current event names */ | 2490 | /* Get current event names */ |
2451 | kfd = open_kprobe_events(true); | 2491 | kfd = open_kprobe_events(true); |
2452 | if (kfd < 0) | 2492 | if (kfd >= 0) |
2453 | return kfd; | 2493 | namelist = get_probe_trace_event_names(kfd, true); |
2454 | 2494 | ||
2455 | namelist = get_probe_trace_event_names(kfd, true); | ||
2456 | ufd = open_uprobe_events(true); | 2495 | ufd = open_uprobe_events(true); |
2457 | |||
2458 | if (ufd >= 0) | 2496 | if (ufd >= 0) |
2459 | unamelist = get_probe_trace_event_names(ufd, true); | 2497 | unamelist = get_probe_trace_event_names(ufd, true); |
2460 | 2498 | ||
2499 | if (kfd < 0 && ufd < 0) { | ||
2500 | print_both_open_warning(kfd, ufd); | ||
2501 | goto error; | ||
2502 | } | ||
2503 | |||
2461 | if (namelist == NULL && unamelist == NULL) | 2504 | if (namelist == NULL && unamelist == NULL) |
2462 | goto error; | 2505 | goto error; |
2463 | 2506 | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 776c9347a3b6..e01e9943139f 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -128,7 +128,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
128 | bool force_add); | 128 | bool force_add); |
129 | extern int del_perf_probe_events(struct strlist *dellist); | 129 | extern int del_perf_probe_events(struct strlist *dellist); |
130 | extern int show_perf_probe_events(void); | 130 | extern int show_perf_probe_events(void); |
131 | extern int show_line_range(struct line_range *lr, const char *module); | 131 | extern int show_line_range(struct line_range *lr, const char *module, |
132 | bool user); | ||
132 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 133 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
133 | int max_probe_points, const char *module, | 134 | int max_probe_points, const char *module, |
134 | struct strfilter *filter, bool externs); | 135 | struct strfilter *filter, bool externs); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index dca9145d704c..c7918f83b300 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -281,6 +281,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
281 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 281 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
282 | Dwarf_Die type; | 282 | Dwarf_Die type; |
283 | char buf[16]; | 283 | char buf[16]; |
284 | char sbuf[STRERR_BUFSIZE]; | ||
284 | int bsize, boffs, total; | 285 | int bsize, boffs, total; |
285 | int ret; | 286 | int ret; |
286 | 287 | ||
@@ -367,7 +368,7 @@ formatted: | |||
367 | if (ret >= 16) | 368 | if (ret >= 16) |
368 | ret = -E2BIG; | 369 | ret = -E2BIG; |
369 | pr_warning("Failed to convert variable type: %s\n", | 370 | pr_warning("Failed to convert variable type: %s\n", |
370 | strerror(-ret)); | 371 | strerror_r(-ret, sbuf, sizeof(sbuf))); |
371 | return ret; | 372 | return ret; |
372 | } | 373 | } |
373 | tvar->type = strdup(buf); | 374 | tvar->type = strdup(buf); |
@@ -608,14 +609,18 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, | |||
608 | return -EINVAL; | 609 | return -EINVAL; |
609 | } | 610 | } |
610 | 611 | ||
611 | /* Get an appropriate symbol from symtab */ | 612 | symbol = dwarf_diename(sp_die); |
612 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); | ||
613 | if (!symbol) { | 613 | if (!symbol) { |
614 | pr_warning("Failed to find symbol at 0x%lx\n", | 614 | /* Try to get the symbol name from symtab */ |
615 | (unsigned long)paddr); | 615 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); |
616 | return -ENOENT; | 616 | if (!symbol) { |
617 | pr_warning("Failed to find symbol at 0x%lx\n", | ||
618 | (unsigned long)paddr); | ||
619 | return -ENOENT; | ||
620 | } | ||
621 | eaddr = sym.st_value; | ||
617 | } | 622 | } |
618 | tp->offset = (unsigned long)(paddr - sym.st_value); | 623 | tp->offset = (unsigned long)(paddr - eaddr); |
619 | tp->address = (unsigned long)paddr; | 624 | tp->address = (unsigned long)paddr; |
620 | tp->symbol = strdup(symbol); | 625 | tp->symbol = strdup(symbol); |
621 | if (!tp->symbol) | 626 | if (!tp->symbol) |
@@ -779,10 +784,12 @@ static int find_lazy_match_lines(struct intlist *list, | |||
779 | size_t line_len; | 784 | size_t line_len; |
780 | ssize_t len; | 785 | ssize_t len; |
781 | int count = 0, linenum = 1; | 786 | int count = 0, linenum = 1; |
787 | char sbuf[STRERR_BUFSIZE]; | ||
782 | 788 | ||
783 | fp = fopen(fname, "r"); | 789 | fp = fopen(fname, "r"); |
784 | if (!fp) { | 790 | if (!fp) { |
785 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); | 791 | pr_warning("Failed to open %s: %s\n", fname, |
792 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
786 | return -errno; | 793 | return -errno; |
787 | } | 794 | } |
788 | 795 | ||
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 12aa9b0d0ba1..3dda85ca50c1 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -736,7 +736,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
736 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 736 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
737 | return NULL; | 737 | return NULL; |
738 | 738 | ||
739 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); | 739 | n = perf_evlist__poll(evlist, timeout); |
740 | if (n < 0) { | 740 | if (n < 0) { |
741 | PyErr_SetFromErrno(PyExc_OSError); | 741 | PyErr_SetFromErrno(PyExc_OSError); |
742 | return NULL; | 742 | return NULL; |
@@ -753,9 +753,9 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | |||
753 | PyObject *list = PyList_New(0); | 753 | PyObject *list = PyList_New(0); |
754 | int i; | 754 | int i; |
755 | 755 | ||
756 | for (i = 0; i < evlist->nr_fds; ++i) { | 756 | for (i = 0; i < evlist->pollfd.nr; ++i) { |
757 | PyObject *file; | 757 | PyObject *file; |
758 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); | 758 | FILE *fp = fdopen(evlist->pollfd.entries[i].fd, "r"); |
759 | 759 | ||
760 | if (fp == NULL) | 760 | if (fp == NULL) |
761 | goto free_list; | 761 | goto free_list; |
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index fe8079edbdc1..cf69325b985f 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c | |||
@@ -14,6 +14,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
14 | struct perf_evsel *evsel; | 14 | struct perf_evsel *evsel; |
15 | unsigned long flags = perf_event_open_cloexec_flag(); | 15 | unsigned long flags = perf_event_open_cloexec_flag(); |
16 | int err = -EAGAIN, fd; | 16 | int err = -EAGAIN, fd; |
17 | static pid_t pid = -1; | ||
17 | 18 | ||
18 | evlist = perf_evlist__new(); | 19 | evlist = perf_evlist__new(); |
19 | if (!evlist) | 20 | if (!evlist) |
@@ -24,14 +25,22 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
24 | 25 | ||
25 | evsel = perf_evlist__first(evlist); | 26 | evsel = perf_evlist__first(evlist); |
26 | 27 | ||
27 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags); | 28 | while (1) { |
28 | if (fd < 0) | 29 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags); |
29 | goto out_delete; | 30 | if (fd < 0) { |
31 | if (pid == -1 && errno == EACCES) { | ||
32 | pid = 0; | ||
33 | continue; | ||
34 | } | ||
35 | goto out_delete; | ||
36 | } | ||
37 | break; | ||
38 | } | ||
30 | close(fd); | 39 | close(fd); |
31 | 40 | ||
32 | fn(evsel); | 41 | fn(evsel); |
33 | 42 | ||
34 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags); | 43 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags); |
35 | if (fd < 0) { | 44 | if (fd < 0) { |
36 | if (errno == EINVAL) | 45 | if (errno == EINVAL) |
37 | err = -EINVAL; | 46 | err = -EINVAL; |
@@ -47,7 +56,7 @@ out_delete: | |||
47 | 56 | ||
48 | static bool perf_probe_api(setup_probe_fn_t fn) | 57 | static bool perf_probe_api(setup_probe_fn_t fn) |
49 | { | 58 | { |
50 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL}; | 59 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL}; |
51 | struct cpu_map *cpus; | 60 | struct cpu_map *cpus; |
52 | int cpu, ret, i = 0; | 61 | int cpu, ret, i = 0; |
53 | 62 | ||
@@ -106,7 +115,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) | |||
106 | 115 | ||
107 | evlist__for_each(evlist, evsel) { | 116 | evlist__for_each(evlist, evsel) { |
108 | perf_evsel__config(evsel, opts); | 117 | perf_evsel__config(evsel, opts); |
109 | if (!evsel->idx && use_comm_exec) | 118 | if (evsel->tracking && use_comm_exec) |
110 | evsel->attr.comm_exec = 1; | 119 | evsel->attr.comm_exec = 1; |
111 | } | 120 | } |
112 | 121 | ||
@@ -201,6 +210,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
201 | struct perf_evsel *evsel; | 210 | struct perf_evsel *evsel; |
202 | int err, fd, cpu; | 211 | int err, fd, cpu; |
203 | bool ret = false; | 212 | bool ret = false; |
213 | pid_t pid = -1; | ||
204 | 214 | ||
205 | temp_evlist = perf_evlist__new(); | 215 | temp_evlist = perf_evlist__new(); |
206 | if (!temp_evlist) | 216 | if (!temp_evlist) |
@@ -221,12 +231,20 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
221 | cpu = evlist->cpus->map[0]; | 231 | cpu = evlist->cpus->map[0]; |
222 | } | 232 | } |
223 | 233 | ||
224 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, | 234 | while (1) { |
225 | perf_event_open_cloexec_flag()); | 235 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, |
226 | if (fd >= 0) { | 236 | perf_event_open_cloexec_flag()); |
227 | close(fd); | 237 | if (fd < 0) { |
228 | ret = true; | 238 | if (pid == -1 && errno == EACCES) { |
239 | pid = 0; | ||
240 | continue; | ||
241 | } | ||
242 | goto out_delete; | ||
243 | } | ||
244 | break; | ||
229 | } | 245 | } |
246 | close(fd); | ||
247 | ret = true; | ||
230 | 248 | ||
231 | out_delete: | 249 | out_delete: |
232 | perf_evlist__delete(temp_evlist); | 250 | perf_evlist__delete(temp_evlist); |
diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c index da8e9b285f51..34622b53e733 100644 --- a/tools/perf/util/run-command.c +++ b/tools/perf/util/run-command.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "cache.h" | 1 | #include "cache.h" |
2 | #include "run-command.h" | 2 | #include "run-command.h" |
3 | #include "exec_cmd.h" | 3 | #include "exec_cmd.h" |
4 | #include "debug.h" | ||
4 | 5 | ||
5 | static inline void close_pair(int fd[2]) | 6 | static inline void close_pair(int fd[2]) |
6 | { | 7 | { |
@@ -19,6 +20,7 @@ int start_command(struct child_process *cmd) | |||
19 | { | 20 | { |
20 | int need_in, need_out, need_err; | 21 | int need_in, need_out, need_err; |
21 | int fdin[2], fdout[2], fderr[2]; | 22 | int fdin[2], fdout[2], fderr[2]; |
23 | char sbuf[STRERR_BUFSIZE]; | ||
22 | 24 | ||
23 | /* | 25 | /* |
24 | * In case of errors we must keep the promise to close FDs | 26 | * In case of errors we must keep the promise to close FDs |
@@ -99,7 +101,7 @@ int start_command(struct child_process *cmd) | |||
99 | 101 | ||
100 | if (cmd->dir && chdir(cmd->dir)) | 102 | if (cmd->dir && chdir(cmd->dir)) |
101 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], | 103 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], |
102 | cmd->dir, strerror(errno)); | 104 | cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); |
103 | if (cmd->env) { | 105 | if (cmd->env) { |
104 | for (; *cmd->env; cmd->env++) { | 106 | for (; *cmd->env; cmd->env++) { |
105 | if (strchr(*cmd->env, '=')) | 107 | if (strchr(*cmd->env, '=')) |
@@ -153,6 +155,8 @@ int start_command(struct child_process *cmd) | |||
153 | 155 | ||
154 | static int wait_or_whine(pid_t pid) | 156 | static int wait_or_whine(pid_t pid) |
155 | { | 157 | { |
158 | char sbuf[STRERR_BUFSIZE]; | ||
159 | |||
156 | for (;;) { | 160 | for (;;) { |
157 | int status, code; | 161 | int status, code; |
158 | pid_t waiting = waitpid(pid, &status, 0); | 162 | pid_t waiting = waitpid(pid, &status, 0); |
@@ -160,7 +164,8 @@ static int wait_or_whine(pid_t pid) | |||
160 | if (waiting < 0) { | 164 | if (waiting < 0) { |
161 | if (errno == EINTR) | 165 | if (errno == EINTR) |
162 | continue; | 166 | continue; |
163 | error("waitpid failed (%s)", strerror(errno)); | 167 | error("waitpid failed (%s)", |
168 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
164 | return -ERR_RUN_COMMAND_WAITPID; | 169 | return -ERR_RUN_COMMAND_WAITPID; |
165 | } | 170 | } |
166 | if (waiting != pid) | 171 | if (waiting != pid) |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b2dba9c0a3a1..0a01bac4ce02 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -432,6 +432,11 @@ error: | |||
432 | return err; | 432 | return err; |
433 | } | 433 | } |
434 | 434 | ||
435 | static int perl_flush_script(void) | ||
436 | { | ||
437 | return 0; | ||
438 | } | ||
439 | |||
435 | /* | 440 | /* |
436 | * Stop trace script | 441 | * Stop trace script |
437 | */ | 442 | */ |
@@ -633,6 +638,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
633 | struct scripting_ops perl_scripting_ops = { | 638 | struct scripting_ops perl_scripting_ops = { |
634 | .name = "Perl", | 639 | .name = "Perl", |
635 | .start_script = perl_start_script, | 640 | .start_script = perl_start_script, |
641 | .flush_script = perl_flush_script, | ||
636 | .stop_script = perl_stop_script, | 642 | .stop_script = perl_stop_script, |
637 | .process_event = perl_process_event, | 643 | .process_event = perl_process_event, |
638 | .generate_script = perl_generate_script, | 644 | .generate_script = perl_generate_script, |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cbce2545da45..56ba07cce549 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -73,6 +73,35 @@ static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObj | |||
73 | Py_DECREF(val); | 73 | Py_DECREF(val); |
74 | } | 74 | } |
75 | 75 | ||
76 | static PyObject *get_handler(const char *handler_name) | ||
77 | { | ||
78 | PyObject *handler; | ||
79 | |||
80 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
81 | if (handler && !PyCallable_Check(handler)) | ||
82 | return NULL; | ||
83 | return handler; | ||
84 | } | ||
85 | |||
86 | static void call_object(PyObject *handler, PyObject *args, const char *die_msg) | ||
87 | { | ||
88 | PyObject *retval; | ||
89 | |||
90 | retval = PyObject_CallObject(handler, args); | ||
91 | if (retval == NULL) | ||
92 | handler_call_die(die_msg); | ||
93 | Py_DECREF(retval); | ||
94 | } | ||
95 | |||
96 | static void try_call_object(const char *handler_name, PyObject *args) | ||
97 | { | ||
98 | PyObject *handler; | ||
99 | |||
100 | handler = get_handler(handler_name); | ||
101 | if (handler) | ||
102 | call_object(handler, args, handler_name); | ||
103 | } | ||
104 | |||
76 | static void define_value(enum print_arg_type field_type, | 105 | static void define_value(enum print_arg_type field_type, |
77 | const char *ev_name, | 106 | const char *ev_name, |
78 | const char *field_name, | 107 | const char *field_name, |
@@ -80,7 +109,7 @@ static void define_value(enum print_arg_type field_type, | |||
80 | const char *field_str) | 109 | const char *field_str) |
81 | { | 110 | { |
82 | const char *handler_name = "define_flag_value"; | 111 | const char *handler_name = "define_flag_value"; |
83 | PyObject *handler, *t, *retval; | 112 | PyObject *t; |
84 | unsigned long long value; | 113 | unsigned long long value; |
85 | unsigned n = 0; | 114 | unsigned n = 0; |
86 | 115 | ||
@@ -98,13 +127,7 @@ static void define_value(enum print_arg_type field_type, | |||
98 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | 127 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); |
99 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | 128 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); |
100 | 129 | ||
101 | handler = PyDict_GetItemString(main_dict, handler_name); | 130 | try_call_object(handler_name, t); |
102 | if (handler && PyCallable_Check(handler)) { | ||
103 | retval = PyObject_CallObject(handler, t); | ||
104 | if (retval == NULL) | ||
105 | handler_call_die(handler_name); | ||
106 | Py_DECREF(retval); | ||
107 | } | ||
108 | 131 | ||
109 | Py_DECREF(t); | 132 | Py_DECREF(t); |
110 | } | 133 | } |
@@ -127,7 +150,7 @@ static void define_field(enum print_arg_type field_type, | |||
127 | const char *delim) | 150 | const char *delim) |
128 | { | 151 | { |
129 | const char *handler_name = "define_flag_field"; | 152 | const char *handler_name = "define_flag_field"; |
130 | PyObject *handler, *t, *retval; | 153 | PyObject *t; |
131 | unsigned n = 0; | 154 | unsigned n = 0; |
132 | 155 | ||
133 | if (field_type == PRINT_SYMBOL) | 156 | if (field_type == PRINT_SYMBOL) |
@@ -145,13 +168,7 @@ static void define_field(enum print_arg_type field_type, | |||
145 | if (field_type == PRINT_FLAGS) | 168 | if (field_type == PRINT_FLAGS) |
146 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | 169 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); |
147 | 170 | ||
148 | handler = PyDict_GetItemString(main_dict, handler_name); | 171 | try_call_object(handler_name, t); |
149 | if (handler && PyCallable_Check(handler)) { | ||
150 | retval = PyObject_CallObject(handler, t); | ||
151 | if (retval == NULL) | ||
152 | handler_call_die(handler_name); | ||
153 | Py_DECREF(retval); | ||
154 | } | ||
155 | 172 | ||
156 | Py_DECREF(t); | 173 | Py_DECREF(t); |
157 | } | 174 | } |
@@ -362,7 +379,7 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
362 | struct thread *thread, | 379 | struct thread *thread, |
363 | struct addr_location *al) | 380 | struct addr_location *al) |
364 | { | 381 | { |
365 | PyObject *handler, *retval, *context, *t, *obj, *callchain; | 382 | PyObject *handler, *context, *t, *obj, *callchain; |
366 | PyObject *dict = NULL; | 383 | PyObject *dict = NULL; |
367 | static char handler_name[256]; | 384 | static char handler_name[256]; |
368 | struct format_field *field; | 385 | struct format_field *field; |
@@ -387,9 +404,7 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
387 | 404 | ||
388 | sprintf(handler_name, "%s__%s", event->system, event->name); | 405 | sprintf(handler_name, "%s__%s", event->system, event->name); |
389 | 406 | ||
390 | handler = PyDict_GetItemString(main_dict, handler_name); | 407 | handler = get_handler(handler_name); |
391 | if (handler && !PyCallable_Check(handler)) | ||
392 | handler = NULL; | ||
393 | if (!handler) { | 408 | if (!handler) { |
394 | dict = PyDict_New(); | 409 | dict = PyDict_New(); |
395 | if (!dict) | 410 | if (!dict) |
@@ -450,19 +465,9 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
450 | Py_FatalError("error resizing Python tuple"); | 465 | Py_FatalError("error resizing Python tuple"); |
451 | 466 | ||
452 | if (handler) { | 467 | if (handler) { |
453 | retval = PyObject_CallObject(handler, t); | 468 | call_object(handler, t, handler_name); |
454 | if (retval == NULL) | ||
455 | handler_call_die(handler_name); | ||
456 | Py_DECREF(retval); | ||
457 | } else { | 469 | } else { |
458 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | 470 | try_call_object("trace_unhandled", t); |
459 | if (handler && PyCallable_Check(handler)) { | ||
460 | |||
461 | retval = PyObject_CallObject(handler, t); | ||
462 | if (retval == NULL) | ||
463 | handler_call_die("trace_unhandled"); | ||
464 | Py_DECREF(retval); | ||
465 | } | ||
466 | Py_DECREF(dict); | 471 | Py_DECREF(dict); |
467 | } | 472 | } |
468 | 473 | ||
@@ -474,7 +479,7 @@ static void python_process_general_event(struct perf_sample *sample, | |||
474 | struct thread *thread, | 479 | struct thread *thread, |
475 | struct addr_location *al) | 480 | struct addr_location *al) |
476 | { | 481 | { |
477 | PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample; | 482 | PyObject *handler, *t, *dict, *callchain, *dict_sample; |
478 | static char handler_name[64]; | 483 | static char handler_name[64]; |
479 | unsigned n = 0; | 484 | unsigned n = 0; |
480 | 485 | ||
@@ -496,8 +501,8 @@ static void python_process_general_event(struct perf_sample *sample, | |||
496 | 501 | ||
497 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); | 502 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); |
498 | 503 | ||
499 | handler = PyDict_GetItemString(main_dict, handler_name); | 504 | handler = get_handler(handler_name); |
500 | if (!handler || !PyCallable_Check(handler)) | 505 | if (!handler) |
501 | goto exit; | 506 | goto exit; |
502 | 507 | ||
503 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | 508 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); |
@@ -539,10 +544,7 @@ static void python_process_general_event(struct perf_sample *sample, | |||
539 | if (_PyTuple_Resize(&t, n) == -1) | 544 | if (_PyTuple_Resize(&t, n) == -1) |
540 | Py_FatalError("error resizing Python tuple"); | 545 | Py_FatalError("error resizing Python tuple"); |
541 | 546 | ||
542 | retval = PyObject_CallObject(handler, t); | 547 | call_object(handler, t, handler_name); |
543 | if (retval == NULL) | ||
544 | handler_call_die(handler_name); | ||
545 | Py_DECREF(retval); | ||
546 | exit: | 548 | exit: |
547 | Py_DECREF(dict); | 549 | Py_DECREF(dict); |
548 | Py_DECREF(t); | 550 | Py_DECREF(t); |
@@ -566,36 +568,24 @@ static void python_process_event(union perf_event *event __maybe_unused, | |||
566 | 568 | ||
567 | static int run_start_sub(void) | 569 | static int run_start_sub(void) |
568 | { | 570 | { |
569 | PyObject *handler, *retval; | ||
570 | int err = 0; | ||
571 | |||
572 | main_module = PyImport_AddModule("__main__"); | 571 | main_module = PyImport_AddModule("__main__"); |
573 | if (main_module == NULL) | 572 | if (main_module == NULL) |
574 | return -1; | 573 | return -1; |
575 | Py_INCREF(main_module); | 574 | Py_INCREF(main_module); |
576 | 575 | ||
577 | main_dict = PyModule_GetDict(main_module); | 576 | main_dict = PyModule_GetDict(main_module); |
578 | if (main_dict == NULL) { | 577 | if (main_dict == NULL) |
579 | err = -1; | ||
580 | goto error; | 578 | goto error; |
581 | } | ||
582 | Py_INCREF(main_dict); | 579 | Py_INCREF(main_dict); |
583 | 580 | ||
584 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | 581 | try_call_object("trace_begin", NULL); |
585 | if (handler == NULL || !PyCallable_Check(handler)) | ||
586 | goto out; | ||
587 | 582 | ||
588 | retval = PyObject_CallObject(handler, NULL); | 583 | return 0; |
589 | if (retval == NULL) | ||
590 | handler_call_die("trace_begin"); | ||
591 | 584 | ||
592 | Py_DECREF(retval); | ||
593 | return err; | ||
594 | error: | 585 | error: |
595 | Py_XDECREF(main_dict); | 586 | Py_XDECREF(main_dict); |
596 | Py_XDECREF(main_module); | 587 | Py_XDECREF(main_module); |
597 | out: | 588 | return -1; |
598 | return err; | ||
599 | } | 589 | } |
600 | 590 | ||
601 | /* | 591 | /* |
@@ -649,28 +639,23 @@ error: | |||
649 | return err; | 639 | return err; |
650 | } | 640 | } |
651 | 641 | ||
642 | static int python_flush_script(void) | ||
643 | { | ||
644 | return 0; | ||
645 | } | ||
646 | |||
652 | /* | 647 | /* |
653 | * Stop trace script | 648 | * Stop trace script |
654 | */ | 649 | */ |
655 | static int python_stop_script(void) | 650 | static int python_stop_script(void) |
656 | { | 651 | { |
657 | PyObject *handler, *retval; | 652 | try_call_object("trace_end", NULL); |
658 | int err = 0; | ||
659 | 653 | ||
660 | handler = PyDict_GetItemString(main_dict, "trace_end"); | ||
661 | if (handler == NULL || !PyCallable_Check(handler)) | ||
662 | goto out; | ||
663 | |||
664 | retval = PyObject_CallObject(handler, NULL); | ||
665 | if (retval == NULL) | ||
666 | handler_call_die("trace_end"); | ||
667 | Py_DECREF(retval); | ||
668 | out: | ||
669 | Py_XDECREF(main_dict); | 654 | Py_XDECREF(main_dict); |
670 | Py_XDECREF(main_module); | 655 | Py_XDECREF(main_module); |
671 | Py_Finalize(); | 656 | Py_Finalize(); |
672 | 657 | ||
673 | return err; | 658 | return 0; |
674 | } | 659 | } |
675 | 660 | ||
676 | static int python_generate_script(struct pevent *pevent, const char *outfile) | 661 | static int python_generate_script(struct pevent *pevent, const char *outfile) |
@@ -843,6 +828,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) | |||
843 | struct scripting_ops python_scripting_ops = { | 828 | struct scripting_ops python_scripting_ops = { |
844 | .name = "Python", | 829 | .name = "Python", |
845 | .start_script = python_start_script, | 830 | .start_script = python_start_script, |
831 | .flush_script = python_flush_script, | ||
846 | .stop_script = python_stop_script, | 832 | .stop_script = python_stop_script, |
847 | .process_event = python_process_event, | 833 | .process_event = python_process_event, |
848 | .generate_script = python_generate_script, | 834 | .generate_script = python_generate_script, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 88dfef70c13d..883406f4b381 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util.h" | 14 | #include "util.h" |
15 | #include "cpumap.h" | 15 | #include "cpumap.h" |
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "asm/bug.h" | ||
17 | 18 | ||
18 | static int perf_session__open(struct perf_session *session) | 19 | static int perf_session__open(struct perf_session *session) |
19 | { | 20 | { |
@@ -66,6 +67,25 @@ static void perf_session__destroy_kernel_maps(struct perf_session *session) | |||
66 | machines__destroy_kernel_maps(&session->machines); | 67 | machines__destroy_kernel_maps(&session->machines); |
67 | } | 68 | } |
68 | 69 | ||
70 | static bool perf_session__has_comm_exec(struct perf_session *session) | ||
71 | { | ||
72 | struct perf_evsel *evsel; | ||
73 | |||
74 | evlist__for_each(session->evlist, evsel) { | ||
75 | if (evsel->attr.comm_exec) | ||
76 | return true; | ||
77 | } | ||
78 | |||
79 | return false; | ||
80 | } | ||
81 | |||
82 | static void perf_session__set_comm_exec(struct perf_session *session) | ||
83 | { | ||
84 | bool comm_exec = perf_session__has_comm_exec(session); | ||
85 | |||
86 | machines__set_comm_exec(&session->machines, comm_exec); | ||
87 | } | ||
88 | |||
69 | struct perf_session *perf_session__new(struct perf_data_file *file, | 89 | struct perf_session *perf_session__new(struct perf_data_file *file, |
70 | bool repipe, struct perf_tool *tool) | 90 | bool repipe, struct perf_tool *tool) |
71 | { | 91 | { |
@@ -75,9 +95,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
75 | goto out; | 95 | goto out; |
76 | 96 | ||
77 | session->repipe = repipe; | 97 | session->repipe = repipe; |
78 | INIT_LIST_HEAD(&session->ordered_samples.samples); | 98 | ordered_events__init(&session->ordered_events); |
79 | INIT_LIST_HEAD(&session->ordered_samples.sample_cache); | ||
80 | INIT_LIST_HEAD(&session->ordered_samples.to_free); | ||
81 | machines__init(&session->machines); | 99 | machines__init(&session->machines); |
82 | 100 | ||
83 | if (file) { | 101 | if (file) { |
@@ -91,6 +109,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
91 | goto out_close; | 109 | goto out_close; |
92 | 110 | ||
93 | perf_session__set_id_hdr_size(session); | 111 | perf_session__set_id_hdr_size(session); |
112 | perf_session__set_comm_exec(session); | ||
94 | } | 113 | } |
95 | } | 114 | } |
96 | 115 | ||
@@ -100,13 +119,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
100 | * kernel MMAP event, in perf_event__process_mmap(). | 119 | * kernel MMAP event, in perf_event__process_mmap(). |
101 | */ | 120 | */ |
102 | if (perf_session__create_kernel_maps(session) < 0) | 121 | if (perf_session__create_kernel_maps(session) < 0) |
103 | goto out_delete; | 122 | pr_warning("Cannot read kernel map\n"); |
104 | } | 123 | } |
105 | 124 | ||
106 | if (tool && tool->ordering_requires_timestamps && | 125 | if (tool && tool->ordering_requires_timestamps && |
107 | tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { | 126 | tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) { |
108 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 127 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
109 | tool->ordered_samples = false; | 128 | tool->ordered_events = false; |
110 | } | 129 | } |
111 | 130 | ||
112 | return session; | 131 | return session; |
@@ -238,7 +257,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
238 | if (tool->build_id == NULL) | 257 | if (tool->build_id == NULL) |
239 | tool->build_id = process_finished_round_stub; | 258 | tool->build_id = process_finished_round_stub; |
240 | if (tool->finished_round == NULL) { | 259 | if (tool->finished_round == NULL) { |
241 | if (tool->ordered_samples) | 260 | if (tool->ordered_events) |
242 | tool->finished_round = process_finished_round; | 261 | tool->finished_round = process_finished_round; |
243 | else | 262 | else |
244 | tool->finished_round = process_finished_round_stub; | 263 | tool->finished_round = process_finished_round_stub; |
@@ -444,87 +463,6 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
444 | [PERF_RECORD_HEADER_MAX] = NULL, | 463 | [PERF_RECORD_HEADER_MAX] = NULL, |
445 | }; | 464 | }; |
446 | 465 | ||
447 | struct sample_queue { | ||
448 | u64 timestamp; | ||
449 | u64 file_offset; | ||
450 | union perf_event *event; | ||
451 | struct list_head list; | ||
452 | }; | ||
453 | |||
454 | static void perf_session_free_sample_buffers(struct perf_session *session) | ||
455 | { | ||
456 | struct ordered_samples *os = &session->ordered_samples; | ||
457 | |||
458 | while (!list_empty(&os->to_free)) { | ||
459 | struct sample_queue *sq; | ||
460 | |||
461 | sq = list_entry(os->to_free.next, struct sample_queue, list); | ||
462 | list_del(&sq->list); | ||
463 | free(sq); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | static int perf_session_deliver_event(struct perf_session *session, | ||
468 | union perf_event *event, | ||
469 | struct perf_sample *sample, | ||
470 | struct perf_tool *tool, | ||
471 | u64 file_offset); | ||
472 | |||
473 | static int flush_sample_queue(struct perf_session *s, | ||
474 | struct perf_tool *tool) | ||
475 | { | ||
476 | struct ordered_samples *os = &s->ordered_samples; | ||
477 | struct list_head *head = &os->samples; | ||
478 | struct sample_queue *tmp, *iter; | ||
479 | struct perf_sample sample; | ||
480 | u64 limit = os->next_flush; | ||
481 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | ||
482 | bool show_progress = limit == ULLONG_MAX; | ||
483 | struct ui_progress prog; | ||
484 | int ret; | ||
485 | |||
486 | if (!tool->ordered_samples || !limit) | ||
487 | return 0; | ||
488 | |||
489 | if (show_progress) | ||
490 | ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); | ||
491 | |||
492 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
493 | if (session_done()) | ||
494 | return 0; | ||
495 | |||
496 | if (iter->timestamp > limit) | ||
497 | break; | ||
498 | |||
499 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
500 | if (ret) | ||
501 | pr_err("Can't parse sample, err = %d\n", ret); | ||
502 | else { | ||
503 | ret = perf_session_deliver_event(s, iter->event, &sample, tool, | ||
504 | iter->file_offset); | ||
505 | if (ret) | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | os->last_flush = iter->timestamp; | ||
510 | list_del(&iter->list); | ||
511 | list_add(&iter->list, &os->sample_cache); | ||
512 | os->nr_samples--; | ||
513 | |||
514 | if (show_progress) | ||
515 | ui_progress__update(&prog, 1); | ||
516 | } | ||
517 | |||
518 | if (list_empty(head)) { | ||
519 | os->last_sample = NULL; | ||
520 | } else if (last_ts <= limit) { | ||
521 | os->last_sample = | ||
522 | list_entry(head->prev, struct sample_queue, list); | ||
523 | } | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* | 466 | /* |
529 | * When perf record finishes a pass on every buffers, it records this pseudo | 467 | * When perf record finishes a pass on every buffers, it records this pseudo |
530 | * event. | 468 | * event. |
@@ -568,99 +506,43 @@ static int process_finished_round(struct perf_tool *tool, | |||
568 | union perf_event *event __maybe_unused, | 506 | union perf_event *event __maybe_unused, |
569 | struct perf_session *session) | 507 | struct perf_session *session) |
570 | { | 508 | { |
571 | int ret = flush_sample_queue(session, tool); | 509 | return ordered_events__flush(session, tool, OE_FLUSH__ROUND); |
572 | if (!ret) | ||
573 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | ||
574 | |||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | /* The queue is ordered by time */ | ||
579 | static void __queue_event(struct sample_queue *new, struct perf_session *s) | ||
580 | { | ||
581 | struct ordered_samples *os = &s->ordered_samples; | ||
582 | struct sample_queue *sample = os->last_sample; | ||
583 | u64 timestamp = new->timestamp; | ||
584 | struct list_head *p; | ||
585 | |||
586 | ++os->nr_samples; | ||
587 | os->last_sample = new; | ||
588 | |||
589 | if (!sample) { | ||
590 | list_add(&new->list, &os->samples); | ||
591 | os->max_timestamp = timestamp; | ||
592 | return; | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * last_sample might point to some random place in the list as it's | ||
597 | * the last queued event. We expect that the new event is close to | ||
598 | * this. | ||
599 | */ | ||
600 | if (sample->timestamp <= timestamp) { | ||
601 | while (sample->timestamp <= timestamp) { | ||
602 | p = sample->list.next; | ||
603 | if (p == &os->samples) { | ||
604 | list_add_tail(&new->list, &os->samples); | ||
605 | os->max_timestamp = timestamp; | ||
606 | return; | ||
607 | } | ||
608 | sample = list_entry(p, struct sample_queue, list); | ||
609 | } | ||
610 | list_add_tail(&new->list, &sample->list); | ||
611 | } else { | ||
612 | while (sample->timestamp > timestamp) { | ||
613 | p = sample->list.prev; | ||
614 | if (p == &os->samples) { | ||
615 | list_add(&new->list, &os->samples); | ||
616 | return; | ||
617 | } | ||
618 | sample = list_entry(p, struct sample_queue, list); | ||
619 | } | ||
620 | list_add(&new->list, &sample->list); | ||
621 | } | ||
622 | } | 510 | } |
623 | 511 | ||
624 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | ||
625 | |||
626 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 512 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
627 | struct perf_sample *sample, u64 file_offset) | 513 | struct perf_tool *tool, struct perf_sample *sample, |
514 | u64 file_offset) | ||
628 | { | 515 | { |
629 | struct ordered_samples *os = &s->ordered_samples; | 516 | struct ordered_events *oe = &s->ordered_events; |
630 | struct list_head *sc = &os->sample_cache; | ||
631 | u64 timestamp = sample->time; | 517 | u64 timestamp = sample->time; |
632 | struct sample_queue *new; | 518 | struct ordered_event *new; |
633 | 519 | ||
634 | if (!timestamp || timestamp == ~0ULL) | 520 | if (!timestamp || timestamp == ~0ULL) |
635 | return -ETIME; | 521 | return -ETIME; |
636 | 522 | ||
637 | if (timestamp < s->ordered_samples.last_flush) { | 523 | if (timestamp < oe->last_flush) { |
638 | printf("Warning: Timestamp below last timeslice flush\n"); | 524 | WARN_ONCE(1, "Timestamp below last timeslice flush\n"); |
639 | return -EINVAL; | 525 | |
526 | pr_oe_time(timestamp, "out of order event"); | ||
527 | pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n", | ||
528 | oe->last_flush_type); | ||
529 | |||
530 | /* We could get out of order messages after forced flush. */ | ||
531 | if (oe->last_flush_type != OE_FLUSH__HALF) | ||
532 | return -EINVAL; | ||
640 | } | 533 | } |
641 | 534 | ||
642 | if (!list_empty(sc)) { | 535 | new = ordered_events__new(oe, timestamp); |
643 | new = list_entry(sc->next, struct sample_queue, list); | 536 | if (!new) { |
644 | list_del(&new->list); | 537 | ordered_events__flush(s, tool, OE_FLUSH__HALF); |
645 | } else if (os->sample_buffer) { | 538 | new = ordered_events__new(oe, timestamp); |
646 | new = os->sample_buffer + os->sample_buffer_idx; | ||
647 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | ||
648 | os->sample_buffer = NULL; | ||
649 | } else { | ||
650 | os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); | ||
651 | if (!os->sample_buffer) | ||
652 | return -ENOMEM; | ||
653 | list_add(&os->sample_buffer->list, &os->to_free); | ||
654 | os->sample_buffer_idx = 2; | ||
655 | new = os->sample_buffer + 1; | ||
656 | } | 539 | } |
657 | 540 | ||
658 | new->timestamp = timestamp; | 541 | if (!new) |
542 | return -ENOMEM; | ||
543 | |||
659 | new->file_offset = file_offset; | 544 | new->file_offset = file_offset; |
660 | new->event = event; | 545 | new->event = event; |
661 | |||
662 | __queue_event(new, s); | ||
663 | |||
664 | return 0; | 546 | return 0; |
665 | } | 547 | } |
666 | 548 | ||
@@ -920,11 +802,10 @@ perf_session__deliver_sample(struct perf_session *session, | |||
920 | &sample->read.one, machine); | 802 | &sample->read.one, machine); |
921 | } | 803 | } |
922 | 804 | ||
923 | static int perf_session_deliver_event(struct perf_session *session, | 805 | int perf_session__deliver_event(struct perf_session *session, |
924 | union perf_event *event, | 806 | union perf_event *event, |
925 | struct perf_sample *sample, | 807 | struct perf_sample *sample, |
926 | struct perf_tool *tool, | 808 | struct perf_tool *tool, u64 file_offset) |
927 | u64 file_offset) | ||
928 | { | 809 | { |
929 | struct perf_evsel *evsel; | 810 | struct perf_evsel *evsel; |
930 | struct machine *machine; | 811 | struct machine *machine; |
@@ -1005,8 +886,10 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
1005 | switch (event->header.type) { | 886 | switch (event->header.type) { |
1006 | case PERF_RECORD_HEADER_ATTR: | 887 | case PERF_RECORD_HEADER_ATTR: |
1007 | err = tool->attr(tool, event, &session->evlist); | 888 | err = tool->attr(tool, event, &session->evlist); |
1008 | if (err == 0) | 889 | if (err == 0) { |
1009 | perf_session__set_id_hdr_size(session); | 890 | perf_session__set_id_hdr_size(session); |
891 | perf_session__set_comm_exec(session); | ||
892 | } | ||
1010 | return err; | 893 | return err; |
1011 | case PERF_RECORD_HEADER_EVENT_TYPE: | 894 | case PERF_RECORD_HEADER_EVENT_TYPE: |
1012 | /* | 895 | /* |
@@ -1036,6 +919,61 @@ static void event_swap(union perf_event *event, bool sample_id_all) | |||
1036 | swap(event, sample_id_all); | 919 | swap(event, sample_id_all); |
1037 | } | 920 | } |
1038 | 921 | ||
922 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
923 | void *buf, size_t buf_sz, | ||
924 | union perf_event **event_ptr, | ||
925 | struct perf_sample *sample) | ||
926 | { | ||
927 | union perf_event *event; | ||
928 | size_t hdr_sz, rest; | ||
929 | int fd; | ||
930 | |||
931 | if (session->one_mmap && !session->header.needs_swap) { | ||
932 | event = file_offset - session->one_mmap_offset + | ||
933 | session->one_mmap_addr; | ||
934 | goto out_parse_sample; | ||
935 | } | ||
936 | |||
937 | if (perf_data_file__is_pipe(session->file)) | ||
938 | return -1; | ||
939 | |||
940 | fd = perf_data_file__fd(session->file); | ||
941 | hdr_sz = sizeof(struct perf_event_header); | ||
942 | |||
943 | if (buf_sz < hdr_sz) | ||
944 | return -1; | ||
945 | |||
946 | if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || | ||
947 | readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz) | ||
948 | return -1; | ||
949 | |||
950 | event = (union perf_event *)buf; | ||
951 | |||
952 | if (session->header.needs_swap) | ||
953 | perf_event_header__bswap(&event->header); | ||
954 | |||
955 | if (event->header.size < hdr_sz) | ||
956 | return -1; | ||
957 | |||
958 | rest = event->header.size - hdr_sz; | ||
959 | |||
960 | if (readn(fd, &buf, rest) != (ssize_t)rest) | ||
961 | return -1; | ||
962 | |||
963 | if (session->header.needs_swap) | ||
964 | event_swap(event, perf_evlist__sample_id_all(session->evlist)); | ||
965 | |||
966 | out_parse_sample: | ||
967 | |||
968 | if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && | ||
969 | perf_evlist__parse_sample(session->evlist, event, sample)) | ||
970 | return -1; | ||
971 | |||
972 | *event_ptr = event; | ||
973 | |||
974 | return 0; | ||
975 | } | ||
976 | |||
1039 | static s64 perf_session__process_event(struct perf_session *session, | 977 | static s64 perf_session__process_event(struct perf_session *session, |
1040 | union perf_event *event, | 978 | union perf_event *event, |
1041 | struct perf_tool *tool, | 979 | struct perf_tool *tool, |
@@ -1062,15 +1000,15 @@ static s64 perf_session__process_event(struct perf_session *session, | |||
1062 | if (ret) | 1000 | if (ret) |
1063 | return ret; | 1001 | return ret; |
1064 | 1002 | ||
1065 | if (tool->ordered_samples) { | 1003 | if (tool->ordered_events) { |
1066 | ret = perf_session_queue_event(session, event, &sample, | 1004 | ret = perf_session_queue_event(session, event, tool, &sample, |
1067 | file_offset); | 1005 | file_offset); |
1068 | if (ret != -ETIME) | 1006 | if (ret != -ETIME) |
1069 | return ret; | 1007 | return ret; |
1070 | } | 1008 | } |
1071 | 1009 | ||
1072 | return perf_session_deliver_event(session, event, &sample, tool, | 1010 | return perf_session__deliver_event(session, event, &sample, tool, |
1073 | file_offset); | 1011 | file_offset); |
1074 | } | 1012 | } |
1075 | 1013 | ||
1076 | void perf_event_header__bswap(struct perf_event_header *hdr) | 1014 | void perf_event_header__bswap(struct perf_event_header *hdr) |
@@ -1222,12 +1160,11 @@ more: | |||
1222 | goto more; | 1160 | goto more; |
1223 | done: | 1161 | done: |
1224 | /* do the final flush for ordered samples */ | 1162 | /* do the final flush for ordered samples */ |
1225 | session->ordered_samples.next_flush = ULLONG_MAX; | 1163 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
1226 | err = flush_sample_queue(session, tool); | ||
1227 | out_err: | 1164 | out_err: |
1228 | free(buf); | 1165 | free(buf); |
1229 | perf_session__warn_about_errors(session, tool); | 1166 | perf_session__warn_about_errors(session, tool); |
1230 | perf_session_free_sample_buffers(session); | 1167 | ordered_events__free(&session->ordered_events); |
1231 | return err; | 1168 | return err; |
1232 | } | 1169 | } |
1233 | 1170 | ||
@@ -1368,12 +1305,11 @@ more: | |||
1368 | 1305 | ||
1369 | out: | 1306 | out: |
1370 | /* do the final flush for ordered samples */ | 1307 | /* do the final flush for ordered samples */ |
1371 | session->ordered_samples.next_flush = ULLONG_MAX; | 1308 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
1372 | err = flush_sample_queue(session, tool); | ||
1373 | out_err: | 1309 | out_err: |
1374 | ui_progress__finish(); | 1310 | ui_progress__finish(); |
1375 | perf_session__warn_about_errors(session, tool); | 1311 | perf_session__warn_about_errors(session, tool); |
1376 | perf_session_free_sample_buffers(session); | 1312 | ordered_events__free(&session->ordered_events); |
1377 | session->one_mmap = false; | 1313 | session->one_mmap = false; |
1378 | return err; | 1314 | return err; |
1379 | } | 1315 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0321013bd9fd..ffb440462008 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -9,26 +9,13 @@ | |||
9 | #include "symbol.h" | 9 | #include "symbol.h" |
10 | #include "thread.h" | 10 | #include "thread.h" |
11 | #include "data.h" | 11 | #include "data.h" |
12 | #include "ordered-events.h" | ||
12 | #include <linux/rbtree.h> | 13 | #include <linux/rbtree.h> |
13 | #include <linux/perf_event.h> | 14 | #include <linux/perf_event.h> |
14 | 15 | ||
15 | struct sample_queue; | ||
16 | struct ip_callchain; | 16 | struct ip_callchain; |
17 | struct thread; | 17 | struct thread; |
18 | 18 | ||
19 | struct ordered_samples { | ||
20 | u64 last_flush; | ||
21 | u64 next_flush; | ||
22 | u64 max_timestamp; | ||
23 | struct list_head samples; | ||
24 | struct list_head sample_cache; | ||
25 | struct list_head to_free; | ||
26 | struct sample_queue *sample_buffer; | ||
27 | struct sample_queue *last_sample; | ||
28 | int sample_buffer_idx; | ||
29 | unsigned int nr_samples; | ||
30 | }; | ||
31 | |||
32 | struct perf_session { | 19 | struct perf_session { |
33 | struct perf_header header; | 20 | struct perf_header header; |
34 | struct machines machines; | 21 | struct machines machines; |
@@ -39,7 +26,7 @@ struct perf_session { | |||
39 | bool one_mmap; | 26 | bool one_mmap; |
40 | void *one_mmap_addr; | 27 | void *one_mmap_addr; |
41 | u64 one_mmap_offset; | 28 | u64 one_mmap_offset; |
42 | struct ordered_samples ordered_samples; | 29 | struct ordered_events ordered_events; |
43 | struct perf_data_file *file; | 30 | struct perf_data_file *file; |
44 | }; | 31 | }; |
45 | 32 | ||
@@ -58,6 +45,11 @@ void perf_session__delete(struct perf_session *session); | |||
58 | 45 | ||
59 | void perf_event_header__bswap(struct perf_event_header *hdr); | 46 | void perf_event_header__bswap(struct perf_event_header *hdr); |
60 | 47 | ||
48 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
49 | void *buf, size_t buf_sz, | ||
50 | union perf_event **event_ptr, | ||
51 | struct perf_sample *sample); | ||
52 | |||
61 | int __perf_session__process_events(struct perf_session *session, | 53 | int __perf_session__process_events(struct perf_session *session, |
62 | u64 data_offset, u64 data_size, u64 size, | 54 | u64 data_offset, u64 data_size, u64 size, |
63 | struct perf_tool *tool); | 55 | struct perf_tool *tool); |
@@ -65,10 +57,16 @@ int perf_session__process_events(struct perf_session *session, | |||
65 | struct perf_tool *tool); | 57 | struct perf_tool *tool); |
66 | 58 | ||
67 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 59 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
68 | struct perf_sample *sample, u64 file_offset); | 60 | struct perf_tool *tool, struct perf_sample *sample, |
61 | u64 file_offset); | ||
69 | 62 | ||
70 | void perf_tool__fill_defaults(struct perf_tool *tool); | 63 | void perf_tool__fill_defaults(struct perf_tool *tool); |
71 | 64 | ||
65 | int perf_session__deliver_event(struct perf_session *session, | ||
66 | union perf_event *event, | ||
67 | struct perf_sample *sample, | ||
68 | struct perf_tool *tool, u64 file_offset); | ||
69 | |||
72 | int perf_session__resolve_callchain(struct perf_session *session, | 70 | int perf_session__resolve_callchain(struct perf_session *session, |
73 | struct perf_evsel *evsel, | 71 | struct perf_evsel *evsel, |
74 | struct thread *thread, | 72 | struct thread *thread, |
@@ -128,5 +126,5 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
128 | 126 | ||
129 | extern volatile int session_done; | 127 | extern volatile int session_done; |
130 | 128 | ||
131 | #define session_done() (*(volatile int *)(&session_done)) | 129 | #define session_done() ACCESS_ONCE(session_done) |
132 | #endif /* __PERF_SESSION_H */ | 130 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 14e5a039bc45..289df9d1e65a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -70,12 +70,14 @@ static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, | |||
70 | size_t size, unsigned int width) | 70 | size_t size, unsigned int width) |
71 | { | 71 | { |
72 | const char *comm = thread__comm_str(he->thread); | 72 | const char *comm = thread__comm_str(he->thread); |
73 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, | 73 | |
74 | comm ?: "", he->thread->tid); | 74 | width = max(7U, width) - 6; |
75 | return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid, | ||
76 | width, width, comm ?: ""); | ||
75 | } | 77 | } |
76 | 78 | ||
77 | struct sort_entry sort_thread = { | 79 | struct sort_entry sort_thread = { |
78 | .se_header = "Command: Pid", | 80 | .se_header = " Pid:Command", |
79 | .se_cmp = sort__thread_cmp, | 81 | .se_cmp = sort__thread_cmp, |
80 | .se_snprintf = hist_entry__thread_snprintf, | 82 | .se_snprintf = hist_entry__thread_snprintf, |
81 | .se_width_idx = HISTC_THREAD, | 83 | .se_width_idx = HISTC_THREAD, |
@@ -106,7 +108,7 @@ sort__comm_sort(struct hist_entry *left, struct hist_entry *right) | |||
106 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, | 108 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, |
107 | size_t size, unsigned int width) | 109 | size_t size, unsigned int width) |
108 | { | 110 | { |
109 | return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); | 111 | return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm)); |
110 | } | 112 | } |
111 | 113 | ||
112 | struct sort_entry sort_comm = { | 114 | struct sort_entry sort_comm = { |
@@ -152,10 +154,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, | |||
152 | if (map && map->dso) { | 154 | if (map && map->dso) { |
153 | const char *dso_name = !verbose ? map->dso->short_name : | 155 | const char *dso_name = !verbose ? map->dso->short_name : |
154 | map->dso->long_name; | 156 | map->dso->long_name; |
155 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | 157 | return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name); |
156 | } | 158 | } |
157 | 159 | ||
158 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 160 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]"); |
159 | } | 161 | } |
160 | 162 | ||
161 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, | 163 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, |
@@ -257,7 +259,10 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
257 | width - ret, ""); | 259 | width - ret, ""); |
258 | } | 260 | } |
259 | 261 | ||
260 | return ret; | 262 | if (ret > width) |
263 | bf[width] = '\0'; | ||
264 | |||
265 | return width; | ||
261 | } | 266 | } |
262 | 267 | ||
263 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | 268 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, |
@@ -302,10 +307,9 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
302 | } | 307 | } |
303 | 308 | ||
304 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, | 309 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
305 | size_t size, | 310 | size_t size, unsigned int width) |
306 | unsigned int width __maybe_unused) | ||
307 | { | 311 | { |
308 | return repsep_snprintf(bf, size, "%s", he->srcline); | 312 | return repsep_snprintf(bf, size, "%*.*-s", width, width, he->srcline); |
309 | } | 313 | } |
310 | 314 | ||
311 | struct sort_entry sort_srcline = { | 315 | struct sort_entry sort_srcline = { |
@@ -332,7 +336,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
332 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, | 336 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, |
333 | size_t size, unsigned int width) | 337 | size_t size, unsigned int width) |
334 | { | 338 | { |
335 | return repsep_snprintf(bf, size, "%-*s", width, | 339 | return repsep_snprintf(bf, size, "%-*.*s", width, width, |
336 | he->parent ? he->parent->name : "[other]"); | 340 | he->parent ? he->parent->name : "[other]"); |
337 | } | 341 | } |
338 | 342 | ||
@@ -354,7 +358,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
354 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, | 358 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, |
355 | size_t size, unsigned int width) | 359 | size_t size, unsigned int width) |
356 | { | 360 | { |
357 | return repsep_snprintf(bf, size, "%*d", width, he->cpu); | 361 | return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu); |
358 | } | 362 | } |
359 | 363 | ||
360 | struct sort_entry sort_cpu = { | 364 | struct sort_entry sort_cpu = { |
@@ -484,7 +488,7 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, | |||
484 | else if (he->branch_info->flags.mispred) | 488 | else if (he->branch_info->flags.mispred) |
485 | out = "Y"; | 489 | out = "Y"; |
486 | 490 | ||
487 | return repsep_snprintf(bf, size, "%-*s", width, out); | 491 | return repsep_snprintf(bf, size, "%-*.*s", width, width, out); |
488 | } | 492 | } |
489 | 493 | ||
490 | /* --sort daddr_sym */ | 494 | /* --sort daddr_sym */ |
@@ -1194,7 +1198,7 @@ bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | |||
1194 | return hse_a->se == hse_b->se; | 1198 | return hse_a->se == hse_b->se; |
1195 | } | 1199 | } |
1196 | 1200 | ||
1197 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 1201 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) |
1198 | { | 1202 | { |
1199 | struct hpp_sort_entry *hse; | 1203 | struct hpp_sort_entry *hse; |
1200 | 1204 | ||
@@ -1202,20 +1206,21 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | |||
1202 | return; | 1206 | return; |
1203 | 1207 | ||
1204 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1208 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1205 | hists__new_col_len(hists, hse->se->se_width_idx, | 1209 | hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name)); |
1206 | strlen(hse->se->se_header)); | ||
1207 | } | 1210 | } |
1208 | 1211 | ||
1209 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1212 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
1210 | struct perf_evsel *evsel) | 1213 | struct perf_evsel *evsel) |
1211 | { | 1214 | { |
1212 | struct hpp_sort_entry *hse; | 1215 | struct hpp_sort_entry *hse; |
1213 | size_t len; | 1216 | size_t len = fmt->user_len; |
1214 | 1217 | ||
1215 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1218 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1216 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
1217 | 1219 | ||
1218 | return scnprintf(hpp->buf, hpp->size, "%-*s", len, hse->se->se_header); | 1220 | if (!len) |
1221 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
1222 | |||
1223 | return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); | ||
1219 | } | 1224 | } |
1220 | 1225 | ||
1221 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | 1226 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, |
@@ -1223,20 +1228,26 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | |||
1223 | struct perf_evsel *evsel) | 1228 | struct perf_evsel *evsel) |
1224 | { | 1229 | { |
1225 | struct hpp_sort_entry *hse; | 1230 | struct hpp_sort_entry *hse; |
1231 | size_t len = fmt->user_len; | ||
1226 | 1232 | ||
1227 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1233 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1228 | 1234 | ||
1229 | return hists__col_len(&evsel->hists, hse->se->se_width_idx); | 1235 | if (!len) |
1236 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
1237 | |||
1238 | return len; | ||
1230 | } | 1239 | } |
1231 | 1240 | ||
1232 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1241 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
1233 | struct hist_entry *he) | 1242 | struct hist_entry *he) |
1234 | { | 1243 | { |
1235 | struct hpp_sort_entry *hse; | 1244 | struct hpp_sort_entry *hse; |
1236 | size_t len; | 1245 | size_t len = fmt->user_len; |
1237 | 1246 | ||
1238 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1247 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1239 | len = hists__col_len(he->hists, hse->se->se_width_idx); | 1248 | |
1249 | if (!len) | ||
1250 | len = hists__col_len(he->hists, hse->se->se_width_idx); | ||
1240 | 1251 | ||
1241 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); | 1252 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); |
1242 | } | 1253 | } |
@@ -1253,6 +1264,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
1253 | } | 1264 | } |
1254 | 1265 | ||
1255 | hse->se = sd->entry; | 1266 | hse->se = sd->entry; |
1267 | hse->hpp.name = sd->entry->se_header; | ||
1256 | hse->hpp.header = __sort__hpp_header; | 1268 | hse->hpp.header = __sort__hpp_header; |
1257 | hse->hpp.width = __sort__hpp_width; | 1269 | hse->hpp.width = __sort__hpp_width; |
1258 | hse->hpp.entry = __sort__hpp_entry; | 1270 | hse->hpp.entry = __sort__hpp_entry; |
@@ -1265,6 +1277,8 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
1265 | INIT_LIST_HEAD(&hse->hpp.list); | 1277 | INIT_LIST_HEAD(&hse->hpp.list); |
1266 | INIT_LIST_HEAD(&hse->hpp.sort_list); | 1278 | INIT_LIST_HEAD(&hse->hpp.sort_list); |
1267 | hse->hpp.elide = false; | 1279 | hse->hpp.elide = false; |
1280 | hse->hpp.len = 0; | ||
1281 | hse->hpp.user_len = 0; | ||
1268 | 1282 | ||
1269 | return hse; | 1283 | return hse; |
1270 | } | 1284 | } |
@@ -1432,14 +1446,49 @@ static const char *get_default_sort_order(void) | |||
1432 | return default_sort_orders[sort__mode]; | 1446 | return default_sort_orders[sort__mode]; |
1433 | } | 1447 | } |
1434 | 1448 | ||
1449 | static int setup_sort_order(void) | ||
1450 | { | ||
1451 | char *new_sort_order; | ||
1452 | |||
1453 | /* | ||
1454 | * Append '+'-prefixed sort order to the default sort | ||
1455 | * order string. | ||
1456 | */ | ||
1457 | if (!sort_order || is_strict_order(sort_order)) | ||
1458 | return 0; | ||
1459 | |||
1460 | if (sort_order[1] == '\0') { | ||
1461 | error("Invalid --sort key: `+'"); | ||
1462 | return -EINVAL; | ||
1463 | } | ||
1464 | |||
1465 | /* | ||
1466 | * We allocate new sort_order string, but we never free it, | ||
1467 | * because it's checked over the rest of the code. | ||
1468 | */ | ||
1469 | if (asprintf(&new_sort_order, "%s,%s", | ||
1470 | get_default_sort_order(), sort_order + 1) < 0) { | ||
1471 | error("Not enough memory to set up --sort"); | ||
1472 | return -ENOMEM; | ||
1473 | } | ||
1474 | |||
1475 | sort_order = new_sort_order; | ||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1435 | static int __setup_sorting(void) | 1479 | static int __setup_sorting(void) |
1436 | { | 1480 | { |
1437 | char *tmp, *tok, *str; | 1481 | char *tmp, *tok, *str; |
1438 | const char *sort_keys = sort_order; | 1482 | const char *sort_keys; |
1439 | int ret = 0; | 1483 | int ret = 0; |
1440 | 1484 | ||
1485 | ret = setup_sort_order(); | ||
1486 | if (ret) | ||
1487 | return ret; | ||
1488 | |||
1489 | sort_keys = sort_order; | ||
1441 | if (sort_keys == NULL) { | 1490 | if (sort_keys == NULL) { |
1442 | if (field_order) { | 1491 | if (is_strict_order(field_order)) { |
1443 | /* | 1492 | /* |
1444 | * If user specified field order but no sort order, | 1493 | * If user specified field order but no sort order, |
1445 | * we'll honor it and not add default sort orders. | 1494 | * we'll honor it and not add default sort orders. |
@@ -1625,23 +1674,36 @@ static void reset_dimensions(void) | |||
1625 | memory_sort_dimensions[i].taken = 0; | 1674 | memory_sort_dimensions[i].taken = 0; |
1626 | } | 1675 | } |
1627 | 1676 | ||
1677 | bool is_strict_order(const char *order) | ||
1678 | { | ||
1679 | return order && (*order != '+'); | ||
1680 | } | ||
1681 | |||
1628 | static int __setup_output_field(void) | 1682 | static int __setup_output_field(void) |
1629 | { | 1683 | { |
1630 | char *tmp, *tok, *str; | 1684 | char *tmp, *tok, *str, *strp; |
1631 | int ret = 0; | 1685 | int ret = -EINVAL; |
1632 | 1686 | ||
1633 | if (field_order == NULL) | 1687 | if (field_order == NULL) |
1634 | return 0; | 1688 | return 0; |
1635 | 1689 | ||
1636 | reset_dimensions(); | 1690 | reset_dimensions(); |
1637 | 1691 | ||
1638 | str = strdup(field_order); | 1692 | strp = str = strdup(field_order); |
1639 | if (str == NULL) { | 1693 | if (str == NULL) { |
1640 | error("Not enough memory to setup output fields"); | 1694 | error("Not enough memory to setup output fields"); |
1641 | return -ENOMEM; | 1695 | return -ENOMEM; |
1642 | } | 1696 | } |
1643 | 1697 | ||
1644 | for (tok = strtok_r(str, ", ", &tmp); | 1698 | if (!is_strict_order(field_order)) |
1699 | strp++; | ||
1700 | |||
1701 | if (!strlen(strp)) { | ||
1702 | error("Invalid --fields key: `+'"); | ||
1703 | goto out; | ||
1704 | } | ||
1705 | |||
1706 | for (tok = strtok_r(strp, ", ", &tmp); | ||
1645 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 1707 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
1646 | ret = output_field_add(tok); | 1708 | ret = output_field_add(tok); |
1647 | if (ret == -EINVAL) { | 1709 | if (ret == -EINVAL) { |
@@ -1653,6 +1715,7 @@ static int __setup_output_field(void) | |||
1653 | } | 1715 | } |
1654 | } | 1716 | } |
1655 | 1717 | ||
1718 | out: | ||
1656 | free(str); | 1719 | free(str); |
1657 | return ret; | 1720 | return ret; |
1658 | } | 1721 | } |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 041f0c9cea2b..c03e4ff8beff 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -218,4 +218,5 @@ void perf_hpp__set_elide(int idx, bool elide); | |||
218 | 218 | ||
219 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); | 219 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); |
220 | 220 | ||
221 | bool is_strict_order(const char *order); | ||
221 | #endif /* __PERF_SORT_H */ | 222 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 2553e5b55b89..d87767f76903 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -9,78 +9,48 @@ | |||
9 | */ | 9 | */ |
10 | s64 perf_atoll(const char *str) | 10 | s64 perf_atoll(const char *str) |
11 | { | 11 | { |
12 | unsigned int i; | 12 | s64 length; |
13 | s64 length = -1, unit = 1; | 13 | char *p; |
14 | char c; | ||
14 | 15 | ||
15 | if (!isdigit(str[0])) | 16 | if (!isdigit(str[0])) |
16 | goto out_err; | 17 | goto out_err; |
17 | 18 | ||
18 | for (i = 1; i < strlen(str); i++) { | 19 | length = strtoll(str, &p, 10); |
19 | switch (str[i]) { | 20 | switch (c = *p++) { |
20 | case 'B': | 21 | case 'b': case 'B': |
21 | case 'b': | 22 | if (*p) |
22 | break; | ||
23 | case 'K': | ||
24 | if (str[i + 1] != 'B') | ||
25 | goto out_err; | ||
26 | else | ||
27 | goto kilo; | ||
28 | case 'k': | ||
29 | if (str[i + 1] != 'b') | ||
30 | goto out_err; | ||
31 | kilo: | ||
32 | unit = K; | ||
33 | break; | ||
34 | case 'M': | ||
35 | if (str[i + 1] != 'B') | ||
36 | goto out_err; | ||
37 | else | ||
38 | goto mega; | ||
39 | case 'm': | ||
40 | if (str[i + 1] != 'b') | ||
41 | goto out_err; | 23 | goto out_err; |
42 | mega: | 24 | case '\0': |
43 | unit = K * K; | 25 | return length; |
26 | default: | ||
27 | goto out_err; | ||
28 | /* two-letter suffices */ | ||
29 | case 'k': case 'K': | ||
30 | length <<= 10; | ||
44 | break; | 31 | break; |
45 | case 'G': | 32 | case 'm': case 'M': |
46 | if (str[i + 1] != 'B') | 33 | length <<= 20; |
47 | goto out_err; | ||
48 | else | ||
49 | goto giga; | ||
50 | case 'g': | ||
51 | if (str[i + 1] != 'b') | ||
52 | goto out_err; | ||
53 | giga: | ||
54 | unit = K * K * K; | ||
55 | break; | 34 | break; |
56 | case 'T': | 35 | case 'g': case 'G': |
57 | if (str[i + 1] != 'B') | 36 | length <<= 30; |
58 | goto out_err; | ||
59 | else | ||
60 | goto tera; | ||
61 | case 't': | ||
62 | if (str[i + 1] != 'b') | ||
63 | goto out_err; | ||
64 | tera: | ||
65 | unit = K * K * K * K; | ||
66 | break; | 37 | break; |
67 | case '\0': /* only specified figures */ | 38 | case 't': case 'T': |
68 | unit = 1; | 39 | length <<= 40; |
69 | break; | 40 | break; |
70 | default: | ||
71 | if (!isdigit(str[i])) | ||
72 | goto out_err; | ||
73 | break; | ||
74 | } | ||
75 | } | 41 | } |
76 | 42 | /* we want the cases to match */ | |
77 | length = atoll(str) * unit; | 43 | if (islower(c)) { |
78 | goto out; | 44 | if (strcmp(p, "b") != 0) |
45 | goto out_err; | ||
46 | } else { | ||
47 | if (strcmp(p, "B") != 0) | ||
48 | goto out_err; | ||
49 | } | ||
50 | return length; | ||
79 | 51 | ||
80 | out_err: | 52 | out_err: |
81 | length = -1; | 53 | return -1; |
82 | out: | ||
83 | return length; | ||
84 | } | 54 | } |
85 | 55 | ||
86 | /* | 56 | /* |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index d75349979e65..1e23a5bfb044 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <inttypes.h> | 6 | #include <inttypes.h> |
7 | 7 | ||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "machine.h" | ||
9 | #include "vdso.h" | 10 | #include "vdso.h" |
10 | #include <symbol/kallsyms.h> | 11 | #include <symbol/kallsyms.h> |
11 | #include "debug.h" | 12 | #include "debug.h" |
@@ -680,6 +681,11 @@ static u64 ref_reloc(struct kmap *kmap) | |||
680 | return 0; | 681 | return 0; |
681 | } | 682 | } |
682 | 683 | ||
684 | static bool want_demangle(bool is_kernel_sym) | ||
685 | { | ||
686 | return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; | ||
687 | } | ||
688 | |||
683 | int dso__load_sym(struct dso *dso, struct map *map, | 689 | int dso__load_sym(struct dso *dso, struct map *map, |
684 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | 690 | struct symsrc *syms_ss, struct symsrc *runtime_ss, |
685 | symbol_filter_t filter, int kmodule) | 691 | symbol_filter_t filter, int kmodule) |
@@ -712,6 +718,14 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
712 | symbols__delete(&dso->symbols[map->type]); | 718 | symbols__delete(&dso->symbols[map->type]); |
713 | 719 | ||
714 | if (!syms_ss->symtab) { | 720 | if (!syms_ss->symtab) { |
721 | /* | ||
722 | * If the vmlinux is stripped, fail so we will fall back | ||
723 | * to using kallsyms. The vmlinux runtime symbols aren't | ||
724 | * of much use. | ||
725 | */ | ||
726 | if (dso->kernel) | ||
727 | goto out_elf_end; | ||
728 | |||
715 | syms_ss->symtab = syms_ss->dynsym; | 729 | syms_ss->symtab = syms_ss->dynsym; |
716 | syms_ss->symshdr = syms_ss->dynshdr; | 730 | syms_ss->symshdr = syms_ss->dynshdr; |
717 | } | 731 | } |
@@ -736,7 +750,7 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
736 | if (symstrs == NULL) | 750 | if (symstrs == NULL) |
737 | goto out_elf_end; | 751 | goto out_elf_end; |
738 | 752 | ||
739 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | 753 | sec_strndx = elf_getscn(runtime_ss->elf, runtime_ss->ehdr.e_shstrndx); |
740 | if (sec_strndx == NULL) | 754 | if (sec_strndx == NULL) |
741 | goto out_elf_end; | 755 | goto out_elf_end; |
742 | 756 | ||
@@ -916,7 +930,11 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
916 | } | 930 | } |
917 | curr_dso->symtab_type = dso->symtab_type; | 931 | curr_dso->symtab_type = dso->symtab_type; |
918 | map_groups__insert(kmap->kmaps, curr_map); | 932 | map_groups__insert(kmap->kmaps, curr_map); |
919 | dsos__add(&dso->node, curr_dso); | 933 | /* |
934 | * The new DSO should go to the kernel DSOS | ||
935 | */ | ||
936 | dsos__add(&map->groups->machine->kernel_dsos, | ||
937 | curr_dso); | ||
920 | dso__set_loaded(curr_dso, map->type); | 938 | dso__set_loaded(curr_dso, map->type); |
921 | } else | 939 | } else |
922 | curr_dso = curr_map->dso; | 940 | curr_dso = curr_map->dso; |
@@ -938,9 +956,12 @@ new_symbol: | |||
938 | * DWARF DW_compile_unit has this, but we don't always have access | 956 | * DWARF DW_compile_unit has this, but we don't always have access |
939 | * to it... | 957 | * to it... |
940 | */ | 958 | */ |
941 | if (symbol_conf.demangle) { | 959 | if (want_demangle(dso->kernel || kmodule)) { |
942 | demangled = bfd_demangle(NULL, elf_name, | 960 | int demangle_flags = DMGL_NO_OPTS; |
943 | DMGL_PARAMS | DMGL_ANSI); | 961 | if (verbose) |
962 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; | ||
963 | |||
964 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); | ||
944 | if (demangled != NULL) | 965 | if (demangled != NULL) |
945 | elf_name = demangled; | 966 | elf_name = demangled; |
946 | } | 967 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index eb06746b06b2..be84f7a9838b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "machine.h" | 15 | #include "machine.h" |
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "strlist.h" | 17 | #include "strlist.h" |
18 | #include "header.h" | ||
18 | 19 | ||
19 | #include <elf.h> | 20 | #include <elf.h> |
20 | #include <limits.h> | 21 | #include <limits.h> |
@@ -33,6 +34,7 @@ struct symbol_conf symbol_conf = { | |||
33 | .try_vmlinux_path = true, | 34 | .try_vmlinux_path = true, |
34 | .annotate_src = true, | 35 | .annotate_src = true, |
35 | .demangle = true, | 36 | .demangle = true, |
37 | .demangle_kernel = false, | ||
36 | .cumulate_callchain = true, | 38 | .cumulate_callchain = true, |
37 | .show_hist_headers = true, | 39 | .show_hist_headers = true, |
38 | .symfs = "", | 40 | .symfs = "", |
@@ -523,10 +525,15 @@ struct process_kallsyms_args { | |||
523 | struct dso *dso; | 525 | struct dso *dso; |
524 | }; | 526 | }; |
525 | 527 | ||
528 | /* | ||
529 | * These are symbols in the kernel image, so make sure that | ||
530 | * sym is from a kernel DSO. | ||
531 | */ | ||
526 | bool symbol__is_idle(struct symbol *sym) | 532 | bool symbol__is_idle(struct symbol *sym) |
527 | { | 533 | { |
528 | const char * const idle_symbols[] = { | 534 | const char * const idle_symbols[] = { |
529 | "cpu_idle", | 535 | "cpu_idle", |
536 | "cpu_startup_entry", | ||
530 | "intel_idle", | 537 | "intel_idle", |
531 | "default_idle", | 538 | "default_idle", |
532 | "native_safe_halt", | 539 | "native_safe_halt", |
@@ -1468,8 +1475,7 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
1468 | if (vmlinux[0] == '/') | 1475 | if (vmlinux[0] == '/') |
1469 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); | 1476 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); |
1470 | else | 1477 | else |
1471 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1478 | symbol__join_symfs(symfs_vmlinux, vmlinux); |
1472 | symbol_conf.symfs, vmlinux); | ||
1473 | 1479 | ||
1474 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1480 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1475 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1481 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
@@ -1745,12 +1751,13 @@ static void vmlinux_path__exit(void) | |||
1745 | zfree(&vmlinux_path); | 1751 | zfree(&vmlinux_path); |
1746 | } | 1752 | } |
1747 | 1753 | ||
1748 | static int vmlinux_path__init(void) | 1754 | static int vmlinux_path__init(struct perf_session_env *env) |
1749 | { | 1755 | { |
1750 | struct utsname uts; | 1756 | struct utsname uts; |
1751 | char bf[PATH_MAX]; | 1757 | char bf[PATH_MAX]; |
1758 | char *kernel_version; | ||
1752 | 1759 | ||
1753 | vmlinux_path = malloc(sizeof(char *) * 5); | 1760 | vmlinux_path = malloc(sizeof(char *) * 6); |
1754 | if (vmlinux_path == NULL) | 1761 | if (vmlinux_path == NULL) |
1755 | return -1; | 1762 | return -1; |
1756 | 1763 | ||
@@ -1763,25 +1770,37 @@ static int vmlinux_path__init(void) | |||
1763 | goto out_fail; | 1770 | goto out_fail; |
1764 | ++vmlinux_path__nr_entries; | 1771 | ++vmlinux_path__nr_entries; |
1765 | 1772 | ||
1766 | /* only try running kernel version if no symfs was given */ | 1773 | /* only try kernel version if no symfs was given */ |
1767 | if (symbol_conf.symfs[0] != 0) | 1774 | if (symbol_conf.symfs[0] != 0) |
1768 | return 0; | 1775 | return 0; |
1769 | 1776 | ||
1770 | if (uname(&uts) < 0) | 1777 | if (env) { |
1771 | return -1; | 1778 | kernel_version = env->os_release; |
1779 | } else { | ||
1780 | if (uname(&uts) < 0) | ||
1781 | goto out_fail; | ||
1772 | 1782 | ||
1773 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | 1783 | kernel_version = uts.release; |
1784 | } | ||
1785 | |||
1786 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version); | ||
1774 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1787 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1775 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1788 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1776 | goto out_fail; | 1789 | goto out_fail; |
1777 | ++vmlinux_path__nr_entries; | 1790 | ++vmlinux_path__nr_entries; |
1778 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | 1791 | snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s", |
1792 | kernel_version); | ||
1793 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1794 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1795 | goto out_fail; | ||
1796 | ++vmlinux_path__nr_entries; | ||
1797 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version); | ||
1779 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1798 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1780 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1799 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1781 | goto out_fail; | 1800 | goto out_fail; |
1782 | ++vmlinux_path__nr_entries; | 1801 | ++vmlinux_path__nr_entries; |
1783 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | 1802 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", |
1784 | uts.release); | 1803 | kernel_version); |
1785 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1804 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1786 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1805 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1787 | goto out_fail; | 1806 | goto out_fail; |
@@ -1827,7 +1846,7 @@ static bool symbol__read_kptr_restrict(void) | |||
1827 | return value; | 1846 | return value; |
1828 | } | 1847 | } |
1829 | 1848 | ||
1830 | int symbol__init(void) | 1849 | int symbol__init(struct perf_session_env *env) |
1831 | { | 1850 | { |
1832 | const char *symfs; | 1851 | const char *symfs; |
1833 | 1852 | ||
@@ -1842,7 +1861,7 @@ int symbol__init(void) | |||
1842 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 1861 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
1843 | sizeof(struct symbol)); | 1862 | sizeof(struct symbol)); |
1844 | 1863 | ||
1845 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | 1864 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0) |
1846 | return -1; | 1865 | return -1; |
1847 | 1866 | ||
1848 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | 1867 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index e7295e93cff9..bec4b7bd09de 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <libgen.h> | 13 | #include <libgen.h> |
14 | #include "build-id.h" | 14 | #include "build-id.h" |
15 | #include "event.h" | 15 | #include "event.h" |
16 | #include "util.h" | ||
16 | 17 | ||
17 | #ifdef HAVE_LIBELF_SUPPORT | 18 | #ifdef HAVE_LIBELF_SUPPORT |
18 | #include <libelf.h> | 19 | #include <libelf.h> |
@@ -59,6 +60,7 @@ extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
59 | #endif | 60 | #endif |
60 | 61 | ||
61 | #ifndef DMGL_PARAMS | 62 | #ifndef DMGL_PARAMS |
63 | #define DMGL_NO_OPTS 0 /* For readability... */ | ||
62 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | 64 | #define DMGL_PARAMS (1 << 0) /* Include function args */ |
63 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 65 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
64 | #endif | 66 | #endif |
@@ -118,6 +120,7 @@ struct symbol_conf { | |||
118 | annotate_src, | 120 | annotate_src, |
119 | event_group, | 121 | event_group, |
120 | demangle, | 122 | demangle, |
123 | demangle_kernel, | ||
121 | filter_relative, | 124 | filter_relative, |
122 | show_hist_headers; | 125 | show_hist_headers; |
123 | const char *vmlinux_name, | 126 | const char *vmlinux_name, |
@@ -143,6 +146,14 @@ struct symbol_conf { | |||
143 | }; | 146 | }; |
144 | 147 | ||
145 | extern struct symbol_conf symbol_conf; | 148 | extern struct symbol_conf symbol_conf; |
149 | |||
150 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) | ||
151 | { | ||
152 | return path__join(bf, size, symbol_conf.symfs, path); | ||
153 | } | ||
154 | |||
155 | #define symbol__join_symfs(bf, path) __symbol__join_symfs(bf, sizeof(bf), path) | ||
156 | |||
146 | extern int vmlinux_path__nr_entries; | 157 | extern int vmlinux_path__nr_entries; |
147 | extern char **vmlinux_path; | 158 | extern char **vmlinux_path; |
148 | 159 | ||
@@ -253,7 +264,8 @@ int modules__parse(const char *filename, void *arg, | |||
253 | int filename__read_debuglink(const char *filename, char *debuglink, | 264 | int filename__read_debuglink(const char *filename, char *debuglink, |
254 | size_t size); | 265 | size_t size); |
255 | 266 | ||
256 | int symbol__init(void); | 267 | struct perf_session_env; |
268 | int symbol__init(struct perf_session_env *env); | ||
257 | void symbol__exit(void); | 269 | void symbol__exit(void); |
258 | void symbol__elf_init(void); | 270 | void symbol__elf_init(void); |
259 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | 271 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 12c7a253a63c..a9df7f2c6dc9 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -42,7 +42,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
42 | goto err_thread; | 42 | goto err_thread; |
43 | 43 | ||
44 | snprintf(comm_str, 32, ":%d", tid); | 44 | snprintf(comm_str, 32, ":%d", tid); |
45 | comm = comm__new(comm_str, 0); | 45 | comm = comm__new(comm_str, 0, false); |
46 | free(comm_str); | 46 | free(comm_str); |
47 | if (!comm) | 47 | if (!comm) |
48 | goto err_thread; | 48 | goto err_thread; |
@@ -81,19 +81,33 @@ struct comm *thread__comm(const struct thread *thread) | |||
81 | return list_first_entry(&thread->comm_list, struct comm, list); | 81 | return list_first_entry(&thread->comm_list, struct comm, list); |
82 | } | 82 | } |
83 | 83 | ||
84 | struct comm *thread__exec_comm(const struct thread *thread) | ||
85 | { | ||
86 | struct comm *comm, *last = NULL; | ||
87 | |||
88 | list_for_each_entry(comm, &thread->comm_list, list) { | ||
89 | if (comm->exec) | ||
90 | return comm; | ||
91 | last = comm; | ||
92 | } | ||
93 | |||
94 | return last; | ||
95 | } | ||
96 | |||
84 | /* CHECKME: time should always be 0 if event aren't ordered */ | 97 | /* CHECKME: time should always be 0 if event aren't ordered */ |
85 | int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) | 98 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, |
99 | bool exec) | ||
86 | { | 100 | { |
87 | struct comm *new, *curr = thread__comm(thread); | 101 | struct comm *new, *curr = thread__comm(thread); |
88 | int err; | 102 | int err; |
89 | 103 | ||
90 | /* Override latest entry if it had no specific time coverage */ | 104 | /* Override latest entry if it had no specific time coverage */ |
91 | if (!curr->start) { | 105 | if (!curr->start && !curr->exec) { |
92 | err = comm__override(curr, str, timestamp); | 106 | err = comm__override(curr, str, timestamp, exec); |
93 | if (err) | 107 | if (err) |
94 | return err; | 108 | return err; |
95 | } else { | 109 | } else { |
96 | new = comm__new(str, timestamp); | 110 | new = comm__new(str, timestamp, exec); |
97 | if (!new) | 111 | if (!new) |
98 | return -ENOMEM; | 112 | return -ENOMEM; |
99 | list_add(&new->list, &thread->comm_list); | 113 | list_add(&new->list, &thread->comm_list); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 716b7723cce2..8c75fa774706 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -38,9 +38,17 @@ static inline void thread__exited(struct thread *thread) | |||
38 | thread->dead = true; | 38 | thread->dead = true; |
39 | } | 39 | } |
40 | 40 | ||
41 | int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); | 41 | int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, |
42 | bool exec); | ||
43 | static inline int thread__set_comm(struct thread *thread, const char *comm, | ||
44 | u64 timestamp) | ||
45 | { | ||
46 | return __thread__set_comm(thread, comm, timestamp, false); | ||
47 | } | ||
48 | |||
42 | int thread__comm_len(struct thread *thread); | 49 | int thread__comm_len(struct thread *thread); |
43 | struct comm *thread__comm(const struct thread *thread); | 50 | struct comm *thread__comm(const struct thread *thread); |
51 | struct comm *thread__exec_comm(const struct thread *thread); | ||
44 | const char *thread__comm_str(const struct thread *thread); | 52 | const char *thread__comm_str(const struct thread *thread); |
45 | void thread__insert_map(struct thread *thread, struct map *map); | 53 | void thread__insert_map(struct thread *thread, struct map *map); |
46 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | 54 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 4385816d3d49..f11636966a0f 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
@@ -40,7 +40,7 @@ struct perf_tool { | |||
40 | event_op2 tracing_data; | 40 | event_op2 tracing_data; |
41 | event_op2 finished_round, | 41 | event_op2 finished_round, |
42 | build_id; | 42 | build_id; |
43 | bool ordered_samples; | 43 | bool ordered_events; |
44 | bool ordering_requires_timestamps; | 44 | bool ordering_requires_timestamps; |
45 | }; | 45 | }; |
46 | 46 | ||
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 57aaccc1692e..5c9bdd1591a9 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -30,6 +30,11 @@ | |||
30 | 30 | ||
31 | struct scripting_context *scripting_context; | 31 | struct scripting_context *scripting_context; |
32 | 32 | ||
33 | static int flush_script_unsupported(void) | ||
34 | { | ||
35 | return 0; | ||
36 | } | ||
37 | |||
33 | static int stop_script_unsupported(void) | 38 | static int stop_script_unsupported(void) |
34 | { | 39 | { |
35 | return 0; | 40 | return 0; |
@@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent | |||
74 | struct scripting_ops python_scripting_unsupported_ops = { | 79 | struct scripting_ops python_scripting_unsupported_ops = { |
75 | .name = "Python", | 80 | .name = "Python", |
76 | .start_script = python_start_script_unsupported, | 81 | .start_script = python_start_script_unsupported, |
82 | .flush_script = flush_script_unsupported, | ||
77 | .stop_script = stop_script_unsupported, | 83 | .stop_script = stop_script_unsupported, |
78 | .process_event = process_event_unsupported, | 84 | .process_event = process_event_unsupported, |
79 | .generate_script = python_generate_script_unsupported, | 85 | .generate_script = python_generate_script_unsupported, |
@@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent | |||
137 | struct scripting_ops perl_scripting_unsupported_ops = { | 143 | struct scripting_ops perl_scripting_unsupported_ops = { |
138 | .name = "Perl", | 144 | .name = "Perl", |
139 | .start_script = perl_start_script_unsupported, | 145 | .start_script = perl_start_script_unsupported, |
146 | .flush_script = flush_script_unsupported, | ||
140 | .stop_script = stop_script_unsupported, | 147 | .stop_script = stop_script_unsupported, |
141 | .process_event = process_event_unsupported, | 148 | .process_event = process_event_unsupported, |
142 | .generate_script = perl_generate_script_unsupported, | 149 | .generate_script = perl_generate_script_unsupported, |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 7b6d68688327..52aaa19e1eb1 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -64,6 +64,7 @@ struct perf_session; | |||
64 | struct scripting_ops { | 64 | struct scripting_ops { |
65 | const char *name; | 65 | const char *name; |
66 | int (*start_script) (const char *script, int argc, const char **argv); | 66 | int (*start_script) (const char *script, int argc, const char **argv); |
67 | int (*flush_script) (void); | ||
67 | int (*stop_script) (void); | 68 | int (*stop_script) (void); |
68 | void (*process_event) (union perf_event *event, | 69 | void (*process_event) (union perf_event *event, |
69 | struct perf_sample *sample, | 70 | struct perf_sample *sample, |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index e52e7461911b..24e8d871b74e 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <limits.h> | 13 | #include <limits.h> |
14 | #include <byteswap.h> | 14 | #include <byteswap.h> |
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <unistd.h> | ||
16 | 17 | ||
17 | /* | 18 | /* |
18 | * XXX We need to find a better place for these things... | 19 | * XXX We need to find a better place for these things... |
@@ -282,6 +283,18 @@ void get_term_dimensions(struct winsize *ws) | |||
282 | ws->ws_col = 80; | 283 | ws->ws_col = 80; |
283 | } | 284 | } |
284 | 285 | ||
286 | void set_term_quiet_input(struct termios *old) | ||
287 | { | ||
288 | struct termios tc; | ||
289 | |||
290 | tcgetattr(0, old); | ||
291 | tc = *old; | ||
292 | tc.c_lflag &= ~(ICANON | ECHO); | ||
293 | tc.c_cc[VMIN] = 0; | ||
294 | tc.c_cc[VTIME] = 0; | ||
295 | tcsetattr(0, TCSANOW, &tc); | ||
296 | } | ||
297 | |||
285 | static void set_tracing_events_path(const char *mountpoint) | 298 | static void set_tracing_events_path(const char *mountpoint) |
286 | { | 299 | { |
287 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | 300 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", |
@@ -443,6 +456,7 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
443 | size_t size = 0, alloc_size = 0; | 456 | size_t size = 0, alloc_size = 0; |
444 | void *bf = NULL, *nbf; | 457 | void *bf = NULL, *nbf; |
445 | int fd, n, err = 0; | 458 | int fd, n, err = 0; |
459 | char sbuf[STRERR_BUFSIZE]; | ||
446 | 460 | ||
447 | fd = open(filename, O_RDONLY); | 461 | fd = open(filename, O_RDONLY); |
448 | if (fd < 0) | 462 | if (fd < 0) |
@@ -463,8 +477,8 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
463 | n = read(fd, bf + size, alloc_size - size); | 477 | n = read(fd, bf + size, alloc_size - size); |
464 | if (n < 0) { | 478 | if (n < 0) { |
465 | if (size) { | 479 | if (size) { |
466 | pr_warning("read failed %d: %s\n", | 480 | pr_warning("read failed %d: %s\n", errno, |
467 | errno, strerror(errno)); | 481 | strerror_r(errno, sbuf, sizeof(sbuf))); |
468 | err = 0; | 482 | err = 0; |
469 | } else | 483 | } else |
470 | err = -errno; | 484 | err = -errno; |
@@ -536,3 +550,39 @@ void mem_bswap_64(void *src, int byte_size) | |||
536 | ++m; | 550 | ++m; |
537 | } | 551 | } |
538 | } | 552 | } |
553 | |||
554 | bool find_process(const char *name) | ||
555 | { | ||
556 | size_t len = strlen(name); | ||
557 | DIR *dir; | ||
558 | struct dirent *d; | ||
559 | int ret = -1; | ||
560 | |||
561 | dir = opendir(procfs__mountpoint()); | ||
562 | if (!dir) | ||
563 | return -1; | ||
564 | |||
565 | /* Walk through the directory. */ | ||
566 | while (ret && (d = readdir(dir)) != NULL) { | ||
567 | char path[PATH_MAX]; | ||
568 | char *data; | ||
569 | size_t size; | ||
570 | |||
571 | if ((d->d_type != DT_DIR) || | ||
572 | !strcmp(".", d->d_name) || | ||
573 | !strcmp("..", d->d_name)) | ||
574 | continue; | ||
575 | |||
576 | scnprintf(path, sizeof(path), "%s/%s/comm", | ||
577 | procfs__mountpoint(), d->d_name); | ||
578 | |||
579 | if (filename__read_str(path, &data, &size)) | ||
580 | continue; | ||
581 | |||
582 | ret = strncmp(name, data, len); | ||
583 | free(data); | ||
584 | } | ||
585 | |||
586 | closedir(dir); | ||
587 | return ret ? false : true; | ||
588 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 66864364ccb4..80bfdaa0e2a4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -39,6 +39,8 @@ | |||
39 | 39 | ||
40 | #define _ALL_SOURCE 1 | 40 | #define _ALL_SOURCE 1 |
41 | #define _BSD_SOURCE 1 | 41 | #define _BSD_SOURCE 1 |
42 | /* glibc 2.20 deprecates _BSD_SOURCE in favour of _DEFAULT_SOURCE */ | ||
43 | #define _DEFAULT_SOURCE 1 | ||
42 | #define HAS_BOOL | 44 | #define HAS_BOOL |
43 | 45 | ||
44 | #include <unistd.h> | 46 | #include <unistd.h> |
@@ -64,16 +66,18 @@ | |||
64 | #include <regex.h> | 66 | #include <regex.h> |
65 | #include <utime.h> | 67 | #include <utime.h> |
66 | #include <sys/wait.h> | 68 | #include <sys/wait.h> |
67 | #include <sys/poll.h> | 69 | #include <poll.h> |
68 | #include <sys/socket.h> | 70 | #include <sys/socket.h> |
69 | #include <sys/ioctl.h> | 71 | #include <sys/ioctl.h> |
70 | #include <inttypes.h> | 72 | #include <inttypes.h> |
73 | #include <linux/kernel.h> | ||
71 | #include <linux/magic.h> | 74 | #include <linux/magic.h> |
72 | #include <linux/types.h> | 75 | #include <linux/types.h> |
73 | #include <sys/ttydefaults.h> | 76 | #include <sys/ttydefaults.h> |
74 | #include <api/fs/debugfs.h> | 77 | #include <api/fs/debugfs.h> |
75 | #include <termios.h> | 78 | #include <termios.h> |
76 | #include <linux/bitops.h> | 79 | #include <linux/bitops.h> |
80 | #include <termios.h> | ||
77 | 81 | ||
78 | extern const char *graph_line; | 82 | extern const char *graph_line; |
79 | extern const char *graph_dotted_line; | 83 | extern const char *graph_dotted_line; |
@@ -307,6 +311,7 @@ extern unsigned int page_size; | |||
307 | extern int cacheline_size; | 311 | extern int cacheline_size; |
308 | 312 | ||
309 | void get_term_dimensions(struct winsize *ws); | 313 | void get_term_dimensions(struct winsize *ws); |
314 | void set_term_quiet_input(struct termios *old); | ||
310 | 315 | ||
311 | struct parse_tag { | 316 | struct parse_tag { |
312 | char tag; | 317 | char tag; |
@@ -317,6 +322,21 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags); | |||
317 | 322 | ||
318 | #define SRCLINE_UNKNOWN ((char *) "??:0") | 323 | #define SRCLINE_UNKNOWN ((char *) "??:0") |
319 | 324 | ||
325 | static inline int path__join(char *bf, size_t size, | ||
326 | const char *path1, const char *path2) | ||
327 | { | ||
328 | return scnprintf(bf, size, "%s%s%s", path1, path1[0] ? "/" : "", path2); | ||
329 | } | ||
330 | |||
331 | static inline int path__join3(char *bf, size_t size, | ||
332 | const char *path1, const char *path2, | ||
333 | const char *path3) | ||
334 | { | ||
335 | return scnprintf(bf, size, "%s%s%s%s%s", | ||
336 | path1, path1[0] ? "/" : "", | ||
337 | path2, path2[0] ? "/" : "", path3); | ||
338 | } | ||
339 | |||
320 | struct dso; | 340 | struct dso; |
321 | 341 | ||
322 | char *get_srcline(struct dso *dso, unsigned long addr); | 342 | char *get_srcline(struct dso *dso, unsigned long addr); |
@@ -330,4 +350,5 @@ void mem_bswap_64(void *src, int byte_size); | |||
330 | void mem_bswap_32(void *src, int byte_size); | 350 | void mem_bswap_32(void *src, int byte_size); |
331 | 351 | ||
332 | const char *get_filename_for_perf_kvm(void); | 352 | const char *get_filename_for_perf_kvm(void); |
353 | bool find_process(const char *name); | ||
333 | #endif /* GIT_COMPAT_UTIL_H */ | 354 | #endif /* GIT_COMPAT_UTIL_H */ |