diff options
author | Ingo Molnar <mingo@kernel.org> | 2018-01-17 11:20:24 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-01-17 11:20:24 -0500 |
commit | a72594ca5c70ef4a94fab8ad541beda0d0a94139 (patch) | |
tree | 2f08d90934e65ea60f73e2bdaf7408fb36f1aecf /tools | |
parent | 7a7368a5f22d13a7ff6654204de92c08ce5d8fea (diff) | |
parent | 81fccd6ca507d3b2012eaf1edeb9b1dbf4bd22db (diff) |
Merge tag 'perf-core-for-mingo-4.16-20180117' 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:
- Fix various per event 'max-stack' and 'call-graph=dwarf' issues,
mostly in 'perf trace', allowing to use 'perf trace --call-graph' with
'dwarf' and 'fp' to setup the callgraph details for the syscall events
and make that apply to other events, whilhe allowing to override that on
a per-event basis, using '-e sched:*switch/call-graph=dwarf/' for
instance (Arnaldo Carvalho de Melo)
- Improve the --time percent support in record/report/script (Jin Yao)
- Fix copyfile_offset update of output offset (Jiri Olsa)
- Add python script to profile and resolve physical mem type (Kan Liang)
- Add ARM Statistical Profiling Extensions (SPE) support (Kim Phillips)
- Remove trailing semicolon in the evlist code (Luis de Bethencourt)
- Fix incorrect handling of type _TERM_DRV_CFG (Mathieu Poirier)
- Use asprintf when possible in libtraceevent (Federico Vaga)
- Fix bad force_token escape sequence in libtraceevent (Michael Sartain)
- Add UL suffix to MISSING_EVENTS in libtraceevent (Michael Sartain)
- value of unknown symbolic fields in libtraceevent (Jan Kiszka)
- libtraceevent updates: (Steven Rostedt)
o Show value of flags that have not been parsed
o Simplify pointer print logic and fix %pF
o Handle new pointer processing of bprint strings
o Show contents (in hex) of data of unrecognized type records
o Fix get_field_str() for dynamic strings
- Add missing break in FALSE case of pevent_filter_clear_trivial() (Taeung Song)
- Fix failed memory allocation for get_cpuid_str (Thomas Richter)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
35 files changed, 1465 insertions, 130 deletions
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 7ce724fc0544..e5f2acbb70cc 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -1094,7 +1094,7 @@ static enum event_type __read_token(char **tok) | |||
1094 | if (strcmp(*tok, "LOCAL_PR_FMT") == 0) { | 1094 | if (strcmp(*tok, "LOCAL_PR_FMT") == 0) { |
1095 | free(*tok); | 1095 | free(*tok); |
1096 | *tok = NULL; | 1096 | *tok = NULL; |
1097 | return force_token("\"\%s\" ", tok); | 1097 | return force_token("\"%s\" ", tok); |
1098 | } else if (strcmp(*tok, "STA_PR_FMT") == 0) { | 1098 | } else if (strcmp(*tok, "STA_PR_FMT") == 0) { |
1099 | free(*tok); | 1099 | free(*tok); |
1100 | *tok = NULL; | 1100 | *tok = NULL; |
@@ -3970,6 +3970,11 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, | |||
3970 | val &= ~fval; | 3970 | val &= ~fval; |
3971 | } | 3971 | } |
3972 | } | 3972 | } |
3973 | if (val) { | ||
3974 | if (print && arg->flags.delim) | ||
3975 | trace_seq_puts(s, arg->flags.delim); | ||
3976 | trace_seq_printf(s, "0x%llx", val); | ||
3977 | } | ||
3973 | break; | 3978 | break; |
3974 | case PRINT_SYMBOL: | 3979 | case PRINT_SYMBOL: |
3975 | val = eval_num_arg(data, size, event, arg->symbol.field); | 3980 | val = eval_num_arg(data, size, event, arg->symbol.field); |
@@ -3980,6 +3985,8 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, | |||
3980 | break; | 3985 | break; |
3981 | } | 3986 | } |
3982 | } | 3987 | } |
3988 | if (!flag) | ||
3989 | trace_seq_printf(s, "0x%llx", val); | ||
3983 | break; | 3990 | break; |
3984 | case PRINT_HEX: | 3991 | case PRINT_HEX: |
3985 | case PRINT_HEX_STR: | 3992 | case PRINT_HEX_STR: |
@@ -4293,6 +4300,26 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc | |||
4293 | goto process_again; | 4300 | goto process_again; |
4294 | case 'p': | 4301 | case 'p': |
4295 | ls = 1; | 4302 | ls = 1; |
4303 | if (isalnum(ptr[1])) { | ||
4304 | ptr++; | ||
4305 | /* Check for special pointers */ | ||
4306 | switch (*ptr) { | ||
4307 | case 's': | ||
4308 | case 'S': | ||
4309 | case 'f': | ||
4310 | case 'F': | ||
4311 | break; | ||
4312 | default: | ||
4313 | /* | ||
4314 | * Older kernels do not process | ||
4315 | * dereferenced pointers. | ||
4316 | * Only process if the pointer | ||
4317 | * value is a printable. | ||
4318 | */ | ||
4319 | if (isprint(*(char *)bptr)) | ||
4320 | goto process_string; | ||
4321 | } | ||
4322 | } | ||
4296 | /* fall through */ | 4323 | /* fall through */ |
4297 | case 'd': | 4324 | case 'd': |
4298 | case 'u': | 4325 | case 'u': |
@@ -4345,6 +4372,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc | |||
4345 | 4372 | ||
4346 | break; | 4373 | break; |
4347 | case 's': | 4374 | case 's': |
4375 | process_string: | ||
4348 | arg = alloc_arg(); | 4376 | arg = alloc_arg(); |
4349 | if (!arg) { | 4377 | if (!arg) { |
4350 | do_warning_event(event, "%s(%d): not enough memory!", | 4378 | do_warning_event(event, "%s(%d): not enough memory!", |
@@ -4949,21 +4977,27 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event | |||
4949 | else | 4977 | else |
4950 | ls = 2; | 4978 | ls = 2; |
4951 | 4979 | ||
4952 | if (*(ptr+1) == 'F' || *(ptr+1) == 'f' || | 4980 | if (isalnum(ptr[1])) |
4953 | *(ptr+1) == 'S' || *(ptr+1) == 's') { | ||
4954 | ptr++; | 4981 | ptr++; |
4982 | |||
4983 | if (arg->type == PRINT_BSTRING) { | ||
4984 | trace_seq_puts(s, arg->string.string); | ||
4985 | break; | ||
4986 | } | ||
4987 | |||
4988 | if (*ptr == 'F' || *ptr == 'f' || | ||
4989 | *ptr == 'S' || *ptr == 's') { | ||
4955 | show_func = *ptr; | 4990 | show_func = *ptr; |
4956 | } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { | 4991 | } else if (*ptr == 'M' || *ptr == 'm') { |
4957 | print_mac_arg(s, *(ptr+1), data, size, event, arg); | 4992 | print_mac_arg(s, *ptr, data, size, event, arg); |
4958 | ptr++; | ||
4959 | arg = arg->next; | 4993 | arg = arg->next; |
4960 | break; | 4994 | break; |
4961 | } else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') { | 4995 | } else if (*ptr == 'I' || *ptr == 'i') { |
4962 | int n; | 4996 | int n; |
4963 | 4997 | ||
4964 | n = print_ip_arg(s, ptr+1, data, size, event, arg); | 4998 | n = print_ip_arg(s, ptr, data, size, event, arg); |
4965 | if (n > 0) { | 4999 | if (n > 0) { |
4966 | ptr += n; | 5000 | ptr += n - 1; |
4967 | arg = arg->next; | 5001 | arg = arg->next; |
4968 | break; | 5002 | break; |
4969 | } | 5003 | } |
@@ -5532,8 +5566,14 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, | |||
5532 | 5566 | ||
5533 | event = pevent_find_event_by_record(pevent, record); | 5567 | event = pevent_find_event_by_record(pevent, record); |
5534 | if (!event) { | 5568 | if (!event) { |
5535 | do_warning("ug! no event found for type %d", | 5569 | int i; |
5536 | trace_parse_common_type(pevent, record->data)); | 5570 | int type = trace_parse_common_type(pevent, record->data); |
5571 | |||
5572 | do_warning("ug! no event found for type %d", type); | ||
5573 | trace_seq_printf(s, "[UNKNOWN TYPE %d]", type); | ||
5574 | for (i = 0; i < record->size; i++) | ||
5575 | trace_seq_printf(s, " %02x", | ||
5576 | ((unsigned char *)record->data)[i]); | ||
5537 | return; | 5577 | return; |
5538 | } | 5578 | } |
5539 | 5579 | ||
diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c index a16756ae3526..d542cb60ca1a 100644 --- a/tools/lib/traceevent/event-plugin.c +++ b/tools/lib/traceevent/event-plugin.c | |||
@@ -120,12 +120,12 @@ char **traceevent_plugin_list_options(void) | |||
120 | for (op = reg->options; op->name; op++) { | 120 | for (op = reg->options; op->name; op++) { |
121 | char *alias = op->plugin_alias ? op->plugin_alias : op->file; | 121 | char *alias = op->plugin_alias ? op->plugin_alias : op->file; |
122 | char **temp = list; | 122 | char **temp = list; |
123 | int ret; | ||
123 | 124 | ||
124 | name = malloc(strlen(op->name) + strlen(alias) + 2); | 125 | ret = asprintf(&name, "%s:%s", alias, op->name); |
125 | if (!name) | 126 | if (ret < 0) |
126 | goto err; | 127 | goto err; |
127 | 128 | ||
128 | sprintf(name, "%s:%s", alias, op->name); | ||
129 | list = realloc(list, count + 2); | 129 | list = realloc(list, count + 2); |
130 | if (!list) { | 130 | if (!list) { |
131 | list = temp; | 131 | list = temp; |
@@ -290,17 +290,14 @@ load_plugin(struct pevent *pevent, const char *path, | |||
290 | const char *alias; | 290 | const char *alias; |
291 | char *plugin; | 291 | char *plugin; |
292 | void *handle; | 292 | void *handle; |
293 | int ret; | ||
293 | 294 | ||
294 | plugin = malloc(strlen(path) + strlen(file) + 2); | 295 | ret = asprintf(&plugin, "%s/%s", path, file); |
295 | if (!plugin) { | 296 | if (ret < 0) { |
296 | warning("could not allocate plugin memory\n"); | 297 | warning("could not allocate plugin memory\n"); |
297 | return; | 298 | return; |
298 | } | 299 | } |
299 | 300 | ||
300 | strcpy(plugin, path); | ||
301 | strcat(plugin, "/"); | ||
302 | strcat(plugin, file); | ||
303 | |||
304 | handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL); | 301 | handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL); |
305 | if (!handle) { | 302 | if (!handle) { |
306 | warning("could not load plugin '%s'\n%s\n", | 303 | warning("could not load plugin '%s'\n%s\n", |
@@ -391,6 +388,7 @@ load_plugins(struct pevent *pevent, const char *suffix, | |||
391 | char *home; | 388 | char *home; |
392 | char *path; | 389 | char *path; |
393 | char *envdir; | 390 | char *envdir; |
391 | int ret; | ||
394 | 392 | ||
395 | if (pevent->flags & PEVENT_DISABLE_PLUGINS) | 393 | if (pevent->flags & PEVENT_DISABLE_PLUGINS) |
396 | return; | 394 | return; |
@@ -421,16 +419,12 @@ load_plugins(struct pevent *pevent, const char *suffix, | |||
421 | if (!home) | 419 | if (!home) |
422 | return; | 420 | return; |
423 | 421 | ||
424 | path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); | 422 | ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR); |
425 | if (!path) { | 423 | if (ret < 0) { |
426 | warning("could not allocate plugin memory\n"); | 424 | warning("could not allocate plugin memory\n"); |
427 | return; | 425 | return; |
428 | } | 426 | } |
429 | 427 | ||
430 | strcpy(path, home); | ||
431 | strcat(path, "/"); | ||
432 | strcat(path, LOCAL_PLUGIN_DIR); | ||
433 | |||
434 | load_plugins_dir(pevent, suffix, path, load_plugin, data); | 428 | load_plugins_dir(pevent, suffix, path, load_plugin, data); |
435 | 429 | ||
436 | free(path); | 430 | free(path); |
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c index c94e3641b046..ca424b157e46 100644 --- a/tools/lib/traceevent/kbuffer-parse.c +++ b/tools/lib/traceevent/kbuffer-parse.c | |||
@@ -24,8 +24,8 @@ | |||
24 | 24 | ||
25 | #include "kbuffer.h" | 25 | #include "kbuffer.h" |
26 | 26 | ||
27 | #define MISSING_EVENTS (1 << 31) | 27 | #define MISSING_EVENTS (1UL << 31) |
28 | #define MISSING_STORED (1 << 30) | 28 | #define MISSING_STORED (1UL << 30) |
29 | 29 | ||
30 | #define COMMIT_MASK ((1 << 27) - 1) | 30 | #define COMMIT_MASK ((1 << 27) - 1) |
31 | 31 | ||
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 315df0a70265..431e8b309f6e 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c | |||
@@ -287,12 +287,10 @@ find_event(struct pevent *pevent, struct event_list **events, | |||
287 | sys_name = NULL; | 287 | sys_name = NULL; |
288 | } | 288 | } |
289 | 289 | ||
290 | reg = malloc(strlen(event_name) + 3); | 290 | ret = asprintf(®, "^%s$", event_name); |
291 | if (reg == NULL) | 291 | if (ret < 0) |
292 | return PEVENT_ERRNO__MEM_ALLOC_FAILED; | 292 | return PEVENT_ERRNO__MEM_ALLOC_FAILED; |
293 | 293 | ||
294 | sprintf(reg, "^%s$", event_name); | ||
295 | |||
296 | ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); | 294 | ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); |
297 | free(reg); | 295 | free(reg); |
298 | 296 | ||
@@ -300,13 +298,12 @@ find_event(struct pevent *pevent, struct event_list **events, | |||
300 | return PEVENT_ERRNO__INVALID_EVENT_NAME; | 298 | return PEVENT_ERRNO__INVALID_EVENT_NAME; |
301 | 299 | ||
302 | if (sys_name) { | 300 | if (sys_name) { |
303 | reg = malloc(strlen(sys_name) + 3); | 301 | ret = asprintf(®, "^%s$", sys_name); |
304 | if (reg == NULL) { | 302 | if (ret < 0) { |
305 | regfree(&ereg); | 303 | regfree(&ereg); |
306 | return PEVENT_ERRNO__MEM_ALLOC_FAILED; | 304 | return PEVENT_ERRNO__MEM_ALLOC_FAILED; |
307 | } | 305 | } |
308 | 306 | ||
309 | sprintf(reg, "^%s$", sys_name); | ||
310 | ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); | 307 | ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); |
311 | free(reg); | 308 | free(reg); |
312 | if (ret) { | 309 | if (ret) { |
@@ -1634,6 +1631,7 @@ int pevent_filter_clear_trivial(struct event_filter *filter, | |||
1634 | case FILTER_TRIVIAL_FALSE: | 1631 | case FILTER_TRIVIAL_FALSE: |
1635 | if (filter_type->filter->boolean.value) | 1632 | if (filter_type->filter->boolean.value) |
1636 | continue; | 1633 | continue; |
1634 | break; | ||
1637 | case FILTER_TRIVIAL_TRUE: | 1635 | case FILTER_TRIVIAL_TRUE: |
1638 | if (!filter_type->filter->boolean.value) | 1636 | if (!filter_type->filter->boolean.value) |
1639 | continue; | 1637 | continue; |
@@ -1879,17 +1877,25 @@ static const char *get_field_str(struct filter_arg *arg, struct pevent_record *r | |||
1879 | struct pevent *pevent; | 1877 | struct pevent *pevent; |
1880 | unsigned long long addr; | 1878 | unsigned long long addr; |
1881 | const char *val = NULL; | 1879 | const char *val = NULL; |
1880 | unsigned int size; | ||
1882 | char hex[64]; | 1881 | char hex[64]; |
1883 | 1882 | ||
1884 | /* If the field is not a string convert it */ | 1883 | /* If the field is not a string convert it */ |
1885 | if (arg->str.field->flags & FIELD_IS_STRING) { | 1884 | if (arg->str.field->flags & FIELD_IS_STRING) { |
1886 | val = record->data + arg->str.field->offset; | 1885 | val = record->data + arg->str.field->offset; |
1886 | size = arg->str.field->size; | ||
1887 | |||
1888 | if (arg->str.field->flags & FIELD_IS_DYNAMIC) { | ||
1889 | addr = *(unsigned int *)val; | ||
1890 | val = record->data + (addr & 0xffff); | ||
1891 | size = addr >> 16; | ||
1892 | } | ||
1887 | 1893 | ||
1888 | /* | 1894 | /* |
1889 | * We need to copy the data since we can't be sure the field | 1895 | * We need to copy the data since we can't be sure the field |
1890 | * is null terminated. | 1896 | * is null terminated. |
1891 | */ | 1897 | */ |
1892 | if (*(val + arg->str.field->size - 1)) { | 1898 | if (*(val + size - 1)) { |
1893 | /* copy it */ | 1899 | /* copy it */ |
1894 | memcpy(arg->str.buffer, val, arg->str.field->size); | 1900 | memcpy(arg->str.buffer, val, arg->str.field->size); |
1895 | /* the buffer is already NULL terminated */ | 1901 | /* the buffer is already NULL terminated */ |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 63d0db3184c9..907e505b6309 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -403,7 +403,7 @@ OPTIONS | |||
403 | to end of file. | 403 | to end of file. |
404 | 404 | ||
405 | Also support time percent with multiple time range. Time string is | 405 | Also support time percent with multiple time range. Time string is |
406 | 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. The maximum number of slices is 10. | 406 | 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. |
407 | 407 | ||
408 | For example: | 408 | For example: |
409 | Select the second 10% time slice: | 409 | Select the second 10% time slice: |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 806ec6391fd6..7730c1d2b5d3 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -351,19 +351,19 @@ include::itrace.txt[] | |||
351 | to end of file. | 351 | to end of file. |
352 | 352 | ||
353 | Also support time percent with multipe time range. Time string is | 353 | Also support time percent with multipe time range. Time string is |
354 | 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. The maximum number of slices is 10. | 354 | 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. |
355 | 355 | ||
356 | For example: | 356 | For example: |
357 | Select the second 10% time slice | 357 | Select the second 10% time slice: |
358 | perf script --time 10%/2 | 358 | perf script --time 10%/2 |
359 | 359 | ||
360 | Select from 0% to 10% time slice | 360 | Select from 0% to 10% time slice: |
361 | perf script --time 0%-10% | 361 | perf script --time 0%-10% |
362 | 362 | ||
363 | Select the first and second 10% time slices | 363 | Select the first and second 10% time slices: |
364 | perf script --time 10%/1,10%/2 | 364 | perf script --time 10%/1,10%/2 |
365 | 365 | ||
366 | Select from 0% to 10% and 30% to 40% slices | 366 | Select from 0% to 10% and 30% to 40% slices: |
367 | perf script --time 0%-10%,30%-40% | 367 | perf script --time 0%-10%,30%-40% |
368 | 368 | ||
369 | --max-blocks:: | 369 | --max-blocks:: |
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c index 8edf2cb71564..2323581b157d 100644 --- a/tools/perf/arch/arm/util/auxtrace.c +++ b/tools/perf/arch/arm/util/auxtrace.c | |||
@@ -22,6 +22,42 @@ | |||
22 | #include "../../util/evlist.h" | 22 | #include "../../util/evlist.h" |
23 | #include "../../util/pmu.h" | 23 | #include "../../util/pmu.h" |
24 | #include "cs-etm.h" | 24 | #include "cs-etm.h" |
25 | #include "arm-spe.h" | ||
26 | |||
27 | static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) | ||
28 | { | ||
29 | struct perf_pmu **arm_spe_pmus = NULL; | ||
30 | int ret, i, nr_cpus = sysconf(_SC_NPROCESSORS_CONF); | ||
31 | /* arm_spe_xxxxxxxxx\0 */ | ||
32 | char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10]; | ||
33 | |||
34 | arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus); | ||
35 | if (!arm_spe_pmus) { | ||
36 | pr_err("spes alloc failed\n"); | ||
37 | *err = -ENOMEM; | ||
38 | return NULL; | ||
39 | } | ||
40 | |||
41 | for (i = 0; i < nr_cpus; i++) { | ||
42 | ret = sprintf(arm_spe_pmu_name, "%s%d", ARM_SPE_PMU_NAME, i); | ||
43 | if (ret < 0) { | ||
44 | pr_err("sprintf failed\n"); | ||
45 | *err = -ENOMEM; | ||
46 | return NULL; | ||
47 | } | ||
48 | |||
49 | arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name); | ||
50 | if (arm_spe_pmus[*nr_spes]) { | ||
51 | pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n", | ||
52 | __func__, __LINE__, *nr_spes, | ||
53 | arm_spe_pmus[*nr_spes]->type, | ||
54 | arm_spe_pmus[*nr_spes]->name); | ||
55 | (*nr_spes)++; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | return arm_spe_pmus; | ||
60 | } | ||
25 | 61 | ||
26 | struct auxtrace_record | 62 | struct auxtrace_record |
27 | *auxtrace_record__init(struct perf_evlist *evlist, int *err) | 63 | *auxtrace_record__init(struct perf_evlist *evlist, int *err) |
@@ -29,22 +65,51 @@ struct auxtrace_record | |||
29 | struct perf_pmu *cs_etm_pmu; | 65 | struct perf_pmu *cs_etm_pmu; |
30 | struct perf_evsel *evsel; | 66 | struct perf_evsel *evsel; |
31 | bool found_etm = false; | 67 | bool found_etm = false; |
68 | bool found_spe = false; | ||
69 | static struct perf_pmu **arm_spe_pmus = NULL; | ||
70 | static int nr_spes = 0; | ||
71 | int i; | ||
72 | |||
73 | if (!evlist) | ||
74 | return NULL; | ||
32 | 75 | ||
33 | cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); | 76 | cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); |
34 | 77 | ||
35 | if (evlist) { | 78 | if (!arm_spe_pmus) |
36 | evlist__for_each_entry(evlist, evsel) { | 79 | arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); |
37 | if (cs_etm_pmu && | 80 | |
38 | evsel->attr.type == cs_etm_pmu->type) | 81 | evlist__for_each_entry(evlist, evsel) { |
39 | found_etm = true; | 82 | if (cs_etm_pmu && |
83 | evsel->attr.type == cs_etm_pmu->type) | ||
84 | found_etm = true; | ||
85 | |||
86 | if (!nr_spes) | ||
87 | continue; | ||
88 | |||
89 | for (i = 0; i < nr_spes; i++) { | ||
90 | if (evsel->attr.type == arm_spe_pmus[i]->type) { | ||
91 | found_spe = true; | ||
92 | break; | ||
93 | } | ||
40 | } | 94 | } |
41 | } | 95 | } |
42 | 96 | ||
97 | if (found_etm && found_spe) { | ||
98 | pr_err("Concurrent ARM Coresight ETM and SPE operation not currently supported\n"); | ||
99 | *err = -EOPNOTSUPP; | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
43 | if (found_etm) | 103 | if (found_etm) |
44 | return cs_etm_record_init(err); | 104 | return cs_etm_record_init(err); |
45 | 105 | ||
106 | #if defined(__aarch64__) | ||
107 | if (found_spe) | ||
108 | return arm_spe_recording_init(err, arm_spe_pmus[i]); | ||
109 | #endif | ||
110 | |||
46 | /* | 111 | /* |
47 | * Clear 'err' even if we haven't found a cs_etm event - that way perf | 112 | * Clear 'err' even if we haven't found an event - that way perf |
48 | * record can still be used even if tracers aren't present. The NULL | 113 | * record can still be used even if tracers aren't present. The NULL |
49 | * return value will take care of telling the infrastructure HW tracing | 114 | * return value will take care of telling the infrastructure HW tracing |
50 | * isn't available. | 115 | * isn't available. |
diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c index 98d67399a0d6..ac4dffc807b8 100644 --- a/tools/perf/arch/arm/util/pmu.c +++ b/tools/perf/arch/arm/util/pmu.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/perf_event.h> | 20 | #include <linux/perf_event.h> |
21 | 21 | ||
22 | #include "cs-etm.h" | 22 | #include "cs-etm.h" |
23 | #include "arm-spe.h" | ||
23 | #include "../../util/pmu.h" | 24 | #include "../../util/pmu.h" |
24 | 25 | ||
25 | struct perf_event_attr | 26 | struct perf_event_attr |
@@ -30,7 +31,12 @@ struct perf_event_attr | |||
30 | /* add ETM default config here */ | 31 | /* add ETM default config here */ |
31 | pmu->selectable = true; | 32 | pmu->selectable = true; |
32 | pmu->set_drv_config = cs_etm_set_drv_config; | 33 | pmu->set_drv_config = cs_etm_set_drv_config; |
34 | #if defined(__aarch64__) | ||
35 | } else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) { | ||
36 | return arm_spe_pmu_default_config(pmu); | ||
37 | #endif | ||
33 | } | 38 | } |
39 | |||
34 | #endif | 40 | #endif |
35 | return NULL; | 41 | return NULL; |
36 | } | 42 | } |
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build index e04f6cdd6f32..c0b8dfef98ba 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build | |||
@@ -5,4 +5,5 @@ libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o | |||
5 | 5 | ||
6 | libperf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \ | 6 | libperf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \ |
7 | ../../arm/util/auxtrace.o \ | 7 | ../../arm/util/auxtrace.o \ |
8 | ../../arm/util/cs-etm.o | 8 | ../../arm/util/cs-etm.o \ |
9 | arm-spe.o | ||
diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c new file mode 100644 index 000000000000..1120e39c1b00 --- /dev/null +++ b/tools/perf/arch/arm64/util/arm-spe.c | |||
@@ -0,0 +1,225 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Arm Statistical Profiling Extensions (SPE) support | ||
4 | * Copyright (c) 2017-2018, Arm Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/log2.h> | ||
11 | #include <time.h> | ||
12 | |||
13 | #include "../../util/cpumap.h" | ||
14 | #include "../../util/evsel.h" | ||
15 | #include "../../util/evlist.h" | ||
16 | #include "../../util/session.h" | ||
17 | #include "../../util/util.h" | ||
18 | #include "../../util/pmu.h" | ||
19 | #include "../../util/debug.h" | ||
20 | #include "../../util/auxtrace.h" | ||
21 | #include "../../util/arm-spe.h" | ||
22 | |||
23 | #define KiB(x) ((x) * 1024) | ||
24 | #define MiB(x) ((x) * 1024 * 1024) | ||
25 | |||
26 | struct arm_spe_recording { | ||
27 | struct auxtrace_record itr; | ||
28 | struct perf_pmu *arm_spe_pmu; | ||
29 | struct perf_evlist *evlist; | ||
30 | }; | ||
31 | |||
32 | static size_t | ||
33 | arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused, | ||
34 | struct perf_evlist *evlist __maybe_unused) | ||
35 | { | ||
36 | return ARM_SPE_AUXTRACE_PRIV_SIZE; | ||
37 | } | ||
38 | |||
39 | static int arm_spe_info_fill(struct auxtrace_record *itr, | ||
40 | struct perf_session *session, | ||
41 | struct auxtrace_info_event *auxtrace_info, | ||
42 | size_t priv_size) | ||
43 | { | ||
44 | struct arm_spe_recording *sper = | ||
45 | container_of(itr, struct arm_spe_recording, itr); | ||
46 | struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; | ||
47 | |||
48 | if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE) | ||
49 | return -EINVAL; | ||
50 | |||
51 | if (!session->evlist->nr_mmaps) | ||
52 | return -EINVAL; | ||
53 | |||
54 | auxtrace_info->type = PERF_AUXTRACE_ARM_SPE; | ||
55 | auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type; | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int arm_spe_recording_options(struct auxtrace_record *itr, | ||
61 | struct perf_evlist *evlist, | ||
62 | struct record_opts *opts) | ||
63 | { | ||
64 | struct arm_spe_recording *sper = | ||
65 | container_of(itr, struct arm_spe_recording, itr); | ||
66 | struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; | ||
67 | struct perf_evsel *evsel, *arm_spe_evsel = NULL; | ||
68 | bool privileged = geteuid() == 0 || perf_event_paranoid() < 0; | ||
69 | struct perf_evsel *tracking_evsel; | ||
70 | int err; | ||
71 | |||
72 | sper->evlist = evlist; | ||
73 | |||
74 | evlist__for_each_entry(evlist, evsel) { | ||
75 | if (evsel->attr.type == arm_spe_pmu->type) { | ||
76 | if (arm_spe_evsel) { | ||
77 | pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n"); | ||
78 | return -EINVAL; | ||
79 | } | ||
80 | evsel->attr.freq = 0; | ||
81 | evsel->attr.sample_period = 1; | ||
82 | arm_spe_evsel = evsel; | ||
83 | opts->full_auxtrace = true; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | if (!opts->full_auxtrace) | ||
88 | return 0; | ||
89 | |||
90 | /* We are in full trace mode but '-m,xyz' wasn't specified */ | ||
91 | if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { | ||
92 | if (privileged) { | ||
93 | opts->auxtrace_mmap_pages = MiB(4) / page_size; | ||
94 | } else { | ||
95 | opts->auxtrace_mmap_pages = KiB(128) / page_size; | ||
96 | if (opts->mmap_pages == UINT_MAX) | ||
97 | opts->mmap_pages = KiB(256) / page_size; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /* Validate auxtrace_mmap_pages */ | ||
102 | if (opts->auxtrace_mmap_pages) { | ||
103 | size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; | ||
104 | size_t min_sz = KiB(8); | ||
105 | |||
106 | if (sz < min_sz || !is_power_of_2(sz)) { | ||
107 | pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n", | ||
108 | min_sz / 1024); | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | |||
114 | /* | ||
115 | * To obtain the auxtrace buffer file descriptor, the auxtrace event | ||
116 | * must come first. | ||
117 | */ | ||
118 | perf_evlist__to_front(evlist, arm_spe_evsel); | ||
119 | |||
120 | perf_evsel__set_sample_bit(arm_spe_evsel, CPU); | ||
121 | perf_evsel__set_sample_bit(arm_spe_evsel, TIME); | ||
122 | perf_evsel__set_sample_bit(arm_spe_evsel, TID); | ||
123 | |||
124 | /* Add dummy event to keep tracking */ | ||
125 | err = parse_events(evlist, "dummy:u", NULL); | ||
126 | if (err) | ||
127 | return err; | ||
128 | |||
129 | tracking_evsel = perf_evlist__last(evlist); | ||
130 | perf_evlist__set_tracking_event(evlist, tracking_evsel); | ||
131 | |||
132 | tracking_evsel->attr.freq = 0; | ||
133 | tracking_evsel->attr.sample_period = 1; | ||
134 | perf_evsel__set_sample_bit(tracking_evsel, TIME); | ||
135 | perf_evsel__set_sample_bit(tracking_evsel, CPU); | ||
136 | perf_evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK); | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static u64 arm_spe_reference(struct auxtrace_record *itr __maybe_unused) | ||
142 | { | ||
143 | struct timespec ts; | ||
144 | |||
145 | clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | ||
146 | |||
147 | return ts.tv_sec ^ ts.tv_nsec; | ||
148 | } | ||
149 | |||
150 | static void arm_spe_recording_free(struct auxtrace_record *itr) | ||
151 | { | ||
152 | struct arm_spe_recording *sper = | ||
153 | container_of(itr, struct arm_spe_recording, itr); | ||
154 | |||
155 | free(sper); | ||
156 | } | ||
157 | |||
158 | static int arm_spe_read_finish(struct auxtrace_record *itr, int idx) | ||
159 | { | ||
160 | struct arm_spe_recording *sper = | ||
161 | container_of(itr, struct arm_spe_recording, itr); | ||
162 | struct perf_evsel *evsel; | ||
163 | |||
164 | evlist__for_each_entry(sper->evlist, evsel) { | ||
165 | if (evsel->attr.type == sper->arm_spe_pmu->type) | ||
166 | return perf_evlist__enable_event_idx(sper->evlist, | ||
167 | evsel, idx); | ||
168 | } | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
172 | struct auxtrace_record *arm_spe_recording_init(int *err, | ||
173 | struct perf_pmu *arm_spe_pmu) | ||
174 | { | ||
175 | struct arm_spe_recording *sper; | ||
176 | |||
177 | if (!arm_spe_pmu) { | ||
178 | *err = -ENODEV; | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | sper = zalloc(sizeof(struct arm_spe_recording)); | ||
183 | if (!sper) { | ||
184 | *err = -ENOMEM; | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | sper->arm_spe_pmu = arm_spe_pmu; | ||
189 | sper->itr.recording_options = arm_spe_recording_options; | ||
190 | sper->itr.info_priv_size = arm_spe_info_priv_size; | ||
191 | sper->itr.info_fill = arm_spe_info_fill; | ||
192 | sper->itr.free = arm_spe_recording_free; | ||
193 | sper->itr.reference = arm_spe_reference; | ||
194 | sper->itr.read_finish = arm_spe_read_finish; | ||
195 | sper->itr.alignment = 0; | ||
196 | |||
197 | return &sper->itr; | ||
198 | } | ||
199 | |||
200 | struct perf_event_attr | ||
201 | *arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu) | ||
202 | { | ||
203 | struct perf_event_attr *attr; | ||
204 | |||
205 | attr = zalloc(sizeof(struct perf_event_attr)); | ||
206 | if (!attr) { | ||
207 | pr_err("arm_spe default config cannot allocate a perf_event_attr\n"); | ||
208 | return NULL; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * If kernel driver doesn't advertise a minimum, | ||
213 | * use max allowable by PMSIDR_EL1.INTERVAL | ||
214 | */ | ||
215 | if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu", | ||
216 | &attr->sample_period) != 1) { | ||
217 | pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n"); | ||
218 | attr->sample_period = 4096; | ||
219 | } | ||
220 | |||
221 | arm_spe_pmu->selectable = true; | ||
222 | arm_spe_pmu->is_uncore = false; | ||
223 | |||
224 | return attr; | ||
225 | } | ||
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index b626d2bad9f1..fb0d71afee8b 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c | |||
@@ -70,7 +70,7 @@ get_cpuid_str(struct perf_pmu *pmu __maybe_unused) | |||
70 | { | 70 | { |
71 | char *buf = malloc(128); | 71 | char *buf = malloc(128); |
72 | 72 | ||
73 | if (__get_cpuid(buf, 128, "%s-%u-%X$") < 0) { | 73 | if (buf && __get_cpuid(buf, 128, "%s-%u-%X$") < 0) { |
74 | free(buf); | 74 | free(buf); |
75 | return NULL; | 75 | return NULL; |
76 | } | 76 | } |
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index c0debc3f79b6..c0815a37fdb5 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c | |||
@@ -2390,9 +2390,10 @@ static int setup_callchain(struct perf_evlist *evlist) | |||
2390 | enum perf_call_graph_mode mode = CALLCHAIN_NONE; | 2390 | enum perf_call_graph_mode mode = CALLCHAIN_NONE; |
2391 | 2391 | ||
2392 | if ((sample_type & PERF_SAMPLE_REGS_USER) && | 2392 | if ((sample_type & PERF_SAMPLE_REGS_USER) && |
2393 | (sample_type & PERF_SAMPLE_STACK_USER)) | 2393 | (sample_type & PERF_SAMPLE_STACK_USER)) { |
2394 | mode = CALLCHAIN_DWARF; | 2394 | mode = CALLCHAIN_DWARF; |
2395 | else if (sample_type & PERF_SAMPLE_BRANCH_STACK) | 2395 | dwarf_callchain_users = true; |
2396 | } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) | ||
2396 | mode = CALLCHAIN_LBR; | 2397 | mode = CALLCHAIN_LBR; |
2397 | else if (sample_type & PERF_SAMPLE_CALLCHAIN) | 2398 | else if (sample_type & PERF_SAMPLE_CALLCHAIN) |
2398 | mode = CALLCHAIN_FP; | 2399 | mode = CALLCHAIN_FP; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index dd4df9a5cd06..42a52dcc41cd 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -54,8 +54,6 @@ | |||
54 | #include <unistd.h> | 54 | #include <unistd.h> |
55 | #include <linux/mman.h> | 55 | #include <linux/mman.h> |
56 | 56 | ||
57 | #define PTIME_RANGE_MAX 10 | ||
58 | |||
59 | struct report { | 57 | struct report { |
60 | struct perf_tool tool; | 58 | struct perf_tool tool; |
61 | struct perf_session *session; | 59 | struct perf_session *session; |
@@ -76,7 +74,8 @@ struct report { | |||
76 | const char *cpu_list; | 74 | const char *cpu_list; |
77 | const char *symbol_filter_str; | 75 | const char *symbol_filter_str; |
78 | const char *time_str; | 76 | const char *time_str; |
79 | struct perf_time_interval ptime_range[PTIME_RANGE_MAX]; | 77 | struct perf_time_interval *ptime_range; |
78 | int range_size; | ||
80 | int range_num; | 79 | int range_num; |
81 | float min_percent; | 80 | float min_percent; |
82 | u64 nr_entries; | 81 | u64 nr_entries; |
@@ -338,9 +337,10 @@ static int report__setup_sample_type(struct report *rep) | |||
338 | 337 | ||
339 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { | 338 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { |
340 | if ((sample_type & PERF_SAMPLE_REGS_USER) && | 339 | if ((sample_type & PERF_SAMPLE_REGS_USER) && |
341 | (sample_type & PERF_SAMPLE_STACK_USER)) | 340 | (sample_type & PERF_SAMPLE_STACK_USER)) { |
342 | callchain_param.record_mode = CALLCHAIN_DWARF; | 341 | callchain_param.record_mode = CALLCHAIN_DWARF; |
343 | else if (sample_type & PERF_SAMPLE_BRANCH_STACK) | 342 | dwarf_callchain_users = true; |
343 | } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) | ||
344 | callchain_param.record_mode = CALLCHAIN_LBR; | 344 | callchain_param.record_mode = CALLCHAIN_LBR; |
345 | else | 345 | else |
346 | callchain_param.record_mode = CALLCHAIN_FP; | 346 | callchain_param.record_mode = CALLCHAIN_FP; |
@@ -403,6 +403,9 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report | |||
403 | if (evname != NULL) | 403 | if (evname != NULL) |
404 | ret += fprintf(fp, " of event '%s'", evname); | 404 | ret += fprintf(fp, " of event '%s'", evname); |
405 | 405 | ||
406 | if (rep->time_str) | ||
407 | ret += fprintf(fp, " (time slices: %s)", rep->time_str); | ||
408 | |||
406 | if (symbol_conf.show_ref_callgraph && | 409 | if (symbol_conf.show_ref_callgraph && |
407 | strstr(evname, "call-graph=no")) { | 410 | strstr(evname, "call-graph=no")) { |
408 | ret += fprintf(fp, ", show reference callgraph"); | 411 | ret += fprintf(fp, ", show reference callgraph"); |
@@ -1296,22 +1299,33 @@ repeat: | |||
1296 | if (symbol__init(&session->header.env) < 0) | 1299 | if (symbol__init(&session->header.env) < 0) |
1297 | goto error; | 1300 | goto error; |
1298 | 1301 | ||
1302 | report.ptime_range = perf_time__range_alloc(report.time_str, | ||
1303 | &report.range_size); | ||
1304 | if (!report.ptime_range) { | ||
1305 | ret = -ENOMEM; | ||
1306 | goto error; | ||
1307 | } | ||
1308 | |||
1299 | if (perf_time__parse_str(report.ptime_range, report.time_str) != 0) { | 1309 | if (perf_time__parse_str(report.ptime_range, report.time_str) != 0) { |
1300 | if (session->evlist->first_sample_time == 0 && | 1310 | if (session->evlist->first_sample_time == 0 && |
1301 | session->evlist->last_sample_time == 0) { | 1311 | session->evlist->last_sample_time == 0) { |
1302 | pr_err("No first/last sample time in perf data\n"); | 1312 | pr_err("HINT: no first/last sample time found in perf data.\n" |
1303 | return -EINVAL; | 1313 | "Please use latest perf binary to execute 'perf record'\n" |
1314 | "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n"); | ||
1315 | ret = -EINVAL; | ||
1316 | goto error; | ||
1304 | } | 1317 | } |
1305 | 1318 | ||
1306 | report.range_num = perf_time__percent_parse_str( | 1319 | report.range_num = perf_time__percent_parse_str( |
1307 | report.ptime_range, PTIME_RANGE_MAX, | 1320 | report.ptime_range, report.range_size, |
1308 | report.time_str, | 1321 | report.time_str, |
1309 | session->evlist->first_sample_time, | 1322 | session->evlist->first_sample_time, |
1310 | session->evlist->last_sample_time); | 1323 | session->evlist->last_sample_time); |
1311 | 1324 | ||
1312 | if (report.range_num < 0) { | 1325 | if (report.range_num < 0) { |
1313 | pr_err("Invalid time string\n"); | 1326 | pr_err("Invalid time string\n"); |
1314 | return -EINVAL; | 1327 | ret = -EINVAL; |
1328 | goto error; | ||
1315 | } | 1329 | } |
1316 | } else { | 1330 | } else { |
1317 | report.range_num = 1; | 1331 | report.range_num = 1; |
@@ -1327,6 +1341,8 @@ repeat: | |||
1327 | ret = 0; | 1341 | ret = 0; |
1328 | 1342 | ||
1329 | error: | 1343 | error: |
1344 | zfree(&report.ptime_range); | ||
1345 | |||
1330 | perf_session__delete(session); | 1346 | perf_session__delete(session); |
1331 | return ret; | 1347 | return ret; |
1332 | } | 1348 | } |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c1cce474c0f1..3499d68e1d70 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -1480,8 +1480,6 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample, | |||
1480 | return 0; | 1480 | return 0; |
1481 | } | 1481 | } |
1482 | 1482 | ||
1483 | #define PTIME_RANGE_MAX 10 | ||
1484 | |||
1485 | struct perf_script { | 1483 | struct perf_script { |
1486 | struct perf_tool tool; | 1484 | struct perf_tool tool; |
1487 | struct perf_session *session; | 1485 | struct perf_session *session; |
@@ -1496,7 +1494,8 @@ struct perf_script { | |||
1496 | struct thread_map *threads; | 1494 | struct thread_map *threads; |
1497 | int name_width; | 1495 | int name_width; |
1498 | const char *time_str; | 1496 | const char *time_str; |
1499 | struct perf_time_interval ptime_range[PTIME_RANGE_MAX]; | 1497 | struct perf_time_interval *ptime_range; |
1498 | int range_size; | ||
1500 | int range_num; | 1499 | int range_num; |
1501 | }; | 1500 | }; |
1502 | 1501 | ||
@@ -2919,9 +2918,10 @@ static void script__setup_sample_type(struct perf_script *script) | |||
2919 | 2918 | ||
2920 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { | 2919 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { |
2921 | if ((sample_type & PERF_SAMPLE_REGS_USER) && | 2920 | if ((sample_type & PERF_SAMPLE_REGS_USER) && |
2922 | (sample_type & PERF_SAMPLE_STACK_USER)) | 2921 | (sample_type & PERF_SAMPLE_STACK_USER)) { |
2923 | callchain_param.record_mode = CALLCHAIN_DWARF; | 2922 | callchain_param.record_mode = CALLCHAIN_DWARF; |
2924 | else if (sample_type & PERF_SAMPLE_BRANCH_STACK) | 2923 | dwarf_callchain_users = true; |
2924 | } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) | ||
2925 | callchain_param.record_mode = CALLCHAIN_LBR; | 2925 | callchain_param.record_mode = CALLCHAIN_LBR; |
2926 | else | 2926 | else |
2927 | callchain_param.record_mode = CALLCHAIN_FP; | 2927 | callchain_param.record_mode = CALLCHAIN_FP; |
@@ -3444,17 +3444,26 @@ int cmd_script(int argc, const char **argv) | |||
3444 | if (err < 0) | 3444 | if (err < 0) |
3445 | goto out_delete; | 3445 | goto out_delete; |
3446 | 3446 | ||
3447 | script.ptime_range = perf_time__range_alloc(script.time_str, | ||
3448 | &script.range_size); | ||
3449 | if (!script.ptime_range) { | ||
3450 | err = -ENOMEM; | ||
3451 | goto out_delete; | ||
3452 | } | ||
3453 | |||
3447 | /* needs to be parsed after looking up reference time */ | 3454 | /* needs to be parsed after looking up reference time */ |
3448 | if (perf_time__parse_str(script.ptime_range, script.time_str) != 0) { | 3455 | if (perf_time__parse_str(script.ptime_range, script.time_str) != 0) { |
3449 | if (session->evlist->first_sample_time == 0 && | 3456 | if (session->evlist->first_sample_time == 0 && |
3450 | session->evlist->last_sample_time == 0) { | 3457 | session->evlist->last_sample_time == 0) { |
3451 | pr_err("No first/last sample time in perf data\n"); | 3458 | pr_err("HINT: no first/last sample time found in perf data.\n" |
3459 | "Please use latest perf binary to execute 'perf record'\n" | ||
3460 | "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n"); | ||
3452 | err = -EINVAL; | 3461 | err = -EINVAL; |
3453 | goto out_delete; | 3462 | goto out_delete; |
3454 | } | 3463 | } |
3455 | 3464 | ||
3456 | script.range_num = perf_time__percent_parse_str( | 3465 | script.range_num = perf_time__percent_parse_str( |
3457 | script.ptime_range, PTIME_RANGE_MAX, | 3466 | script.ptime_range, script.range_size, |
3458 | script.time_str, | 3467 | script.time_str, |
3459 | session->evlist->first_sample_time, | 3468 | session->evlist->first_sample_time, |
3460 | session->evlist->last_sample_time); | 3469 | session->evlist->last_sample_time); |
@@ -3473,6 +3482,8 @@ int cmd_script(int argc, const char **argv) | |||
3473 | flush_scripting(); | 3482 | flush_scripting(); |
3474 | 3483 | ||
3475 | out_delete: | 3484 | out_delete: |
3485 | zfree(&script.ptime_range); | ||
3486 | |||
3476 | perf_evlist__free_stats(session->evlist); | 3487 | perf_evlist__free_stats(session->evlist); |
3477 | perf_session__delete(session); | 3488 | perf_session__delete(session); |
3478 | 3489 | ||
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 71e64bdca86f..531d43bf57e1 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1644,7 +1644,7 @@ static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evse | |||
1644 | struct addr_location al; | 1644 | struct addr_location al; |
1645 | 1645 | ||
1646 | if (machine__resolve(trace->host, &al, sample) < 0 || | 1646 | if (machine__resolve(trace->host, &al, sample) < 0 || |
1647 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, trace->max_stack)) | 1647 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, evsel->attr.sample_max_stack)) |
1648 | return -1; | 1648 | return -1; |
1649 | 1649 | ||
1650 | return 0; | 1650 | return 0; |
@@ -2222,6 +2222,9 @@ static int trace__add_syscall_newtp(struct trace *trace) | |||
2222 | if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) | 2222 | if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) |
2223 | goto out_delete_sys_exit; | 2223 | goto out_delete_sys_exit; |
2224 | 2224 | ||
2225 | perf_evsel__config_callchain(sys_enter, &trace->opts, &callchain_param); | ||
2226 | perf_evsel__config_callchain(sys_exit, &trace->opts, &callchain_param); | ||
2227 | |||
2225 | perf_evlist__add(evlist, sys_enter); | 2228 | perf_evlist__add(evlist, sys_enter); |
2226 | perf_evlist__add(evlist, sys_exit); | 2229 | perf_evlist__add(evlist, sys_exit); |
2227 | 2230 | ||
@@ -2318,6 +2321,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2318 | pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ); | 2321 | pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ); |
2319 | if (pgfault_maj == NULL) | 2322 | if (pgfault_maj == NULL) |
2320 | goto out_error_mem; | 2323 | goto out_error_mem; |
2324 | perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param); | ||
2321 | perf_evlist__add(evlist, pgfault_maj); | 2325 | perf_evlist__add(evlist, pgfault_maj); |
2322 | } | 2326 | } |
2323 | 2327 | ||
@@ -2325,6 +2329,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2325 | pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN); | 2329 | pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN); |
2326 | if (pgfault_min == NULL) | 2330 | if (pgfault_min == NULL) |
2327 | goto out_error_mem; | 2331 | goto out_error_mem; |
2332 | perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param); | ||
2328 | perf_evlist__add(evlist, pgfault_min); | 2333 | perf_evlist__add(evlist, pgfault_min); |
2329 | } | 2334 | } |
2330 | 2335 | ||
@@ -2345,45 +2350,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2345 | goto out_delete_evlist; | 2350 | goto out_delete_evlist; |
2346 | } | 2351 | } |
2347 | 2352 | ||
2348 | perf_evlist__config(evlist, &trace->opts, NULL); | 2353 | perf_evlist__config(evlist, &trace->opts, &callchain_param); |
2349 | |||
2350 | if (callchain_param.enabled) { | ||
2351 | bool use_identifier = false; | ||
2352 | |||
2353 | if (trace->syscalls.events.sys_exit) { | ||
2354 | perf_evsel__config_callchain(trace->syscalls.events.sys_exit, | ||
2355 | &trace->opts, &callchain_param); | ||
2356 | use_identifier = true; | ||
2357 | } | ||
2358 | |||
2359 | if (pgfault_maj) { | ||
2360 | perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param); | ||
2361 | use_identifier = true; | ||
2362 | } | ||
2363 | |||
2364 | if (pgfault_min) { | ||
2365 | perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param); | ||
2366 | use_identifier = true; | ||
2367 | } | ||
2368 | |||
2369 | if (use_identifier) { | ||
2370 | /* | ||
2371 | * Now we have evsels with different sample_ids, use | ||
2372 | * PERF_SAMPLE_IDENTIFIER to map from sample to evsel | ||
2373 | * from a fixed position in each ring buffer record. | ||
2374 | * | ||
2375 | * As of this the changeset introducing this comment, this | ||
2376 | * isn't strictly needed, as the fields that can come before | ||
2377 | * PERF_SAMPLE_ID are all used, but we'll probably disable | ||
2378 | * some of those for things like copying the payload of | ||
2379 | * pointer syscall arguments, and for vfs_getname we don't | ||
2380 | * need PERF_SAMPLE_ADDR and PERF_SAMPLE_IP, so do this | ||
2381 | * here as a warning we need to use PERF_SAMPLE_IDENTIFIER. | ||
2382 | */ | ||
2383 | perf_evlist__set_sample_bit(evlist, IDENTIFIER); | ||
2384 | perf_evlist__reset_sample_bit(evlist, ID); | ||
2385 | } | ||
2386 | } | ||
2387 | 2354 | ||
2388 | signal(SIGCHLD, sig_handler); | 2355 | signal(SIGCHLD, sig_handler); |
2389 | signal(SIGINT, sig_handler); | 2356 | signal(SIGINT, sig_handler); |
@@ -2456,6 +2423,18 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2456 | trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 || | 2423 | trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 || |
2457 | evlist->threads->nr > 1 || | 2424 | evlist->threads->nr > 1 || |
2458 | perf_evlist__first(evlist)->attr.inherit; | 2425 | perf_evlist__first(evlist)->attr.inherit; |
2426 | |||
2427 | /* | ||
2428 | * Now that we already used evsel->attr to ask the kernel to setup the | ||
2429 | * events, lets reuse evsel->attr.sample_max_stack as the limit in | ||
2430 | * trace__resolve_callchain(), allowing per-event max-stack settings | ||
2431 | * to override an explicitely set --max-stack global setting. | ||
2432 | */ | ||
2433 | evlist__for_each_entry(evlist, evsel) { | ||
2434 | if ((evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) && | ||
2435 | evsel->attr.sample_max_stack == 0) | ||
2436 | evsel->attr.sample_max_stack = trace->max_stack; | ||
2437 | } | ||
2459 | again: | 2438 | again: |
2460 | before = trace->nr_events; | 2439 | before = trace->nr_events; |
2461 | 2440 | ||
@@ -3098,8 +3077,9 @@ int cmd_trace(int argc, const char **argv) | |||
3098 | } | 3077 | } |
3099 | 3078 | ||
3100 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 3079 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
3101 | if ((trace.min_stack || max_stack_user_set) && !callchain_param.enabled && trace.trace_syscalls) | 3080 | if ((trace.min_stack || max_stack_user_set) && !callchain_param.enabled) { |
3102 | record_opts__parse_callchain(&trace.opts, &callchain_param, "dwarf", false); | 3081 | record_opts__parse_callchain(&trace.opts, &callchain_param, "dwarf", false); |
3082 | } | ||
3103 | #endif | 3083 | #endif |
3104 | 3084 | ||
3105 | if (callchain_param.enabled) { | 3085 | if (callchain_param.enabled) { |
diff --git a/tools/perf/scripts/python/bin/mem-phys-addr-record b/tools/perf/scripts/python/bin/mem-phys-addr-record new file mode 100644 index 000000000000..5a875122a904 --- /dev/null +++ b/tools/perf/scripts/python/bin/mem-phys-addr-record | |||
@@ -0,0 +1,19 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | # | ||
4 | # Profiling physical memory by all retired load instructions/uops event | ||
5 | # MEM_INST_RETIRED.ALL_LOADS or MEM_UOPS_RETIRED.ALL_LOADS | ||
6 | # | ||
7 | |||
8 | load=`perf list | grep mem_inst_retired.all_loads` | ||
9 | if [ -z "$load" ]; then | ||
10 | load=`perf list | grep mem_uops_retired.all_loads` | ||
11 | fi | ||
12 | if [ -z "$load" ]; then | ||
13 | echo "There is no event to count all retired load instructions/uops." | ||
14 | exit 1 | ||
15 | fi | ||
16 | |||
17 | arg=$(echo $load | tr -d ' ') | ||
18 | arg="$arg:P" | ||
19 | perf record --phys-data -e $arg $@ | ||
diff --git a/tools/perf/scripts/python/bin/mem-phys-addr-report b/tools/perf/scripts/python/bin/mem-phys-addr-report new file mode 100644 index 000000000000..3f2b847e2eab --- /dev/null +++ b/tools/perf/scripts/python/bin/mem-phys-addr-report | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/bash | ||
2 | # description: resolve physical address samples | ||
3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/mem-phys-addr.py | ||
diff --git a/tools/perf/scripts/python/mem-phys-addr.py b/tools/perf/scripts/python/mem-phys-addr.py new file mode 100644 index 000000000000..ebee2c5ae496 --- /dev/null +++ b/tools/perf/scripts/python/mem-phys-addr.py | |||
@@ -0,0 +1,95 @@ | |||
1 | # mem-phys-addr.py: Resolve physical address samples | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # | ||
4 | # Copyright (c) 2018, Intel Corporation. | ||
5 | |||
6 | from __future__ import division | ||
7 | import os | ||
8 | import sys | ||
9 | import struct | ||
10 | import re | ||
11 | import bisect | ||
12 | import collections | ||
13 | |||
14 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
15 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
16 | |||
17 | #physical address ranges for System RAM | ||
18 | system_ram = [] | ||
19 | #physical address ranges for Persistent Memory | ||
20 | pmem = [] | ||
21 | #file object for proc iomem | ||
22 | f = None | ||
23 | #Count for each type of memory | ||
24 | load_mem_type_cnt = collections.Counter() | ||
25 | #perf event name | ||
26 | event_name = None | ||
27 | |||
28 | def parse_iomem(): | ||
29 | global f | ||
30 | f = open('/proc/iomem', 'r') | ||
31 | for i, j in enumerate(f): | ||
32 | m = re.split('-|:',j,2) | ||
33 | if m[2].strip() == 'System RAM': | ||
34 | system_ram.append(long(m[0], 16)) | ||
35 | system_ram.append(long(m[1], 16)) | ||
36 | if m[2].strip() == 'Persistent Memory': | ||
37 | pmem.append(long(m[0], 16)) | ||
38 | pmem.append(long(m[1], 16)) | ||
39 | |||
40 | def print_memory_type(): | ||
41 | print "Event: %s" % (event_name) | ||
42 | print "%-40s %10s %10s\n" % ("Memory type", "count", "percentage"), | ||
43 | print "%-40s %10s %10s\n" % ("----------------------------------------", \ | ||
44 | "-----------", "-----------"), | ||
45 | total = sum(load_mem_type_cnt.values()) | ||
46 | for mem_type, count in sorted(load_mem_type_cnt.most_common(), \ | ||
47 | key = lambda(k, v): (v, k), reverse = True): | ||
48 | print "%-40s %10d %10.1f%%\n" % (mem_type, count, 100 * count / total), | ||
49 | |||
50 | def trace_begin(): | ||
51 | parse_iomem() | ||
52 | |||
53 | def trace_end(): | ||
54 | print_memory_type() | ||
55 | f.close() | ||
56 | |||
57 | def is_system_ram(phys_addr): | ||
58 | #/proc/iomem is sorted | ||
59 | position = bisect.bisect(system_ram, phys_addr) | ||
60 | if position % 2 == 0: | ||
61 | return False | ||
62 | return True | ||
63 | |||
64 | def is_persistent_mem(phys_addr): | ||
65 | position = bisect.bisect(pmem, phys_addr) | ||
66 | if position % 2 == 0: | ||
67 | return False | ||
68 | return True | ||
69 | |||
70 | def find_memory_type(phys_addr): | ||
71 | if phys_addr == 0: | ||
72 | return "N/A" | ||
73 | if is_system_ram(phys_addr): | ||
74 | return "System RAM" | ||
75 | |||
76 | if is_persistent_mem(phys_addr): | ||
77 | return "Persistent Memory" | ||
78 | |||
79 | #slow path, search all | ||
80 | f.seek(0, 0) | ||
81 | for j in f: | ||
82 | m = re.split('-|:',j,2) | ||
83 | if long(m[0], 16) <= phys_addr <= long(m[1], 16): | ||
84 | return m[2] | ||
85 | return "N/A" | ||
86 | |||
87 | def process_event(param_dict): | ||
88 | name = param_dict["ev_name"] | ||
89 | sample = param_dict["sample"] | ||
90 | phys_addr = sample["phys_addr"] | ||
91 | |||
92 | global event_name | ||
93 | if event_name == None: | ||
94 | event_name = name | ||
95 | load_mem_type_cnt[find_memory_type(phys_addr)] += 1 | ||
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index ac40e05bcab4..260418969120 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -173,6 +173,7 @@ int test__dwarf_unwind(struct test *test __maybe_unused, int subtest __maybe_unu | |||
173 | } | 173 | } |
174 | 174 | ||
175 | callchain_param.record_mode = CALLCHAIN_DWARF; | 175 | callchain_param.record_mode = CALLCHAIN_DWARF; |
176 | dwarf_callchain_users = true; | ||
176 | 177 | ||
177 | if (init_live_machine(machine)) { | 178 | if (init_live_machine(machine)) { |
178 | pr_err("Could not init machine\n"); | 179 | pr_err("Could not init machine\n"); |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index a3de7916fe63..7c6a8b461e24 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -86,6 +86,8 @@ libperf-$(CONFIG_AUXTRACE) += auxtrace.o | |||
86 | libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/ | 86 | libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/ |
87 | libperf-$(CONFIG_AUXTRACE) += intel-pt.o | 87 | libperf-$(CONFIG_AUXTRACE) += intel-pt.o |
88 | libperf-$(CONFIG_AUXTRACE) += intel-bts.o | 88 | libperf-$(CONFIG_AUXTRACE) += intel-bts.o |
89 | libperf-$(CONFIG_AUXTRACE) += arm-spe.o | ||
90 | libperf-$(CONFIG_AUXTRACE) += arm-spe-pkt-decoder.o | ||
89 | libperf-y += parse-branch-options.o | 91 | libperf-y += parse-branch-options.o |
90 | libperf-y += dump-insn.o | 92 | libperf-y += dump-insn.o |
91 | libperf-y += parse-regs-options.o | 93 | libperf-y += parse-regs-options.o |
diff --git a/tools/perf/util/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-pkt-decoder.c new file mode 100644 index 000000000000..b94001b756c7 --- /dev/null +++ b/tools/perf/util/arm-spe-pkt-decoder.c | |||
@@ -0,0 +1,462 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Arm Statistical Profiling Extensions (SPE) support | ||
4 | * Copyright (c) 2017-2018, Arm Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <stdio.h> | ||
8 | #include <string.h> | ||
9 | #include <endian.h> | ||
10 | #include <byteswap.h> | ||
11 | |||
12 | #include "arm-spe-pkt-decoder.h" | ||
13 | |||
14 | #define BIT(n) (1ULL << (n)) | ||
15 | |||
16 | #define NS_FLAG BIT(63) | ||
17 | #define EL_FLAG (BIT(62) | BIT(61)) | ||
18 | |||
19 | #define SPE_HEADER0_PAD 0x0 | ||
20 | #define SPE_HEADER0_END 0x1 | ||
21 | #define SPE_HEADER0_ADDRESS 0x30 /* address packet (short) */ | ||
22 | #define SPE_HEADER0_ADDRESS_MASK 0x38 | ||
23 | #define SPE_HEADER0_COUNTER 0x18 /* counter packet (short) */ | ||
24 | #define SPE_HEADER0_COUNTER_MASK 0x38 | ||
25 | #define SPE_HEADER0_TIMESTAMP 0x71 | ||
26 | #define SPE_HEADER0_TIMESTAMP 0x71 | ||
27 | #define SPE_HEADER0_EVENTS 0x2 | ||
28 | #define SPE_HEADER0_EVENTS_MASK 0xf | ||
29 | #define SPE_HEADER0_SOURCE 0x3 | ||
30 | #define SPE_HEADER0_SOURCE_MASK 0xf | ||
31 | #define SPE_HEADER0_CONTEXT 0x24 | ||
32 | #define SPE_HEADER0_CONTEXT_MASK 0x3c | ||
33 | #define SPE_HEADER0_OP_TYPE 0x8 | ||
34 | #define SPE_HEADER0_OP_TYPE_MASK 0x3c | ||
35 | #define SPE_HEADER1_ALIGNMENT 0x0 | ||
36 | #define SPE_HEADER1_ADDRESS 0xb0 /* address packet (extended) */ | ||
37 | #define SPE_HEADER1_ADDRESS_MASK 0xf8 | ||
38 | #define SPE_HEADER1_COUNTER 0x98 /* counter packet (extended) */ | ||
39 | #define SPE_HEADER1_COUNTER_MASK 0xf8 | ||
40 | |||
41 | #if __BYTE_ORDER == __BIG_ENDIAN | ||
42 | #define le16_to_cpu bswap_16 | ||
43 | #define le32_to_cpu bswap_32 | ||
44 | #define le64_to_cpu bswap_64 | ||
45 | #define memcpy_le64(d, s, n) do { \ | ||
46 | memcpy((d), (s), (n)); \ | ||
47 | *(d) = le64_to_cpu(*(d)); \ | ||
48 | } while (0) | ||
49 | #else | ||
50 | #define le16_to_cpu | ||
51 | #define le32_to_cpu | ||
52 | #define le64_to_cpu | ||
53 | #define memcpy_le64 memcpy | ||
54 | #endif | ||
55 | |||
56 | static const char * const arm_spe_packet_name[] = { | ||
57 | [ARM_SPE_PAD] = "PAD", | ||
58 | [ARM_SPE_END] = "END", | ||
59 | [ARM_SPE_TIMESTAMP] = "TS", | ||
60 | [ARM_SPE_ADDRESS] = "ADDR", | ||
61 | [ARM_SPE_COUNTER] = "LAT", | ||
62 | [ARM_SPE_CONTEXT] = "CONTEXT", | ||
63 | [ARM_SPE_OP_TYPE] = "OP-TYPE", | ||
64 | [ARM_SPE_EVENTS] = "EVENTS", | ||
65 | [ARM_SPE_DATA_SOURCE] = "DATA-SOURCE", | ||
66 | }; | ||
67 | |||
68 | const char *arm_spe_pkt_name(enum arm_spe_pkt_type type) | ||
69 | { | ||
70 | return arm_spe_packet_name[type]; | ||
71 | } | ||
72 | |||
73 | /* return ARM SPE payload size from its encoding, | ||
74 | * which is in bits 5:4 of the byte. | ||
75 | * 00 : byte | ||
76 | * 01 : halfword (2) | ||
77 | * 10 : word (4) | ||
78 | * 11 : doubleword (8) | ||
79 | */ | ||
80 | static int payloadlen(unsigned char byte) | ||
81 | { | ||
82 | return 1 << ((byte & 0x30) >> 4); | ||
83 | } | ||
84 | |||
85 | static int arm_spe_get_payload(const unsigned char *buf, size_t len, | ||
86 | struct arm_spe_pkt *packet) | ||
87 | { | ||
88 | size_t payload_len = payloadlen(buf[0]); | ||
89 | |||
90 | if (len < 1 + payload_len) | ||
91 | return ARM_SPE_NEED_MORE_BYTES; | ||
92 | |||
93 | buf++; | ||
94 | |||
95 | switch (payload_len) { | ||
96 | case 1: packet->payload = *(uint8_t *)buf; break; | ||
97 | case 2: packet->payload = le16_to_cpu(*(uint16_t *)buf); break; | ||
98 | case 4: packet->payload = le32_to_cpu(*(uint32_t *)buf); break; | ||
99 | case 8: packet->payload = le64_to_cpu(*(uint64_t *)buf); break; | ||
100 | default: return ARM_SPE_BAD_PACKET; | ||
101 | } | ||
102 | |||
103 | return 1 + payload_len; | ||
104 | } | ||
105 | |||
106 | static int arm_spe_get_pad(struct arm_spe_pkt *packet) | ||
107 | { | ||
108 | packet->type = ARM_SPE_PAD; | ||
109 | return 1; | ||
110 | } | ||
111 | |||
112 | static int arm_spe_get_alignment(const unsigned char *buf, size_t len, | ||
113 | struct arm_spe_pkt *packet) | ||
114 | { | ||
115 | unsigned int alignment = 1 << ((buf[0] & 0xf) + 1); | ||
116 | |||
117 | if (len < alignment) | ||
118 | return ARM_SPE_NEED_MORE_BYTES; | ||
119 | |||
120 | packet->type = ARM_SPE_PAD; | ||
121 | return alignment - (((uintptr_t)buf) & (alignment - 1)); | ||
122 | } | ||
123 | |||
124 | static int arm_spe_get_end(struct arm_spe_pkt *packet) | ||
125 | { | ||
126 | packet->type = ARM_SPE_END; | ||
127 | return 1; | ||
128 | } | ||
129 | |||
130 | static int arm_spe_get_timestamp(const unsigned char *buf, size_t len, | ||
131 | struct arm_spe_pkt *packet) | ||
132 | { | ||
133 | packet->type = ARM_SPE_TIMESTAMP; | ||
134 | return arm_spe_get_payload(buf, len, packet); | ||
135 | } | ||
136 | |||
137 | static int arm_spe_get_events(const unsigned char *buf, size_t len, | ||
138 | struct arm_spe_pkt *packet) | ||
139 | { | ||
140 | int ret = arm_spe_get_payload(buf, len, packet); | ||
141 | |||
142 | packet->type = ARM_SPE_EVENTS; | ||
143 | |||
144 | /* we use index to identify Events with a less number of | ||
145 | * comparisons in arm_spe_pkt_desc(): E.g., the LLC-ACCESS, | ||
146 | * LLC-REFILL, and REMOTE-ACCESS events are identified iff | ||
147 | * index > 1. | ||
148 | */ | ||
149 | packet->index = ret - 1; | ||
150 | |||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | static int arm_spe_get_data_source(const unsigned char *buf, size_t len, | ||
155 | struct arm_spe_pkt *packet) | ||
156 | { | ||
157 | packet->type = ARM_SPE_DATA_SOURCE; | ||
158 | return arm_spe_get_payload(buf, len, packet); | ||
159 | } | ||
160 | |||
161 | static int arm_spe_get_context(const unsigned char *buf, size_t len, | ||
162 | struct arm_spe_pkt *packet) | ||
163 | { | ||
164 | packet->type = ARM_SPE_CONTEXT; | ||
165 | packet->index = buf[0] & 0x3; | ||
166 | |||
167 | return arm_spe_get_payload(buf, len, packet); | ||
168 | } | ||
169 | |||
170 | static int arm_spe_get_op_type(const unsigned char *buf, size_t len, | ||
171 | struct arm_spe_pkt *packet) | ||
172 | { | ||
173 | packet->type = ARM_SPE_OP_TYPE; | ||
174 | packet->index = buf[0] & 0x3; | ||
175 | return arm_spe_get_payload(buf, len, packet); | ||
176 | } | ||
177 | |||
178 | static int arm_spe_get_counter(const unsigned char *buf, size_t len, | ||
179 | const unsigned char ext_hdr, struct arm_spe_pkt *packet) | ||
180 | { | ||
181 | if (len < 2) | ||
182 | return ARM_SPE_NEED_MORE_BYTES; | ||
183 | |||
184 | packet->type = ARM_SPE_COUNTER; | ||
185 | if (ext_hdr) | ||
186 | packet->index = ((buf[0] & 0x3) << 3) | (buf[1] & 0x7); | ||
187 | else | ||
188 | packet->index = buf[0] & 0x7; | ||
189 | |||
190 | packet->payload = le16_to_cpu(*(uint16_t *)(buf + 1)); | ||
191 | |||
192 | return 1 + ext_hdr + 2; | ||
193 | } | ||
194 | |||
195 | static int arm_spe_get_addr(const unsigned char *buf, size_t len, | ||
196 | const unsigned char ext_hdr, struct arm_spe_pkt *packet) | ||
197 | { | ||
198 | if (len < 8) | ||
199 | return ARM_SPE_NEED_MORE_BYTES; | ||
200 | |||
201 | packet->type = ARM_SPE_ADDRESS; | ||
202 | if (ext_hdr) | ||
203 | packet->index = ((buf[0] & 0x3) << 3) | (buf[1] & 0x7); | ||
204 | else | ||
205 | packet->index = buf[0] & 0x7; | ||
206 | |||
207 | memcpy_le64(&packet->payload, buf + 1, 8); | ||
208 | |||
209 | return 1 + ext_hdr + 8; | ||
210 | } | ||
211 | |||
212 | static int arm_spe_do_get_packet(const unsigned char *buf, size_t len, | ||
213 | struct arm_spe_pkt *packet) | ||
214 | { | ||
215 | unsigned int byte; | ||
216 | |||
217 | memset(packet, 0, sizeof(struct arm_spe_pkt)); | ||
218 | |||
219 | if (!len) | ||
220 | return ARM_SPE_NEED_MORE_BYTES; | ||
221 | |||
222 | byte = buf[0]; | ||
223 | if (byte == SPE_HEADER0_PAD) | ||
224 | return arm_spe_get_pad(packet); | ||
225 | else if (byte == SPE_HEADER0_END) /* no timestamp at end of record */ | ||
226 | return arm_spe_get_end(packet); | ||
227 | else if (byte & 0xc0 /* 0y11xxxxxx */) { | ||
228 | if (byte & 0x80) { | ||
229 | if ((byte & SPE_HEADER0_ADDRESS_MASK) == SPE_HEADER0_ADDRESS) | ||
230 | return arm_spe_get_addr(buf, len, 0, packet); | ||
231 | if ((byte & SPE_HEADER0_COUNTER_MASK) == SPE_HEADER0_COUNTER) | ||
232 | return arm_spe_get_counter(buf, len, 0, packet); | ||
233 | } else | ||
234 | if (byte == SPE_HEADER0_TIMESTAMP) | ||
235 | return arm_spe_get_timestamp(buf, len, packet); | ||
236 | else if ((byte & SPE_HEADER0_EVENTS_MASK) == SPE_HEADER0_EVENTS) | ||
237 | return arm_spe_get_events(buf, len, packet); | ||
238 | else if ((byte & SPE_HEADER0_SOURCE_MASK) == SPE_HEADER0_SOURCE) | ||
239 | return arm_spe_get_data_source(buf, len, packet); | ||
240 | else if ((byte & SPE_HEADER0_CONTEXT_MASK) == SPE_HEADER0_CONTEXT) | ||
241 | return arm_spe_get_context(buf, len, packet); | ||
242 | else if ((byte & SPE_HEADER0_OP_TYPE_MASK) == SPE_HEADER0_OP_TYPE) | ||
243 | return arm_spe_get_op_type(buf, len, packet); | ||
244 | } else if ((byte & 0xe0) == 0x20 /* 0y001xxxxx */) { | ||
245 | /* 16-bit header */ | ||
246 | byte = buf[1]; | ||
247 | if (byte == SPE_HEADER1_ALIGNMENT) | ||
248 | return arm_spe_get_alignment(buf, len, packet); | ||
249 | else if ((byte & SPE_HEADER1_ADDRESS_MASK) == SPE_HEADER1_ADDRESS) | ||
250 | return arm_spe_get_addr(buf, len, 1, packet); | ||
251 | else if ((byte & SPE_HEADER1_COUNTER_MASK) == SPE_HEADER1_COUNTER) | ||
252 | return arm_spe_get_counter(buf, len, 1, packet); | ||
253 | } | ||
254 | |||
255 | return ARM_SPE_BAD_PACKET; | ||
256 | } | ||
257 | |||
258 | int arm_spe_get_packet(const unsigned char *buf, size_t len, | ||
259 | struct arm_spe_pkt *packet) | ||
260 | { | ||
261 | int ret; | ||
262 | |||
263 | ret = arm_spe_do_get_packet(buf, len, packet); | ||
264 | /* put multiple consecutive PADs on the same line, up to | ||
265 | * the fixed-width output format of 16 bytes per line. | ||
266 | */ | ||
267 | if (ret > 0 && packet->type == ARM_SPE_PAD) { | ||
268 | while (ret < 16 && len > (size_t)ret && !buf[ret]) | ||
269 | ret += 1; | ||
270 | } | ||
271 | return ret; | ||
272 | } | ||
273 | |||
274 | int arm_spe_pkt_desc(const struct arm_spe_pkt *packet, char *buf, | ||
275 | size_t buf_len) | ||
276 | { | ||
277 | int ret, ns, el, idx = packet->index; | ||
278 | unsigned long long payload = packet->payload; | ||
279 | const char *name = arm_spe_pkt_name(packet->type); | ||
280 | |||
281 | switch (packet->type) { | ||
282 | case ARM_SPE_BAD: | ||
283 | case ARM_SPE_PAD: | ||
284 | case ARM_SPE_END: | ||
285 | return snprintf(buf, buf_len, "%s", name); | ||
286 | case ARM_SPE_EVENTS: { | ||
287 | size_t blen = buf_len; | ||
288 | |||
289 | ret = 0; | ||
290 | ret = snprintf(buf, buf_len, "EV"); | ||
291 | buf += ret; | ||
292 | blen -= ret; | ||
293 | if (payload & 0x1) { | ||
294 | ret = snprintf(buf, buf_len, " EXCEPTION-GEN"); | ||
295 | buf += ret; | ||
296 | blen -= ret; | ||
297 | } | ||
298 | if (payload & 0x2) { | ||
299 | ret = snprintf(buf, buf_len, " RETIRED"); | ||
300 | buf += ret; | ||
301 | blen -= ret; | ||
302 | } | ||
303 | if (payload & 0x4) { | ||
304 | ret = snprintf(buf, buf_len, " L1D-ACCESS"); | ||
305 | buf += ret; | ||
306 | blen -= ret; | ||
307 | } | ||
308 | if (payload & 0x8) { | ||
309 | ret = snprintf(buf, buf_len, " L1D-REFILL"); | ||
310 | buf += ret; | ||
311 | blen -= ret; | ||
312 | } | ||
313 | if (payload & 0x10) { | ||
314 | ret = snprintf(buf, buf_len, " TLB-ACCESS"); | ||
315 | buf += ret; | ||
316 | blen -= ret; | ||
317 | } | ||
318 | if (payload & 0x20) { | ||
319 | ret = snprintf(buf, buf_len, " TLB-REFILL"); | ||
320 | buf += ret; | ||
321 | blen -= ret; | ||
322 | } | ||
323 | if (payload & 0x40) { | ||
324 | ret = snprintf(buf, buf_len, " NOT-TAKEN"); | ||
325 | buf += ret; | ||
326 | blen -= ret; | ||
327 | } | ||
328 | if (payload & 0x80) { | ||
329 | ret = snprintf(buf, buf_len, " MISPRED"); | ||
330 | buf += ret; | ||
331 | blen -= ret; | ||
332 | } | ||
333 | if (idx > 1) { | ||
334 | if (payload & 0x100) { | ||
335 | ret = snprintf(buf, buf_len, " LLC-ACCESS"); | ||
336 | buf += ret; | ||
337 | blen -= ret; | ||
338 | } | ||
339 | if (payload & 0x200) { | ||
340 | ret = snprintf(buf, buf_len, " LLC-REFILL"); | ||
341 | buf += ret; | ||
342 | blen -= ret; | ||
343 | } | ||
344 | if (payload & 0x400) { | ||
345 | ret = snprintf(buf, buf_len, " REMOTE-ACCESS"); | ||
346 | buf += ret; | ||
347 | blen -= ret; | ||
348 | } | ||
349 | } | ||
350 | if (ret < 0) | ||
351 | return ret; | ||
352 | blen -= ret; | ||
353 | return buf_len - blen; | ||
354 | } | ||
355 | case ARM_SPE_OP_TYPE: | ||
356 | switch (idx) { | ||
357 | case 0: return snprintf(buf, buf_len, "%s", payload & 0x1 ? | ||
358 | "COND-SELECT" : "INSN-OTHER"); | ||
359 | case 1: { | ||
360 | size_t blen = buf_len; | ||
361 | |||
362 | if (payload & 0x1) | ||
363 | ret = snprintf(buf, buf_len, "ST"); | ||
364 | else | ||
365 | ret = snprintf(buf, buf_len, "LD"); | ||
366 | buf += ret; | ||
367 | blen -= ret; | ||
368 | if (payload & 0x2) { | ||
369 | if (payload & 0x4) { | ||
370 | ret = snprintf(buf, buf_len, " AT"); | ||
371 | buf += ret; | ||
372 | blen -= ret; | ||
373 | } | ||
374 | if (payload & 0x8) { | ||
375 | ret = snprintf(buf, buf_len, " EXCL"); | ||
376 | buf += ret; | ||
377 | blen -= ret; | ||
378 | } | ||
379 | if (payload & 0x10) { | ||
380 | ret = snprintf(buf, buf_len, " AR"); | ||
381 | buf += ret; | ||
382 | blen -= ret; | ||
383 | } | ||
384 | } else if (payload & 0x4) { | ||
385 | ret = snprintf(buf, buf_len, " SIMD-FP"); | ||
386 | buf += ret; | ||
387 | blen -= ret; | ||
388 | } | ||
389 | if (ret < 0) | ||
390 | return ret; | ||
391 | blen -= ret; | ||
392 | return buf_len - blen; | ||
393 | } | ||
394 | case 2: { | ||
395 | size_t blen = buf_len; | ||
396 | |||
397 | ret = snprintf(buf, buf_len, "B"); | ||
398 | buf += ret; | ||
399 | blen -= ret; | ||
400 | if (payload & 0x1) { | ||
401 | ret = snprintf(buf, buf_len, " COND"); | ||
402 | buf += ret; | ||
403 | blen -= ret; | ||
404 | } | ||
405 | if (payload & 0x2) { | ||
406 | ret = snprintf(buf, buf_len, " IND"); | ||
407 | buf += ret; | ||
408 | blen -= ret; | ||
409 | } | ||
410 | if (ret < 0) | ||
411 | return ret; | ||
412 | blen -= ret; | ||
413 | return buf_len - blen; | ||
414 | } | ||
415 | default: return 0; | ||
416 | } | ||
417 | case ARM_SPE_DATA_SOURCE: | ||
418 | case ARM_SPE_TIMESTAMP: | ||
419 | return snprintf(buf, buf_len, "%s %lld", name, payload); | ||
420 | case ARM_SPE_ADDRESS: | ||
421 | switch (idx) { | ||
422 | case 0: | ||
423 | case 1: ns = !!(packet->payload & NS_FLAG); | ||
424 | el = (packet->payload & EL_FLAG) >> 61; | ||
425 | payload &= ~(0xffULL << 56); | ||
426 | return snprintf(buf, buf_len, "%s 0x%llx el%d ns=%d", | ||
427 | (idx == 1) ? "TGT" : "PC", payload, el, ns); | ||
428 | case 2: return snprintf(buf, buf_len, "VA 0x%llx", payload); | ||
429 | case 3: ns = !!(packet->payload & NS_FLAG); | ||
430 | payload &= ~(0xffULL << 56); | ||
431 | return snprintf(buf, buf_len, "PA 0x%llx ns=%d", | ||
432 | payload, ns); | ||
433 | default: return 0; | ||
434 | } | ||
435 | case ARM_SPE_CONTEXT: | ||
436 | return snprintf(buf, buf_len, "%s 0x%lx el%d", name, | ||
437 | (unsigned long)payload, idx + 1); | ||
438 | case ARM_SPE_COUNTER: { | ||
439 | size_t blen = buf_len; | ||
440 | |||
441 | ret = snprintf(buf, buf_len, "%s %d ", name, | ||
442 | (unsigned short)payload); | ||
443 | buf += ret; | ||
444 | blen -= ret; | ||
445 | switch (idx) { | ||
446 | case 0: ret = snprintf(buf, buf_len, "TOT"); break; | ||
447 | case 1: ret = snprintf(buf, buf_len, "ISSUE"); break; | ||
448 | case 2: ret = snprintf(buf, buf_len, "XLAT"); break; | ||
449 | default: ret = 0; | ||
450 | } | ||
451 | if (ret < 0) | ||
452 | return ret; | ||
453 | blen -= ret; | ||
454 | return buf_len - blen; | ||
455 | } | ||
456 | default: | ||
457 | break; | ||
458 | } | ||
459 | |||
460 | return snprintf(buf, buf_len, "%s 0x%llx (%d)", | ||
461 | name, payload, packet->index); | ||
462 | } | ||
diff --git a/tools/perf/util/arm-spe-pkt-decoder.h b/tools/perf/util/arm-spe-pkt-decoder.h new file mode 100644 index 000000000000..d786ef65113f --- /dev/null +++ b/tools/perf/util/arm-spe-pkt-decoder.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Arm Statistical Profiling Extensions (SPE) support | ||
4 | * Copyright (c) 2017-2018, Arm Ltd. | ||
5 | */ | ||
6 | |||
7 | #ifndef INCLUDE__ARM_SPE_PKT_DECODER_H__ | ||
8 | #define INCLUDE__ARM_SPE_PKT_DECODER_H__ | ||
9 | |||
10 | #include <stddef.h> | ||
11 | #include <stdint.h> | ||
12 | |||
13 | #define ARM_SPE_PKT_DESC_MAX 256 | ||
14 | |||
15 | #define ARM_SPE_NEED_MORE_BYTES -1 | ||
16 | #define ARM_SPE_BAD_PACKET -2 | ||
17 | |||
18 | enum arm_spe_pkt_type { | ||
19 | ARM_SPE_BAD, | ||
20 | ARM_SPE_PAD, | ||
21 | ARM_SPE_END, | ||
22 | ARM_SPE_TIMESTAMP, | ||
23 | ARM_SPE_ADDRESS, | ||
24 | ARM_SPE_COUNTER, | ||
25 | ARM_SPE_CONTEXT, | ||
26 | ARM_SPE_OP_TYPE, | ||
27 | ARM_SPE_EVENTS, | ||
28 | ARM_SPE_DATA_SOURCE, | ||
29 | }; | ||
30 | |||
31 | struct arm_spe_pkt { | ||
32 | enum arm_spe_pkt_type type; | ||
33 | unsigned char index; | ||
34 | uint64_t payload; | ||
35 | }; | ||
36 | |||
37 | const char *arm_spe_pkt_name(enum arm_spe_pkt_type); | ||
38 | |||
39 | int arm_spe_get_packet(const unsigned char *buf, size_t len, | ||
40 | struct arm_spe_pkt *packet); | ||
41 | |||
42 | int arm_spe_pkt_desc(const struct arm_spe_pkt *packet, char *buf, size_t len); | ||
43 | #endif | ||
diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c new file mode 100644 index 000000000000..6067267cc76c --- /dev/null +++ b/tools/perf/util/arm-spe.c | |||
@@ -0,0 +1,231 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Arm Statistical Profiling Extensions (SPE) support | ||
4 | * Copyright (c) 2017-2018, Arm Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <endian.h> | ||
8 | #include <errno.h> | ||
9 | #include <byteswap.h> | ||
10 | #include <inttypes.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/log2.h> | ||
15 | |||
16 | #include "cpumap.h" | ||
17 | #include "color.h" | ||
18 | #include "evsel.h" | ||
19 | #include "evlist.h" | ||
20 | #include "machine.h" | ||
21 | #include "session.h" | ||
22 | #include "util.h" | ||
23 | #include "thread.h" | ||
24 | #include "debug.h" | ||
25 | #include "auxtrace.h" | ||
26 | #include "arm-spe.h" | ||
27 | #include "arm-spe-pkt-decoder.h" | ||
28 | |||
29 | struct arm_spe { | ||
30 | struct auxtrace auxtrace; | ||
31 | struct auxtrace_queues queues; | ||
32 | struct auxtrace_heap heap; | ||
33 | u32 auxtrace_type; | ||
34 | struct perf_session *session; | ||
35 | struct machine *machine; | ||
36 | u32 pmu_type; | ||
37 | }; | ||
38 | |||
39 | struct arm_spe_queue { | ||
40 | struct arm_spe *spe; | ||
41 | unsigned int queue_nr; | ||
42 | struct auxtrace_buffer *buffer; | ||
43 | bool on_heap; | ||
44 | bool done; | ||
45 | pid_t pid; | ||
46 | pid_t tid; | ||
47 | int cpu; | ||
48 | }; | ||
49 | |||
50 | static void arm_spe_dump(struct arm_spe *spe __maybe_unused, | ||
51 | unsigned char *buf, size_t len) | ||
52 | { | ||
53 | struct arm_spe_pkt packet; | ||
54 | size_t pos = 0; | ||
55 | int ret, pkt_len, i; | ||
56 | char desc[ARM_SPE_PKT_DESC_MAX]; | ||
57 | const char *color = PERF_COLOR_BLUE; | ||
58 | |||
59 | color_fprintf(stdout, color, | ||
60 | ". ... ARM SPE data: size %zu bytes\n", | ||
61 | len); | ||
62 | |||
63 | while (len) { | ||
64 | ret = arm_spe_get_packet(buf, len, &packet); | ||
65 | if (ret > 0) | ||
66 | pkt_len = ret; | ||
67 | else | ||
68 | pkt_len = 1; | ||
69 | printf("."); | ||
70 | color_fprintf(stdout, color, " %08x: ", pos); | ||
71 | for (i = 0; i < pkt_len; i++) | ||
72 | color_fprintf(stdout, color, " %02x", buf[i]); | ||
73 | for (; i < 16; i++) | ||
74 | color_fprintf(stdout, color, " "); | ||
75 | if (ret > 0) { | ||
76 | ret = arm_spe_pkt_desc(&packet, desc, | ||
77 | ARM_SPE_PKT_DESC_MAX); | ||
78 | if (ret > 0) | ||
79 | color_fprintf(stdout, color, " %s\n", desc); | ||
80 | } else { | ||
81 | color_fprintf(stdout, color, " Bad packet!\n"); | ||
82 | } | ||
83 | pos += pkt_len; | ||
84 | buf += pkt_len; | ||
85 | len -= pkt_len; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static void arm_spe_dump_event(struct arm_spe *spe, unsigned char *buf, | ||
90 | size_t len) | ||
91 | { | ||
92 | printf(".\n"); | ||
93 | arm_spe_dump(spe, buf, len); | ||
94 | } | ||
95 | |||
96 | static int arm_spe_process_event(struct perf_session *session __maybe_unused, | ||
97 | union perf_event *event __maybe_unused, | ||
98 | struct perf_sample *sample __maybe_unused, | ||
99 | struct perf_tool *tool __maybe_unused) | ||
100 | { | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int arm_spe_process_auxtrace_event(struct perf_session *session, | ||
105 | union perf_event *event, | ||
106 | struct perf_tool *tool __maybe_unused) | ||
107 | { | ||
108 | struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, | ||
109 | auxtrace); | ||
110 | struct auxtrace_buffer *buffer; | ||
111 | off_t data_offset; | ||
112 | int fd = perf_data__fd(session->data); | ||
113 | int err; | ||
114 | |||
115 | if (perf_data__is_pipe(session->data)) { | ||
116 | data_offset = 0; | ||
117 | } else { | ||
118 | data_offset = lseek(fd, 0, SEEK_CUR); | ||
119 | if (data_offset == -1) | ||
120 | return -errno; | ||
121 | } | ||
122 | |||
123 | err = auxtrace_queues__add_event(&spe->queues, session, event, | ||
124 | data_offset, &buffer); | ||
125 | if (err) | ||
126 | return err; | ||
127 | |||
128 | /* Dump here now we have copied a piped trace out of the pipe */ | ||
129 | if (dump_trace) { | ||
130 | if (auxtrace_buffer__get_data(buffer, fd)) { | ||
131 | arm_spe_dump_event(spe, buffer->data, | ||
132 | buffer->size); | ||
133 | auxtrace_buffer__put_data(buffer); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int arm_spe_flush(struct perf_session *session __maybe_unused, | ||
141 | struct perf_tool *tool __maybe_unused) | ||
142 | { | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static void arm_spe_free_queue(void *priv) | ||
147 | { | ||
148 | struct arm_spe_queue *speq = priv; | ||
149 | |||
150 | if (!speq) | ||
151 | return; | ||
152 | free(speq); | ||
153 | } | ||
154 | |||
155 | static void arm_spe_free_events(struct perf_session *session) | ||
156 | { | ||
157 | struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, | ||
158 | auxtrace); | ||
159 | struct auxtrace_queues *queues = &spe->queues; | ||
160 | unsigned int i; | ||
161 | |||
162 | for (i = 0; i < queues->nr_queues; i++) { | ||
163 | arm_spe_free_queue(queues->queue_array[i].priv); | ||
164 | queues->queue_array[i].priv = NULL; | ||
165 | } | ||
166 | auxtrace_queues__free(queues); | ||
167 | } | ||
168 | |||
169 | static void arm_spe_free(struct perf_session *session) | ||
170 | { | ||
171 | struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, | ||
172 | auxtrace); | ||
173 | |||
174 | auxtrace_heap__free(&spe->heap); | ||
175 | arm_spe_free_events(session); | ||
176 | session->auxtrace = NULL; | ||
177 | free(spe); | ||
178 | } | ||
179 | |||
180 | static const char * const arm_spe_info_fmts[] = { | ||
181 | [ARM_SPE_PMU_TYPE] = " PMU Type %"PRId64"\n", | ||
182 | }; | ||
183 | |||
184 | static void arm_spe_print_info(u64 *arr) | ||
185 | { | ||
186 | if (!dump_trace) | ||
187 | return; | ||
188 | |||
189 | fprintf(stdout, arm_spe_info_fmts[ARM_SPE_PMU_TYPE], arr[ARM_SPE_PMU_TYPE]); | ||
190 | } | ||
191 | |||
192 | int arm_spe_process_auxtrace_info(union perf_event *event, | ||
193 | struct perf_session *session) | ||
194 | { | ||
195 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; | ||
196 | size_t min_sz = sizeof(u64) * ARM_SPE_PMU_TYPE; | ||
197 | struct arm_spe *spe; | ||
198 | int err; | ||
199 | |||
200 | if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + | ||
201 | min_sz) | ||
202 | return -EINVAL; | ||
203 | |||
204 | spe = zalloc(sizeof(struct arm_spe)); | ||
205 | if (!spe) | ||
206 | return -ENOMEM; | ||
207 | |||
208 | err = auxtrace_queues__init(&spe->queues); | ||
209 | if (err) | ||
210 | goto err_free; | ||
211 | |||
212 | spe->session = session; | ||
213 | spe->machine = &session->machines.host; /* No kvm support */ | ||
214 | spe->auxtrace_type = auxtrace_info->type; | ||
215 | spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE]; | ||
216 | |||
217 | spe->auxtrace.process_event = arm_spe_process_event; | ||
218 | spe->auxtrace.process_auxtrace_event = arm_spe_process_auxtrace_event; | ||
219 | spe->auxtrace.flush_events = arm_spe_flush; | ||
220 | spe->auxtrace.free_events = arm_spe_free_events; | ||
221 | spe->auxtrace.free = arm_spe_free; | ||
222 | session->auxtrace = &spe->auxtrace; | ||
223 | |||
224 | arm_spe_print_info(&auxtrace_info->priv[0]); | ||
225 | |||
226 | return 0; | ||
227 | |||
228 | err_free: | ||
229 | free(spe); | ||
230 | return err; | ||
231 | } | ||
diff --git a/tools/perf/util/arm-spe.h b/tools/perf/util/arm-spe.h new file mode 100644 index 000000000000..98d3235781c3 --- /dev/null +++ b/tools/perf/util/arm-spe.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Arm Statistical Profiling Extensions (SPE) support | ||
4 | * Copyright (c) 2017-2018, Arm Ltd. | ||
5 | */ | ||
6 | |||
7 | #ifndef INCLUDE__PERF_ARM_SPE_H__ | ||
8 | #define INCLUDE__PERF_ARM_SPE_H__ | ||
9 | |||
10 | #define ARM_SPE_PMU_NAME "arm_spe_" | ||
11 | |||
12 | enum { | ||
13 | ARM_SPE_PMU_TYPE, | ||
14 | ARM_SPE_PER_CPU_MMAPS, | ||
15 | ARM_SPE_AUXTRACE_PRIV_MAX, | ||
16 | }; | ||
17 | |||
18 | #define ARM_SPE_AUXTRACE_PRIV_SIZE (ARM_SPE_AUXTRACE_PRIV_MAX * sizeof(u64)) | ||
19 | |||
20 | union perf_event; | ||
21 | struct perf_session; | ||
22 | struct perf_pmu; | ||
23 | |||
24 | struct auxtrace_record *arm_spe_recording_init(int *err, | ||
25 | struct perf_pmu *arm_spe_pmu); | ||
26 | |||
27 | int arm_spe_process_auxtrace_info(union perf_event *event, | ||
28 | struct perf_session *session); | ||
29 | |||
30 | struct perf_event_attr *arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu); | ||
31 | #endif | ||
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index c76687e42344..3bba9947ab7f 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c | |||
@@ -54,6 +54,7 @@ | |||
54 | 54 | ||
55 | #include "intel-pt.h" | 55 | #include "intel-pt.h" |
56 | #include "intel-bts.h" | 56 | #include "intel-bts.h" |
57 | #include "arm-spe.h" | ||
57 | 58 | ||
58 | #include "sane_ctype.h" | 59 | #include "sane_ctype.h" |
59 | #include "symbol/kallsyms.h" | 60 | #include "symbol/kallsyms.h" |
@@ -910,6 +911,8 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused, | |||
910 | return intel_pt_process_auxtrace_info(event, session); | 911 | return intel_pt_process_auxtrace_info(event, session); |
911 | case PERF_AUXTRACE_INTEL_BTS: | 912 | case PERF_AUXTRACE_INTEL_BTS: |
912 | return intel_bts_process_auxtrace_info(event, session); | 913 | return intel_bts_process_auxtrace_info(event, session); |
914 | case PERF_AUXTRACE_ARM_SPE: | ||
915 | return arm_spe_process_auxtrace_info(event, session); | ||
913 | case PERF_AUXTRACE_CS_ETM: | 916 | case PERF_AUXTRACE_CS_ETM: |
914 | case PERF_AUXTRACE_UNKNOWN: | 917 | case PERF_AUXTRACE_UNKNOWN: |
915 | default: | 918 | default: |
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index d19e11b68de7..453c148d2158 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h | |||
@@ -43,6 +43,7 @@ enum auxtrace_type { | |||
43 | PERF_AUXTRACE_INTEL_PT, | 43 | PERF_AUXTRACE_INTEL_PT, |
44 | PERF_AUXTRACE_INTEL_BTS, | 44 | PERF_AUXTRACE_INTEL_BTS, |
45 | PERF_AUXTRACE_CS_ETM, | 45 | PERF_AUXTRACE_CS_ETM, |
46 | PERF_AUXTRACE_ARM_SPE, | ||
46 | }; | 47 | }; |
47 | 48 | ||
48 | enum itrace_period_type { | 49 | enum itrace_period_type { |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 082505d08d72..32ef7bdca1cf 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -37,6 +37,15 @@ struct callchain_param callchain_param = { | |||
37 | CALLCHAIN_PARAM_DEFAULT | 37 | CALLCHAIN_PARAM_DEFAULT |
38 | }; | 38 | }; |
39 | 39 | ||
40 | /* | ||
41 | * Are there any events usind DWARF callchains? | ||
42 | * | ||
43 | * I.e. | ||
44 | * | ||
45 | * -e cycles/call-graph=dwarf/ | ||
46 | */ | ||
47 | bool dwarf_callchain_users; | ||
48 | |||
40 | struct callchain_param callchain_param_default = { | 49 | struct callchain_param callchain_param_default = { |
41 | CALLCHAIN_PARAM_DEFAULT | 50 | CALLCHAIN_PARAM_DEFAULT |
42 | }; | 51 | }; |
@@ -265,6 +274,7 @@ int parse_callchain_record(const char *arg, struct callchain_param *param) | |||
265 | ret = 0; | 274 | ret = 0; |
266 | param->record_mode = CALLCHAIN_DWARF; | 275 | param->record_mode = CALLCHAIN_DWARF; |
267 | param->dump_size = default_stack_dump_size; | 276 | param->dump_size = default_stack_dump_size; |
277 | dwarf_callchain_users = true; | ||
268 | 278 | ||
269 | tok = strtok_r(NULL, ",", &saveptr); | 279 | tok = strtok_r(NULL, ",", &saveptr); |
270 | if (tok) { | 280 | if (tok) { |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index b79ef2478a57..154560b1eb65 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -89,6 +89,8 @@ enum chain_value { | |||
89 | CCVAL_COUNT, | 89 | CCVAL_COUNT, |
90 | }; | 90 | }; |
91 | 91 | ||
92 | extern bool dwarf_callchain_users; | ||
93 | |||
92 | struct callchain_param { | 94 | struct callchain_param { |
93 | bool enabled; | 95 | bool enabled; |
94 | enum perf_call_graph_mode record_mode; | 96 | enum perf_call_graph_mode record_mode; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f0a5e09c4071..120efd85f2c8 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -1760,7 +1760,7 @@ void perf_evlist__toggle_bkw_mmap(struct perf_evlist *evlist, | |||
1760 | switch (old_state) { | 1760 | switch (old_state) { |
1761 | case BKW_MMAP_NOTREADY: { | 1761 | case BKW_MMAP_NOTREADY: { |
1762 | if (state != BKW_MMAP_RUNNING) | 1762 | if (state != BKW_MMAP_RUNNING) |
1763 | goto state_err;; | 1763 | goto state_err; |
1764 | break; | 1764 | break; |
1765 | } | 1765 | } |
1766 | case BKW_MMAP_RUNNING: { | 1766 | case BKW_MMAP_RUNNING: { |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d934f04e3110..85eb84dfdf91 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -651,9 +651,9 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | |||
651 | return ret; | 651 | return ret; |
652 | } | 652 | } |
653 | 653 | ||
654 | void perf_evsel__config_callchain(struct perf_evsel *evsel, | 654 | static void __perf_evsel__config_callchain(struct perf_evsel *evsel, |
655 | struct record_opts *opts, | 655 | struct record_opts *opts, |
656 | struct callchain_param *param) | 656 | struct callchain_param *param) |
657 | { | 657 | { |
658 | bool function = perf_evsel__is_function_event(evsel); | 658 | bool function = perf_evsel__is_function_event(evsel); |
659 | struct perf_event_attr *attr = &evsel->attr; | 659 | struct perf_event_attr *attr = &evsel->attr; |
@@ -699,6 +699,14 @@ void perf_evsel__config_callchain(struct perf_evsel *evsel, | |||
699 | } | 699 | } |
700 | } | 700 | } |
701 | 701 | ||
702 | void perf_evsel__config_callchain(struct perf_evsel *evsel, | ||
703 | struct record_opts *opts, | ||
704 | struct callchain_param *param) | ||
705 | { | ||
706 | if (param->enabled) | ||
707 | return __perf_evsel__config_callchain(evsel, opts, param); | ||
708 | } | ||
709 | |||
702 | static void | 710 | static void |
703 | perf_evsel__reset_callgraph(struct perf_evsel *evsel, | 711 | perf_evsel__reset_callgraph(struct perf_evsel *evsel, |
704 | struct callchain_param *param) | 712 | struct callchain_param *param) |
@@ -718,19 +726,19 @@ perf_evsel__reset_callgraph(struct perf_evsel *evsel, | |||
718 | } | 726 | } |
719 | 727 | ||
720 | static void apply_config_terms(struct perf_evsel *evsel, | 728 | static void apply_config_terms(struct perf_evsel *evsel, |
721 | struct record_opts *opts) | 729 | struct record_opts *opts, bool track) |
722 | { | 730 | { |
723 | struct perf_evsel_config_term *term; | 731 | struct perf_evsel_config_term *term; |
724 | struct list_head *config_terms = &evsel->config_terms; | 732 | struct list_head *config_terms = &evsel->config_terms; |
725 | struct perf_event_attr *attr = &evsel->attr; | 733 | struct perf_event_attr *attr = &evsel->attr; |
726 | struct callchain_param param; | 734 | /* callgraph default */ |
735 | struct callchain_param param = { | ||
736 | .record_mode = callchain_param.record_mode, | ||
737 | }; | ||
727 | u32 dump_size = 0; | 738 | u32 dump_size = 0; |
728 | int max_stack = 0; | 739 | int max_stack = 0; |
729 | const char *callgraph_buf = NULL; | 740 | const char *callgraph_buf = NULL; |
730 | 741 | ||
731 | /* callgraph default */ | ||
732 | param.record_mode = callchain_param.record_mode; | ||
733 | |||
734 | list_for_each_entry(term, config_terms, list) { | 742 | list_for_each_entry(term, config_terms, list) { |
735 | switch (term->type) { | 743 | switch (term->type) { |
736 | case PERF_EVSEL__CONFIG_TERM_PERIOD: | 744 | case PERF_EVSEL__CONFIG_TERM_PERIOD: |
@@ -781,7 +789,7 @@ static void apply_config_terms(struct perf_evsel *evsel, | |||
781 | attr->write_backward = term->val.overwrite ? 1 : 0; | 789 | attr->write_backward = term->val.overwrite ? 1 : 0; |
782 | break; | 790 | break; |
783 | case PERF_EVSEL__CONFIG_TERM_DRV_CFG: | 791 | case PERF_EVSEL__CONFIG_TERM_DRV_CFG: |
784 | BUG_ON(1); | 792 | break; |
785 | default: | 793 | default: |
786 | break; | 794 | break; |
787 | } | 795 | } |
@@ -789,6 +797,8 @@ static void apply_config_terms(struct perf_evsel *evsel, | |||
789 | 797 | ||
790 | /* User explicitly set per-event callgraph, clear the old setting and reset. */ | 798 | /* User explicitly set per-event callgraph, clear the old setting and reset. */ |
791 | if ((callgraph_buf != NULL) || (dump_size > 0) || max_stack) { | 799 | if ((callgraph_buf != NULL) || (dump_size > 0) || max_stack) { |
800 | bool sample_address = false; | ||
801 | |||
792 | if (max_stack) { | 802 | if (max_stack) { |
793 | param.max_stack = max_stack; | 803 | param.max_stack = max_stack; |
794 | if (callgraph_buf == NULL) | 804 | if (callgraph_buf == NULL) |
@@ -808,6 +818,8 @@ static void apply_config_terms(struct perf_evsel *evsel, | |||
808 | evsel->name); | 818 | evsel->name); |
809 | return; | 819 | return; |
810 | } | 820 | } |
821 | if (param.record_mode == CALLCHAIN_DWARF) | ||
822 | sample_address = true; | ||
811 | } | 823 | } |
812 | } | 824 | } |
813 | if (dump_size > 0) { | 825 | if (dump_size > 0) { |
@@ -820,8 +832,14 @@ static void apply_config_terms(struct perf_evsel *evsel, | |||
820 | perf_evsel__reset_callgraph(evsel, &callchain_param); | 832 | perf_evsel__reset_callgraph(evsel, &callchain_param); |
821 | 833 | ||
822 | /* set perf-event callgraph */ | 834 | /* set perf-event callgraph */ |
823 | if (param.enabled) | 835 | if (param.enabled) { |
836 | if (sample_address) { | ||
837 | perf_evsel__set_sample_bit(evsel, ADDR); | ||
838 | perf_evsel__set_sample_bit(evsel, DATA_SRC); | ||
839 | evsel->attr.mmap_data = track; | ||
840 | } | ||
824 | perf_evsel__config_callchain(evsel, opts, ¶m); | 841 | perf_evsel__config_callchain(evsel, opts, ¶m); |
842 | } | ||
825 | } | 843 | } |
826 | } | 844 | } |
827 | 845 | ||
@@ -1052,7 +1070,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
1052 | * Apply event specific term settings, | 1070 | * Apply event specific term settings, |
1053 | * it overloads any global configuration. | 1071 | * it overloads any global configuration. |
1054 | */ | 1072 | */ |
1055 | apply_config_terms(evsel, opts); | 1073 | apply_config_terms(evsel, opts, track); |
1056 | 1074 | ||
1057 | evsel->ignore_missing_thread = opts->ignore_missing_thread; | 1075 | evsel->ignore_missing_thread = opts->ignore_missing_thread; |
1058 | } | 1076 | } |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c1848b543f27..ea070883c593 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -499,6 +499,8 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, | |||
499 | PyLong_FromUnsignedLongLong(sample->time)); | 499 | PyLong_FromUnsignedLongLong(sample->time)); |
500 | pydict_set_item_string_decref(dict_sample, "period", | 500 | pydict_set_item_string_decref(dict_sample, "period", |
501 | PyLong_FromUnsignedLongLong(sample->period)); | 501 | PyLong_FromUnsignedLongLong(sample->period)); |
502 | pydict_set_item_string_decref(dict_sample, "phys_addr", | ||
503 | PyLong_FromUnsignedLongLong(sample->phys_addr)); | ||
502 | set_sample_read_in_dict(dict_sample, sample, evsel); | 504 | set_sample_read_in_dict(dict_sample, sample, evsel); |
503 | pydict_set_item_string_decref(dict, "sample", dict_sample); | 505 | pydict_set_item_string_decref(dict, "sample", dict_sample); |
504 | 506 | ||
diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 3f7f18f06982..6193b46050a5 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c | |||
@@ -116,7 +116,8 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr) | |||
116 | 116 | ||
117 | static int parse_percent(double *pcnt, char *str) | 117 | static int parse_percent(double *pcnt, char *str) |
118 | { | 118 | { |
119 | char *c; | 119 | char *c, *endptr; |
120 | double d; | ||
120 | 121 | ||
121 | c = strchr(str, '%'); | 122 | c = strchr(str, '%'); |
122 | if (c) | 123 | if (c) |
@@ -124,8 +125,11 @@ static int parse_percent(double *pcnt, char *str) | |||
124 | else | 125 | else |
125 | return -1; | 126 | return -1; |
126 | 127 | ||
127 | *pcnt = atof(str) / 100.0; | 128 | d = strtod(str, &endptr); |
129 | if (endptr != str + strlen(str)) | ||
130 | return -1; | ||
128 | 131 | ||
132 | *pcnt = d / 100.0; | ||
129 | return 0; | 133 | return 0; |
130 | } | 134 | } |
131 | 135 | ||
@@ -257,6 +261,37 @@ static int percent_comma_split(struct perf_time_interval *ptime_buf, int num, | |||
257 | return i; | 261 | return i; |
258 | } | 262 | } |
259 | 263 | ||
264 | static int one_percent_convert(struct perf_time_interval *ptime_buf, | ||
265 | const char *ostr, u64 start, u64 end, char *c) | ||
266 | { | ||
267 | char *str; | ||
268 | int len = strlen(ostr), ret; | ||
269 | |||
270 | /* | ||
271 | * c points to '%'. | ||
272 | * '%' should be the last character | ||
273 | */ | ||
274 | if (ostr + len - 1 != c) | ||
275 | return -1; | ||
276 | |||
277 | /* | ||
278 | * Construct a string like "xx%/1" | ||
279 | */ | ||
280 | str = malloc(len + 3); | ||
281 | if (str == NULL) | ||
282 | return -ENOMEM; | ||
283 | |||
284 | memcpy(str, ostr, len); | ||
285 | strcpy(str + len, "/1"); | ||
286 | |||
287 | ret = percent_slash_split(str, ptime_buf, start, end); | ||
288 | if (ret == 0) | ||
289 | ret = 1; | ||
290 | |||
291 | free(str); | ||
292 | return ret; | ||
293 | } | ||
294 | |||
260 | int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, | 295 | int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, |
261 | const char *ostr, u64 start, u64 end) | 296 | const char *ostr, u64 start, u64 end) |
262 | { | 297 | { |
@@ -266,6 +301,7 @@ int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, | |||
266 | * ostr example: | 301 | * ostr example: |
267 | * 10%/2,10%/3: select the second 10% slice and the third 10% slice | 302 | * 10%/2,10%/3: select the second 10% slice and the third 10% slice |
268 | * 0%-10%,30%-40%: multiple time range | 303 | * 0%-10%,30%-40%: multiple time range |
304 | * 50%: just one percent | ||
269 | */ | 305 | */ |
270 | 306 | ||
271 | memset(ptime_buf, 0, sizeof(*ptime_buf) * num); | 307 | memset(ptime_buf, 0, sizeof(*ptime_buf) * num); |
@@ -282,9 +318,41 @@ int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, | |||
282 | end, percent_dash_split); | 318 | end, percent_dash_split); |
283 | } | 319 | } |
284 | 320 | ||
321 | c = strchr(ostr, '%'); | ||
322 | if (c) | ||
323 | return one_percent_convert(ptime_buf, ostr, start, end, c); | ||
324 | |||
285 | return -1; | 325 | return -1; |
286 | } | 326 | } |
287 | 327 | ||
328 | struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size) | ||
329 | { | ||
330 | const char *p1, *p2; | ||
331 | int i = 1; | ||
332 | struct perf_time_interval *ptime; | ||
333 | |||
334 | /* | ||
335 | * At least allocate one time range. | ||
336 | */ | ||
337 | if (!ostr) | ||
338 | goto alloc; | ||
339 | |||
340 | p1 = ostr; | ||
341 | while (p1 < ostr + strlen(ostr)) { | ||
342 | p2 = strchr(p1, ','); | ||
343 | if (!p2) | ||
344 | break; | ||
345 | |||
346 | p1 = p2 + 1; | ||
347 | i++; | ||
348 | } | ||
349 | |||
350 | alloc: | ||
351 | *size = i; | ||
352 | ptime = calloc(i, sizeof(*ptime)); | ||
353 | return ptime; | ||
354 | } | ||
355 | |||
288 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp) | 356 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp) |
289 | { | 357 | { |
290 | /* if time is not set don't drop sample */ | 358 | /* if time is not set don't drop sample */ |
diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h index 34d5eba26bf5..70b177d2b98c 100644 --- a/tools/perf/util/time-utils.h +++ b/tools/perf/util/time-utils.h | |||
@@ -16,6 +16,8 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr); | |||
16 | int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, | 16 | int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, |
17 | const char *ostr, u64 start, u64 end); | 17 | const char *ostr, u64 start, u64 end); |
18 | 18 | ||
19 | struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size); | ||
20 | |||
19 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp); | 21 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp); |
20 | 22 | ||
21 | bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, | 23 | bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, |
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 7a42f703e858..af873044d33a 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c | |||
@@ -631,9 +631,8 @@ static unw_accessors_t accessors = { | |||
631 | 631 | ||
632 | static int _unwind__prepare_access(struct thread *thread) | 632 | static int _unwind__prepare_access(struct thread *thread) |
633 | { | 633 | { |
634 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | 634 | if (!dwarf_callchain_users) |
635 | return 0; | 635 | return 0; |
636 | |||
637 | thread->addr_space = unw_create_addr_space(&accessors, 0); | 636 | thread->addr_space = unw_create_addr_space(&accessors, 0); |
638 | if (!thread->addr_space) { | 637 | if (!thread->addr_space) { |
639 | pr_err("unwind: Can't create unwind address space.\n"); | 638 | pr_err("unwind: Can't create unwind address space.\n"); |
@@ -646,17 +645,15 @@ static int _unwind__prepare_access(struct thread *thread) | |||
646 | 645 | ||
647 | static void _unwind__flush_access(struct thread *thread) | 646 | static void _unwind__flush_access(struct thread *thread) |
648 | { | 647 | { |
649 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | 648 | if (!dwarf_callchain_users) |
650 | return; | 649 | return; |
651 | |||
652 | unw_flush_cache(thread->addr_space, 0, 0); | 650 | unw_flush_cache(thread->addr_space, 0, 0); |
653 | } | 651 | } |
654 | 652 | ||
655 | static void _unwind__finish_access(struct thread *thread) | 653 | static void _unwind__finish_access(struct thread *thread) |
656 | { | 654 | { |
657 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | 655 | if (!dwarf_callchain_users) |
658 | return; | 656 | return; |
659 | |||
660 | unw_destroy_addr_space(thread->addr_space); | 657 | unw_destroy_addr_space(thread->addr_space); |
661 | } | 658 | } |
662 | 659 | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index a789f952b3e9..443892dabedb 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -210,7 +210,7 @@ static int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 | |||
210 | 210 | ||
211 | size -= ret; | 211 | size -= ret; |
212 | off_in += ret; | 212 | off_in += ret; |
213 | off_out -= ret; | 213 | off_out += ret; |
214 | } | 214 | } |
215 | munmap(ptr, off_in + size); | 215 | munmap(ptr, off_in + size); |
216 | 216 | ||