diff options
| author | Ingo Molnar <mingo@kernel.org> | 2015-03-24 12:22:44 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2015-03-24 12:22:44 -0400 |
| commit | baa5a7bc5dd069bb37de9c8bdb5ea7f4e2e939e9 (patch) | |
| tree | 50864d78ede1a333b0b6fb8fe09486ed3f3ad1c2 | |
| parent | 963a70b8a2d65538f7d58b2b84a2ae10a3ecb6ea (diff) | |
| parent | e03eaa400cf8b8bded86cc5c41018a1c69152f16 (diff) | |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- Improve support of compressed kernel modules (Jiri Olsa)
- Add --kallsyms option to 'perf diff' (David Ahern)
- Add pid/tid filtering to 'report' and 'script' commands (David Ahern)
- Add support for __print_array() in libtraceevent (Javi Merino)
- Save DSO loading errno to better report errors (Arnaldo Carvalho de Melo)
- Fix 'probe' to get ummapped symbol address on kernel (Masami Hiramatsu)
- Print big numbers using thousands' group in 'kmem' (Namhyung Kim)
- Remove (null) value of "Sort order" for perf mem report (Yunlong Song)
Infrastructure changes:
- Handle NULL comm name in libtracevent (Josef Bacik)
- Libtraceevent synchronization with trace-cmd repo (Steven Rostedt)
- Work around lack of sched_getcpu() in glibc < 2.6. (Vinson Lee)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
31 files changed, 621 insertions, 131 deletions
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index d7c37a7d9255..b6d11eea8a57 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
| @@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) | |||
| 304 | if (!item) | 304 | if (!item) |
| 305 | return -1; | 305 | return -1; |
| 306 | 306 | ||
| 307 | item->comm = strdup(comm); | 307 | if (comm) |
| 308 | item->comm = strdup(comm); | ||
| 309 | else | ||
| 310 | item->comm = strdup("<...>"); | ||
| 308 | if (!item->comm) { | 311 | if (!item->comm) { |
| 309 | free(item); | 312 | free(item); |
| 310 | return -1; | 313 | return -1; |
| @@ -318,9 +321,14 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) | |||
| 318 | return 0; | 321 | return 0; |
| 319 | } | 322 | } |
| 320 | 323 | ||
| 321 | void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock) | 324 | int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock) |
| 322 | { | 325 | { |
| 323 | pevent->trace_clock = trace_clock; | 326 | pevent->trace_clock = strdup(trace_clock); |
| 327 | if (!pevent->trace_clock) { | ||
| 328 | errno = ENOMEM; | ||
| 329 | return -1; | ||
| 330 | } | ||
| 331 | return 0; | ||
| 324 | } | 332 | } |
| 325 | 333 | ||
| 326 | struct func_map { | 334 | struct func_map { |
| @@ -758,6 +766,11 @@ static void free_arg(struct print_arg *arg) | |||
| 758 | free_arg(arg->hex.field); | 766 | free_arg(arg->hex.field); |
| 759 | free_arg(arg->hex.size); | 767 | free_arg(arg->hex.size); |
| 760 | break; | 768 | break; |
| 769 | case PRINT_INT_ARRAY: | ||
| 770 | free_arg(arg->int_array.field); | ||
| 771 | free_arg(arg->int_array.count); | ||
| 772 | free_arg(arg->int_array.el_size); | ||
| 773 | break; | ||
| 761 | case PRINT_TYPE: | 774 | case PRINT_TYPE: |
| 762 | free(arg->typecast.type); | 775 | free(arg->typecast.type); |
| 763 | free_arg(arg->typecast.item); | 776 | free_arg(arg->typecast.item); |
| @@ -2014,6 +2027,38 @@ process_entry(struct event_format *event __maybe_unused, struct print_arg *arg, | |||
| 2014 | return EVENT_ERROR; | 2027 | return EVENT_ERROR; |
| 2015 | } | 2028 | } |
| 2016 | 2029 | ||
| 2030 | static int alloc_and_process_delim(struct event_format *event, char *next_token, | ||
| 2031 | struct print_arg **print_arg) | ||
| 2032 | { | ||
| 2033 | struct print_arg *field; | ||
| 2034 | enum event_type type; | ||
| 2035 | char *token; | ||
| 2036 | int ret = 0; | ||
| 2037 | |||
| 2038 | field = alloc_arg(); | ||
| 2039 | if (!field) { | ||
| 2040 | do_warning_event(event, "%s: not enough memory!", __func__); | ||
| 2041 | errno = ENOMEM; | ||
| 2042 | return -1; | ||
| 2043 | } | ||
| 2044 | |||
| 2045 | type = process_arg(event, field, &token); | ||
| 2046 | |||
| 2047 | if (test_type_token(type, token, EVENT_DELIM, next_token)) { | ||
| 2048 | errno = EINVAL; | ||
| 2049 | ret = -1; | ||
| 2050 | free_arg(field); | ||
| 2051 | goto out_free_token; | ||
| 2052 | } | ||
| 2053 | |||
| 2054 | *print_arg = field; | ||
| 2055 | |||
| 2056 | out_free_token: | ||
| 2057 | free_token(token); | ||
| 2058 | |||
| 2059 | return ret; | ||
| 2060 | } | ||
| 2061 | |||
| 2017 | static char *arg_eval (struct print_arg *arg); | 2062 | static char *arg_eval (struct print_arg *arg); |
| 2018 | 2063 | ||
| 2019 | static unsigned long long | 2064 | static unsigned long long |
| @@ -2486,49 +2531,46 @@ out_free: | |||
| 2486 | static enum event_type | 2531 | static enum event_type |
| 2487 | process_hex(struct event_format *event, struct print_arg *arg, char **tok) | 2532 | process_hex(struct event_format *event, struct print_arg *arg, char **tok) |
| 2488 | { | 2533 | { |
| 2489 | struct print_arg *field; | ||
| 2490 | enum event_type type; | ||
| 2491 | char *token = NULL; | ||
| 2492 | |||
| 2493 | memset(arg, 0, sizeof(*arg)); | 2534 | memset(arg, 0, sizeof(*arg)); |
| 2494 | arg->type = PRINT_HEX; | 2535 | arg->type = PRINT_HEX; |
| 2495 | 2536 | ||
| 2496 | field = alloc_arg(); | 2537 | if (alloc_and_process_delim(event, ",", &arg->hex.field)) |
| 2497 | if (!field) { | 2538 | goto out; |
| 2498 | do_warning_event(event, "%s: not enough memory!", __func__); | ||
| 2499 | goto out_free; | ||
| 2500 | } | ||
| 2501 | |||
| 2502 | type = process_arg(event, field, &token); | ||
| 2503 | 2539 | ||
| 2504 | if (test_type_token(type, token, EVENT_DELIM, ",")) | 2540 | if (alloc_and_process_delim(event, ")", &arg->hex.size)) |
| 2505 | goto out_free; | 2541 | goto free_field; |
| 2506 | 2542 | ||
| 2507 | arg->hex.field = field; | 2543 | return read_token_item(tok); |
| 2508 | 2544 | ||
| 2509 | free_token(token); | 2545 | free_field: |
| 2546 | free_arg(arg->hex.field); | ||
| 2547 | out: | ||
| 2548 | *tok = NULL; | ||
| 2549 | return EVENT_ERROR; | ||
| 2550 | } | ||
| 2510 | 2551 | ||
| 2511 | field = alloc_arg(); | 2552 | static enum event_type |
| 2512 | if (!field) { | 2553 | process_int_array(struct event_format *event, struct print_arg *arg, char **tok) |
| 2513 | do_warning_event(event, "%s: not enough memory!", __func__); | 2554 | { |
| 2514 | *tok = NULL; | 2555 | memset(arg, 0, sizeof(*arg)); |
| 2515 | return EVENT_ERROR; | 2556 | arg->type = PRINT_INT_ARRAY; |
| 2516 | } | ||
| 2517 | 2557 | ||
| 2518 | type = process_arg(event, field, &token); | 2558 | if (alloc_and_process_delim(event, ",", &arg->int_array.field)) |
| 2559 | goto out; | ||
| 2519 | 2560 | ||
| 2520 | if (test_type_token(type, token, EVENT_DELIM, ")")) | 2561 | if (alloc_and_process_delim(event, ",", &arg->int_array.count)) |
| 2521 | goto out_free; | 2562 | goto free_field; |
| 2522 | 2563 | ||
| 2523 | arg->hex.size = field; | 2564 | if (alloc_and_process_delim(event, ")", &arg->int_array.el_size)) |
| 2565 | goto free_size; | ||
| 2524 | 2566 | ||
| 2525 | free_token(token); | 2567 | return read_token_item(tok); |
| 2526 | type = read_token_item(tok); | ||
| 2527 | return type; | ||
| 2528 | 2568 | ||
| 2529 | out_free: | 2569 | free_size: |
| 2530 | free_arg(field); | 2570 | free_arg(arg->int_array.count); |
| 2531 | free_token(token); | 2571 | free_field: |
| 2572 | free_arg(arg->int_array.field); | ||
| 2573 | out: | ||
| 2532 | *tok = NULL; | 2574 | *tok = NULL; |
| 2533 | return EVENT_ERROR; | 2575 | return EVENT_ERROR; |
| 2534 | } | 2576 | } |
| @@ -2828,6 +2870,10 @@ process_function(struct event_format *event, struct print_arg *arg, | |||
| 2828 | free_token(token); | 2870 | free_token(token); |
| 2829 | return process_hex(event, arg, tok); | 2871 | return process_hex(event, arg, tok); |
| 2830 | } | 2872 | } |
| 2873 | if (strcmp(token, "__print_array") == 0) { | ||
| 2874 | free_token(token); | ||
| 2875 | return process_int_array(event, arg, tok); | ||
| 2876 | } | ||
| 2831 | if (strcmp(token, "__get_str") == 0) { | 2877 | if (strcmp(token, "__get_str") == 0) { |
| 2832 | free_token(token); | 2878 | free_token(token); |
| 2833 | return process_str(event, arg, tok); | 2879 | return process_str(event, arg, tok); |
| @@ -3356,6 +3402,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg | |||
| 3356 | break; | 3402 | break; |
| 3357 | case PRINT_FLAGS: | 3403 | case PRINT_FLAGS: |
| 3358 | case PRINT_SYMBOL: | 3404 | case PRINT_SYMBOL: |
| 3405 | case PRINT_INT_ARRAY: | ||
| 3359 | case PRINT_HEX: | 3406 | case PRINT_HEX: |
| 3360 | break; | 3407 | break; |
| 3361 | case PRINT_TYPE: | 3408 | case PRINT_TYPE: |
| @@ -3766,6 +3813,54 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, | |||
| 3766 | } | 3813 | } |
| 3767 | break; | 3814 | break; |
| 3768 | 3815 | ||
| 3816 | case PRINT_INT_ARRAY: { | ||
| 3817 | void *num; | ||
| 3818 | int el_size; | ||
| 3819 | |||
| 3820 | if (arg->int_array.field->type == PRINT_DYNAMIC_ARRAY) { | ||
| 3821 | unsigned long offset; | ||
| 3822 | struct format_field *field = | ||
| 3823 | arg->int_array.field->dynarray.field; | ||
| 3824 | offset = pevent_read_number(pevent, | ||
| 3825 | data + field->offset, | ||
| 3826 | field->size); | ||
| 3827 | num = data + (offset & 0xffff); | ||
| 3828 | } else { | ||
| 3829 | field = arg->int_array.field->field.field; | ||
| 3830 | if (!field) { | ||
| 3831 | str = arg->int_array.field->field.name; | ||
| 3832 | field = pevent_find_any_field(event, str); | ||
| 3833 | if (!field) | ||
| 3834 | goto out_warning_field; | ||
| 3835 | arg->int_array.field->field.field = field; | ||
| 3836 | } | ||
| 3837 | num = data + field->offset; | ||
| 3838 | } | ||
| 3839 | len = eval_num_arg(data, size, event, arg->int_array.count); | ||
| 3840 | el_size = eval_num_arg(data, size, event, | ||
| 3841 | arg->int_array.el_size); | ||
| 3842 | for (i = 0; i < len; i++) { | ||
| 3843 | if (i) | ||
| 3844 | trace_seq_putc(s, ' '); | ||
| 3845 | |||
| 3846 | if (el_size == 1) { | ||
| 3847 | trace_seq_printf(s, "%u", *(uint8_t *)num); | ||
| 3848 | } else if (el_size == 2) { | ||
| 3849 | trace_seq_printf(s, "%u", *(uint16_t *)num); | ||
| 3850 | } else if (el_size == 4) { | ||
| 3851 | trace_seq_printf(s, "%u", *(uint32_t *)num); | ||
| 3852 | } else if (el_size == 8) { | ||
| 3853 | trace_seq_printf(s, "%lu", *(uint64_t *)num); | ||
| 3854 | } else { | ||
| 3855 | trace_seq_printf(s, "BAD SIZE:%d 0x%x", | ||
| 3856 | el_size, *(uint8_t *)num); | ||
| 3857 | el_size = 1; | ||
| 3858 | } | ||
| 3859 | |||
| 3860 | num += el_size; | ||
| 3861 | } | ||
| 3862 | break; | ||
| 3863 | } | ||
| 3769 | case PRINT_TYPE: | 3864 | case PRINT_TYPE: |
| 3770 | break; | 3865 | break; |
| 3771 | case PRINT_STRING: { | 3866 | case PRINT_STRING: { |
| @@ -3997,6 +4092,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc | |||
| 3997 | goto process_again; | 4092 | goto process_again; |
| 3998 | case '.': | 4093 | case '.': |
| 3999 | goto process_again; | 4094 | goto process_again; |
| 4095 | case 'z': | ||
| 4096 | case 'Z': | ||
| 4097 | ls = 1; | ||
| 4098 | goto process_again; | ||
| 4000 | case 'p': | 4099 | case 'p': |
| 4001 | ls = 1; | 4100 | ls = 1; |
| 4002 | /* fall through */ | 4101 | /* fall through */ |
| @@ -4939,6 +5038,96 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) | |||
| 4939 | return comm; | 5038 | return comm; |
| 4940 | } | 5039 | } |
| 4941 | 5040 | ||
| 5041 | static struct cmdline * | ||
| 5042 | pid_from_cmdlist(struct pevent *pevent, const char *comm, struct cmdline *next) | ||
| 5043 | { | ||
| 5044 | struct cmdline_list *cmdlist = (struct cmdline_list *)next; | ||
| 5045 | |||
| 5046 | if (cmdlist) | ||
| 5047 | cmdlist = cmdlist->next; | ||
| 5048 | else | ||
| 5049 | cmdlist = pevent->cmdlist; | ||
| 5050 | |||
| 5051 | while (cmdlist && strcmp(cmdlist->comm, comm) != 0) | ||
| 5052 | cmdlist = cmdlist->next; | ||
| 5053 | |||
| 5054 | return (struct cmdline *)cmdlist; | ||
| 5055 | } | ||
| 5056 | |||
| 5057 | /** | ||
| 5058 | * pevent_data_pid_from_comm - return the pid from a given comm | ||
| 5059 | * @pevent: a handle to the pevent | ||
| 5060 | * @comm: the cmdline to find the pid from | ||
| 5061 | * @next: the cmdline structure to find the next comm | ||
| 5062 | * | ||
| 5063 | * This returns the cmdline structure that holds a pid for a given | ||
| 5064 | * comm, or NULL if none found. As there may be more than one pid for | ||
| 5065 | * a given comm, the result of this call can be passed back into | ||
| 5066 | * a recurring call in the @next paramater, and then it will find the | ||
| 5067 | * next pid. | ||
| 5068 | * Also, it does a linear seach, so it may be slow. | ||
| 5069 | */ | ||
| 5070 | struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm, | ||
| 5071 | struct cmdline *next) | ||
| 5072 | { | ||
| 5073 | struct cmdline *cmdline; | ||
| 5074 | |||
| 5075 | /* | ||
| 5076 | * If the cmdlines have not been converted yet, then use | ||
| 5077 | * the list. | ||
| 5078 | */ | ||
| 5079 | if (!pevent->cmdlines) | ||
| 5080 | return pid_from_cmdlist(pevent, comm, next); | ||
| 5081 | |||
| 5082 | if (next) { | ||
| 5083 | /* | ||
| 5084 | * The next pointer could have been still from | ||
| 5085 | * a previous call before cmdlines were created | ||
| 5086 | */ | ||
| 5087 | if (next < pevent->cmdlines || | ||
| 5088 | next >= pevent->cmdlines + pevent->cmdline_count) | ||
| 5089 | next = NULL; | ||
| 5090 | else | ||
| 5091 | cmdline = next++; | ||
| 5092 | } | ||
| 5093 | |||
| 5094 | if (!next) | ||
| 5095 | cmdline = pevent->cmdlines; | ||
| 5096 | |||
| 5097 | while (cmdline < pevent->cmdlines + pevent->cmdline_count) { | ||
| 5098 | if (strcmp(cmdline->comm, comm) == 0) | ||
| 5099 | return cmdline; | ||
| 5100 | cmdline++; | ||
| 5101 | } | ||
| 5102 | return NULL; | ||
| 5103 | } | ||
| 5104 | |||
| 5105 | /** | ||
| 5106 | * pevent_cmdline_pid - return the pid associated to a given cmdline | ||
| 5107 | * @cmdline: The cmdline structure to get the pid from | ||
| 5108 | * | ||
| 5109 | * Returns the pid for a give cmdline. If @cmdline is NULL, then | ||
| 5110 | * -1 is returned. | ||
| 5111 | */ | ||
| 5112 | int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline) | ||
| 5113 | { | ||
| 5114 | struct cmdline_list *cmdlist = (struct cmdline_list *)cmdline; | ||
| 5115 | |||
| 5116 | if (!cmdline) | ||
| 5117 | return -1; | ||
| 5118 | |||
| 5119 | /* | ||
| 5120 | * If cmdlines have not been created yet, or cmdline is | ||
| 5121 | * not part of the array, then treat it as a cmdlist instead. | ||
| 5122 | */ | ||
| 5123 | if (!pevent->cmdlines || | ||
| 5124 | cmdline < pevent->cmdlines || | ||
| 5125 | cmdline >= pevent->cmdlines + pevent->cmdline_count) | ||
| 5126 | return cmdlist->pid; | ||
| 5127 | |||
| 5128 | return cmdline->pid; | ||
| 5129 | } | ||
| 5130 | |||
| 4942 | /** | 5131 | /** |
| 4943 | * pevent_data_comm_from_pid - parse the data into the print format | 5132 | * pevent_data_comm_from_pid - parse the data into the print format |
| 4944 | * @s: the trace_seq to write to | 5133 | * @s: the trace_seq to write to |
| @@ -5256,6 +5445,15 @@ static void print_args(struct print_arg *args) | |||
| 5256 | print_args(args->hex.size); | 5445 | print_args(args->hex.size); |
| 5257 | printf(")"); | 5446 | printf(")"); |
| 5258 | break; | 5447 | break; |
| 5448 | case PRINT_INT_ARRAY: | ||
| 5449 | printf("__print_array("); | ||
| 5450 | print_args(args->int_array.field); | ||
| 5451 | printf(", "); | ||
| 5452 | print_args(args->int_array.count); | ||
| 5453 | printf(", "); | ||
| 5454 | print_args(args->int_array.el_size); | ||
| 5455 | printf(")"); | ||
| 5456 | break; | ||
| 5259 | case PRINT_STRING: | 5457 | case PRINT_STRING: |
| 5260 | case PRINT_BSTRING: | 5458 | case PRINT_BSTRING: |
| 5261 | printf("__get_str(%s)", args->string.string); | 5459 | printf("__get_str(%s)", args->string.string); |
| @@ -6346,6 +6544,7 @@ void pevent_free(struct pevent *pevent) | |||
| 6346 | free_handler(handle); | 6544 | free_handler(handle); |
| 6347 | } | 6545 | } |
| 6348 | 6546 | ||
| 6547 | free(pevent->trace_clock); | ||
| 6349 | free(pevent->events); | 6548 | free(pevent->events); |
| 6350 | free(pevent->sort_events); | 6549 | free(pevent->sort_events); |
| 6351 | 6550 | ||
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 6abda54d76f2..86a5839fb048 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
| @@ -116,7 +116,7 @@ struct pevent_plugin_option { | |||
| 116 | char *name; | 116 | char *name; |
| 117 | char *plugin_alias; | 117 | char *plugin_alias; |
| 118 | char *description; | 118 | char *description; |
| 119 | char *value; | 119 | const char *value; |
| 120 | void *priv; | 120 | void *priv; |
| 121 | int set; | 121 | int set; |
| 122 | }; | 122 | }; |
| @@ -154,6 +154,10 @@ struct pevent_plugin_option { | |||
| 154 | * .plugin_alias is used to give a shorter name to access | 154 | * .plugin_alias is used to give a shorter name to access |
| 155 | * the vairable. Useful if a plugin handles more than one event. | 155 | * the vairable. Useful if a plugin handles more than one event. |
| 156 | * | 156 | * |
| 157 | * If .value is not set, then it is considered a boolean and only | ||
| 158 | * .set will be processed. If .value is defined, then it is considered | ||
| 159 | * a string option and .set will be ignored. | ||
| 160 | * | ||
| 157 | * PEVENT_PLUGIN_ALIAS: (optional) | 161 | * PEVENT_PLUGIN_ALIAS: (optional) |
| 158 | * The name to use for finding options (uses filename if not defined) | 162 | * The name to use for finding options (uses filename if not defined) |
| 159 | */ | 163 | */ |
| @@ -247,6 +251,12 @@ struct print_arg_hex { | |||
| 247 | struct print_arg *size; | 251 | struct print_arg *size; |
| 248 | }; | 252 | }; |
| 249 | 253 | ||
| 254 | struct print_arg_int_array { | ||
| 255 | struct print_arg *field; | ||
| 256 | struct print_arg *count; | ||
| 257 | struct print_arg *el_size; | ||
| 258 | }; | ||
| 259 | |||
| 250 | struct print_arg_dynarray { | 260 | struct print_arg_dynarray { |
| 251 | struct format_field *field; | 261 | struct format_field *field; |
| 252 | struct print_arg *index; | 262 | struct print_arg *index; |
| @@ -275,6 +285,7 @@ enum print_arg_type { | |||
| 275 | PRINT_FLAGS, | 285 | PRINT_FLAGS, |
| 276 | PRINT_SYMBOL, | 286 | PRINT_SYMBOL, |
| 277 | PRINT_HEX, | 287 | PRINT_HEX, |
| 288 | PRINT_INT_ARRAY, | ||
| 278 | PRINT_TYPE, | 289 | PRINT_TYPE, |
| 279 | PRINT_STRING, | 290 | PRINT_STRING, |
| 280 | PRINT_BSTRING, | 291 | PRINT_BSTRING, |
| @@ -294,6 +305,7 @@ struct print_arg { | |||
| 294 | struct print_arg_flags flags; | 305 | struct print_arg_flags flags; |
| 295 | struct print_arg_symbol symbol; | 306 | struct print_arg_symbol symbol; |
| 296 | struct print_arg_hex hex; | 307 | struct print_arg_hex hex; |
| 308 | struct print_arg_int_array int_array; | ||
| 297 | struct print_arg_func func; | 309 | struct print_arg_func func; |
| 298 | struct print_arg_string string; | 310 | struct print_arg_string string; |
| 299 | struct print_arg_bitmask bitmask; | 311 | struct print_arg_bitmask bitmask; |
| @@ -599,7 +611,7 @@ enum trace_flag_type { | |||
| 599 | }; | 611 | }; |
| 600 | 612 | ||
| 601 | int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); | 613 | int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); |
| 602 | void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock); | 614 | int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock); |
| 603 | int pevent_register_function(struct pevent *pevent, char *name, | 615 | int pevent_register_function(struct pevent *pevent, char *name, |
| 604 | unsigned long long addr, char *mod); | 616 | unsigned long long addr, char *mod); |
| 605 | int pevent_register_print_string(struct pevent *pevent, const char *fmt, | 617 | int pevent_register_print_string(struct pevent *pevent, const char *fmt, |
| @@ -678,6 +690,11 @@ int pevent_data_type(struct pevent *pevent, struct pevent_record *rec); | |||
| 678 | struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); | 690 | struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); |
| 679 | int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); | 691 | int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); |
| 680 | const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); | 692 | const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); |
| 693 | struct cmdline; | ||
| 694 | struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm, | ||
| 695 | struct cmdline *next); | ||
| 696 | int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline); | ||
| 697 | |||
| 681 | void pevent_event_info(struct trace_seq *s, struct event_format *event, | 698 | void pevent_event_info(struct trace_seq *s, struct event_format *event, |
| 682 | struct pevent_record *record); | 699 | struct pevent_record *record); |
| 683 | int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | 700 | int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, |
diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c index 136162c03af1..a16756ae3526 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 <ctype.h> | ||
| 21 | #include <stdio.h> | 22 | #include <stdio.h> |
| 22 | #include <string.h> | 23 | #include <string.h> |
| 23 | #include <dlfcn.h> | 24 | #include <dlfcn.h> |
| @@ -49,6 +50,52 @@ struct plugin_list { | |||
| 49 | void *handle; | 50 | void *handle; |
| 50 | }; | 51 | }; |
| 51 | 52 | ||
| 53 | static void lower_case(char *str) | ||
| 54 | { | ||
| 55 | if (!str) | ||
| 56 | return; | ||
| 57 | for (; *str; str++) | ||
| 58 | *str = tolower(*str); | ||
| 59 | } | ||
| 60 | |||
| 61 | static int update_option_value(struct pevent_plugin_option *op, const char *val) | ||
| 62 | { | ||
| 63 | char *op_val; | ||
| 64 | |||
| 65 | if (!val) { | ||
| 66 | /* toggle, only if option is boolean */ | ||
| 67 | if (op->value) | ||
| 68 | /* Warn? */ | ||
| 69 | return 0; | ||
| 70 | op->set ^= 1; | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* | ||
| 75 | * If the option has a value then it takes a string | ||
| 76 | * otherwise the option is a boolean. | ||
| 77 | */ | ||
| 78 | if (op->value) { | ||
| 79 | op->value = val; | ||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Option is boolean, must be either "1", "0", "true" or "false" */ | ||
| 84 | |||
| 85 | op_val = strdup(val); | ||
| 86 | if (!op_val) | ||
| 87 | return -1; | ||
| 88 | lower_case(op_val); | ||
| 89 | |||
| 90 | if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0) | ||
| 91 | op->set = 1; | ||
| 92 | else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0) | ||
| 93 | op->set = 0; | ||
| 94 | free(op_val); | ||
| 95 | |||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 52 | /** | 99 | /** |
| 53 | * traceevent_plugin_list_options - get list of plugin options | 100 | * traceevent_plugin_list_options - get list of plugin options |
| 54 | * | 101 | * |
| @@ -120,6 +167,7 @@ update_option(const char *file, struct pevent_plugin_option *option) | |||
| 120 | { | 167 | { |
| 121 | struct trace_plugin_options *op; | 168 | struct trace_plugin_options *op; |
| 122 | char *plugin; | 169 | char *plugin; |
| 170 | int ret = 0; | ||
| 123 | 171 | ||
| 124 | if (option->plugin_alias) { | 172 | if (option->plugin_alias) { |
| 125 | plugin = strdup(option->plugin_alias); | 173 | plugin = strdup(option->plugin_alias); |
| @@ -144,9 +192,10 @@ update_option(const char *file, struct pevent_plugin_option *option) | |||
| 144 | if (strcmp(op->option, option->name) != 0) | 192 | if (strcmp(op->option, option->name) != 0) |
| 145 | continue; | 193 | continue; |
| 146 | 194 | ||
| 147 | option->value = op->value; | 195 | ret = update_option_value(option, op->value); |
| 148 | option->set ^= 1; | 196 | if (ret) |
| 149 | goto out; | 197 | goto out; |
| 198 | break; | ||
| 150 | } | 199 | } |
| 151 | 200 | ||
| 152 | /* first look for unnamed options */ | 201 | /* first look for unnamed options */ |
| @@ -156,14 +205,13 @@ update_option(const char *file, struct pevent_plugin_option *option) | |||
| 156 | if (strcmp(op->option, option->name) != 0) | 205 | if (strcmp(op->option, option->name) != 0) |
| 157 | continue; | 206 | continue; |
| 158 | 207 | ||
| 159 | option->value = op->value; | 208 | ret = update_option_value(option, op->value); |
| 160 | option->set ^= 1; | ||
| 161 | break; | 209 | break; |
| 162 | } | 210 | } |
| 163 | 211 | ||
| 164 | out: | 212 | out: |
| 165 | free(plugin); | 213 | free(plugin); |
| 166 | return 0; | 214 | return ret; |
| 167 | } | 215 | } |
| 168 | 216 | ||
| 169 | /** | 217 | /** |
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c index dcc665228c71..3bcada3ae05a 100644 --- a/tools/lib/traceevent/kbuffer-parse.c +++ b/tools/lib/traceevent/kbuffer-parse.c | |||
| @@ -372,7 +372,6 @@ translate_data(struct kbuffer *kbuf, void *data, void **rptr, | |||
| 372 | switch (type_len) { | 372 | switch (type_len) { |
| 373 | case KBUFFER_TYPE_PADDING: | 373 | case KBUFFER_TYPE_PADDING: |
| 374 | *length = read_4(kbuf, data); | 374 | *length = read_4(kbuf, data); |
| 375 | data += *length; | ||
| 376 | break; | 375 | break; |
| 377 | 376 | ||
| 378 | case KBUFFER_TYPE_TIME_EXTEND: | 377 | case KBUFFER_TYPE_TIME_EXTEND: |
| @@ -730,3 +729,14 @@ void kbuffer_set_old_format(struct kbuffer *kbuf) | |||
| 730 | 729 | ||
| 731 | kbuf->next_event = __old_next_event; | 730 | kbuf->next_event = __old_next_event; |
| 732 | } | 731 | } |
| 732 | |||
| 733 | /** | ||
| 734 | * kbuffer_start_of_data - return offset of where data starts on subbuffer | ||
| 735 | * @kbuf: The kbuffer | ||
| 736 | * | ||
| 737 | * Returns the location on the subbuffer where the data starts. | ||
| 738 | */ | ||
| 739 | int kbuffer_start_of_data(struct kbuffer *kbuf) | ||
| 740 | { | ||
| 741 | return kbuf->start; | ||
| 742 | } | ||
diff --git a/tools/lib/traceevent/kbuffer.h b/tools/lib/traceevent/kbuffer.h index c831f64b17a0..03dce757553f 100644 --- a/tools/lib/traceevent/kbuffer.h +++ b/tools/lib/traceevent/kbuffer.h | |||
| @@ -63,5 +63,6 @@ int kbuffer_missed_events(struct kbuffer *kbuf); | |||
| 63 | int kbuffer_subbuffer_size(struct kbuffer *kbuf); | 63 | int kbuffer_subbuffer_size(struct kbuffer *kbuf); |
| 64 | 64 | ||
| 65 | void kbuffer_set_old_format(struct kbuffer *kbuf); | 65 | void kbuffer_set_old_format(struct kbuffer *kbuf); |
| 66 | int kbuffer_start_of_data(struct kbuffer *kbuf); | ||
| 66 | 67 | ||
| 67 | #endif /* _K_BUFFER_H */ | 68 | #endif /* _K_BUFFER_H */ |
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index b50234402fc2..0144b3d1bb77 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c | |||
| @@ -1058,6 +1058,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, | |||
| 1058 | *parg = current_op; | 1058 | *parg = current_op; |
| 1059 | else | 1059 | else |
| 1060 | *parg = current_exp; | 1060 | *parg = current_exp; |
| 1061 | free(token); | ||
| 1061 | return PEVENT_ERRNO__UNBALANCED_PAREN; | 1062 | return PEVENT_ERRNO__UNBALANCED_PAREN; |
| 1062 | } | 1063 | } |
| 1063 | break; | 1064 | break; |
| @@ -1168,6 +1169,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, | |||
| 1168 | 1169 | ||
| 1169 | *parg = current_op; | 1170 | *parg = current_op; |
| 1170 | 1171 | ||
| 1172 | free(token); | ||
| 1171 | return 0; | 1173 | return 0; |
| 1172 | 1174 | ||
| 1173 | fail_alloc: | 1175 | fail_alloc: |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 518266192d67..d1deb573877f 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
| @@ -31,6 +31,9 @@ OPTIONS | |||
| 31 | --dump-raw-trace:: | 31 | --dump-raw-trace:: |
| 32 | Dump raw trace in ASCII. | 32 | Dump raw trace in ASCII. |
| 33 | 33 | ||
| 34 | --kallsyms=<file>:: | ||
| 35 | kallsyms pathname | ||
| 36 | |||
| 34 | -m:: | 37 | -m:: |
| 35 | --modules:: | 38 | --modules:: |
| 36 | Load module symbols. WARNING: use only with -k and LIVE kernel | 39 | Load module symbols. WARNING: use only with -k and LIVE kernel |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index dd7cccdde498..4879cf638824 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -40,6 +40,11 @@ OPTIONS | |||
| 40 | Only consider symbols in these comms. CSV that understands | 40 | Only consider symbols in these comms. CSV that understands |
| 41 | file://filename entries. This option will affect the percentage of | 41 | file://filename entries. This option will affect the percentage of |
| 42 | the overhead column. See --percentage for more info. | 42 | the overhead column. See --percentage for more info. |
| 43 | --pid=:: | ||
| 44 | Only show events for given process ID (comma separated list). | ||
| 45 | |||
| 46 | --tid=:: | ||
| 47 | Only show events for given thread ID (comma separated list). | ||
| 43 | -d:: | 48 | -d:: |
| 44 | --dsos=:: | 49 | --dsos=:: |
| 45 | Only consider symbols in these dsos. CSV that understands | 50 | Only consider symbols in these dsos. CSV that understands |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index a21eec05bc42..79445750fcb3 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
| @@ -193,6 +193,12 @@ OPTIONS | |||
| 193 | Only display events for these comms. CSV that understands | 193 | Only display events for these comms. CSV that understands |
| 194 | file://filename entries. | 194 | file://filename entries. |
| 195 | 195 | ||
| 196 | --pid=:: | ||
| 197 | Only show events for given process ID (comma separated list). | ||
| 198 | |||
| 199 | --tid=:: | ||
| 200 | Only show events for given thread ID (comma separated list). | ||
| 201 | |||
| 196 | -I:: | 202 | -I:: |
| 197 | --show-info:: | 203 | --show-info:: |
| 198 | Display extended information about the perf.data file. This adds | 204 | Display extended information about the perf.data file. This adds |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 473887d1d61a..df6307b4050a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -791,6 +791,8 @@ static const struct option options[] = { | |||
| 791 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 791 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| 792 | "dump raw trace in ASCII"), | 792 | "dump raw trace in ASCII"), |
| 793 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 793 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
| 794 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, | ||
| 795 | "file", "kallsyms pathname"), | ||
| 794 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 796 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
| 795 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 797 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
| 796 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 798 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 8c85aeb3327a..64d3623d45a0 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | #include <linux/rbtree.h> | 21 | #include <linux/rbtree.h> |
| 22 | #include <linux/string.h> | 22 | #include <linux/string.h> |
| 23 | #include <locale.h> | ||
| 23 | 24 | ||
| 24 | struct alloc_stat; | 25 | struct alloc_stat; |
| 25 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); | 26 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); |
| @@ -325,13 +326,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
| 325 | static void print_summary(void) | 326 | static void print_summary(void) |
| 326 | { | 327 | { |
| 327 | printf("\nSUMMARY\n=======\n"); | 328 | printf("\nSUMMARY\n=======\n"); |
| 328 | printf("Total bytes requested: %lu\n", total_requested); | 329 | printf("Total bytes requested: %'lu\n", total_requested); |
| 329 | printf("Total bytes allocated: %lu\n", total_allocated); | 330 | printf("Total bytes allocated: %'lu\n", total_allocated); |
| 330 | printf("Total bytes wasted on internal fragmentation: %lu\n", | 331 | printf("Total bytes wasted on internal fragmentation: %'lu\n", |
| 331 | total_allocated - total_requested); | 332 | total_allocated - total_requested); |
| 332 | printf("Internal fragmentation: %f%%\n", | 333 | printf("Internal fragmentation: %f%%\n", |
| 333 | fragmentation(total_requested, total_allocated)); | 334 | fragmentation(total_requested, total_allocated)); |
| 334 | printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs); | 335 | printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs); |
| 335 | } | 336 | } |
| 336 | 337 | ||
| 337 | static void print_result(struct perf_session *session) | 338 | static void print_result(struct perf_session *session) |
| @@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 706 | symbol__init(&session->header.env); | 707 | symbol__init(&session->header.env); |
| 707 | 708 | ||
| 708 | if (!strcmp(argv[0], "stat")) { | 709 | if (!strcmp(argv[0], "stat")) { |
| 710 | setlocale(LC_ALL, ""); | ||
| 711 | |||
| 709 | if (cpu__setup_cpunode_map()) | 712 | if (cpu__setup_cpunode_map()) |
| 710 | goto out_delete; | 713 | goto out_delete; |
| 711 | 714 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 0ae482689e3c..b5b2ad4ca9c4 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -304,7 +304,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report | |||
| 304 | 304 | ||
| 305 | if (rep->mem_mode) { | 305 | if (rep->mem_mode) { |
| 306 | ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events); | 306 | ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events); |
| 307 | ret += fprintf(fp, "\n# Sort order : %s", sort_order); | 307 | ret += fprintf(fp, "\n# Sort order : %s", sort_order ? : default_mem_sort_order); |
| 308 | } else | 308 | } else |
| 309 | ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); | 309 | ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); |
| 310 | return ret + fprintf(fp, "\n#\n"); | 310 | return ret + fprintf(fp, "\n#\n"); |
| @@ -669,6 +669,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 669 | "only consider symbols in these dsos"), | 669 | "only consider symbols in these dsos"), |
| 670 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 670 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
| 671 | "only consider symbols in these comms"), | 671 | "only consider symbols in these comms"), |
| 672 | OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]", | ||
| 673 | "only consider symbols in these pids"), | ||
| 674 | OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", | ||
| 675 | "only consider symbols in these tids"), | ||
| 672 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 676 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
| 673 | "only consider these symbols"), | 677 | "only consider these symbols"), |
| 674 | OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter", | 678 | OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter", |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index f2a348b57b8f..662366ceb572 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -1562,6 +1562,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1562 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 1562 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
| 1563 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 1563 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
| 1564 | "only display events for these comms"), | 1564 | "only display events for these comms"), |
| 1565 | OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]", | ||
| 1566 | "only consider symbols in these pids"), | ||
| 1567 | OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", | ||
| 1568 | "only consider symbols in these tids"), | ||
| 1565 | OPT_BOOLEAN('I', "show-info", &show_full_info, | 1569 | OPT_BOOLEAN('I', "show-info", &show_full_info, |
| 1566 | "display extended information from perf.data file"), | 1570 | "display extended information from perf.data file"), |
| 1567 | OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, | 1571 | OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5fb8723c7128..1cb3436276d1 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
| 757 | al.map == machine->vmlinux_maps[MAP__FUNCTION] && | 757 | al.map == machine->vmlinux_maps[MAP__FUNCTION] && |
| 758 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | 758 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
| 759 | if (symbol_conf.vmlinux_name) { | 759 | if (symbol_conf.vmlinux_name) { |
| 760 | ui__warning("The %s file can't be used.\n%s", | 760 | char serr[256]; |
| 761 | symbol_conf.vmlinux_name, msg); | 761 | dso__strerror_load(al.map->dso, serr, sizeof(serr)); |
| 762 | ui__warning("The %s file can't be used: %s\n%s", | ||
| 763 | symbol_conf.vmlinux_name, serr, msg); | ||
| 762 | } else { | 764 | } else { |
| 763 | ui__warning("A vmlinux file was not found.\n%s", | 765 | ui__warning("A vmlinux file was not found.\n%s", |
| 764 | msg); | 766 | msg); |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 61bf9128e1f2..b72086eca943 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -1008,6 +1008,32 @@ fallback: | |||
| 1008 | } | 1008 | } |
| 1009 | filename = symfs_filename; | 1009 | filename = symfs_filename; |
| 1010 | } | 1010 | } |
| 1011 | } else if (dso__needs_decompress(dso)) { | ||
| 1012 | char tmp[PATH_MAX]; | ||
| 1013 | struct kmod_path m; | ||
| 1014 | int fd; | ||
| 1015 | bool ret; | ||
| 1016 | |||
| 1017 | if (kmod_path__parse_ext(&m, symfs_filename)) | ||
| 1018 | goto out_free_filename; | ||
| 1019 | |||
| 1020 | snprintf(tmp, PATH_MAX, "/tmp/perf-kmod-XXXXXX"); | ||
| 1021 | |||
| 1022 | fd = mkstemp(tmp); | ||
| 1023 | if (fd < 0) { | ||
| 1024 | free(m.ext); | ||
| 1025 | goto out_free_filename; | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | ret = decompress_to_file(m.ext, symfs_filename, fd); | ||
| 1029 | |||
| 1030 | free(m.ext); | ||
| 1031 | close(fd); | ||
| 1032 | |||
| 1033 | if (!ret) | ||
| 1034 | goto out_free_filename; | ||
| 1035 | |||
| 1036 | strcpy(symfs_filename, tmp); | ||
| 1011 | } | 1037 | } |
| 1012 | 1038 | ||
| 1013 | snprintf(command, sizeof(command), | 1039 | snprintf(command, sizeof(command), |
| @@ -1027,7 +1053,7 @@ fallback: | |||
| 1027 | 1053 | ||
| 1028 | file = popen(command, "r"); | 1054 | file = popen(command, "r"); |
| 1029 | if (!file) | 1055 | if (!file) |
| 1030 | goto out_free_filename; | 1056 | goto out_remove_tmp; |
| 1031 | 1057 | ||
| 1032 | while (!feof(file)) | 1058 | while (!feof(file)) |
| 1033 | if (symbol__parse_objdump_line(sym, map, file, privsize, | 1059 | if (symbol__parse_objdump_line(sym, map, file, privsize, |
| @@ -1042,6 +1068,10 @@ fallback: | |||
| 1042 | delete_last_nop(sym); | 1068 | delete_last_nop(sym); |
| 1043 | 1069 | ||
| 1044 | pclose(file); | 1070 | pclose(file); |
| 1071 | |||
| 1072 | out_remove_tmp: | ||
| 1073 | if (dso__needs_decompress(dso)) | ||
| 1074 | unlink(symfs_filename); | ||
| 1045 | out_free_filename: | 1075 | out_free_filename: |
| 1046 | if (delete_extract) | 1076 | if (delete_extract) |
| 1047 | kcore_extract__delete(&kce); | 1077 | kcore_extract__delete(&kce); |
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c index 6da965bdbc2c..85b523885f9d 100644 --- a/tools/perf/util/cloexec.c +++ b/tools/perf/util/cloexec.c | |||
| @@ -7,6 +7,12 @@ | |||
| 7 | 7 | ||
| 8 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; | 8 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; |
| 9 | 9 | ||
| 10 | int __weak sched_getcpu(void) | ||
| 11 | { | ||
| 12 | errno = ENOSYS; | ||
| 13 | return -1; | ||
| 14 | } | ||
| 15 | |||
| 10 | static int perf_flag_probe(void) | 16 | static int perf_flag_probe(void) |
| 11 | { | 17 | { |
| 12 | /* use 'safest' configuration as used in perf_evsel__fallback() */ | 18 | /* use 'safest' configuration as used in perf_evsel__fallback() */ |
diff --git a/tools/perf/util/cloexec.h b/tools/perf/util/cloexec.h index 94a5a7d829d5..68888c29b04a 100644 --- a/tools/perf/util/cloexec.h +++ b/tools/perf/util/cloexec.h | |||
| @@ -3,4 +3,10 @@ | |||
| 3 | 3 | ||
| 4 | unsigned long perf_event_open_cloexec_flag(void); | 4 | unsigned long perf_event_open_cloexec_flag(void); |
| 5 | 5 | ||
| 6 | #ifdef __GLIBC_PREREQ | ||
| 7 | #if !__GLIBC_PREREQ(2, 6) | ||
| 8 | extern int sched_getcpu(void) __THROW; | ||
| 9 | #endif | ||
| 10 | #endif | ||
| 11 | |||
| 6 | #endif /* __PERF_CLOEXEC_H */ | 12 | #endif /* __PERF_CLOEXEC_H */ |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 7a7c54b42b41..fc0ddd5792a9 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
| @@ -165,32 +165,14 @@ bool is_supported_compression(const char *ext) | |||
| 165 | return false; | 165 | return false; |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | bool is_kmodule_extension(const char *ext) | 168 | bool is_kernel_module(const char *pathname) |
| 169 | { | 169 | { |
| 170 | if (strncmp(ext, "ko", 2)) | 170 | struct kmod_path m; |
| 171 | return false; | ||
| 172 | 171 | ||
| 173 | if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3))) | 172 | if (kmod_path__parse(&m, pathname)) |
| 174 | return true; | 173 | return NULL; |
| 175 | |||
| 176 | return false; | ||
| 177 | } | ||
| 178 | |||
| 179 | bool is_kernel_module(const char *pathname, bool *compressed) | ||
| 180 | { | ||
| 181 | const char *ext = strrchr(pathname, '.'); | ||
| 182 | |||
| 183 | if (ext == NULL) | ||
| 184 | return false; | ||
| 185 | |||
| 186 | if (is_supported_compression(ext + 1)) { | ||
| 187 | if (compressed) | ||
| 188 | *compressed = true; | ||
| 189 | ext -= 3; | ||
| 190 | } else if (compressed) | ||
| 191 | *compressed = false; | ||
| 192 | 174 | ||
| 193 | return is_kmodule_extension(ext + 1); | 175 | return m.kmod; |
| 194 | } | 176 | } |
| 195 | 177 | ||
| 196 | bool decompress_to_file(const char *ext, const char *filename, int output_fd) | 178 | bool decompress_to_file(const char *ext, const char *filename, int output_fd) |
| @@ -1155,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine) | |||
| 1155 | 1137 | ||
| 1156 | return dso__type_fd(fd); | 1138 | return dso__type_fd(fd); |
| 1157 | } | 1139 | } |
| 1140 | |||
| 1141 | int dso__strerror_load(struct dso *dso, char *buf, size_t buflen) | ||
| 1142 | { | ||
| 1143 | int idx, errnum = dso->load_errno; | ||
| 1144 | /* | ||
| 1145 | * This must have a same ordering as the enum dso_load_errno. | ||
| 1146 | */ | ||
| 1147 | static const char *dso_load__error_str[] = { | ||
| 1148 | "Internal tools/perf/ library error", | ||
| 1149 | "Invalid ELF file", | ||
| 1150 | "Can not read build id", | ||
| 1151 | "Mismatching build id", | ||
| 1152 | "Decompression failure", | ||
| 1153 | }; | ||
| 1154 | |||
| 1155 | BUG_ON(buflen == 0); | ||
| 1156 | |||
| 1157 | if (errnum >= 0) { | ||
| 1158 | const char *err = strerror_r(errnum, buf, buflen); | ||
| 1159 | |||
| 1160 | if (err != buf) | ||
| 1161 | scnprintf(buf, buflen, "%s", err); | ||
| 1162 | |||
| 1163 | return 0; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | if (errnum < __DSO_LOAD_ERRNO__START || errnum >= __DSO_LOAD_ERRNO__END) | ||
| 1167 | return -1; | ||
| 1168 | |||
| 1169 | idx = errnum - __DSO_LOAD_ERRNO__START; | ||
| 1170 | scnprintf(buf, buflen, "%s", dso_load__error_str[idx]); | ||
| 1171 | return 0; | ||
| 1172 | } | ||
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 3c81d8378bc7..e0901b4ed8de 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
| @@ -60,6 +60,31 @@ enum dso_type { | |||
| 60 | DSO__TYPE_X32BIT, | 60 | DSO__TYPE_X32BIT, |
| 61 | }; | 61 | }; |
| 62 | 62 | ||
| 63 | enum dso_load_errno { | ||
| 64 | DSO_LOAD_ERRNO__SUCCESS = 0, | ||
| 65 | |||
| 66 | /* | ||
| 67 | * Choose an arbitrary negative big number not to clash with standard | ||
| 68 | * errno since SUS requires the errno has distinct positive values. | ||
| 69 | * See 'Issue 6' in the link below. | ||
| 70 | * | ||
| 71 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html | ||
| 72 | */ | ||
| 73 | __DSO_LOAD_ERRNO__START = -10000, | ||
| 74 | |||
| 75 | DSO_LOAD_ERRNO__INTERNAL_ERROR = __DSO_LOAD_ERRNO__START, | ||
| 76 | |||
| 77 | /* for symsrc__init() */ | ||
| 78 | DSO_LOAD_ERRNO__INVALID_ELF, | ||
| 79 | DSO_LOAD_ERRNO__CANNOT_READ_BUILDID, | ||
| 80 | DSO_LOAD_ERRNO__MISMATCHING_BUILDID, | ||
| 81 | |||
| 82 | /* for decompress_kmodule */ | ||
| 83 | DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE, | ||
| 84 | |||
| 85 | __DSO_LOAD_ERRNO__END, | ||
| 86 | }; | ||
| 87 | |||
| 63 | #define DSO__SWAP(dso, type, val) \ | 88 | #define DSO__SWAP(dso, type, val) \ |
| 64 | ({ \ | 89 | ({ \ |
| 65 | type ____r = val; \ | 90 | type ____r = val; \ |
| @@ -113,6 +138,7 @@ struct dso { | |||
| 113 | enum dso_swap_type needs_swap; | 138 | enum dso_swap_type needs_swap; |
| 114 | enum dso_binary_type symtab_type; | 139 | enum dso_binary_type symtab_type; |
| 115 | enum dso_binary_type binary_type; | 140 | enum dso_binary_type binary_type; |
| 141 | enum dso_load_errno load_errno; | ||
| 116 | u8 adjust_symbols:1; | 142 | u8 adjust_symbols:1; |
| 117 | u8 has_build_id:1; | 143 | u8 has_build_id:1; |
| 118 | u8 has_srcline:1; | 144 | u8 has_srcline:1; |
| @@ -190,8 +216,7 @@ char dso__symtab_origin(const struct dso *dso); | |||
| 190 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 216 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
| 191 | char *root_dir, char *filename, size_t size); | 217 | char *root_dir, char *filename, size_t size); |
| 192 | bool is_supported_compression(const char *ext); | 218 | bool is_supported_compression(const char *ext); |
| 193 | bool is_kmodule_extension(const char *ext); | 219 | bool is_kernel_module(const char *pathname); |
| 194 | bool is_kernel_module(const char *pathname, bool *compressed); | ||
| 195 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); | 220 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); |
| 196 | bool dso__needs_decompress(struct dso *dso); | 221 | bool dso__needs_decompress(struct dso *dso); |
| 197 | 222 | ||
| @@ -295,4 +320,6 @@ void dso__free_a2l(struct dso *dso); | |||
| 295 | 320 | ||
| 296 | enum dso_type dso__type(struct dso *dso, struct machine *machine); | 321 | enum dso_type dso__type(struct dso *dso, struct machine *machine); |
| 297 | 322 | ||
| 323 | int dso__strerror_load(struct dso *dso, char *buf, size_t buflen); | ||
| 324 | |||
| 298 | #endif /* __PERF_DSO */ | 325 | #endif /* __PERF_DSO */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1f407f7352a7..fb432153e2aa 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -1266,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
| 1266 | 1266 | ||
| 1267 | dso__set_build_id(dso, &bev->build_id); | 1267 | dso__set_build_id(dso, &bev->build_id); |
| 1268 | 1268 | ||
| 1269 | if (!is_kernel_module(filename, NULL)) | 1269 | if (!is_kernel_module(filename)) |
| 1270 | dso->kernel = dso_type; | 1270 | dso->kernel = dso_type; |
| 1271 | 1271 | ||
| 1272 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 1272 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index eb95b883fb44..e3353307330c 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -498,6 +498,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 498 | if (kmod_path__parse_name(&m, filename)) | 498 | if (kmod_path__parse_name(&m, filename)) |
| 499 | return NULL; | 499 | return NULL; |
| 500 | 500 | ||
| 501 | map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION, | ||
| 502 | m.name); | ||
| 503 | if (map) | ||
| 504 | goto out; | ||
| 505 | |||
| 501 | dso = machine__module_dso(machine, &m, filename); | 506 | dso = machine__module_dso(machine, &m, filename); |
| 502 | if (dso == NULL) | 507 | if (dso == NULL) |
| 503 | goto out; | 508 | goto out; |
| @@ -851,6 +856,39 @@ static char *get_kernel_version(const char *root_dir) | |||
| 851 | return strdup(name); | 856 | return strdup(name); |
| 852 | } | 857 | } |
| 853 | 858 | ||
| 859 | static bool is_kmod_dso(struct dso *dso) | ||
| 860 | { | ||
| 861 | return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | ||
| 862 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; | ||
| 863 | } | ||
| 864 | |||
| 865 | static int map_groups__set_module_path(struct map_groups *mg, const char *path, | ||
| 866 | struct kmod_path *m) | ||
| 867 | { | ||
| 868 | struct map *map; | ||
| 869 | char *long_name; | ||
| 870 | |||
| 871 | map = map_groups__find_by_name(mg, MAP__FUNCTION, m->name); | ||
| 872 | if (map == NULL) | ||
| 873 | return 0; | ||
| 874 | |||
| 875 | long_name = strdup(path); | ||
| 876 | if (long_name == NULL) | ||
| 877 | return -ENOMEM; | ||
| 878 | |||
| 879 | dso__set_long_name(map->dso, long_name, true); | ||
| 880 | dso__kernel_module_get_build_id(map->dso, ""); | ||
| 881 | |||
| 882 | /* | ||
| 883 | * Full name could reveal us kmod compression, so | ||
| 884 | * we need to update the symtab_type if needed. | ||
| 885 | */ | ||
| 886 | if (m->comp && is_kmod_dso(map->dso)) | ||
| 887 | map->dso->symtab_type++; | ||
| 888 | |||
| 889 | return 0; | ||
| 890 | } | ||
| 891 | |||
| 854 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | 892 | static int map_groups__set_modules_path_dir(struct map_groups *mg, |
| 855 | const char *dir_name, int depth) | 893 | const char *dir_name, int depth) |
| 856 | { | 894 | { |
| @@ -889,35 +927,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, | |||
| 889 | if (ret < 0) | 927 | if (ret < 0) |
| 890 | goto out; | 928 | goto out; |
| 891 | } else { | 929 | } else { |
| 892 | char *dot = strrchr(dent->d_name, '.'), | 930 | struct kmod_path m; |
| 893 | dso_name[PATH_MAX]; | ||
| 894 | struct map *map; | ||
| 895 | char *long_name; | ||
| 896 | |||
| 897 | if (dot == NULL) | ||
| 898 | continue; | ||
| 899 | 931 | ||
| 900 | /* On some system, modules are compressed like .ko.gz */ | 932 | ret = kmod_path__parse_name(&m, dent->d_name); |
| 901 | if (is_supported_compression(dot + 1) && | 933 | if (ret) |
| 902 | is_kmodule_extension(dot - 2)) | 934 | goto out; |
| 903 | dot -= 3; | ||
| 904 | 935 | ||
| 905 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | 936 | if (m.kmod) |
| 906 | (int)(dot - dent->d_name), dent->d_name); | 937 | ret = map_groups__set_module_path(mg, path, &m); |
| 907 | 938 | ||
| 908 | strxfrchar(dso_name, '-', '_'); | 939 | free(m.name); |
| 909 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
| 910 | dso_name); | ||
| 911 | if (map == NULL) | ||
| 912 | continue; | ||
| 913 | 940 | ||
| 914 | long_name = strdup(path); | 941 | if (ret) |
| 915 | if (long_name == NULL) { | ||
| 916 | ret = -1; | ||
| 917 | goto out; | 942 | goto out; |
| 918 | } | ||
| 919 | dso__set_long_name(map->dso, long_name, true); | ||
| 920 | dso__kernel_module_get_build_id(map->dso, ""); | ||
| 921 | } | 943 | } |
| 922 | } | 944 | } |
| 923 | 945 | ||
| @@ -1087,7 +1109,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1087 | struct dso *dso; | 1109 | struct dso *dso; |
| 1088 | 1110 | ||
| 1089 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { | 1111 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { |
| 1090 | if (is_kernel_module(dso->long_name, NULL)) | 1112 | if (is_kernel_module(dso->long_name)) |
| 1091 | continue; | 1113 | continue; |
| 1092 | 1114 | ||
| 1093 | kernel = dso; | 1115 | kernel = dso; |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6b95985db5b0..8feac0774c41 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -310,7 +310,10 @@ static int find_alternative_probe_point(struct debuginfo *dinfo, | |||
| 310 | 310 | ||
| 311 | /* Find the address of given function */ | 311 | /* Find the address of given function */ |
| 312 | map__for_each_symbol_by_name(map, pp->function, sym) { | 312 | map__for_each_symbol_by_name(map, pp->function, sym) { |
| 313 | address = sym->start; | 313 | if (uprobes) |
| 314 | address = sym->start; | ||
| 315 | else | ||
| 316 | address = map->unmap_ip(map, sym->start); | ||
| 314 | break; | 317 | break; |
| 315 | } | 318 | } |
| 316 | if (!address) { | 319 | if (!address) { |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 22ebc46226e7..8171fed4136e 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
| @@ -214,6 +214,11 @@ static void define_event_symbols(struct event_format *event, | |||
| 214 | define_event_symbols(event, ev_name, args->hex.field); | 214 | define_event_symbols(event, ev_name, args->hex.field); |
| 215 | define_event_symbols(event, ev_name, args->hex.size); | 215 | define_event_symbols(event, ev_name, args->hex.size); |
| 216 | break; | 216 | break; |
| 217 | case PRINT_INT_ARRAY: | ||
| 218 | define_event_symbols(event, ev_name, args->int_array.field); | ||
| 219 | define_event_symbols(event, ev_name, args->int_array.count); | ||
| 220 | define_event_symbols(event, ev_name, args->int_array.el_size); | ||
| 221 | break; | ||
| 217 | case PRINT_BSTRING: | 222 | case PRINT_BSTRING: |
| 218 | case PRINT_DYNAMIC_ARRAY: | 223 | case PRINT_DYNAMIC_ARRAY: |
| 219 | case PRINT_STRING: | 224 | case PRINT_STRING: |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 0c815a40a6e8..2ec5dfb5a456 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
| @@ -231,6 +231,11 @@ static void define_event_symbols(struct event_format *event, | |||
| 231 | define_event_symbols(event, ev_name, args->hex.field); | 231 | define_event_symbols(event, ev_name, args->hex.field); |
| 232 | define_event_symbols(event, ev_name, args->hex.size); | 232 | define_event_symbols(event, ev_name, args->hex.size); |
| 233 | break; | 233 | break; |
| 234 | case PRINT_INT_ARRAY: | ||
| 235 | define_event_symbols(event, ev_name, args->int_array.field); | ||
| 236 | define_event_symbols(event, ev_name, args->int_array.count); | ||
| 237 | define_event_symbols(event, ev_name, args->int_array.el_size); | ||
| 238 | break; | ||
| 234 | case PRINT_STRING: | 239 | case PRINT_STRING: |
| 235 | break; | 240 | break; |
| 236 | case PRINT_TYPE: | 241 | case PRINT_TYPE: |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 9c01b83eebca..846036a921dc 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -44,6 +44,7 @@ extern struct sort_entry sort_dso_to; | |||
| 44 | extern struct sort_entry sort_sym_from; | 44 | extern struct sort_entry sort_sym_from; |
| 45 | extern struct sort_entry sort_sym_to; | 45 | extern struct sort_entry sort_sym_to; |
| 46 | extern enum sort_type sort__first_dimension; | 46 | extern enum sort_type sort__first_dimension; |
| 47 | extern const char default_mem_sort_order[]; | ||
| 47 | 48 | ||
| 48 | struct he_stat { | 49 | struct he_stat { |
| 49 | u64 period; | 50 | u64 period; |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 62742e46c010..476268c99431 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
| @@ -579,32 +579,37 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata) | |||
| 579 | static int decompress_kmodule(struct dso *dso, const char *name, | 579 | static int decompress_kmodule(struct dso *dso, const char *name, |
| 580 | enum dso_binary_type type) | 580 | enum dso_binary_type type) |
| 581 | { | 581 | { |
| 582 | int fd; | 582 | int fd = -1; |
| 583 | const char *ext = strrchr(name, '.'); | ||
| 584 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; | 583 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; |
| 584 | struct kmod_path m; | ||
| 585 | 585 | ||
| 586 | if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && | 586 | if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && |
| 587 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP && | 587 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP && |
| 588 | type != DSO_BINARY_TYPE__BUILD_ID_CACHE) | 588 | type != DSO_BINARY_TYPE__BUILD_ID_CACHE) |
| 589 | return -1; | 589 | return -1; |
| 590 | 590 | ||
| 591 | if (!ext || !is_supported_compression(ext + 1)) { | 591 | if (type == DSO_BINARY_TYPE__BUILD_ID_CACHE) |
| 592 | ext = strrchr(dso->name, '.'); | 592 | name = dso->long_name; |
| 593 | if (!ext || !is_supported_compression(ext + 1)) | ||
| 594 | return -1; | ||
| 595 | } | ||
| 596 | 593 | ||
| 597 | fd = mkstemp(tmpbuf); | 594 | if (kmod_path__parse_ext(&m, name) || !m.comp) |
| 598 | if (fd < 0) | ||
| 599 | return -1; | 595 | return -1; |
| 600 | 596 | ||
| 601 | if (!decompress_to_file(ext + 1, name, fd)) { | 597 | fd = mkstemp(tmpbuf); |
| 598 | if (fd < 0) { | ||
| 599 | dso->load_errno = errno; | ||
| 600 | goto out; | ||
| 601 | } | ||
| 602 | |||
| 603 | if (!decompress_to_file(m.ext, name, fd)) { | ||
| 604 | dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE; | ||
| 602 | close(fd); | 605 | close(fd); |
| 603 | fd = -1; | 606 | fd = -1; |
| 604 | } | 607 | } |
| 605 | 608 | ||
| 606 | unlink(tmpbuf); | 609 | unlink(tmpbuf); |
| 607 | 610 | ||
| 611 | out: | ||
| 612 | free(m.ext); | ||
| 608 | return fd; | 613 | return fd; |
| 609 | } | 614 | } |
| 610 | 615 | ||
| @@ -633,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
| 633 | Elf *elf; | 638 | Elf *elf; |
| 634 | int fd; | 639 | int fd; |
| 635 | 640 | ||
| 636 | if (dso__needs_decompress(dso)) | 641 | if (dso__needs_decompress(dso)) { |
| 637 | fd = decompress_kmodule(dso, name, type); | 642 | fd = decompress_kmodule(dso, name, type); |
| 638 | else | 643 | if (fd < 0) |
| 644 | return -1; | ||
| 645 | } else { | ||
| 639 | fd = open(name, O_RDONLY); | 646 | fd = open(name, O_RDONLY); |
| 640 | 647 | if (fd < 0) { | |
| 641 | if (fd < 0) | 648 | dso->load_errno = errno; |
| 642 | return -1; | 649 | return -1; |
| 650 | } | ||
| 651 | } | ||
| 643 | 652 | ||
| 644 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 653 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
| 645 | if (elf == NULL) { | 654 | if (elf == NULL) { |
| 646 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); | 655 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); |
| 656 | dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF; | ||
| 647 | goto out_close; | 657 | goto out_close; |
| 648 | } | 658 | } |
| 649 | 659 | ||
| 650 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 660 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
| 661 | dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF; | ||
| 651 | pr_debug("%s: cannot get elf header.\n", __func__); | 662 | pr_debug("%s: cannot get elf header.\n", __func__); |
| 652 | goto out_elf_end; | 663 | goto out_elf_end; |
| 653 | } | 664 | } |
| 654 | 665 | ||
| 655 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) | 666 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) { |
| 667 | dso->load_errno = DSO_LOAD_ERRNO__INTERNAL_ERROR; | ||
| 656 | goto out_elf_end; | 668 | goto out_elf_end; |
| 669 | } | ||
| 657 | 670 | ||
| 658 | /* Always reject images with a mismatched build-id: */ | 671 | /* Always reject images with a mismatched build-id: */ |
| 659 | if (dso->has_build_id) { | 672 | if (dso->has_build_id) { |
| 660 | u8 build_id[BUILD_ID_SIZE]; | 673 | u8 build_id[BUILD_ID_SIZE]; |
| 661 | 674 | ||
| 662 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) | 675 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) { |
| 676 | dso->load_errno = DSO_LOAD_ERRNO__CANNOT_READ_BUILDID; | ||
| 663 | goto out_elf_end; | 677 | goto out_elf_end; |
| 678 | } | ||
| 664 | 679 | ||
| 665 | if (!dso__build_id_equal(dso, build_id)) | 680 | if (!dso__build_id_equal(dso, build_id)) { |
| 681 | dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID; | ||
| 666 | goto out_elf_end; | 682 | goto out_elf_end; |
| 683 | } | ||
| 667 | } | 684 | } |
| 668 | 685 | ||
| 669 | ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); | 686 | ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); |
| @@ -699,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
| 699 | } | 716 | } |
| 700 | 717 | ||
| 701 | ss->name = strdup(name); | 718 | ss->name = strdup(name); |
| 702 | if (!ss->name) | 719 | if (!ss->name) { |
| 720 | dso->load_errno = errno; | ||
| 703 | goto out_elf_end; | 721 | goto out_elf_end; |
| 722 | } | ||
| 704 | 723 | ||
| 705 | ss->elf = elf; | 724 | ss->elf = elf; |
| 706 | ss->fd = fd; | 725 | ss->fd = fd; |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index d7efb03b3f9a..fd8477cacf88 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
| @@ -246,13 +246,12 @@ out: | |||
| 246 | return ret; | 246 | return ret; |
| 247 | } | 247 | } |
| 248 | 248 | ||
| 249 | int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, | 249 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, |
| 250 | const char *name, | ||
| 251 | enum dso_binary_type type) | 250 | enum dso_binary_type type) |
| 252 | { | 251 | { |
| 253 | int fd = open(name, O_RDONLY); | 252 | int fd = open(name, O_RDONLY); |
| 254 | if (fd < 0) | 253 | if (fd < 0) |
| 255 | return -1; | 254 | goto out_errno; |
| 256 | 255 | ||
| 257 | ss->name = strdup(name); | 256 | ss->name = strdup(name); |
| 258 | if (!ss->name) | 257 | if (!ss->name) |
| @@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, | |||
| 264 | return 0; | 263 | return 0; |
| 265 | out_close: | 264 | out_close: |
| 266 | close(fd); | 265 | close(fd); |
| 266 | out_errno: | ||
| 267 | dso->load_errno = errno; | ||
| 267 | return -1; | 268 | return -1; |
| 268 | } | 269 | } |
| 269 | 270 | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a69066865a55..fddeb9073039 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "machine.h" | 15 | #include "machine.h" |
| 16 | #include "symbol.h" | 16 | #include "symbol.h" |
| 17 | #include "strlist.h" | 17 | #include "strlist.h" |
| 18 | #include "intlist.h" | ||
| 18 | #include "header.h" | 19 | #include "header.h" |
| 19 | 20 | ||
| 20 | #include <elf.h> | 21 | #include <elf.h> |
| @@ -1859,6 +1860,20 @@ int setup_list(struct strlist **list, const char *list_str, | |||
| 1859 | return 0; | 1860 | return 0; |
| 1860 | } | 1861 | } |
| 1861 | 1862 | ||
| 1863 | int setup_intlist(struct intlist **list, const char *list_str, | ||
| 1864 | const char *list_name) | ||
| 1865 | { | ||
| 1866 | if (list_str == NULL) | ||
| 1867 | return 0; | ||
| 1868 | |||
| 1869 | *list = intlist__new(list_str); | ||
| 1870 | if (!*list) { | ||
| 1871 | pr_err("problems parsing %s list\n", list_name); | ||
| 1872 | return -1; | ||
| 1873 | } | ||
| 1874 | return 0; | ||
| 1875 | } | ||
| 1876 | |||
| 1862 | static bool symbol__read_kptr_restrict(void) | 1877 | static bool symbol__read_kptr_restrict(void) |
| 1863 | { | 1878 | { |
| 1864 | bool value = false; | 1879 | bool value = false; |
| @@ -1909,9 +1924,17 @@ int symbol__init(struct perf_session_env *env) | |||
| 1909 | symbol_conf.comm_list_str, "comm") < 0) | 1924 | symbol_conf.comm_list_str, "comm") < 0) |
| 1910 | goto out_free_dso_list; | 1925 | goto out_free_dso_list; |
| 1911 | 1926 | ||
| 1927 | if (setup_intlist(&symbol_conf.pid_list, | ||
| 1928 | symbol_conf.pid_list_str, "pid") < 0) | ||
| 1929 | goto out_free_comm_list; | ||
| 1930 | |||
| 1931 | if (setup_intlist(&symbol_conf.tid_list, | ||
| 1932 | symbol_conf.tid_list_str, "tid") < 0) | ||
| 1933 | goto out_free_pid_list; | ||
| 1934 | |||
| 1912 | if (setup_list(&symbol_conf.sym_list, | 1935 | if (setup_list(&symbol_conf.sym_list, |
| 1913 | symbol_conf.sym_list_str, "symbol") < 0) | 1936 | symbol_conf.sym_list_str, "symbol") < 0) |
| 1914 | goto out_free_comm_list; | 1937 | goto out_free_tid_list; |
| 1915 | 1938 | ||
| 1916 | /* | 1939 | /* |
| 1917 | * A path to symbols of "/" is identical to "" | 1940 | * A path to symbols of "/" is identical to "" |
| @@ -1930,6 +1953,10 @@ int symbol__init(struct perf_session_env *env) | |||
| 1930 | symbol_conf.initialized = true; | 1953 | symbol_conf.initialized = true; |
| 1931 | return 0; | 1954 | return 0; |
| 1932 | 1955 | ||
| 1956 | out_free_tid_list: | ||
| 1957 | intlist__delete(symbol_conf.tid_list); | ||
| 1958 | out_free_pid_list: | ||
| 1959 | intlist__delete(symbol_conf.pid_list); | ||
| 1933 | out_free_comm_list: | 1960 | out_free_comm_list: |
| 1934 | strlist__delete(symbol_conf.comm_list); | 1961 | strlist__delete(symbol_conf.comm_list); |
| 1935 | out_free_dso_list: | 1962 | out_free_dso_list: |
| @@ -1944,6 +1971,8 @@ void symbol__exit(void) | |||
| 1944 | strlist__delete(symbol_conf.sym_list); | 1971 | strlist__delete(symbol_conf.sym_list); |
| 1945 | strlist__delete(symbol_conf.dso_list); | 1972 | strlist__delete(symbol_conf.dso_list); |
| 1946 | strlist__delete(symbol_conf.comm_list); | 1973 | strlist__delete(symbol_conf.comm_list); |
| 1974 | intlist__delete(symbol_conf.tid_list); | ||
| 1975 | intlist__delete(symbol_conf.pid_list); | ||
| 1947 | vmlinux_path__exit(); | 1976 | vmlinux_path__exit(); |
| 1948 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 1977 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
| 1949 | symbol_conf.initialized = false; | 1978 | symbol_conf.initialized = false; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index efdaaa544041..09561500164a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -78,6 +78,7 @@ static inline size_t symbol__size(const struct symbol *sym) | |||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | struct strlist; | 80 | struct strlist; |
| 81 | struct intlist; | ||
| 81 | 82 | ||
| 82 | struct symbol_conf { | 83 | struct symbol_conf { |
| 83 | unsigned short priv_size; | 84 | unsigned short priv_size; |
| @@ -115,6 +116,8 @@ struct symbol_conf { | |||
| 115 | const char *guestmount; | 116 | const char *guestmount; |
| 116 | const char *dso_list_str, | 117 | const char *dso_list_str, |
| 117 | *comm_list_str, | 118 | *comm_list_str, |
| 119 | *pid_list_str, | ||
| 120 | *tid_list_str, | ||
| 118 | *sym_list_str, | 121 | *sym_list_str, |
| 119 | *col_width_list_str; | 122 | *col_width_list_str; |
| 120 | struct strlist *dso_list, | 123 | struct strlist *dso_list, |
| @@ -124,6 +127,8 @@ struct symbol_conf { | |||
| 124 | *dso_to_list, | 127 | *dso_to_list, |
| 125 | *sym_from_list, | 128 | *sym_from_list, |
| 126 | *sym_to_list; | 129 | *sym_to_list; |
| 130 | struct intlist *pid_list, | ||
| 131 | *tid_list; | ||
| 127 | const char *symfs; | 132 | const char *symfs; |
| 128 | }; | 133 | }; |
| 129 | 134 | ||
| @@ -295,5 +300,7 @@ int compare_proc_modules(const char *from, const char *to); | |||
| 295 | 300 | ||
| 296 | int setup_list(struct strlist **list, const char *list_str, | 301 | int setup_list(struct strlist **list, const char *list_str, |
| 297 | const char *list_name); | 302 | const char *list_name); |
| 303 | int setup_intlist(struct intlist **list, const char *list_str, | ||
| 304 | const char *list_name); | ||
| 298 | 305 | ||
| 299 | #endif /* __PERF_SYMBOL */ | 306 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index e74c5963dc7a..a53603b27e52 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c | |||
| @@ -123,11 +123,8 @@ int target__strerror(struct target *target, int errnum, | |||
| 123 | if (errnum >= 0) { | 123 | if (errnum >= 0) { |
| 124 | const char *err = strerror_r(errnum, buf, buflen); | 124 | const char *err = strerror_r(errnum, buf, buflen); |
| 125 | 125 | ||
| 126 | if (err != buf) { | 126 | if (err != buf) |
| 127 | size_t len = strlen(err); | 127 | scnprintf(buf, buflen, "%s", err); |
| 128 | memcpy(buf, err, min(buflen - 1, len)); | ||
| 129 | *(buf + min(buflen - 1, len)) = '\0'; | ||
| 130 | } | ||
| 131 | 128 | ||
| 132 | return 0; | 129 | return 0; |
| 133 | } | 130 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 783b6688d2f7..9b8a54dc34a8 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <sys/types.h> | 7 | #include <sys/types.h> |
| 8 | #include "symbol.h" | 8 | #include "symbol.h" |
| 9 | #include <strlist.h> | 9 | #include <strlist.h> |
| 10 | #include <intlist.h> | ||
| 10 | 11 | ||
| 11 | struct thread_stack; | 12 | struct thread_stack; |
| 12 | 13 | ||
| @@ -100,6 +101,16 @@ static inline bool thread__is_filtered(struct thread *thread) | |||
| 100 | return true; | 101 | return true; |
| 101 | } | 102 | } |
| 102 | 103 | ||
| 104 | if (symbol_conf.pid_list && | ||
| 105 | !intlist__has_entry(symbol_conf.pid_list, thread->pid_)) { | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | if (symbol_conf.tid_list && | ||
| 110 | !intlist__has_entry(symbol_conf.tid_list, thread->tid)) { | ||
| 111 | return true; | ||
| 112 | } | ||
| 113 | |||
| 103 | return false; | 114 | return false; |
| 104 | } | 115 | } |
| 105 | 116 | ||
