diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-03 15:48:59 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-03 15:48:59 -0400 |
commit | 868b60e0550247fc83630070ff64bbfb803b2347 (patch) | |
tree | 1467d993b4923ed0e4f77fc5ab8c81e23f0d533b /tools | |
parent | c9d53c0f2d23c792e4b9cf1551b63de4516f839e (diff) | |
parent | 6955b58254c2bcee8a7b55ce06468a645dc98ec5 (diff) |
Merge branch 'component-for-driver' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into driver-core-next
Russell writes:
These updates fix one bug in the component helper where the matched
components are not properly cleaned up when the master fails to bind.
I'll provide a version of this for stable trees if it's deemed that
we need to backport it.
The second patch causes the component helper to ignore duplicate
matches when adding components - this is something that was originally
needed for imx-drm, but since that has now been updated, we no longer
need to skip over a component which has already been matched.
The final patch starts the process of updating the component helper
API to achieve two goals: to allow the API to be more efficient when
deferred probing occurs, and to allow for future improvements to the
component helper without having a major impact on the users.
This represents groundwork for some other changes; once this has been
merged, I will then send two further pull requests (one for the staging
tree, and one for the DRM tree) to update the drivers to the new API.
This will result in these three commits being shared with those trees.
Diffstat (limited to 'tools')
39 files changed, 1220 insertions, 123 deletions
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index b83184f2d484..93825a17dcce 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -765,6 +765,9 @@ static void free_arg(struct print_arg *arg) | |||
765 | case PRINT_BSTRING: | 765 | case PRINT_BSTRING: |
766 | free(arg->string.string); | 766 | free(arg->string.string); |
767 | break; | 767 | break; |
768 | case PRINT_BITMASK: | ||
769 | free(arg->bitmask.bitmask); | ||
770 | break; | ||
768 | case PRINT_DYNAMIC_ARRAY: | 771 | case PRINT_DYNAMIC_ARRAY: |
769 | free(arg->dynarray.index); | 772 | free(arg->dynarray.index); |
770 | break; | 773 | break; |
@@ -2268,6 +2271,7 @@ static int arg_num_eval(struct print_arg *arg, long long *val) | |||
2268 | case PRINT_FIELD ... PRINT_SYMBOL: | 2271 | case PRINT_FIELD ... PRINT_SYMBOL: |
2269 | case PRINT_STRING: | 2272 | case PRINT_STRING: |
2270 | case PRINT_BSTRING: | 2273 | case PRINT_BSTRING: |
2274 | case PRINT_BITMASK: | ||
2271 | default: | 2275 | default: |
2272 | do_warning("invalid eval type %d", arg->type); | 2276 | do_warning("invalid eval type %d", arg->type); |
2273 | ret = 0; | 2277 | ret = 0; |
@@ -2296,6 +2300,7 @@ static char *arg_eval (struct print_arg *arg) | |||
2296 | case PRINT_FIELD ... PRINT_SYMBOL: | 2300 | case PRINT_FIELD ... PRINT_SYMBOL: |
2297 | case PRINT_STRING: | 2301 | case PRINT_STRING: |
2298 | case PRINT_BSTRING: | 2302 | case PRINT_BSTRING: |
2303 | case PRINT_BITMASK: | ||
2299 | default: | 2304 | default: |
2300 | do_warning("invalid eval type %d", arg->type); | 2305 | do_warning("invalid eval type %d", arg->type); |
2301 | break; | 2306 | break; |
@@ -2683,6 +2688,35 @@ process_str(struct event_format *event __maybe_unused, struct print_arg *arg, | |||
2683 | return EVENT_ERROR; | 2688 | return EVENT_ERROR; |
2684 | } | 2689 | } |
2685 | 2690 | ||
2691 | static enum event_type | ||
2692 | process_bitmask(struct event_format *event __maybe_unused, struct print_arg *arg, | ||
2693 | char **tok) | ||
2694 | { | ||
2695 | enum event_type type; | ||
2696 | char *token; | ||
2697 | |||
2698 | if (read_expect_type(EVENT_ITEM, &token) < 0) | ||
2699 | goto out_free; | ||
2700 | |||
2701 | arg->type = PRINT_BITMASK; | ||
2702 | arg->bitmask.bitmask = token; | ||
2703 | arg->bitmask.offset = -1; | ||
2704 | |||
2705 | if (read_expected(EVENT_DELIM, ")") < 0) | ||
2706 | goto out_err; | ||
2707 | |||
2708 | type = read_token(&token); | ||
2709 | *tok = token; | ||
2710 | |||
2711 | return type; | ||
2712 | |||
2713 | out_free: | ||
2714 | free_token(token); | ||
2715 | out_err: | ||
2716 | *tok = NULL; | ||
2717 | return EVENT_ERROR; | ||
2718 | } | ||
2719 | |||
2686 | static struct pevent_function_handler * | 2720 | static struct pevent_function_handler * |
2687 | find_func_handler(struct pevent *pevent, char *func_name) | 2721 | find_func_handler(struct pevent *pevent, char *func_name) |
2688 | { | 2722 | { |
@@ -2797,6 +2831,10 @@ process_function(struct event_format *event, struct print_arg *arg, | |||
2797 | free_token(token); | 2831 | free_token(token); |
2798 | return process_str(event, arg, tok); | 2832 | return process_str(event, arg, tok); |
2799 | } | 2833 | } |
2834 | if (strcmp(token, "__get_bitmask") == 0) { | ||
2835 | free_token(token); | ||
2836 | return process_bitmask(event, arg, tok); | ||
2837 | } | ||
2800 | if (strcmp(token, "__get_dynamic_array") == 0) { | 2838 | if (strcmp(token, "__get_dynamic_array") == 0) { |
2801 | free_token(token); | 2839 | free_token(token); |
2802 | return process_dynamic_array(event, arg, tok); | 2840 | return process_dynamic_array(event, arg, tok); |
@@ -3324,6 +3362,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg | |||
3324 | return eval_type(val, arg, 0); | 3362 | return eval_type(val, arg, 0); |
3325 | case PRINT_STRING: | 3363 | case PRINT_STRING: |
3326 | case PRINT_BSTRING: | 3364 | case PRINT_BSTRING: |
3365 | case PRINT_BITMASK: | ||
3327 | return 0; | 3366 | return 0; |
3328 | case PRINT_FUNC: { | 3367 | case PRINT_FUNC: { |
3329 | struct trace_seq s; | 3368 | struct trace_seq s; |
@@ -3556,6 +3595,60 @@ static void print_str_to_seq(struct trace_seq *s, const char *format, | |||
3556 | trace_seq_printf(s, format, str); | 3595 | trace_seq_printf(s, format, str); |
3557 | } | 3596 | } |
3558 | 3597 | ||
3598 | static void print_bitmask_to_seq(struct pevent *pevent, | ||
3599 | struct trace_seq *s, const char *format, | ||
3600 | int len_arg, const void *data, int size) | ||
3601 | { | ||
3602 | int nr_bits = size * 8; | ||
3603 | int str_size = (nr_bits + 3) / 4; | ||
3604 | int len = 0; | ||
3605 | char buf[3]; | ||
3606 | char *str; | ||
3607 | int index; | ||
3608 | int i; | ||
3609 | |||
3610 | /* | ||
3611 | * The kernel likes to put in commas every 32 bits, we | ||
3612 | * can do the same. | ||
3613 | */ | ||
3614 | str_size += (nr_bits - 1) / 32; | ||
3615 | |||
3616 | str = malloc(str_size + 1); | ||
3617 | if (!str) { | ||
3618 | do_warning("%s: not enough memory!", __func__); | ||
3619 | return; | ||
3620 | } | ||
3621 | str[str_size] = 0; | ||
3622 | |||
3623 | /* Start out with -2 for the two chars per byte */ | ||
3624 | for (i = str_size - 2; i >= 0; i -= 2) { | ||
3625 | /* | ||
3626 | * data points to a bit mask of size bytes. | ||
3627 | * In the kernel, this is an array of long words, thus | ||
3628 | * endianess is very important. | ||
3629 | */ | ||
3630 | if (pevent->file_bigendian) | ||
3631 | index = size - (len + 1); | ||
3632 | else | ||
3633 | index = len; | ||
3634 | |||
3635 | snprintf(buf, 3, "%02x", *((unsigned char *)data + index)); | ||
3636 | memcpy(str + i, buf, 2); | ||
3637 | len++; | ||
3638 | if (!(len & 3) && i > 0) { | ||
3639 | i--; | ||
3640 | str[i] = ','; | ||
3641 | } | ||
3642 | } | ||
3643 | |||
3644 | if (len_arg >= 0) | ||
3645 | trace_seq_printf(s, format, len_arg, str); | ||
3646 | else | ||
3647 | trace_seq_printf(s, format, str); | ||
3648 | |||
3649 | free(str); | ||
3650 | } | ||
3651 | |||
3559 | static void print_str_arg(struct trace_seq *s, void *data, int size, | 3652 | static void print_str_arg(struct trace_seq *s, void *data, int size, |
3560 | struct event_format *event, const char *format, | 3653 | struct event_format *event, const char *format, |
3561 | int len_arg, struct print_arg *arg) | 3654 | int len_arg, struct print_arg *arg) |
@@ -3691,6 +3784,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, | |||
3691 | case PRINT_BSTRING: | 3784 | case PRINT_BSTRING: |
3692 | print_str_to_seq(s, format, len_arg, arg->string.string); | 3785 | print_str_to_seq(s, format, len_arg, arg->string.string); |
3693 | break; | 3786 | break; |
3787 | case PRINT_BITMASK: { | ||
3788 | int bitmask_offset; | ||
3789 | int bitmask_size; | ||
3790 | |||
3791 | if (arg->bitmask.offset == -1) { | ||
3792 | struct format_field *f; | ||
3793 | |||
3794 | f = pevent_find_any_field(event, arg->bitmask.bitmask); | ||
3795 | arg->bitmask.offset = f->offset; | ||
3796 | } | ||
3797 | bitmask_offset = data2host4(pevent, data + arg->bitmask.offset); | ||
3798 | bitmask_size = bitmask_offset >> 16; | ||
3799 | bitmask_offset &= 0xffff; | ||
3800 | print_bitmask_to_seq(pevent, s, format, len_arg, | ||
3801 | data + bitmask_offset, bitmask_size); | ||
3802 | break; | ||
3803 | } | ||
3694 | case PRINT_OP: | 3804 | case PRINT_OP: |
3695 | /* | 3805 | /* |
3696 | * The only op for string should be ? : | 3806 | * The only op for string should be ? : |
@@ -4822,6 +4932,9 @@ static void print_args(struct print_arg *args) | |||
4822 | case PRINT_BSTRING: | 4932 | case PRINT_BSTRING: |
4823 | printf("__get_str(%s)", args->string.string); | 4933 | printf("__get_str(%s)", args->string.string); |
4824 | break; | 4934 | break; |
4935 | case PRINT_BITMASK: | ||
4936 | printf("__get_bitmask(%s)", args->bitmask.bitmask); | ||
4937 | break; | ||
4825 | case PRINT_TYPE: | 4938 | case PRINT_TYPE: |
4826 | printf("(%s)", args->typecast.type); | 4939 | printf("(%s)", args->typecast.type); |
4827 | print_args(args->typecast.item); | 4940 | print_args(args->typecast.item); |
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index feab94281634..7a3873ff9a4f 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
@@ -107,8 +107,8 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s, | |||
107 | typedef int (*pevent_plugin_load_func)(struct pevent *pevent); | 107 | typedef int (*pevent_plugin_load_func)(struct pevent *pevent); |
108 | typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); | 108 | typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); |
109 | 109 | ||
110 | struct plugin_option { | 110 | struct pevent_plugin_option { |
111 | struct plugin_option *next; | 111 | struct pevent_plugin_option *next; |
112 | void *handle; | 112 | void *handle; |
113 | char *file; | 113 | char *file; |
114 | char *name; | 114 | char *name; |
@@ -135,7 +135,7 @@ struct plugin_option { | |||
135 | * PEVENT_PLUGIN_OPTIONS: (optional) | 135 | * PEVENT_PLUGIN_OPTIONS: (optional) |
136 | * Plugin options that can be set before loading | 136 | * Plugin options that can be set before loading |
137 | * | 137 | * |
138 | * struct plugin_option PEVENT_PLUGIN_OPTIONS[] = { | 138 | * struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = { |
139 | * { | 139 | * { |
140 | * .name = "option-name", | 140 | * .name = "option-name", |
141 | * .plugin_alias = "overide-file-name", (optional) | 141 | * .plugin_alias = "overide-file-name", (optional) |
@@ -208,6 +208,11 @@ struct print_arg_string { | |||
208 | int offset; | 208 | int offset; |
209 | }; | 209 | }; |
210 | 210 | ||
211 | struct print_arg_bitmask { | ||
212 | char *bitmask; | ||
213 | int offset; | ||
214 | }; | ||
215 | |||
211 | struct print_arg_field { | 216 | struct print_arg_field { |
212 | char *name; | 217 | char *name; |
213 | struct format_field *field; | 218 | struct format_field *field; |
@@ -274,6 +279,7 @@ enum print_arg_type { | |||
274 | PRINT_DYNAMIC_ARRAY, | 279 | PRINT_DYNAMIC_ARRAY, |
275 | PRINT_OP, | 280 | PRINT_OP, |
276 | PRINT_FUNC, | 281 | PRINT_FUNC, |
282 | PRINT_BITMASK, | ||
277 | }; | 283 | }; |
278 | 284 | ||
279 | struct print_arg { | 285 | struct print_arg { |
@@ -288,6 +294,7 @@ struct print_arg { | |||
288 | struct print_arg_hex hex; | 294 | struct print_arg_hex hex; |
289 | struct print_arg_func func; | 295 | struct print_arg_func func; |
290 | struct print_arg_string string; | 296 | struct print_arg_string string; |
297 | struct print_arg_bitmask bitmask; | ||
291 | struct print_arg_op op; | 298 | struct print_arg_op op; |
292 | struct print_arg_dynarray dynarray; | 299 | struct print_arg_dynarray dynarray; |
293 | }; | 300 | }; |
@@ -354,6 +361,8 @@ enum pevent_func_arg_type { | |||
354 | 361 | ||
355 | enum pevent_flag { | 362 | enum pevent_flag { |
356 | PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ | 363 | PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ |
364 | PEVENT_DISABLE_SYS_PLUGINS = 1 << 1, | ||
365 | PEVENT_DISABLE_PLUGINS = 1 << 2, | ||
357 | }; | 366 | }; |
358 | 367 | ||
359 | #define PEVENT_ERRORS \ | 368 | #define PEVENT_ERRORS \ |
@@ -410,9 +419,19 @@ enum pevent_errno { | |||
410 | 419 | ||
411 | struct plugin_list; | 420 | struct plugin_list; |
412 | 421 | ||
422 | #define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1)) | ||
423 | |||
413 | struct plugin_list *traceevent_load_plugins(struct pevent *pevent); | 424 | struct plugin_list *traceevent_load_plugins(struct pevent *pevent); |
414 | void traceevent_unload_plugins(struct plugin_list *plugin_list, | 425 | void traceevent_unload_plugins(struct plugin_list *plugin_list, |
415 | struct pevent *pevent); | 426 | struct pevent *pevent); |
427 | char **traceevent_plugin_list_options(void); | ||
428 | void traceevent_plugin_free_options_list(char **list); | ||
429 | int traceevent_plugin_add_options(const char *name, | ||
430 | struct pevent_plugin_option *options); | ||
431 | void traceevent_plugin_remove_options(struct pevent_plugin_option *options); | ||
432 | void traceevent_print_plugins(struct trace_seq *s, | ||
433 | const char *prefix, const char *suffix, | ||
434 | const struct plugin_list *list); | ||
416 | 435 | ||
417 | struct cmdline; | 436 | struct cmdline; |
418 | struct cmdline_list; | 437 | struct cmdline_list; |
diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c index 0c8bf6780e4d..136162c03af1 100644 --- a/tools/lib/traceevent/event-plugin.c +++ b/tools/lib/traceevent/event-plugin.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <stdio.h> | ||
21 | #include <string.h> | 22 | #include <string.h> |
22 | #include <dlfcn.h> | 23 | #include <dlfcn.h> |
23 | #include <stdlib.h> | 24 | #include <stdlib.h> |
@@ -30,12 +31,207 @@ | |||
30 | 31 | ||
31 | #define LOCAL_PLUGIN_DIR ".traceevent/plugins" | 32 | #define LOCAL_PLUGIN_DIR ".traceevent/plugins" |
32 | 33 | ||
34 | static struct registered_plugin_options { | ||
35 | struct registered_plugin_options *next; | ||
36 | struct pevent_plugin_option *options; | ||
37 | } *registered_options; | ||
38 | |||
39 | static struct trace_plugin_options { | ||
40 | struct trace_plugin_options *next; | ||
41 | char *plugin; | ||
42 | char *option; | ||
43 | char *value; | ||
44 | } *trace_plugin_options; | ||
45 | |||
33 | struct plugin_list { | 46 | struct plugin_list { |
34 | struct plugin_list *next; | 47 | struct plugin_list *next; |
35 | char *name; | 48 | char *name; |
36 | void *handle; | 49 | void *handle; |
37 | }; | 50 | }; |
38 | 51 | ||
52 | /** | ||
53 | * traceevent_plugin_list_options - get list of plugin options | ||
54 | * | ||
55 | * Returns an array of char strings that list the currently registered | ||
56 | * plugin options in the format of <plugin>:<option>. This list can be | ||
57 | * used by toggling the option. | ||
58 | * | ||
59 | * Returns NULL if there's no options registered. On error it returns | ||
60 | * INVALID_PLUGIN_LIST_OPTION | ||
61 | * | ||
62 | * Must be freed with traceevent_plugin_free_options_list(). | ||
63 | */ | ||
64 | char **traceevent_plugin_list_options(void) | ||
65 | { | ||
66 | struct registered_plugin_options *reg; | ||
67 | struct pevent_plugin_option *op; | ||
68 | char **list = NULL; | ||
69 | char *name; | ||
70 | int count = 0; | ||
71 | |||
72 | for (reg = registered_options; reg; reg = reg->next) { | ||
73 | for (op = reg->options; op->name; op++) { | ||
74 | char *alias = op->plugin_alias ? op->plugin_alias : op->file; | ||
75 | char **temp = list; | ||
76 | |||
77 | name = malloc(strlen(op->name) + strlen(alias) + 2); | ||
78 | if (!name) | ||
79 | goto err; | ||
80 | |||
81 | sprintf(name, "%s:%s", alias, op->name); | ||
82 | list = realloc(list, count + 2); | ||
83 | if (!list) { | ||
84 | list = temp; | ||
85 | free(name); | ||
86 | goto err; | ||
87 | } | ||
88 | list[count++] = name; | ||
89 | list[count] = NULL; | ||
90 | } | ||
91 | } | ||
92 | return list; | ||
93 | |||
94 | err: | ||
95 | while (--count >= 0) | ||
96 | free(list[count]); | ||
97 | free(list); | ||
98 | |||
99 | return INVALID_PLUGIN_LIST_OPTION; | ||
100 | } | ||
101 | |||
102 | void traceevent_plugin_free_options_list(char **list) | ||
103 | { | ||
104 | int i; | ||
105 | |||
106 | if (!list) | ||
107 | return; | ||
108 | |||
109 | if (list == INVALID_PLUGIN_LIST_OPTION) | ||
110 | return; | ||
111 | |||
112 | for (i = 0; list[i]; i++) | ||
113 | free(list[i]); | ||
114 | |||
115 | free(list); | ||
116 | } | ||
117 | |||
118 | static int | ||
119 | update_option(const char *file, struct pevent_plugin_option *option) | ||
120 | { | ||
121 | struct trace_plugin_options *op; | ||
122 | char *plugin; | ||
123 | |||
124 | if (option->plugin_alias) { | ||
125 | plugin = strdup(option->plugin_alias); | ||
126 | if (!plugin) | ||
127 | return -1; | ||
128 | } else { | ||
129 | char *p; | ||
130 | plugin = strdup(file); | ||
131 | if (!plugin) | ||
132 | return -1; | ||
133 | p = strstr(plugin, "."); | ||
134 | if (p) | ||
135 | *p = '\0'; | ||
136 | } | ||
137 | |||
138 | /* first look for named options */ | ||
139 | for (op = trace_plugin_options; op; op = op->next) { | ||
140 | if (!op->plugin) | ||
141 | continue; | ||
142 | if (strcmp(op->plugin, plugin) != 0) | ||
143 | continue; | ||
144 | if (strcmp(op->option, option->name) != 0) | ||
145 | continue; | ||
146 | |||
147 | option->value = op->value; | ||
148 | option->set ^= 1; | ||
149 | goto out; | ||
150 | } | ||
151 | |||
152 | /* first look for unnamed options */ | ||
153 | for (op = trace_plugin_options; op; op = op->next) { | ||
154 | if (op->plugin) | ||
155 | continue; | ||
156 | if (strcmp(op->option, option->name) != 0) | ||
157 | continue; | ||
158 | |||
159 | option->value = op->value; | ||
160 | option->set ^= 1; | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | out: | ||
165 | free(plugin); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * traceevent_plugin_add_options - Add a set of options by a plugin | ||
171 | * @name: The name of the plugin adding the options | ||
172 | * @options: The set of options being loaded | ||
173 | * | ||
174 | * Sets the options with the values that have been added by user. | ||
175 | */ | ||
176 | int traceevent_plugin_add_options(const char *name, | ||
177 | struct pevent_plugin_option *options) | ||
178 | { | ||
179 | struct registered_plugin_options *reg; | ||
180 | |||
181 | reg = malloc(sizeof(*reg)); | ||
182 | if (!reg) | ||
183 | return -1; | ||
184 | reg->next = registered_options; | ||
185 | reg->options = options; | ||
186 | registered_options = reg; | ||
187 | |||
188 | while (options->name) { | ||
189 | update_option(name, options); | ||
190 | options++; | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * traceevent_plugin_remove_options - remove plugin options that were registered | ||
197 | * @options: Options to removed that were registered with traceevent_plugin_add_options | ||
198 | */ | ||
199 | void traceevent_plugin_remove_options(struct pevent_plugin_option *options) | ||
200 | { | ||
201 | struct registered_plugin_options **last; | ||
202 | struct registered_plugin_options *reg; | ||
203 | |||
204 | for (last = ®istered_options; *last; last = &(*last)->next) { | ||
205 | if ((*last)->options == options) { | ||
206 | reg = *last; | ||
207 | *last = reg->next; | ||
208 | free(reg); | ||
209 | return; | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * traceevent_print_plugins - print out the list of plugins loaded | ||
216 | * @s: the trace_seq descripter to write to | ||
217 | * @prefix: The prefix string to add before listing the option name | ||
218 | * @suffix: The suffix string ot append after the option name | ||
219 | * @list: The list of plugins (usually returned by traceevent_load_plugins() | ||
220 | * | ||
221 | * Writes to the trace_seq @s the list of plugins (files) that is | ||
222 | * returned by traceevent_load_plugins(). Use @prefix and @suffix for formating: | ||
223 | * @prefix = " ", @suffix = "\n". | ||
224 | */ | ||
225 | void traceevent_print_plugins(struct trace_seq *s, | ||
226 | const char *prefix, const char *suffix, | ||
227 | const struct plugin_list *list) | ||
228 | { | ||
229 | while (list) { | ||
230 | trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix); | ||
231 | list = list->next; | ||
232 | } | ||
233 | } | ||
234 | |||
39 | static void | 235 | static void |
40 | load_plugin(struct pevent *pevent, const char *path, | 236 | load_plugin(struct pevent *pevent, const char *path, |
41 | const char *file, void *data) | 237 | const char *file, void *data) |
@@ -148,12 +344,17 @@ load_plugins(struct pevent *pevent, const char *suffix, | |||
148 | char *path; | 344 | char *path; |
149 | char *envdir; | 345 | char *envdir; |
150 | 346 | ||
347 | if (pevent->flags & PEVENT_DISABLE_PLUGINS) | ||
348 | return; | ||
349 | |||
151 | /* | 350 | /* |
152 | * If a system plugin directory was defined, | 351 | * If a system plugin directory was defined, |
153 | * check that first. | 352 | * check that first. |
154 | */ | 353 | */ |
155 | #ifdef PLUGIN_DIR | 354 | #ifdef PLUGIN_DIR |
156 | load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data); | 355 | if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS)) |
356 | load_plugins_dir(pevent, suffix, PLUGIN_DIR, | ||
357 | load_plugin, data); | ||
157 | #endif | 358 | #endif |
158 | 359 | ||
159 | /* | 360 | /* |
diff --git a/tools/lib/traceevent/plugin_function.c b/tools/lib/traceevent/plugin_function.c index 80ba4ff1fe84..a00ec190821a 100644 --- a/tools/lib/traceevent/plugin_function.c +++ b/tools/lib/traceevent/plugin_function.c | |||
@@ -33,6 +33,29 @@ static int cpus = -1; | |||
33 | 33 | ||
34 | #define STK_BLK 10 | 34 | #define STK_BLK 10 |
35 | 35 | ||
36 | struct pevent_plugin_option plugin_options[] = | ||
37 | { | ||
38 | { | ||
39 | .name = "parent", | ||
40 | .plugin_alias = "ftrace", | ||
41 | .description = | ||
42 | "Print parent of functions for function events", | ||
43 | }, | ||
44 | { | ||
45 | .name = "indent", | ||
46 | .plugin_alias = "ftrace", | ||
47 | .description = | ||
48 | "Try to show function call indents, based on parents", | ||
49 | .set = 1, | ||
50 | }, | ||
51 | { | ||
52 | .name = NULL, | ||
53 | } | ||
54 | }; | ||
55 | |||
56 | static struct pevent_plugin_option *ftrace_parent = &plugin_options[0]; | ||
57 | static struct pevent_plugin_option *ftrace_indent = &plugin_options[1]; | ||
58 | |||
36 | static void add_child(struct func_stack *stack, const char *child, int pos) | 59 | static void add_child(struct func_stack *stack, const char *child, int pos) |
37 | { | 60 | { |
38 | int i; | 61 | int i; |
@@ -119,7 +142,8 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, | |||
119 | 142 | ||
120 | parent = pevent_find_function(pevent, pfunction); | 143 | parent = pevent_find_function(pevent, pfunction); |
121 | 144 | ||
122 | index = add_and_get_index(parent, func, record->cpu); | 145 | if (parent && ftrace_indent->set) |
146 | index = add_and_get_index(parent, func, record->cpu); | ||
123 | 147 | ||
124 | trace_seq_printf(s, "%*s", index*3, ""); | 148 | trace_seq_printf(s, "%*s", index*3, ""); |
125 | 149 | ||
@@ -128,11 +152,13 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, | |||
128 | else | 152 | else |
129 | trace_seq_printf(s, "0x%llx", function); | 153 | trace_seq_printf(s, "0x%llx", function); |
130 | 154 | ||
131 | trace_seq_printf(s, " <-- "); | 155 | if (ftrace_parent->set) { |
132 | if (parent) | 156 | trace_seq_printf(s, " <-- "); |
133 | trace_seq_printf(s, "%s", parent); | 157 | if (parent) |
134 | else | 158 | trace_seq_printf(s, "%s", parent); |
135 | trace_seq_printf(s, "0x%llx", pfunction); | 159 | else |
160 | trace_seq_printf(s, "0x%llx", pfunction); | ||
161 | } | ||
136 | 162 | ||
137 | return 0; | 163 | return 0; |
138 | } | 164 | } |
@@ -141,6 +167,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) | |||
141 | { | 167 | { |
142 | pevent_register_event_handler(pevent, -1, "ftrace", "function", | 168 | pevent_register_event_handler(pevent, -1, "ftrace", "function", |
143 | function_handler, NULL); | 169 | function_handler, NULL); |
170 | |||
171 | traceevent_plugin_add_options("ftrace", plugin_options); | ||
172 | |||
144 | return 0; | 173 | return 0; |
145 | } | 174 | } |
146 | 175 | ||
@@ -157,6 +186,8 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) | |||
157 | free(fstack[i].stack); | 186 | free(fstack[i].stack); |
158 | } | 187 | } |
159 | 188 | ||
189 | traceevent_plugin_remove_options(plugin_options); | ||
190 | |||
160 | free(fstack); | 191 | free(fstack); |
161 | fstack = NULL; | 192 | fstack = NULL; |
162 | cpus = -1; | 193 | cpus = -1; |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index cefdf430d1b4..d2b59af62bc0 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -117,6 +117,22 @@ OPTIONS | |||
117 | By default, every sort keys not specified in -F will be appended | 117 | By default, every sort keys not specified in -F will be appended |
118 | automatically. | 118 | automatically. |
119 | 119 | ||
120 | If --mem-mode option is used, following sort keys are also available | ||
121 | (incompatible with --branch-stack): | ||
122 | symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline. | ||
123 | |||
124 | - symbol_daddr: name of data symbol being executed on at the time of sample | ||
125 | - dso_daddr: name of library or module containing the data being executed | ||
126 | on at the time of sample | ||
127 | - locked: whether the bus was locked at the time of sample | ||
128 | - tlb: type of tlb access for the data at the time of sample | ||
129 | - mem: type of memory access for the data at the time of sample | ||
130 | - snoop: type of snoop (if any) for the data at the time of sample | ||
131 | - dcacheline: the cacheline the data address is on at the time of sample | ||
132 | |||
133 | And default sort keys are changed to local_weight, mem, sym, dso, | ||
134 | symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'. | ||
135 | |||
120 | -p:: | 136 | -p:: |
121 | --parent=<regex>:: | 137 | --parent=<regex>:: |
122 | A regex filter to identify parent. The parent is a caller of this | 138 | A regex filter to identify parent. The parent is a caller of this |
@@ -260,6 +276,13 @@ OPTIONS | |||
260 | Demangle symbol names to human readable form. It's enabled by default, | 276 | Demangle symbol names to human readable form. It's enabled by default, |
261 | disable with --no-demangle. | 277 | disable with --no-demangle. |
262 | 278 | ||
279 | --mem-mode:: | ||
280 | Use the data addresses of samples in addition to instruction addresses | ||
281 | to build the histograms. To generate meaningful output, the perf.data | ||
282 | file must have been obtained using perf record -d -W and using a | ||
283 | special event -e cpu/mem-loads/ or -e cpu/mem-stores/. See | ||
284 | 'perf mem' for simpler access. | ||
285 | |||
263 | --percent-limit:: | 286 | --percent-limit:: |
264 | Do not show entries which have an overhead under that percent. | 287 | Do not show entries which have an overhead under that percent. |
265 | (Default: 0). | 288 | (Default: 0). |
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index bc5990c33dc0..5e0f986dff38 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt | |||
@@ -43,27 +43,6 @@ TIMECHART OPTIONS | |||
43 | 43 | ||
44 | --symfs=<directory>:: | 44 | --symfs=<directory>:: |
45 | Look for files with symbols relative to this directory. | 45 | Look for files with symbols relative to this directory. |
46 | |||
47 | EXAMPLES | ||
48 | -------- | ||
49 | |||
50 | $ perf timechart record git pull | ||
51 | |||
52 | [ perf record: Woken up 13 times to write data ] | ||
53 | [ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ] | ||
54 | |||
55 | $ perf timechart | ||
56 | |||
57 | Written 10.2 seconds of trace to output.svg. | ||
58 | |||
59 | Record system-wide timechart: | ||
60 | |||
61 | $ perf timechart record | ||
62 | |||
63 | then generate timechart and highlight 'gcc' tasks: | ||
64 | |||
65 | $ perf timechart --highlight gcc | ||
66 | |||
67 | -n:: | 46 | -n:: |
68 | --proc-num:: | 47 | --proc-num:: |
69 | Print task info for at least given number of tasks. | 48 | Print task info for at least given number of tasks. |
@@ -88,6 +67,26 @@ RECORD OPTIONS | |||
88 | --callchain:: | 67 | --callchain:: |
89 | Do call-graph (stack chain/backtrace) recording | 68 | Do call-graph (stack chain/backtrace) recording |
90 | 69 | ||
70 | EXAMPLES | ||
71 | -------- | ||
72 | |||
73 | $ perf timechart record git pull | ||
74 | |||
75 | [ perf record: Woken up 13 times to write data ] | ||
76 | [ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ] | ||
77 | |||
78 | $ perf timechart | ||
79 | |||
80 | Written 10.2 seconds of trace to output.svg. | ||
81 | |||
82 | Record system-wide timechart: | ||
83 | |||
84 | $ perf timechart record | ||
85 | |||
86 | then generate timechart and highlight 'gcc' tasks: | ||
87 | |||
88 | $ perf timechart --highlight gcc | ||
89 | |||
91 | SEE ALSO | 90 | SEE ALSO |
92 | -------- | 91 | -------- |
93 | linkperf:perf-record[1] | 92 | linkperf:perf-record[1] |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index ae20edfcc3f7..9670a16fa577 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -819,15 +819,15 @@ TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol | |||
819 | TAG_FILES= ../../include/uapi/linux/perf_event.h | 819 | TAG_FILES= ../../include/uapi/linux/perf_event.h |
820 | 820 | ||
821 | TAGS: | 821 | TAGS: |
822 | $(RM) TAGS | 822 | $(QUIET_GEN)$(RM) TAGS; \ |
823 | $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES) | 823 | $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES) |
824 | 824 | ||
825 | tags: | 825 | tags: |
826 | $(RM) tags | 826 | $(QUIET_GEN)$(RM) tags; \ |
827 | $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES) | 827 | $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES) |
828 | 828 | ||
829 | cscope: | 829 | cscope: |
830 | $(RM) cscope* | 830 | $(QUIET_GEN)$(RM) cscope*; \ |
831 | $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES) | 831 | $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES) |
832 | 832 | ||
833 | ### Detect prefix changes | 833 | ### Detect prefix changes |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6a3af0013d68..16c7c11ad06e 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -72,7 +72,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool, | |||
72 | if (ret) | 72 | if (ret) |
73 | return ret; | 73 | return ret; |
74 | 74 | ||
75 | if (&inject->output.is_pipe) | 75 | if (!inject->output.is_pipe) |
76 | return 0; | 76 | return 0; |
77 | 77 | ||
78 | return perf_event__repipe_synth(tool, event); | 78 | return perf_event__repipe_synth(tool, event); |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index cdcd4eb3a57d..c63fa2925075 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -288,6 +288,13 @@ static void cleanup_params(void) | |||
288 | memset(¶ms, 0, sizeof(params)); | 288 | memset(¶ms, 0, sizeof(params)); |
289 | } | 289 | } |
290 | 290 | ||
291 | static void pr_err_with_code(const char *msg, int err) | ||
292 | { | ||
293 | pr_err("%s", msg); | ||
294 | pr_debug(" Reason: %s (Code: %d)", strerror(-err), err); | ||
295 | pr_err("\n"); | ||
296 | } | ||
297 | |||
291 | static int | 298 | static int |
292 | __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | 299 | __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) |
293 | { | 300 | { |
@@ -379,7 +386,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
379 | } | 386 | } |
380 | ret = parse_probe_event_argv(argc, argv); | 387 | ret = parse_probe_event_argv(argc, argv); |
381 | if (ret < 0) { | 388 | if (ret < 0) { |
382 | pr_err(" Error: Parse Error. (%d)\n", ret); | 389 | pr_err_with_code(" Error: Command Parse Error.", ret); |
383 | return ret; | 390 | return ret; |
384 | } | 391 | } |
385 | } | 392 | } |
@@ -419,8 +426,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
419 | } | 426 | } |
420 | ret = show_perf_probe_events(); | 427 | ret = show_perf_probe_events(); |
421 | if (ret < 0) | 428 | if (ret < 0) |
422 | pr_err(" Error: Failed to show event list. (%d)\n", | 429 | pr_err_with_code(" Error: Failed to show event list.", ret); |
423 | ret); | ||
424 | return ret; | 430 | return ret; |
425 | } | 431 | } |
426 | if (params.show_funcs) { | 432 | if (params.show_funcs) { |
@@ -445,8 +451,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
445 | strfilter__delete(params.filter); | 451 | strfilter__delete(params.filter); |
446 | params.filter = NULL; | 452 | params.filter = NULL; |
447 | if (ret < 0) | 453 | if (ret < 0) |
448 | pr_err(" Error: Failed to show functions." | 454 | pr_err_with_code(" Error: Failed to show functions.", ret); |
449 | " (%d)\n", ret); | ||
450 | return ret; | 455 | return ret; |
451 | } | 456 | } |
452 | 457 | ||
@@ -464,7 +469,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
464 | 469 | ||
465 | ret = show_line_range(¶ms.line_range, params.target); | 470 | ret = show_line_range(¶ms.line_range, params.target); |
466 | if (ret < 0) | 471 | if (ret < 0) |
467 | pr_err(" Error: Failed to show lines. (%d)\n", ret); | 472 | pr_err_with_code(" Error: Failed to show lines.", ret); |
468 | return ret; | 473 | return ret; |
469 | } | 474 | } |
470 | if (params.show_vars) { | 475 | if (params.show_vars) { |
@@ -485,7 +490,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
485 | strfilter__delete(params.filter); | 490 | strfilter__delete(params.filter); |
486 | params.filter = NULL; | 491 | params.filter = NULL; |
487 | if (ret < 0) | 492 | if (ret < 0) |
488 | pr_err(" Error: Failed to show vars. (%d)\n", ret); | 493 | pr_err_with_code(" Error: Failed to show vars.", ret); |
489 | return ret; | 494 | return ret; |
490 | } | 495 | } |
491 | #endif | 496 | #endif |
@@ -493,7 +498,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
493 | if (params.dellist) { | 498 | if (params.dellist) { |
494 | ret = del_perf_probe_events(params.dellist); | 499 | ret = del_perf_probe_events(params.dellist); |
495 | if (ret < 0) { | 500 | if (ret < 0) { |
496 | pr_err(" Error: Failed to delete events. (%d)\n", ret); | 501 | pr_err_with_code(" Error: Failed to delete events.", ret); |
497 | return ret; | 502 | return ret; |
498 | } | 503 | } |
499 | } | 504 | } |
@@ -504,7 +509,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
504 | params.target, | 509 | params.target, |
505 | params.force_add); | 510 | params.force_add); |
506 | if (ret < 0) { | 511 | if (ret < 0) { |
507 | pr_err(" Error: Failed to add events. (%d)\n", ret); | 512 | pr_err_with_code(" Error: Failed to add events.", ret); |
508 | return ret; | 513 | return ret; |
509 | } | 514 | } |
510 | } | 515 | } |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 4f100b54ba8b..f30ac5e5d271 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -299,7 +299,11 @@ else | |||
299 | NO_LIBUNWIND := 1 | 299 | NO_LIBUNWIND := 1 |
300 | NO_LIBDW_DWARF_UNWIND := 1 | 300 | NO_LIBDW_DWARF_UNWIND := 1 |
301 | else | 301 | else |
302 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | 302 | ifneq ($(filter s% -static%,$(LDFLAGS),),) |
303 | msg := $(error No static glibc found, please install glibc-static); | ||
304 | else | ||
305 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]); | ||
306 | endif | ||
303 | endif | 307 | endif |
304 | else | 308 | else |
305 | ifndef NO_LIBDW_DWARF_UNWIND | 309 | ifndef NO_LIBDW_DWARF_UNWIND |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 78f7b920e548..95c58fc15284 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -458,6 +458,7 @@ int main(int argc, const char **argv) | |||
458 | 458 | ||
459 | /* The page_size is placed in util object. */ | 459 | /* The page_size is placed in util object. */ |
460 | page_size = sysconf(_SC_PAGE_SIZE); | 460 | page_size = sysconf(_SC_PAGE_SIZE); |
461 | cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); | ||
461 | 462 | ||
462 | cmd = perf_extract_argv0_path(argv[0]); | 463 | cmd = perf_extract_argv0_path(argv[0]); |
463 | if (!cmd) | 464 | if (!cmd) |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 802e3cd50f6f..6f8b01bc6033 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -3,6 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Builtin regression testing command: ever growing number of sanity tests | 4 | * Builtin regression testing command: ever growing number of sanity tests |
5 | */ | 5 | */ |
6 | #include <unistd.h> | ||
7 | #include <string.h> | ||
6 | #include "builtin.h" | 8 | #include "builtin.h" |
7 | #include "intlist.h" | 9 | #include "intlist.h" |
8 | #include "tests.h" | 10 | #include "tests.h" |
@@ -50,10 +52,18 @@ static struct test { | |||
50 | .func = test__pmu, | 52 | .func = test__pmu, |
51 | }, | 53 | }, |
52 | { | 54 | { |
53 | .desc = "Test dso data interface", | 55 | .desc = "Test dso data read", |
54 | .func = test__dso_data, | 56 | .func = test__dso_data, |
55 | }, | 57 | }, |
56 | { | 58 | { |
59 | .desc = "Test dso data cache", | ||
60 | .func = test__dso_data_cache, | ||
61 | }, | ||
62 | { | ||
63 | .desc = "Test dso data reopen", | ||
64 | .func = test__dso_data_reopen, | ||
65 | }, | ||
66 | { | ||
57 | .desc = "roundtrip evsel->name check", | 67 | .desc = "roundtrip evsel->name check", |
58 | .func = test__perf_evsel__roundtrip_name_test, | 68 | .func = test__perf_evsel__roundtrip_name_test, |
59 | }, | 69 | }, |
@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) | |||
172 | return false; | 182 | return false; |
173 | } | 183 | } |
174 | 184 | ||
185 | static int run_test(struct test *test) | ||
186 | { | ||
187 | int status, err = -1, child = fork(); | ||
188 | |||
189 | if (child < 0) { | ||
190 | pr_err("failed to fork test: %s\n", strerror(errno)); | ||
191 | return -1; | ||
192 | } | ||
193 | |||
194 | if (!child) { | ||
195 | pr_debug("test child forked, pid %d\n", getpid()); | ||
196 | err = test->func(); | ||
197 | exit(err); | ||
198 | } | ||
199 | |||
200 | wait(&status); | ||
201 | |||
202 | if (WIFEXITED(status)) { | ||
203 | err = WEXITSTATUS(status); | ||
204 | pr_debug("test child finished with %d\n", err); | ||
205 | } else if (WIFSIGNALED(status)) { | ||
206 | err = -1; | ||
207 | pr_debug("test child interrupted\n"); | ||
208 | } | ||
209 | |||
210 | return err; | ||
211 | } | ||
212 | |||
175 | static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) | 213 | static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) |
176 | { | 214 | { |
177 | int i = 0; | 215 | int i = 0; |
@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) | |||
200 | } | 238 | } |
201 | 239 | ||
202 | pr_debug("\n--- start ---\n"); | 240 | pr_debug("\n--- start ---\n"); |
203 | err = tests[curr].func(); | 241 | err = run_test(&tests[curr]); |
204 | pr_debug("---- end ----\n%s:", tests[curr].desc); | 242 | pr_debug("---- end ----\n%s:", tests[curr].desc); |
205 | 243 | ||
206 | switch (err) { | 244 | switch (err) { |
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 3e6cb171e3d3..630808cd7cc2 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c | |||
@@ -1,22 +1,27 @@ | |||
1 | #include "util.h" | ||
2 | |||
3 | #include <stdlib.h> | 1 | #include <stdlib.h> |
4 | #include <linux/types.h> | 2 | #include <linux/types.h> |
5 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
6 | #include <fcntl.h> | 4 | #include <fcntl.h> |
7 | #include <string.h> | 5 | #include <string.h> |
8 | 6 | #include <sys/time.h> | |
7 | #include <sys/resource.h> | ||
8 | #include <api/fs/fs.h> | ||
9 | #include "util.h" | ||
9 | #include "machine.h" | 10 | #include "machine.h" |
10 | #include "symbol.h" | 11 | #include "symbol.h" |
11 | #include "tests.h" | 12 | #include "tests.h" |
12 | 13 | ||
13 | static char *test_file(int size) | 14 | static char *test_file(int size) |
14 | { | 15 | { |
15 | static char buf_templ[] = "/tmp/test-XXXXXX"; | 16 | #define TEMPL "/tmp/perf-test-XXXXXX" |
17 | static char buf_templ[sizeof(TEMPL)]; | ||
16 | char *templ = buf_templ; | 18 | char *templ = buf_templ; |
17 | int fd, i; | 19 | int fd, i; |
18 | unsigned char *buf; | 20 | unsigned char *buf; |
19 | 21 | ||
22 | strcpy(buf_templ, TEMPL); | ||
23 | #undef TEMPL | ||
24 | |||
20 | fd = mkstemp(templ); | 25 | fd = mkstemp(templ); |
21 | if (fd < 0) { | 26 | if (fd < 0) { |
22 | perror("mkstemp failed"); | 27 | perror("mkstemp failed"); |
@@ -150,3 +155,204 @@ int test__dso_data(void) | |||
150 | unlink(file); | 155 | unlink(file); |
151 | return 0; | 156 | return 0; |
152 | } | 157 | } |
158 | |||
159 | static long open_files_cnt(void) | ||
160 | { | ||
161 | char path[PATH_MAX]; | ||
162 | struct dirent *dent; | ||
163 | DIR *dir; | ||
164 | long nr = 0; | ||
165 | |||
166 | scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint()); | ||
167 | pr_debug("fd path: %s\n", path); | ||
168 | |||
169 | dir = opendir(path); | ||
170 | TEST_ASSERT_VAL("failed to open fd directory", dir); | ||
171 | |||
172 | while ((dent = readdir(dir)) != NULL) { | ||
173 | if (!strcmp(dent->d_name, ".") || | ||
174 | !strcmp(dent->d_name, "..")) | ||
175 | continue; | ||
176 | |||
177 | nr++; | ||
178 | } | ||
179 | |||
180 | closedir(dir); | ||
181 | return nr - 1; | ||
182 | } | ||
183 | |||
184 | static struct dso **dsos; | ||
185 | |||
186 | static int dsos__create(int cnt, int size) | ||
187 | { | ||
188 | int i; | ||
189 | |||
190 | dsos = malloc(sizeof(dsos) * cnt); | ||
191 | TEST_ASSERT_VAL("failed to alloc dsos array", dsos); | ||
192 | |||
193 | for (i = 0; i < cnt; i++) { | ||
194 | char *file; | ||
195 | |||
196 | file = test_file(size); | ||
197 | TEST_ASSERT_VAL("failed to get dso file", file); | ||
198 | |||
199 | dsos[i] = dso__new(file); | ||
200 | TEST_ASSERT_VAL("failed to get dso", dsos[i]); | ||
201 | } | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void dsos__delete(int cnt) | ||
207 | { | ||
208 | int i; | ||
209 | |||
210 | for (i = 0; i < cnt; i++) { | ||
211 | struct dso *dso = dsos[i]; | ||
212 | |||
213 | unlink(dso->name); | ||
214 | dso__delete(dso); | ||
215 | } | ||
216 | |||
217 | free(dsos); | ||
218 | } | ||
219 | |||
220 | static int set_fd_limit(int n) | ||
221 | { | ||
222 | struct rlimit rlim; | ||
223 | |||
224 | if (getrlimit(RLIMIT_NOFILE, &rlim)) | ||
225 | return -1; | ||
226 | |||
227 | pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n); | ||
228 | |||
229 | rlim.rlim_cur = n; | ||
230 | return setrlimit(RLIMIT_NOFILE, &rlim); | ||
231 | } | ||
232 | |||
233 | int test__dso_data_cache(void) | ||
234 | { | ||
235 | struct machine machine; | ||
236 | long nr_end, nr = open_files_cnt(); | ||
237 | int dso_cnt, limit, i, fd; | ||
238 | |||
239 | memset(&machine, 0, sizeof(machine)); | ||
240 | |||
241 | /* set as system limit */ | ||
242 | limit = nr * 4; | ||
243 | TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit)); | ||
244 | |||
245 | /* and this is now our dso open FDs limit + 1 extra */ | ||
246 | dso_cnt = limit / 2 + 1; | ||
247 | TEST_ASSERT_VAL("failed to create dsos\n", | ||
248 | !dsos__create(dso_cnt, TEST_FILE_SIZE)); | ||
249 | |||
250 | for (i = 0; i < (dso_cnt - 1); i++) { | ||
251 | struct dso *dso = dsos[i]; | ||
252 | |||
253 | /* | ||
254 | * Open dsos via dso__data_fd or dso__data_read_offset. | ||
255 | * Both opens the data file and keep it open. | ||
256 | */ | ||
257 | if (i % 2) { | ||
258 | fd = dso__data_fd(dso, &machine); | ||
259 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
260 | } else { | ||
261 | #define BUFSIZE 10 | ||
262 | u8 buf[BUFSIZE]; | ||
263 | ssize_t n; | ||
264 | |||
265 | n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE); | ||
266 | TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | /* open +1 dso over the allowed limit */ | ||
271 | fd = dso__data_fd(dsos[i], &machine); | ||
272 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
273 | |||
274 | /* should force the first one to be closed */ | ||
275 | TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1); | ||
276 | |||
277 | /* cleanup everything */ | ||
278 | dsos__delete(dso_cnt); | ||
279 | |||
280 | /* Make sure we did not leak any file descriptor. */ | ||
281 | nr_end = open_files_cnt(); | ||
282 | pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); | ||
283 | TEST_ASSERT_VAL("failed leadking files", nr == nr_end); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | int test__dso_data_reopen(void) | ||
288 | { | ||
289 | struct machine machine; | ||
290 | long nr_end, nr = open_files_cnt(); | ||
291 | int fd, fd_extra; | ||
292 | |||
293 | #define dso_0 (dsos[0]) | ||
294 | #define dso_1 (dsos[1]) | ||
295 | #define dso_2 (dsos[2]) | ||
296 | |||
297 | memset(&machine, 0, sizeof(machine)); | ||
298 | |||
299 | /* | ||
300 | * Test scenario: | ||
301 | * - create 3 dso objects | ||
302 | * - set process file descriptor limit to current | ||
303 | * files count + 3 | ||
304 | * - test that the first dso gets closed when we | ||
305 | * reach the files count limit | ||
306 | */ | ||
307 | |||
308 | /* Make sure we are able to open 3 fds anyway */ | ||
309 | TEST_ASSERT_VAL("failed to set file limit", | ||
310 | !set_fd_limit((nr + 3))); | ||
311 | |||
312 | TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE)); | ||
313 | |||
314 | /* open dso_0 */ | ||
315 | fd = dso__data_fd(dso_0, &machine); | ||
316 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
317 | |||
318 | /* open dso_1 */ | ||
319 | fd = dso__data_fd(dso_1, &machine); | ||
320 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
321 | |||
322 | /* | ||
323 | * open extra file descriptor and we just | ||
324 | * reached the files count limit | ||
325 | */ | ||
326 | fd_extra = open("/dev/null", O_RDONLY); | ||
327 | TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0); | ||
328 | |||
329 | /* open dso_2 */ | ||
330 | fd = dso__data_fd(dso_2, &machine); | ||
331 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
332 | |||
333 | /* | ||
334 | * dso_0 should get closed, because we reached | ||
335 | * the file descriptor limit | ||
336 | */ | ||
337 | TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1); | ||
338 | |||
339 | /* open dso_0 */ | ||
340 | fd = dso__data_fd(dso_0, &machine); | ||
341 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
342 | |||
343 | /* | ||
344 | * dso_1 should get closed, because we reached | ||
345 | * the file descriptor limit | ||
346 | */ | ||
347 | TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1); | ||
348 | |||
349 | /* cleanup everything */ | ||
350 | close(fd_extra); | ||
351 | dsos__delete(3); | ||
352 | |||
353 | /* Make sure we did not leak any file descriptor. */ | ||
354 | nr_end = open_files_cnt(); | ||
355 | pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); | ||
356 | TEST_ASSERT_VAL("failed leadking files", nr == nr_end); | ||
357 | return 0; | ||
358 | } | ||
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 108f0cd49f4e..96adb730b744 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -15,7 +15,7 @@ static int mmap_handler(struct perf_tool *tool __maybe_unused, | |||
15 | struct perf_sample *sample __maybe_unused, | 15 | struct perf_sample *sample __maybe_unused, |
16 | struct machine *machine) | 16 | struct machine *machine) |
17 | { | 17 | { |
18 | return machine__process_mmap_event(machine, event, NULL); | 18 | return machine__process_mmap2_event(machine, event, NULL); |
19 | } | 19 | } |
20 | 20 | ||
21 | static int init_live_machine(struct machine *machine) | 21 | static int init_live_machine(struct machine *machine) |
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 2f92d6e7ee00..69a71ff84e01 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
@@ -205,8 +205,7 @@ $(run): | |||
205 | ( eval $$cmd ) >> $@ 2>&1; \ | 205 | ( eval $$cmd ) >> $@ 2>&1; \ |
206 | echo " test: $(call test,$@)" >> $@ 2>&1; \ | 206 | echo " test: $(call test,$@)" >> $@ 2>&1; \ |
207 | $(call test,$@) && \ | 207 | $(call test,$@) && \ |
208 | rm -f $@ \ | 208 | rm -rf $@ $$TMP_DEST || (cat $@ ; false) |
209 | rm -rf $$TMP_DEST | ||
210 | 209 | ||
211 | $(run_O): | 210 | $(run_O): |
212 | $(call clean) | 211 | $(call clean) |
@@ -217,9 +216,7 @@ $(run_O): | |||
217 | ( eval $$cmd ) >> $@ 2>&1 && \ | 216 | ( eval $$cmd ) >> $@ 2>&1 && \ |
218 | echo " test: $(call test_O,$@)" >> $@ 2>&1; \ | 217 | echo " test: $(call test_O,$@)" >> $@ 2>&1; \ |
219 | $(call test_O,$@) && \ | 218 | $(call test_O,$@) && \ |
220 | rm -f $@ && \ | 219 | rm -rf $@ $$TMP_O $$TMP_DEST || (cat $@ ; false) |
221 | rm -rf $$TMP_O \ | ||
222 | rm -rf $$TMP_DEST | ||
223 | 220 | ||
224 | tarpkg: | 221 | tarpkg: |
225 | @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ | 222 | @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ |
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 022bb68fd9c7..ed64790a395f 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void); | |||
28 | int test__pmu(void); | 28 | int test__pmu(void); |
29 | int test__attr(void); | 29 | int test__attr(void); |
30 | int test__dso_data(void); | 30 | int test__dso_data(void); |
31 | int test__dso_data_cache(void); | ||
32 | int test__dso_data_reopen(void); | ||
31 | int test__parse_events(void); | 33 | int test__parse_events(void); |
32 | int test__hists_link(void); | 34 | int test__hists_link(void); |
33 | int test__python_use(void); | 35 | int test__python_use(void); |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 64453d63b971..819f10414f08 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -1,3 +1,6 @@ | |||
1 | #include <asm/bug.h> | ||
2 | #include <sys/time.h> | ||
3 | #include <sys/resource.h> | ||
1 | #include "symbol.h" | 4 | #include "symbol.h" |
2 | #include "dso.h" | 5 | #include "dso.h" |
3 | #include "machine.h" | 6 | #include "machine.h" |
@@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
136 | return ret; | 139 | return ret; |
137 | } | 140 | } |
138 | 141 | ||
139 | static int open_dso(struct dso *dso, struct machine *machine) | 142 | /* |
143 | * Global list of open DSOs and the counter. | ||
144 | */ | ||
145 | static LIST_HEAD(dso__data_open); | ||
146 | static long dso__data_open_cnt; | ||
147 | |||
148 | static void dso__list_add(struct dso *dso) | ||
149 | { | ||
150 | list_add_tail(&dso->data.open_entry, &dso__data_open); | ||
151 | dso__data_open_cnt++; | ||
152 | } | ||
153 | |||
154 | static void dso__list_del(struct dso *dso) | ||
155 | { | ||
156 | list_del(&dso->data.open_entry); | ||
157 | WARN_ONCE(dso__data_open_cnt <= 0, | ||
158 | "DSO data fd counter out of bounds."); | ||
159 | dso__data_open_cnt--; | ||
160 | } | ||
161 | |||
162 | static void close_first_dso(void); | ||
163 | |||
164 | static int do_open(char *name) | ||
165 | { | ||
166 | int fd; | ||
167 | |||
168 | do { | ||
169 | fd = open(name, O_RDONLY); | ||
170 | if (fd >= 0) | ||
171 | return fd; | ||
172 | |||
173 | pr_debug("dso open failed, mmap: %s\n", strerror(errno)); | ||
174 | if (!dso__data_open_cnt || errno != EMFILE) | ||
175 | break; | ||
176 | |||
177 | close_first_dso(); | ||
178 | } while (1); | ||
179 | |||
180 | return -1; | ||
181 | } | ||
182 | |||
183 | static int __open_dso(struct dso *dso, struct machine *machine) | ||
140 | { | 184 | { |
141 | int fd; | 185 | int fd; |
142 | char *root_dir = (char *)""; | 186 | char *root_dir = (char *)""; |
@@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine) | |||
154 | return -EINVAL; | 198 | return -EINVAL; |
155 | } | 199 | } |
156 | 200 | ||
157 | fd = open(name, O_RDONLY); | 201 | fd = do_open(name); |
158 | free(name); | 202 | free(name); |
159 | return fd; | 203 | return fd; |
160 | } | 204 | } |
161 | 205 | ||
206 | static void check_data_close(void); | ||
207 | |||
208 | /** | ||
209 | * dso_close - Open DSO data file | ||
210 | * @dso: dso object | ||
211 | * | ||
212 | * Open @dso's data file descriptor and updates | ||
213 | * list/count of open DSO objects. | ||
214 | */ | ||
215 | static int open_dso(struct dso *dso, struct machine *machine) | ||
216 | { | ||
217 | int fd = __open_dso(dso, machine); | ||
218 | |||
219 | if (fd > 0) { | ||
220 | dso__list_add(dso); | ||
221 | /* | ||
222 | * Check if we crossed the allowed number | ||
223 | * of opened DSOs and close one if needed. | ||
224 | */ | ||
225 | check_data_close(); | ||
226 | } | ||
227 | |||
228 | return fd; | ||
229 | } | ||
230 | |||
231 | static void close_data_fd(struct dso *dso) | ||
232 | { | ||
233 | if (dso->data.fd >= 0) { | ||
234 | close(dso->data.fd); | ||
235 | dso->data.fd = -1; | ||
236 | dso->data.file_size = 0; | ||
237 | dso__list_del(dso); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * dso_close - Close DSO data file | ||
243 | * @dso: dso object | ||
244 | * | ||
245 | * Close @dso's data file descriptor and updates | ||
246 | * list/count of open DSO objects. | ||
247 | */ | ||
248 | static void close_dso(struct dso *dso) | ||
249 | { | ||
250 | close_data_fd(dso); | ||
251 | } | ||
252 | |||
253 | static void close_first_dso(void) | ||
254 | { | ||
255 | struct dso *dso; | ||
256 | |||
257 | dso = list_first_entry(&dso__data_open, struct dso, data.open_entry); | ||
258 | close_dso(dso); | ||
259 | } | ||
260 | |||
261 | static rlim_t get_fd_limit(void) | ||
262 | { | ||
263 | struct rlimit l; | ||
264 | rlim_t limit = 0; | ||
265 | |||
266 | /* Allow half of the current open fd limit. */ | ||
267 | if (getrlimit(RLIMIT_NOFILE, &l) == 0) { | ||
268 | if (l.rlim_cur == RLIM_INFINITY) | ||
269 | limit = l.rlim_cur; | ||
270 | else | ||
271 | limit = l.rlim_cur / 2; | ||
272 | } else { | ||
273 | pr_err("failed to get fd limit\n"); | ||
274 | limit = 1; | ||
275 | } | ||
276 | |||
277 | return limit; | ||
278 | } | ||
279 | |||
280 | static bool may_cache_fd(void) | ||
281 | { | ||
282 | static rlim_t limit; | ||
283 | |||
284 | if (!limit) | ||
285 | limit = get_fd_limit(); | ||
286 | |||
287 | if (limit == RLIM_INFINITY) | ||
288 | return true; | ||
289 | |||
290 | return limit > (rlim_t) dso__data_open_cnt; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Check and close LRU dso if we crossed allowed limit | ||
295 | * for opened dso file descriptors. The limit is half | ||
296 | * of the RLIMIT_NOFILE files opened. | ||
297 | */ | ||
298 | static void check_data_close(void) | ||
299 | { | ||
300 | bool cache_fd = may_cache_fd(); | ||
301 | |||
302 | if (!cache_fd) | ||
303 | close_first_dso(); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * dso__data_close - Close DSO data file | ||
308 | * @dso: dso object | ||
309 | * | ||
310 | * External interface to close @dso's data file descriptor. | ||
311 | */ | ||
312 | void dso__data_close(struct dso *dso) | ||
313 | { | ||
314 | close_dso(dso); | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * dso__data_fd - Get dso's data file descriptor | ||
319 | * @dso: dso object | ||
320 | * @machine: machine object | ||
321 | * | ||
322 | * External interface to find dso's file, open it and | ||
323 | * returns file descriptor. | ||
324 | */ | ||
162 | int dso__data_fd(struct dso *dso, struct machine *machine) | 325 | int dso__data_fd(struct dso *dso, struct machine *machine) |
163 | { | 326 | { |
164 | enum dso_binary_type binary_type_data[] = { | 327 | enum dso_binary_type binary_type_data[] = { |
@@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine) | |||
168 | }; | 331 | }; |
169 | int i = 0; | 332 | int i = 0; |
170 | 333 | ||
171 | if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) | 334 | if (dso->data.fd >= 0) |
172 | return open_dso(dso, machine); | 335 | return dso->data.fd; |
336 | |||
337 | if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { | ||
338 | dso->data.fd = open_dso(dso, machine); | ||
339 | return dso->data.fd; | ||
340 | } | ||
173 | 341 | ||
174 | do { | 342 | do { |
175 | int fd; | 343 | int fd; |
@@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine) | |||
178 | 346 | ||
179 | fd = open_dso(dso, machine); | 347 | fd = open_dso(dso, machine); |
180 | if (fd >= 0) | 348 | if (fd >= 0) |
181 | return fd; | 349 | return dso->data.fd = fd; |
182 | 350 | ||
183 | } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); | 351 | } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); |
184 | 352 | ||
@@ -260,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, | |||
260 | } | 428 | } |
261 | 429 | ||
262 | static ssize_t | 430 | static ssize_t |
263 | dso_cache__read(struct dso *dso, struct machine *machine, | 431 | dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) |
264 | u64 offset, u8 *data, ssize_t size) | ||
265 | { | 432 | { |
266 | struct dso_cache *cache; | 433 | struct dso_cache *cache; |
267 | ssize_t ret; | 434 | ssize_t ret; |
268 | int fd; | ||
269 | |||
270 | fd = dso__data_fd(dso, machine); | ||
271 | if (fd < 0) | ||
272 | return -1; | ||
273 | 435 | ||
274 | do { | 436 | do { |
275 | u64 cache_offset; | 437 | u64 cache_offset; |
@@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine, | |||
283 | cache_offset = offset & DSO__DATA_CACHE_MASK; | 445 | cache_offset = offset & DSO__DATA_CACHE_MASK; |
284 | ret = -EINVAL; | 446 | ret = -EINVAL; |
285 | 447 | ||
286 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | 448 | if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET)) |
287 | break; | 449 | break; |
288 | 450 | ||
289 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | 451 | ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE); |
290 | if (ret <= 0) | 452 | if (ret <= 0) |
291 | break; | 453 | break; |
292 | 454 | ||
293 | cache->offset = cache_offset; | 455 | cache->offset = cache_offset; |
294 | cache->size = ret; | 456 | cache->size = ret; |
295 | dso_cache__insert(&dso->cache, cache); | 457 | dso_cache__insert(&dso->data.cache, cache); |
296 | 458 | ||
297 | ret = dso_cache__memcpy(cache, offset, data, size); | 459 | ret = dso_cache__memcpy(cache, offset, data, size); |
298 | 460 | ||
@@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine, | |||
301 | if (ret <= 0) | 463 | if (ret <= 0) |
302 | free(cache); | 464 | free(cache); |
303 | 465 | ||
304 | close(fd); | ||
305 | return ret; | 466 | return ret; |
306 | } | 467 | } |
307 | 468 | ||
308 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | 469 | static ssize_t dso_cache_read(struct dso *dso, u64 offset, |
309 | u64 offset, u8 *data, ssize_t size) | 470 | u8 *data, ssize_t size) |
310 | { | 471 | { |
311 | struct dso_cache *cache; | 472 | struct dso_cache *cache; |
312 | 473 | ||
313 | cache = dso_cache__find(&dso->cache, offset); | 474 | cache = dso_cache__find(&dso->data.cache, offset); |
314 | if (cache) | 475 | if (cache) |
315 | return dso_cache__memcpy(cache, offset, data, size); | 476 | return dso_cache__memcpy(cache, offset, data, size); |
316 | else | 477 | else |
317 | return dso_cache__read(dso, machine, offset, data, size); | 478 | return dso_cache__read(dso, offset, data, size); |
318 | } | 479 | } |
319 | 480 | ||
320 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | 481 | /* |
321 | u64 offset, u8 *data, ssize_t size) | 482 | * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks |
483 | * in the rb_tree. Any read to already cached data is served | ||
484 | * by cached data. | ||
485 | */ | ||
486 | static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) | ||
322 | { | 487 | { |
323 | ssize_t r = 0; | 488 | ssize_t r = 0; |
324 | u8 *p = data; | 489 | u8 *p = data; |
@@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | |||
326 | do { | 491 | do { |
327 | ssize_t ret; | 492 | ssize_t ret; |
328 | 493 | ||
329 | ret = dso_cache_read(dso, machine, offset, p, size); | 494 | ret = dso_cache_read(dso, offset, p, size); |
330 | if (ret < 0) | 495 | if (ret < 0) |
331 | return ret; | 496 | return ret; |
332 | 497 | ||
@@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | |||
346 | return r; | 511 | return r; |
347 | } | 512 | } |
348 | 513 | ||
514 | static int data_file_size(struct dso *dso) | ||
515 | { | ||
516 | struct stat st; | ||
517 | |||
518 | if (!dso->data.file_size) { | ||
519 | if (fstat(dso->data.fd, &st)) { | ||
520 | pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); | ||
521 | return -1; | ||
522 | } | ||
523 | dso->data.file_size = st.st_size; | ||
524 | } | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | static ssize_t data_read_offset(struct dso *dso, u64 offset, | ||
530 | u8 *data, ssize_t size) | ||
531 | { | ||
532 | if (data_file_size(dso)) | ||
533 | return -1; | ||
534 | |||
535 | /* Check the offset sanity. */ | ||
536 | if (offset > dso->data.file_size) | ||
537 | return -1; | ||
538 | |||
539 | if (offset + size < offset) | ||
540 | return -1; | ||
541 | |||
542 | return cached_read(dso, offset, data, size); | ||
543 | } | ||
544 | |||
545 | /** | ||
546 | * dso__data_read_offset - Read data from dso file offset | ||
547 | * @dso: dso object | ||
548 | * @machine: machine object | ||
549 | * @offset: file offset | ||
550 | * @data: buffer to store data | ||
551 | * @size: size of the @data buffer | ||
552 | * | ||
553 | * External interface to read data from dso file offset. Open | ||
554 | * dso data file and use cached_read to get the data. | ||
555 | */ | ||
556 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
557 | u64 offset, u8 *data, ssize_t size) | ||
558 | { | ||
559 | if (dso__data_fd(dso, machine) < 0) | ||
560 | return -1; | ||
561 | |||
562 | return data_read_offset(dso, offset, data, size); | ||
563 | } | ||
564 | |||
565 | /** | ||
566 | * dso__data_read_addr - Read data from dso address | ||
567 | * @dso: dso object | ||
568 | * @machine: machine object | ||
569 | * @add: virtual memory address | ||
570 | * @data: buffer to store data | ||
571 | * @size: size of the @data buffer | ||
572 | * | ||
573 | * External interface to read data from dso address. | ||
574 | */ | ||
349 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | 575 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, |
350 | struct machine *machine, u64 addr, | 576 | struct machine *machine, u64 addr, |
351 | u8 *data, ssize_t size) | 577 | u8 *data, ssize_t size) |
@@ -473,7 +699,8 @@ struct dso *dso__new(const char *name) | |||
473 | dso__set_short_name(dso, dso->name, false); | 699 | dso__set_short_name(dso, dso->name, false); |
474 | for (i = 0; i < MAP__NR_TYPES; ++i) | 700 | for (i = 0; i < MAP__NR_TYPES; ++i) |
475 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | 701 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; |
476 | dso->cache = RB_ROOT; | 702 | dso->data.cache = RB_ROOT; |
703 | dso->data.fd = -1; | ||
477 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | 704 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; |
478 | dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; | 705 | dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; |
479 | dso->loaded = 0; | 706 | dso->loaded = 0; |
@@ -485,6 +712,7 @@ struct dso *dso__new(const char *name) | |||
485 | dso->kernel = DSO_TYPE_USER; | 712 | dso->kernel = DSO_TYPE_USER; |
486 | dso->needs_swap = DSO_SWAP__UNSET; | 713 | dso->needs_swap = DSO_SWAP__UNSET; |
487 | INIT_LIST_HEAD(&dso->node); | 714 | INIT_LIST_HEAD(&dso->node); |
715 | INIT_LIST_HEAD(&dso->data.open_entry); | ||
488 | } | 716 | } |
489 | 717 | ||
490 | return dso; | 718 | return dso; |
@@ -506,7 +734,8 @@ void dso__delete(struct dso *dso) | |||
506 | dso->long_name_allocated = false; | 734 | dso->long_name_allocated = false; |
507 | } | 735 | } |
508 | 736 | ||
509 | dso_cache__free(&dso->cache); | 737 | dso__data_close(dso); |
738 | dso_cache__free(&dso->data.cache); | ||
510 | dso__free_a2l(dso); | 739 | dso__free_a2l(dso); |
511 | zfree(&dso->symsrc_filename); | 740 | zfree(&dso->symsrc_filename); |
512 | free(dso); | 741 | free(dso); |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 38efe95a7fdd..ad553ba257bf 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -76,7 +76,6 @@ struct dso { | |||
76 | struct list_head node; | 76 | struct list_head node; |
77 | struct rb_root symbols[MAP__NR_TYPES]; | 77 | struct rb_root symbols[MAP__NR_TYPES]; |
78 | struct rb_root symbol_names[MAP__NR_TYPES]; | 78 | struct rb_root symbol_names[MAP__NR_TYPES]; |
79 | struct rb_root cache; | ||
80 | void *a2l; | 79 | void *a2l; |
81 | char *symsrc_filename; | 80 | char *symsrc_filename; |
82 | unsigned int a2l_fails; | 81 | unsigned int a2l_fails; |
@@ -99,6 +98,15 @@ struct dso { | |||
99 | const char *long_name; | 98 | const char *long_name; |
100 | u16 long_name_len; | 99 | u16 long_name_len; |
101 | u16 short_name_len; | 100 | u16 short_name_len; |
101 | |||
102 | /* dso data file */ | ||
103 | struct { | ||
104 | struct rb_root cache; | ||
105 | int fd; | ||
106 | size_t file_size; | ||
107 | struct list_head open_entry; | ||
108 | } data; | ||
109 | |||
102 | char name[0]; | 110 | char name[0]; |
103 | }; | 111 | }; |
104 | 112 | ||
@@ -141,7 +149,47 @@ char dso__symtab_origin(const struct dso *dso); | |||
141 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 149 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
142 | char *root_dir, char *filename, size_t size); | 150 | char *root_dir, char *filename, size_t size); |
143 | 151 | ||
152 | /* | ||
153 | * The dso__data_* external interface provides following functions: | ||
154 | * dso__data_fd | ||
155 | * dso__data_close | ||
156 | * dso__data_read_offset | ||
157 | * dso__data_read_addr | ||
158 | * | ||
159 | * Please refer to the dso.c object code for each function and | ||
160 | * arguments documentation. Following text tries to explain the | ||
161 | * dso file descriptor caching. | ||
162 | * | ||
163 | * The dso__data* interface allows caching of opened file descriptors | ||
164 | * to speed up the dso data accesses. The idea is to leave the file | ||
165 | * descriptor opened ideally for the whole life of the dso object. | ||
166 | * | ||
167 | * The current usage of the dso__data_* interface is as follows: | ||
168 | * | ||
169 | * Get DSO's fd: | ||
170 | * int fd = dso__data_fd(dso, machine); | ||
171 | * USE 'fd' SOMEHOW | ||
172 | * | ||
173 | * Read DSO's data: | ||
174 | * n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE); | ||
175 | * n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE); | ||
176 | * | ||
177 | * Eventually close DSO's fd: | ||
178 | * dso__data_close(dso); | ||
179 | * | ||
180 | * It is not necessary to close the DSO object data file. Each time new | ||
181 | * DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once | ||
182 | * it is crossed, the oldest opened DSO object is closed. | ||
183 | * | ||
184 | * The dso__delete function calls close_dso function to ensure the | ||
185 | * data file descriptor gets closed/unmapped before the dso object | ||
186 | * is freed. | ||
187 | * | ||
188 | * TODO | ||
189 | */ | ||
144 | int dso__data_fd(struct dso *dso, struct machine *machine); | 190 | int dso__data_fd(struct dso *dso, struct machine *machine); |
191 | void dso__data_close(struct dso *dso); | ||
192 | |||
145 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | 193 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, |
146 | u64 offset, u8 *data, ssize_t size); | 194 | u64 offset, u8 *data, ssize_t size); |
147 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | 195 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 65795b835b39..d0281bdfa582 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <linux/types.h> | 1 | #include <linux/types.h> |
2 | #include <sys/mman.h> | ||
2 | #include "event.h" | 3 | #include "event.h" |
3 | #include "debug.h" | 4 | #include "debug.h" |
4 | #include "hist.h" | 5 | #include "hist.h" |
@@ -178,13 +179,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
178 | return -1; | 179 | return -1; |
179 | } | 180 | } |
180 | 181 | ||
181 | event->header.type = PERF_RECORD_MMAP; | 182 | event->header.type = PERF_RECORD_MMAP2; |
182 | 183 | ||
183 | while (1) { | 184 | while (1) { |
184 | char bf[BUFSIZ]; | 185 | char bf[BUFSIZ]; |
185 | char prot[5]; | 186 | char prot[5]; |
186 | char execname[PATH_MAX]; | 187 | char execname[PATH_MAX]; |
187 | char anonstr[] = "//anon"; | 188 | char anonstr[] = "//anon"; |
189 | unsigned int ino; | ||
188 | size_t size; | 190 | size_t size; |
189 | ssize_t n; | 191 | ssize_t n; |
190 | 192 | ||
@@ -195,15 +197,20 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
195 | strcpy(execname, ""); | 197 | strcpy(execname, ""); |
196 | 198 | ||
197 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 199 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
198 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", | 200 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", |
199 | &event->mmap.start, &event->mmap.len, prot, | 201 | &event->mmap2.start, &event->mmap2.len, prot, |
200 | &event->mmap.pgoff, | 202 | &event->mmap2.pgoff, &event->mmap2.maj, |
201 | execname); | 203 | &event->mmap2.min, |
204 | &ino, execname); | ||
205 | |||
202 | /* | 206 | /* |
203 | * Anon maps don't have the execname. | 207 | * Anon maps don't have the execname. |
204 | */ | 208 | */ |
205 | if (n < 4) | 209 | if (n < 7) |
206 | continue; | 210 | continue; |
211 | |||
212 | event->mmap2.ino = (u64)ino; | ||
213 | |||
207 | /* | 214 | /* |
208 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | 215 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c |
209 | */ | 216 | */ |
@@ -212,6 +219,21 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
212 | else | 219 | else |
213 | event->header.misc = PERF_RECORD_MISC_GUEST_USER; | 220 | event->header.misc = PERF_RECORD_MISC_GUEST_USER; |
214 | 221 | ||
222 | /* map protection and flags bits */ | ||
223 | event->mmap2.prot = 0; | ||
224 | event->mmap2.flags = 0; | ||
225 | if (prot[0] == 'r') | ||
226 | event->mmap2.prot |= PROT_READ; | ||
227 | if (prot[1] == 'w') | ||
228 | event->mmap2.prot |= PROT_WRITE; | ||
229 | if (prot[2] == 'x') | ||
230 | event->mmap2.prot |= PROT_EXEC; | ||
231 | |||
232 | if (prot[3] == 's') | ||
233 | event->mmap2.flags |= MAP_SHARED; | ||
234 | else | ||
235 | event->mmap2.flags |= MAP_PRIVATE; | ||
236 | |||
215 | if (prot[2] != 'x') { | 237 | if (prot[2] != 'x') { |
216 | if (!mmap_data || prot[0] != 'r') | 238 | if (!mmap_data || prot[0] != 'r') |
217 | continue; | 239 | continue; |
@@ -223,15 +245,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
223 | strcpy(execname, anonstr); | 245 | strcpy(execname, anonstr); |
224 | 246 | ||
225 | size = strlen(execname) + 1; | 247 | size = strlen(execname) + 1; |
226 | memcpy(event->mmap.filename, execname, size); | 248 | memcpy(event->mmap2.filename, execname, size); |
227 | size = PERF_ALIGN(size, sizeof(u64)); | 249 | size = PERF_ALIGN(size, sizeof(u64)); |
228 | event->mmap.len -= event->mmap.start; | 250 | event->mmap2.len -= event->mmap.start; |
229 | event->mmap.header.size = (sizeof(event->mmap) - | 251 | event->mmap2.header.size = (sizeof(event->mmap2) - |
230 | (sizeof(event->mmap.filename) - size)); | 252 | (sizeof(event->mmap2.filename) - size)); |
231 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); | 253 | memset(event->mmap2.filename + size, 0, machine->id_hdr_size); |
232 | event->mmap.header.size += machine->id_hdr_size; | 254 | event->mmap2.header.size += machine->id_hdr_size; |
233 | event->mmap.pid = tgid; | 255 | event->mmap2.pid = tgid; |
234 | event->mmap.tid = pid; | 256 | event->mmap2.tid = pid; |
235 | 257 | ||
236 | if (process(tool, event, &synth_sample, machine) != 0) { | 258 | if (process(tool, event, &synth_sample, machine) != 0) { |
237 | rc = -1; | 259 | rc = -1; |
@@ -612,12 +634,15 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | |||
612 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) | 634 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) |
613 | { | 635 | { |
614 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 | 636 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 |
615 | " %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n", | 637 | " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n", |
616 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, | 638 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, |
617 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, | 639 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, |
618 | event->mmap2.min, event->mmap2.ino, | 640 | event->mmap2.min, event->mmap2.ino, |
619 | event->mmap2.ino_generation, | 641 | event->mmap2.ino_generation, |
620 | (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', | 642 | (event->mmap2.prot & PROT_READ) ? 'r' : '-', |
643 | (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', | ||
644 | (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', | ||
645 | (event->mmap2.flags & MAP_SHARED) ? 's' : 'p', | ||
621 | event->mmap2.filename); | 646 | event->mmap2.filename); |
622 | } | 647 | } |
623 | 648 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index d970232cb270..e5dd40addb30 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "../perf.h" | 7 | #include "../perf.h" |
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | 9 | #include "build-id.h" |
10 | #include "perf_regs.h" | ||
10 | 11 | ||
11 | struct mmap_event { | 12 | struct mmap_event { |
12 | struct perf_event_header header; | 13 | struct perf_event_header header; |
@@ -27,6 +28,8 @@ struct mmap2_event { | |||
27 | u32 min; | 28 | u32 min; |
28 | u64 ino; | 29 | u64 ino; |
29 | u64 ino_generation; | 30 | u64 ino_generation; |
31 | u32 prot; | ||
32 | u32 flags; | ||
30 | char filename[PATH_MAX]; | 33 | char filename[PATH_MAX]; |
31 | }; | 34 | }; |
32 | 35 | ||
@@ -87,6 +90,10 @@ struct regs_dump { | |||
87 | u64 abi; | 90 | u64 abi; |
88 | u64 mask; | 91 | u64 mask; |
89 | u64 *regs; | 92 | u64 *regs; |
93 | |||
94 | /* Cached values/mask filled by first register access. */ | ||
95 | u64 cache_regs[PERF_REGS_MAX]; | ||
96 | u64 cache_mask; | ||
90 | }; | 97 | }; |
91 | 98 | ||
92 | struct stack_dump { | 99 | struct stack_dump { |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 5c28d82b76c4..8606175fe1e8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
589 | } | 589 | } |
590 | 590 | ||
591 | /* | 591 | /* |
592 | * We default some events to a 1 default interval. But keep | 592 | * We default some events to have a default interval. But keep |
593 | * it a weak assumption overridable by the user. | 593 | * it a weak assumption overridable by the user. |
594 | */ | 594 | */ |
595 | if (!attr->sample_period || (opts->user_freq != UINT_MAX && | 595 | if (!attr->sample_period || (opts->user_freq != UINT_MAX || |
596 | opts->user_interval != ULLONG_MAX)) { | 596 | opts->user_interval != ULLONG_MAX)) { |
597 | if (opts->freq) { | 597 | if (opts->freq) { |
598 | perf_evsel__set_sample_bit(evsel, PERIOD); | 598 | perf_evsel__set_sample_bit(evsel, PERIOD); |
@@ -659,6 +659,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
659 | perf_evsel__set_sample_bit(evsel, WEIGHT); | 659 | perf_evsel__set_sample_bit(evsel, WEIGHT); |
660 | 660 | ||
661 | attr->mmap = track; | 661 | attr->mmap = track; |
662 | attr->mmap2 = track && !perf_missing_features.mmap2; | ||
662 | attr->comm = track; | 663 | attr->comm = track; |
663 | 664 | ||
664 | if (opts->sample_transaction) | 665 | if (opts->sample_transaction) |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5a0a4b2cadc4..30df6187ee02 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -128,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
128 | + unresolved_col_width + 2; | 128 | + unresolved_col_width + 2; |
129 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | 129 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, |
130 | symlen); | 130 | symlen); |
131 | hists__new_col_len(hists, HISTC_MEM_DCACHELINE, | ||
132 | symlen + 1); | ||
131 | } else { | 133 | } else { |
132 | symlen = unresolved_col_width + 4 + 2; | 134 | symlen = unresolved_col_width + 4 + 2; |
133 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | 135 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, |
@@ -439,9 +441,10 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
439 | .map = al->map, | 441 | .map = al->map, |
440 | .sym = al->sym, | 442 | .sym = al->sym, |
441 | }, | 443 | }, |
442 | .cpu = al->cpu, | 444 | .cpu = al->cpu, |
443 | .ip = al->addr, | 445 | .cpumode = al->cpumode, |
444 | .level = al->level, | 446 | .ip = al->addr, |
447 | .level = al->level, | ||
445 | .stat = { | 448 | .stat = { |
446 | .nr_events = 1, | 449 | .nr_events = 1, |
447 | .period = period, | 450 | .period = period, |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index d2bf03575d5f..742f49a85725 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -72,6 +72,7 @@ enum hist_column { | |||
72 | HISTC_MEM_TLB, | 72 | HISTC_MEM_TLB, |
73 | HISTC_MEM_LVL, | 73 | HISTC_MEM_LVL, |
74 | HISTC_MEM_SNOOP, | 74 | HISTC_MEM_SNOOP, |
75 | HISTC_MEM_DCACHELINE, | ||
75 | HISTC_TRANSACTION, | 76 | HISTC_TRANSACTION, |
76 | HISTC_NR_COLS, /* Last entry */ | 77 | HISTC_NR_COLS, /* Last entry */ |
77 | }; | 78 | }; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 7409ac8de51c..0e5fea95d596 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1060,6 +1060,8 @@ int machine__process_mmap2_event(struct machine *machine, | |||
1060 | event->mmap2.pid, event->mmap2.maj, | 1060 | event->mmap2.pid, event->mmap2.maj, |
1061 | event->mmap2.min, event->mmap2.ino, | 1061 | event->mmap2.min, event->mmap2.ino, |
1062 | event->mmap2.ino_generation, | 1062 | event->mmap2.ino_generation, |
1063 | event->mmap2.prot, | ||
1064 | event->mmap2.flags, | ||
1063 | event->mmap2.filename, type); | 1065 | event->mmap2.filename, type); |
1064 | 1066 | ||
1065 | if (map == NULL) | 1067 | if (map == NULL) |
@@ -1105,7 +1107,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
1105 | 1107 | ||
1106 | map = map__new(&machine->user_dsos, event->mmap.start, | 1108 | map = map__new(&machine->user_dsos, event->mmap.start, |
1107 | event->mmap.len, event->mmap.pgoff, | 1109 | event->mmap.len, event->mmap.pgoff, |
1108 | event->mmap.pid, 0, 0, 0, 0, | 1110 | event->mmap.pid, 0, 0, 0, 0, 0, 0, |
1109 | event->mmap.filename, | 1111 | event->mmap.filename, |
1110 | type); | 1112 | type); |
1111 | 1113 | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 8ccbb32eda25..25c571f4cba6 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -138,7 +138,7 @@ void map__init(struct map *map, enum map_type type, | |||
138 | 138 | ||
139 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 139 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
140 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, | 140 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, |
141 | u64 ino_gen, char *filename, | 141 | u64 ino_gen, u32 prot, u32 flags, char *filename, |
142 | enum map_type type) | 142 | enum map_type type) |
143 | { | 143 | { |
144 | struct map *map = malloc(sizeof(*map)); | 144 | struct map *map = malloc(sizeof(*map)); |
@@ -157,6 +157,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
157 | map->min = d_min; | 157 | map->min = d_min; |
158 | map->ino = ino; | 158 | map->ino = ino; |
159 | map->ino_generation = ino_gen; | 159 | map->ino_generation = ino_gen; |
160 | map->prot = prot; | ||
161 | map->flags = flags; | ||
160 | 162 | ||
161 | if ((anon || no_dso) && type == MAP__FUNCTION) { | 163 | if ((anon || no_dso) && type == MAP__FUNCTION) { |
162 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); | 164 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index ae2d45110588..7758c72522ef 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -35,6 +35,8 @@ struct map { | |||
35 | bool referenced; | 35 | bool referenced; |
36 | bool erange_warned; | 36 | bool erange_warned; |
37 | u32 priv; | 37 | u32 priv; |
38 | u32 prot; | ||
39 | u32 flags; | ||
38 | u64 pgoff; | 40 | u64 pgoff; |
39 | u64 reloc; | 41 | u64 reloc; |
40 | u32 maj, min; /* only valid for MMAP2 record */ | 42 | u32 maj, min; /* only valid for MMAP2 record */ |
@@ -118,7 +120,7 @@ void map__init(struct map *map, enum map_type type, | |||
118 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 120 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
119 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 121 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
120 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, | 122 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, |
121 | u64 ino_gen, | 123 | u64 ino_gen, u32 prot, u32 flags, |
122 | char *filename, enum map_type type); | 124 | char *filename, enum map_type type); |
123 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 125 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
124 | void map__delete(struct map *map); | 126 | void map__delete(struct map *map); |
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index a3539ef30b15..43168fb0d9a2 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c | |||
@@ -1,11 +1,15 @@ | |||
1 | #include <errno.h> | 1 | #include <errno.h> |
2 | #include "perf_regs.h" | 2 | #include "perf_regs.h" |
3 | #include "event.h" | ||
3 | 4 | ||
4 | int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) | 5 | int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) |
5 | { | 6 | { |
6 | int i, idx = 0; | 7 | int i, idx = 0; |
7 | u64 mask = regs->mask; | 8 | u64 mask = regs->mask; |
8 | 9 | ||
10 | if (regs->cache_mask & (1 << id)) | ||
11 | goto out; | ||
12 | |||
9 | if (!(mask & (1 << id))) | 13 | if (!(mask & (1 << id))) |
10 | return -EINVAL; | 14 | return -EINVAL; |
11 | 15 | ||
@@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) | |||
14 | idx++; | 18 | idx++; |
15 | } | 19 | } |
16 | 20 | ||
17 | *valp = regs->regs[idx]; | 21 | regs->cache_mask |= (1 << id); |
22 | regs->cache_regs[id] = regs->regs[idx]; | ||
23 | |||
24 | out: | ||
25 | *valp = regs->cache_regs[id]; | ||
18 | return 0; | 26 | return 0; |
19 | } | 27 | } |
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 79c78f74e0cf..980dbf76bc98 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h | |||
@@ -2,7 +2,8 @@ | |||
2 | #define __PERF_REGS_H | 2 | #define __PERF_REGS_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include "event.h" | 5 | |
6 | struct regs_dump; | ||
6 | 7 | ||
7 | #ifdef HAVE_PERF_REGS_SUPPORT | 8 | #ifdef HAVE_PERF_REGS_SUPPORT |
8 | #include <perf_regs.h> | 9 | #include <perf_regs.h> |
@@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); | |||
11 | 12 | ||
12 | #else | 13 | #else |
13 | #define PERF_REGS_MASK 0 | 14 | #define PERF_REGS_MASK 0 |
15 | #define PERF_REGS_MAX 0 | ||
14 | 16 | ||
15 | static inline const char *perf_reg_name(int id __maybe_unused) | 17 | static inline const char *perf_reg_name(int id __maybe_unused) |
16 | { | 18 | { |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 0d1542f33d87..9a0a1839a377 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -628,11 +628,11 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
628 | 628 | ||
629 | ret = debuginfo__find_line_range(dinfo, lr); | 629 | ret = debuginfo__find_line_range(dinfo, lr); |
630 | debuginfo__delete(dinfo); | 630 | debuginfo__delete(dinfo); |
631 | if (ret == 0) { | 631 | if (ret == 0 || ret == -ENOENT) { |
632 | pr_warning("Specified source line is not found.\n"); | 632 | pr_warning("Specified source line is not found.\n"); |
633 | return -ENOENT; | 633 | return -ENOENT; |
634 | } else if (ret < 0) { | 634 | } else if (ret < 0) { |
635 | pr_warning("Debuginfo analysis failed. (%d)\n", ret); | 635 | pr_warning("Debuginfo analysis failed.\n"); |
636 | return ret; | 636 | return ret; |
637 | } | 637 | } |
638 | 638 | ||
@@ -641,7 +641,7 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
641 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); | 641 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); |
642 | free(tmp); /* Free old path */ | 642 | free(tmp); /* Free old path */ |
643 | if (ret < 0) { | 643 | if (ret < 0) { |
644 | pr_warning("Failed to find source file. (%d)\n", ret); | 644 | pr_warning("Failed to find source file path.\n"); |
645 | return ret; | 645 | return ret; |
646 | } | 646 | } |
647 | 647 | ||
@@ -721,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo, | |||
721 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, | 721 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
722 | max_vls, externs); | 722 | max_vls, externs); |
723 | if (ret <= 0) { | 723 | if (ret <= 0) { |
724 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 724 | if (ret == 0 || ret == -ENOENT) { |
725 | pr_err("Failed to find the address of %s\n", buf); | ||
726 | ret = -ENOENT; | ||
727 | } else | ||
728 | pr_warning("Debuginfo analysis failed.\n"); | ||
725 | goto end; | 729 | goto end; |
726 | } | 730 | } |
731 | |||
727 | /* Some variables are found */ | 732 | /* Some variables are found */ |
728 | fprintf(stdout, "Available variables at %s\n", buf); | 733 | fprintf(stdout, "Available variables at %s\n", buf); |
729 | for (i = 0; i < ret; i++) { | 734 | for (i = 0; i < ret; i++) { |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 9d8eb26f0533..98e304766416 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -573,14 +573,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
573 | if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { | 573 | if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { |
574 | /* Search again in global variables */ | 574 | /* Search again in global variables */ |
575 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) | 575 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) |
576 | pr_warning("Failed to find '%s' in this function.\n", | ||
577 | pf->pvar->var); | ||
576 | ret = -ENOENT; | 578 | ret = -ENOENT; |
577 | } | 579 | } |
578 | if (ret >= 0) | 580 | if (ret >= 0) |
579 | ret = convert_variable(&vr_die, pf); | 581 | ret = convert_variable(&vr_die, pf); |
580 | 582 | ||
581 | if (ret < 0) | ||
582 | pr_warning("Failed to find '%s' in this function.\n", | ||
583 | pf->pvar->var); | ||
584 | return ret; | 583 | return ret; |
585 | } | 584 | } |
586 | 585 | ||
@@ -1281,7 +1280,11 @@ out: | |||
1281 | return ret; | 1280 | return ret; |
1282 | } | 1281 | } |
1283 | 1282 | ||
1284 | /* Find available variables at given probe point */ | 1283 | /* |
1284 | * Find available variables at given probe point | ||
1285 | * Return the number of found probe points. Return 0 if there is no | ||
1286 | * matched probe point. Return <0 if an error occurs. | ||
1287 | */ | ||
1285 | int debuginfo__find_available_vars_at(struct debuginfo *dbg, | 1288 | int debuginfo__find_available_vars_at(struct debuginfo *dbg, |
1286 | struct perf_probe_event *pev, | 1289 | struct perf_probe_event *pev, |
1287 | struct variable_list **vls, | 1290 | struct variable_list **vls, |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index e108207c5de0..af7da565a750 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -215,6 +215,7 @@ static void define_event_symbols(struct event_format *event, | |||
215 | case PRINT_BSTRING: | 215 | case PRINT_BSTRING: |
216 | case PRINT_DYNAMIC_ARRAY: | 216 | case PRINT_DYNAMIC_ARRAY: |
217 | case PRINT_STRING: | 217 | case PRINT_STRING: |
218 | case PRINT_BITMASK: | ||
218 | break; | 219 | break; |
219 | case PRINT_TYPE: | 220 | case PRINT_TYPE: |
220 | define_event_symbols(event, ev_name, args->typecast.item); | 221 | define_event_symbols(event, ev_name, args->typecast.item); |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cd9774df3750..1c419321f707 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -197,6 +197,7 @@ static void define_event_symbols(struct event_format *event, | |||
197 | case PRINT_BSTRING: | 197 | case PRINT_BSTRING: |
198 | case PRINT_DYNAMIC_ARRAY: | 198 | case PRINT_DYNAMIC_ARRAY: |
199 | case PRINT_FUNC: | 199 | case PRINT_FUNC: |
200 | case PRINT_BITMASK: | ||
200 | /* we should warn... */ | 201 | /* we should warn... */ |
201 | return; | 202 | return; |
202 | } | 203 | } |
@@ -622,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) | |||
622 | fprintf(ofp, "%s=", f->name); | 623 | fprintf(ofp, "%s=", f->name); |
623 | if (f->flags & FIELD_IS_STRING || | 624 | if (f->flags & FIELD_IS_STRING || |
624 | f->flags & FIELD_IS_FLAG || | 625 | f->flags & FIELD_IS_FLAG || |
626 | f->flags & FIELD_IS_ARRAY || | ||
625 | f->flags & FIELD_IS_SYMBOLIC) | 627 | f->flags & FIELD_IS_SYMBOLIC) |
626 | fprintf(ofp, "%%s"); | 628 | fprintf(ofp, "%%s"); |
627 | else if (f->flags & FIELD_IS_SIGNED) | 629 | else if (f->flags & FIELD_IS_SIGNED) |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 45512baaab67..1ec57dd82284 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <sys/mman.h> | ||
1 | #include "sort.h" | 2 | #include "sort.h" |
2 | #include "hist.h" | 3 | #include "hist.h" |
3 | #include "comm.h" | 4 | #include "comm.h" |
@@ -784,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, | |||
784 | return repsep_snprintf(bf, size, "%-*s", width, out); | 785 | return repsep_snprintf(bf, size, "%-*s", width, out); |
785 | } | 786 | } |
786 | 787 | ||
788 | static inline u64 cl_address(u64 address) | ||
789 | { | ||
790 | /* return the cacheline of the address */ | ||
791 | return (address & ~(cacheline_size - 1)); | ||
792 | } | ||
793 | |||
794 | static int64_t | ||
795 | sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) | ||
796 | { | ||
797 | u64 l, r; | ||
798 | struct map *l_map, *r_map; | ||
799 | |||
800 | if (!left->mem_info) return -1; | ||
801 | if (!right->mem_info) return 1; | ||
802 | |||
803 | /* group event types together */ | ||
804 | if (left->cpumode > right->cpumode) return -1; | ||
805 | if (left->cpumode < right->cpumode) return 1; | ||
806 | |||
807 | l_map = left->mem_info->daddr.map; | ||
808 | r_map = right->mem_info->daddr.map; | ||
809 | |||
810 | /* if both are NULL, jump to sort on al_addr instead */ | ||
811 | if (!l_map && !r_map) | ||
812 | goto addr; | ||
813 | |||
814 | if (!l_map) return -1; | ||
815 | if (!r_map) return 1; | ||
816 | |||
817 | if (l_map->maj > r_map->maj) return -1; | ||
818 | if (l_map->maj < r_map->maj) return 1; | ||
819 | |||
820 | if (l_map->min > r_map->min) return -1; | ||
821 | if (l_map->min < r_map->min) return 1; | ||
822 | |||
823 | if (l_map->ino > r_map->ino) return -1; | ||
824 | if (l_map->ino < r_map->ino) return 1; | ||
825 | |||
826 | if (l_map->ino_generation > r_map->ino_generation) return -1; | ||
827 | if (l_map->ino_generation < r_map->ino_generation) return 1; | ||
828 | |||
829 | /* | ||
830 | * Addresses with no major/minor numbers are assumed to be | ||
831 | * anonymous in userspace. Sort those on pid then address. | ||
832 | * | ||
833 | * The kernel and non-zero major/minor mapped areas are | ||
834 | * assumed to be unity mapped. Sort those on address. | ||
835 | */ | ||
836 | |||
837 | if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && | ||
838 | (!(l_map->flags & MAP_SHARED)) && | ||
839 | !l_map->maj && !l_map->min && !l_map->ino && | ||
840 | !l_map->ino_generation) { | ||
841 | /* userspace anonymous */ | ||
842 | |||
843 | if (left->thread->pid_ > right->thread->pid_) return -1; | ||
844 | if (left->thread->pid_ < right->thread->pid_) return 1; | ||
845 | } | ||
846 | |||
847 | addr: | ||
848 | /* al_addr does all the right addr - start + offset calculations */ | ||
849 | l = cl_address(left->mem_info->daddr.al_addr); | ||
850 | r = cl_address(right->mem_info->daddr.al_addr); | ||
851 | |||
852 | if (l > r) return -1; | ||
853 | if (l < r) return 1; | ||
854 | |||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, | ||
859 | size_t size, unsigned int width) | ||
860 | { | ||
861 | |||
862 | uint64_t addr = 0; | ||
863 | struct map *map = NULL; | ||
864 | struct symbol *sym = NULL; | ||
865 | char level = he->level; | ||
866 | |||
867 | if (he->mem_info) { | ||
868 | addr = cl_address(he->mem_info->daddr.al_addr); | ||
869 | map = he->mem_info->daddr.map; | ||
870 | sym = he->mem_info->daddr.sym; | ||
871 | |||
872 | /* print [s] for shared data mmaps */ | ||
873 | if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && | ||
874 | map && (map->type == MAP__VARIABLE) && | ||
875 | (map->flags & MAP_SHARED) && | ||
876 | (map->maj || map->min || map->ino || | ||
877 | map->ino_generation)) | ||
878 | level = 's'; | ||
879 | else if (!map) | ||
880 | level = 'X'; | ||
881 | } | ||
882 | return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size, | ||
883 | width); | ||
884 | } | ||
885 | |||
787 | struct sort_entry sort_mispredict = { | 886 | struct sort_entry sort_mispredict = { |
788 | .se_header = "Branch Mispredicted", | 887 | .se_header = "Branch Mispredicted", |
789 | .se_cmp = sort__mispredict_cmp, | 888 | .se_cmp = sort__mispredict_cmp, |
@@ -876,6 +975,13 @@ struct sort_entry sort_mem_snoop = { | |||
876 | .se_width_idx = HISTC_MEM_SNOOP, | 975 | .se_width_idx = HISTC_MEM_SNOOP, |
877 | }; | 976 | }; |
878 | 977 | ||
978 | struct sort_entry sort_mem_dcacheline = { | ||
979 | .se_header = "Data Cacheline", | ||
980 | .se_cmp = sort__dcacheline_cmp, | ||
981 | .se_snprintf = hist_entry__dcacheline_snprintf, | ||
982 | .se_width_idx = HISTC_MEM_DCACHELINE, | ||
983 | }; | ||
984 | |||
879 | static int64_t | 985 | static int64_t |
880 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) | 986 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) |
881 | { | 987 | { |
@@ -1043,6 +1149,7 @@ static struct sort_dimension memory_sort_dimensions[] = { | |||
1043 | DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), | 1149 | DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), |
1044 | DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), | 1150 | DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), |
1045 | DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), | 1151 | DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), |
1152 | DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline), | ||
1046 | }; | 1153 | }; |
1047 | 1154 | ||
1048 | #undef DIM | 1155 | #undef DIM |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5bf0098d6b06..041f0c9cea2b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -89,6 +89,7 @@ struct hist_entry { | |||
89 | u64 ip; | 89 | u64 ip; |
90 | u64 transaction; | 90 | u64 transaction; |
91 | s32 cpu; | 91 | s32 cpu; |
92 | u8 cpumode; | ||
92 | 93 | ||
93 | struct hist_entry_diff diff; | 94 | struct hist_entry_diff diff; |
94 | 95 | ||
@@ -185,6 +186,7 @@ enum sort_type { | |||
185 | SORT_MEM_TLB, | 186 | SORT_MEM_TLB, |
186 | SORT_MEM_LVL, | 187 | SORT_MEM_LVL, |
187 | SORT_MEM_SNOOP, | 188 | SORT_MEM_SNOOP, |
189 | SORT_MEM_DCACHELINE, | ||
188 | }; | 190 | }; |
189 | 191 | ||
190 | /* | 192 | /* |
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index bd5768d74f01..25578b98f5c5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
@@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, | |||
250 | 250 | ||
251 | /* Check the .eh_frame section for unwinding info */ | 251 | /* Check the .eh_frame section for unwinding info */ |
252 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | 252 | offset = elf_section_offset(fd, ".eh_frame_hdr"); |
253 | close(fd); | ||
254 | 253 | ||
255 | if (offset) | 254 | if (offset) |
256 | ret = unwind_spec_ehframe(dso, machine, offset, | 255 | ret = unwind_spec_ehframe(dso, machine, offset, |
@@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso, | |||
271 | 270 | ||
272 | /* Check the .debug_frame section for unwinding info */ | 271 | /* Check the .debug_frame section for unwinding info */ |
273 | *offset = elf_section_offset(fd, ".debug_frame"); | 272 | *offset = elf_section_offset(fd, ".debug_frame"); |
274 | close(fd); | ||
275 | 273 | ||
276 | if (*offset) | 274 | if (*offset) |
277 | return 0; | 275 | return 0; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 7fff6be07f07..95aefa78bb07 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -17,6 +17,7 @@ | |||
17 | * XXX We need to find a better place for these things... | 17 | * XXX We need to find a better place for these things... |
18 | */ | 18 | */ |
19 | unsigned int page_size; | 19 | unsigned int page_size; |
20 | int cacheline_size; | ||
20 | 21 | ||
21 | bool test_attr__enabled; | 22 | bool test_attr__enabled; |
22 | 23 | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b03da44e94e4..66864364ccb4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -304,6 +304,7 @@ char *rtrim(char *s); | |||
304 | void dump_stack(void); | 304 | void dump_stack(void); |
305 | 305 | ||
306 | extern unsigned int page_size; | 306 | extern unsigned int page_size; |
307 | extern int cacheline_size; | ||
307 | 308 | ||
308 | void get_term_dimensions(struct winsize *ws); | 309 | void get_term_dimensions(struct winsize *ws); |
309 | 310 | ||
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 51267f4184a6..2cede239a074 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile | |||
@@ -2,7 +2,7 @@ PROGS := tm-resched-dscr | |||
2 | 2 | ||
3 | all: $(PROGS) | 3 | all: $(PROGS) |
4 | 4 | ||
5 | $(PROGS): | 5 | $(PROGS): ../harness.c |
6 | 6 | ||
7 | run_tests: all | 7 | run_tests: all |
8 | @-for PROG in $(PROGS); do \ | 8 | @-for PROG in $(PROGS); do \ |
diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c index ee98e3886af2..42d4c8caad81 100644 --- a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c +++ b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <assert.h> | 28 | #include <assert.h> |
29 | #include <asm/tm.h> | 29 | #include <asm/tm.h> |
30 | 30 | ||
31 | #include "utils.h" | ||
32 | |||
31 | #define TBEGIN ".long 0x7C00051D ;" | 33 | #define TBEGIN ".long 0x7C00051D ;" |
32 | #define TEND ".long 0x7C00055D ;" | 34 | #define TEND ".long 0x7C00055D ;" |
33 | #define TCHECK ".long 0x7C00059C ;" | 35 | #define TCHECK ".long 0x7C00059C ;" |
@@ -36,7 +38,8 @@ | |||
36 | #define SPRN_TEXASR 0x82 | 38 | #define SPRN_TEXASR 0x82 |
37 | #define SPRN_DSCR 0x03 | 39 | #define SPRN_DSCR 0x03 |
38 | 40 | ||
39 | int main(void) { | 41 | int test_body(void) |
42 | { | ||
40 | uint64_t rv, dscr1 = 1, dscr2, texasr; | 43 | uint64_t rv, dscr1 = 1, dscr2, texasr; |
41 | 44 | ||
42 | printf("Check DSCR TM context switch: "); | 45 | printf("Check DSCR TM context switch: "); |
@@ -81,10 +84,15 @@ int main(void) { | |||
81 | } | 84 | } |
82 | if (dscr2 != dscr1) { | 85 | if (dscr2 != dscr1) { |
83 | printf(" FAIL\n"); | 86 | printf(" FAIL\n"); |
84 | exit(EXIT_FAILURE); | 87 | return 1; |
85 | } else { | 88 | } else { |
86 | printf(" OK\n"); | 89 | printf(" OK\n"); |
87 | exit(EXIT_SUCCESS); | 90 | return 0; |
88 | } | 91 | } |
89 | } | 92 | } |
90 | } | 93 | } |
94 | |||
95 | int main(void) | ||
96 | { | ||
97 | return test_harness(test_body, "tm_resched_dscr"); | ||
98 | } | ||