diff options
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/build-id.c | 10 | ||||
-rw-r--r-- | tools/perf/util/cache.h | 1 | ||||
-rw-r--r-- | tools/perf/util/callchain.c | 2 | ||||
-rw-r--r-- | tools/perf/util/callchain.h | 2 | ||||
-rw-r--r-- | tools/perf/util/config.c | 64 | ||||
-rw-r--r-- | tools/perf/util/cpumap.c | 57 | ||||
-rw-r--r-- | tools/perf/util/cpumap.h | 2 | ||||
-rw-r--r-- | tools/perf/util/debug.c | 10 | ||||
-rw-r--r-- | tools/perf/util/event.c | 37 | ||||
-rw-r--r-- | tools/perf/util/event.h | 5 | ||||
-rw-r--r-- | tools/perf/util/header.c | 13 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 3 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 58 | ||||
-rw-r--r-- | tools/perf/util/session.c | 6 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 27 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 6 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 37 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 6 | ||||
-rw-r--r-- | tools/perf/util/util.h | 3 |
19 files changed, 309 insertions, 40 deletions
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 70c5cf87d020..5c26e2d314af 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -43,19 +43,17 @@ struct perf_event_ops build_id__mark_dso_hit_ops = { | |||
43 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 43 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) |
44 | { | 44 | { |
45 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 45 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
46 | const char *home; | ||
47 | 46 | ||
48 | if (!self->has_build_id) | 47 | if (!self->has_build_id) |
49 | return NULL; | 48 | return NULL; |
50 | 49 | ||
51 | build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); | 50 | build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); |
52 | home = getenv("HOME"); | ||
53 | if (bf == NULL) { | 51 | if (bf == NULL) { |
54 | if (asprintf(&bf, "%s/%s/.build-id/%.2s/%s", home, | 52 | if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, |
55 | DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2) < 0) | 53 | build_id_hex, build_id_hex + 2) < 0) |
56 | return NULL; | 54 | return NULL; |
57 | } else | 55 | } else |
58 | snprintf(bf, size, "%s/%s/.build-id/%.2s/%s", home, | 56 | snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir, |
59 | DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2); | 57 | build_id_hex, build_id_hex + 2); |
60 | return bf; | 58 | return bf; |
61 | } | 59 | } |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 65fe664fddf6..27e9ebe4076e 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -23,6 +23,7 @@ extern int perf_config(config_fn_t fn, void *); | |||
23 | extern int perf_config_int(const char *, const char *); | 23 | extern int perf_config_int(const char *, const char *); |
24 | extern int perf_config_bool(const char *, const char *); | 24 | extern int perf_config_bool(const char *, const char *); |
25 | extern int config_error_nonbool(const char *); | 25 | extern int config_error_nonbool(const char *); |
26 | extern const char *perf_config_dirname(const char *, const char *); | ||
26 | 27 | ||
27 | /* pager.c */ | 28 | /* pager.c */ |
28 | extern void setup_pager(void); | 29 | extern void setup_pager(void); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 62b69ad4aa73..e63c997d6c1b 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include "util.h" | 18 | #include "util.h" |
19 | #include "callchain.h" | 19 | #include "callchain.h" |
20 | 20 | ||
21 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) | 21 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) |
22 | { | 22 | { |
23 | unsigned int chain_size = event->header.size; | 23 | unsigned int chain_size = event->header.size; |
24 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | 24 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 1ca73e4a2723..809850fb75fb 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -60,5 +60,5 @@ int register_callchain_param(struct callchain_param *param); | |||
60 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, | 60 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, |
61 | struct map_symbol *syms); | 61 | struct map_symbol *syms); |
62 | 62 | ||
63 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); | 63 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); |
64 | #endif /* __PERF_CALLCHAIN_H */ | 64 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index dabe892d0e53..e02d78cae70f 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -11,6 +11,11 @@ | |||
11 | 11 | ||
12 | #define MAXNAME (256) | 12 | #define MAXNAME (256) |
13 | 13 | ||
14 | #define DEBUG_CACHE_DIR ".debug" | ||
15 | |||
16 | |||
17 | char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ | ||
18 | |||
14 | static FILE *config_file; | 19 | static FILE *config_file; |
15 | static const char *config_file_name; | 20 | static const char *config_file_name; |
16 | static int config_linenr; | 21 | static int config_linenr; |
@@ -127,7 +132,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) | |||
127 | break; | 132 | break; |
128 | if (!iskeychar(c)) | 133 | if (!iskeychar(c)) |
129 | break; | 134 | break; |
130 | name[len++] = tolower(c); | 135 | name[len++] = c; |
131 | if (len >= MAXNAME) | 136 | if (len >= MAXNAME) |
132 | return -1; | 137 | return -1; |
133 | } | 138 | } |
@@ -327,6 +332,13 @@ int perf_config_bool(const char *name, const char *value) | |||
327 | return !!perf_config_bool_or_int(name, value, &discard); | 332 | return !!perf_config_bool_or_int(name, value, &discard); |
328 | } | 333 | } |
329 | 334 | ||
335 | const char *perf_config_dirname(const char *name, const char *value) | ||
336 | { | ||
337 | if (!name) | ||
338 | return NULL; | ||
339 | return value; | ||
340 | } | ||
341 | |||
330 | static int perf_default_core_config(const char *var __used, const char *value __used) | 342 | static int perf_default_core_config(const char *var __used, const char *value __used) |
331 | { | 343 | { |
332 | /* Add other config variables here and to Documentation/config.txt. */ | 344 | /* Add other config variables here and to Documentation/config.txt. */ |
@@ -428,3 +440,53 @@ int config_error_nonbool(const char *var) | |||
428 | { | 440 | { |
429 | return error("Missing value for '%s'", var); | 441 | return error("Missing value for '%s'", var); |
430 | } | 442 | } |
443 | |||
444 | struct buildid_dir_config { | ||
445 | char *dir; | ||
446 | }; | ||
447 | |||
448 | static int buildid_dir_command_config(const char *var, const char *value, | ||
449 | void *data) | ||
450 | { | ||
451 | struct buildid_dir_config *c = data; | ||
452 | const char *v; | ||
453 | |||
454 | /* same dir for all commands */ | ||
455 | if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { | ||
456 | v = perf_config_dirname(var, value); | ||
457 | if (!v) | ||
458 | return -1; | ||
459 | strncpy(c->dir, v, MAXPATHLEN-1); | ||
460 | c->dir[MAXPATHLEN-1] = '\0'; | ||
461 | } | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static void check_buildid_dir_config(void) | ||
466 | { | ||
467 | struct buildid_dir_config c; | ||
468 | c.dir = buildid_dir; | ||
469 | perf_config(buildid_dir_command_config, &c); | ||
470 | } | ||
471 | |||
472 | void set_buildid_dir(void) | ||
473 | { | ||
474 | buildid_dir[0] = '\0'; | ||
475 | |||
476 | /* try config file */ | ||
477 | check_buildid_dir_config(); | ||
478 | |||
479 | /* default to $HOME/.debug */ | ||
480 | if (buildid_dir[0] == '\0') { | ||
481 | char *v = getenv("HOME"); | ||
482 | if (v) { | ||
483 | snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", | ||
484 | v, DEBUG_CACHE_DIR); | ||
485 | } else { | ||
486 | strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); | ||
487 | } | ||
488 | buildid_dir[MAXPATHLEN-1] = '\0'; | ||
489 | } | ||
490 | /* for communicating with external commands */ | ||
491 | setenv("PERF_BUILDID_DIR", buildid_dir, 1); | ||
492 | } | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 4e01490e51e5..0f9b8d7a7d7e 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -20,7 +20,7 @@ static int default_cpu_map(void) | |||
20 | return nr_cpus; | 20 | return nr_cpus; |
21 | } | 21 | } |
22 | 22 | ||
23 | int read_cpu_map(void) | 23 | static int read_all_cpu_map(void) |
24 | { | 24 | { |
25 | FILE *onlnf; | 25 | FILE *onlnf; |
26 | int nr_cpus = 0; | 26 | int nr_cpus = 0; |
@@ -57,3 +57,58 @@ int read_cpu_map(void) | |||
57 | 57 | ||
58 | return default_cpu_map(); | 58 | return default_cpu_map(); |
59 | } | 59 | } |
60 | |||
61 | int read_cpu_map(const char *cpu_list) | ||
62 | { | ||
63 | unsigned long start_cpu, end_cpu = 0; | ||
64 | char *p = NULL; | ||
65 | int i, nr_cpus = 0; | ||
66 | |||
67 | if (!cpu_list) | ||
68 | return read_all_cpu_map(); | ||
69 | |||
70 | if (!isdigit(*cpu_list)) | ||
71 | goto invalid; | ||
72 | |||
73 | while (isdigit(*cpu_list)) { | ||
74 | p = NULL; | ||
75 | start_cpu = strtoul(cpu_list, &p, 0); | ||
76 | if (start_cpu >= INT_MAX | ||
77 | || (*p != '\0' && *p != ',' && *p != '-')) | ||
78 | goto invalid; | ||
79 | |||
80 | if (*p == '-') { | ||
81 | cpu_list = ++p; | ||
82 | p = NULL; | ||
83 | end_cpu = strtoul(cpu_list, &p, 0); | ||
84 | |||
85 | if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) | ||
86 | goto invalid; | ||
87 | |||
88 | if (end_cpu < start_cpu) | ||
89 | goto invalid; | ||
90 | } else { | ||
91 | end_cpu = start_cpu; | ||
92 | } | ||
93 | |||
94 | for (; start_cpu <= end_cpu; start_cpu++) { | ||
95 | /* check for duplicates */ | ||
96 | for (i = 0; i < nr_cpus; i++) | ||
97 | if (cpumap[i] == (int)start_cpu) | ||
98 | goto invalid; | ||
99 | |||
100 | assert(nr_cpus < MAX_NR_CPUS); | ||
101 | cpumap[nr_cpus++] = (int)start_cpu; | ||
102 | } | ||
103 | if (*p) | ||
104 | ++p; | ||
105 | |||
106 | cpu_list = p; | ||
107 | } | ||
108 | if (nr_cpus > 0) | ||
109 | return nr_cpus; | ||
110 | |||
111 | return default_cpu_map(); | ||
112 | invalid: | ||
113 | return -1; | ||
114 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 86c78bb33098..3e60f56e490e 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_CPUMAP_H | 1 | #ifndef __PERF_CPUMAP_H |
2 | #define __PERF_CPUMAP_H | 2 | #define __PERF_CPUMAP_H |
3 | 3 | ||
4 | extern int read_cpu_map(void); | 4 | extern int read_cpu_map(const char *cpu_list); |
5 | extern int cpumap[]; | 5 | extern int cpumap[]; |
6 | 6 | ||
7 | #endif /* __PERF_CPUMAP_H */ | 7 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 6cddff2bc970..318dab15d177 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -86,12 +86,10 @@ void trace_event(event_t *event) | |||
86 | dump_printf_color(" ", color); | 86 | dump_printf_color(" ", color); |
87 | for (j = 0; j < 15-(i & 15); j++) | 87 | for (j = 0; j < 15-(i & 15); j++) |
88 | dump_printf_color(" ", color); | 88 | dump_printf_color(" ", color); |
89 | for (j = 0; j < (i & 15); j++) { | 89 | for (j = i & ~15; j <= i; j++) { |
90 | if (isprint(raw_event[i-15+j])) | 90 | dump_printf_color("%c", color, |
91 | dump_printf_color("%c", color, | 91 | isprint(raw_event[j]) ? |
92 | raw_event[i-15+j]); | 92 | raw_event[j] : '.'); |
93 | else | ||
94 | dump_printf_color(".", color); | ||
95 | } | 93 | } |
96 | dump_printf_color("\n", color); | 94 | dump_printf_color("\n", color); |
97 | } | 95 | } |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1f08f008d289..a7460868124b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -655,11 +655,36 @@ static void dso__calc_col_width(struct dso *self) | |||
655 | } | 655 | } |
656 | 656 | ||
657 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 657 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
658 | struct addr_location *al, symbol_filter_t filter) | 658 | struct addr_location *al, struct sample_data *data, |
659 | symbol_filter_t filter) | ||
659 | { | 660 | { |
660 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 661 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
661 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | 662 | struct thread *thread; |
663 | |||
664 | event__parse_sample(self, session->sample_type, data); | ||
665 | |||
666 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", | ||
667 | self->header.misc, data->pid, data->tid, data->ip, | ||
668 | data->period, data->cpu); | ||
669 | |||
670 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
671 | unsigned int i; | ||
672 | |||
673 | dump_printf("... chain: nr:%Lu\n", data->callchain->nr); | ||
674 | |||
675 | if (!ip_callchain__valid(data->callchain, self)) { | ||
676 | pr_debug("call-chain problem with event, " | ||
677 | "skipping it.\n"); | ||
678 | goto out_filtered; | ||
679 | } | ||
662 | 680 | ||
681 | if (dump_trace) { | ||
682 | for (i = 0; i < data->callchain->nr; i++) | ||
683 | dump_printf("..... %2d: %016Lx\n", | ||
684 | i, data->callchain->ips[i]); | ||
685 | } | ||
686 | } | ||
687 | thread = perf_session__findnew(session, self->ip.pid); | ||
663 | if (thread == NULL) | 688 | if (thread == NULL) |
664 | return -1; | 689 | return -1; |
665 | 690 | ||
@@ -685,6 +710,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
685 | al->map ? al->map->dso->long_name : | 710 | al->map ? al->map->dso->long_name : |
686 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 711 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
687 | al->sym = NULL; | 712 | al->sym = NULL; |
713 | al->cpu = data->cpu; | ||
688 | 714 | ||
689 | if (al->map) { | 715 | if (al->map) { |
690 | if (symbol_conf.dso_list && | 716 | if (symbol_conf.dso_list && |
@@ -724,9 +750,9 @@ out_filtered: | |||
724 | return 0; | 750 | return 0; |
725 | } | 751 | } |
726 | 752 | ||
727 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | 753 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) |
728 | { | 754 | { |
729 | u64 *array = event->sample.array; | 755 | const u64 *array = event->sample.array; |
730 | 756 | ||
731 | if (type & PERF_SAMPLE_IP) { | 757 | if (type & PERF_SAMPLE_IP) { |
732 | data->ip = event->ip.ip; | 758 | data->ip = event->ip.ip; |
@@ -765,7 +791,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | |||
765 | u32 *p = (u32 *)array; | 791 | u32 *p = (u32 *)array; |
766 | data->cpu = *p; | 792 | data->cpu = *p; |
767 | array++; | 793 | array++; |
768 | } | 794 | } else |
795 | data->cpu = -1; | ||
769 | 796 | ||
770 | if (type & PERF_SAMPLE_PERIOD) { | 797 | if (type & PERF_SAMPLE_PERIOD) { |
771 | data->period = *array; | 798 | data->period = *array; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8577085db067..887ee63bbb62 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -157,8 +157,9 @@ int event__process_task(event_t *self, struct perf_session *session); | |||
157 | 157 | ||
158 | struct addr_location; | 158 | struct addr_location; |
159 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 159 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
160 | struct addr_location *al, symbol_filter_t filter); | 160 | struct addr_location *al, struct sample_data *data, |
161 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | 161 | symbol_filter_t filter); |
162 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); | ||
162 | 163 | ||
163 | extern const char *event__name[]; | 164 | extern const char *event__name[]; |
164 | 165 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1f62435f96c2..d7e67b167ea3 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "debug.h" | 17 | #include "debug.h" |
18 | 18 | ||
19 | static bool no_buildid_cache = false; | ||
20 | |||
19 | /* | 21 | /* |
20 | * Create new perf.data header attribute: | 22 | * Create new perf.data header attribute: |
21 | */ | 23 | */ |
@@ -385,8 +387,7 @@ static int perf_session__cache_build_ids(struct perf_session *self) | |||
385 | int ret; | 387 | int ret; |
386 | char debugdir[PATH_MAX]; | 388 | char debugdir[PATH_MAX]; |
387 | 389 | ||
388 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | 390 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); |
389 | DEBUG_CACHE_DIR); | ||
390 | 391 | ||
391 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 392 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
392 | return -1; | 393 | return -1; |
@@ -471,7 +472,8 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
471 | } | 472 | } |
472 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 473 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - |
473 | buildid_sec->offset; | 474 | buildid_sec->offset; |
474 | perf_session__cache_build_ids(session); | 475 | if (!no_buildid_cache) |
476 | perf_session__cache_build_ids(session); | ||
475 | } | 477 | } |
476 | 478 | ||
477 | lseek(fd, sec_start, SEEK_SET); | 479 | lseek(fd, sec_start, SEEK_SET); |
@@ -1190,3 +1192,8 @@ int event__process_build_id(event_t *self, | |||
1190 | session); | 1192 | session); |
1191 | return 0; | 1193 | return 0; |
1192 | } | 1194 | } |
1195 | |||
1196 | void disable_buildid_cache(void) | ||
1197 | { | ||
1198 | no_buildid_cache = true; | ||
1199 | } | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 07f89b66b318..68d288c975de 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -70,6 +70,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
70 | .map = al->map, | 70 | .map = al->map, |
71 | .sym = al->sym, | 71 | .sym = al->sym, |
72 | }, | 72 | }, |
73 | .cpu = al->cpu, | ||
73 | .ip = al->addr, | 74 | .ip = al->addr, |
74 | .level = al->level, | 75 | .level = al->level, |
75 | .period = period, | 76 | .period = period, |
@@ -1037,7 +1038,7 @@ fallback: | |||
1037 | dso, dso->long_name, sym, sym->name); | 1038 | dso, dso->long_name, sym, sym->name); |
1038 | 1039 | ||
1039 | snprintf(command, sizeof(command), | 1040 | snprintf(command, sizeof(command), |
1040 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | 1041 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", |
1041 | map__rip_2objdump(map, sym->start), | 1042 | map__rip_2objdump(map, sym->start), |
1042 | map__rip_2objdump(map, sym->end), | 1043 | map__rip_2objdump(map, sym->end), |
1043 | filename, filename); | 1044 | filename, filename); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d964cb199c67..baf665383498 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include "event.h" | 37 | #include "event.h" |
38 | #include "debug.h" | 38 | #include "debug.h" |
39 | #include "util.h" | 39 | #include "util.h" |
40 | #include "symbol.h" | ||
40 | #include "probe-finder.h" | 41 | #include "probe-finder.h" |
41 | 42 | ||
42 | /* Kprobe tracer basic type is up to u64 */ | 43 | /* Kprobe tracer basic type is up to u64 */ |
@@ -57,6 +58,55 @@ static int strtailcmp(const char *s1, const char *s2) | |||
57 | return 0; | 58 | return 0; |
58 | } | 59 | } |
59 | 60 | ||
61 | /* | ||
62 | * Find a src file from a DWARF tag path. Prepend optional source path prefix | ||
63 | * and chop off leading directories that do not exist. Result is passed back as | ||
64 | * a newly allocated path on success. | ||
65 | * Return 0 if file was found and readable, -errno otherwise. | ||
66 | */ | ||
67 | static int get_real_path(const char *raw_path, char **new_path) | ||
68 | { | ||
69 | if (!symbol_conf.source_prefix) { | ||
70 | if (access(raw_path, R_OK) == 0) { | ||
71 | *new_path = strdup(raw_path); | ||
72 | return 0; | ||
73 | } else | ||
74 | return -errno; | ||
75 | } | ||
76 | |||
77 | *new_path = malloc((strlen(symbol_conf.source_prefix) + | ||
78 | strlen(raw_path) + 2)); | ||
79 | if (!*new_path) | ||
80 | return -ENOMEM; | ||
81 | |||
82 | for (;;) { | ||
83 | sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, | ||
84 | raw_path); | ||
85 | |||
86 | if (access(*new_path, R_OK) == 0) | ||
87 | return 0; | ||
88 | |||
89 | switch (errno) { | ||
90 | case ENAMETOOLONG: | ||
91 | case ENOENT: | ||
92 | case EROFS: | ||
93 | case EFAULT: | ||
94 | raw_path = strchr(++raw_path, '/'); | ||
95 | if (!raw_path) { | ||
96 | free(*new_path); | ||
97 | *new_path = NULL; | ||
98 | return -ENOENT; | ||
99 | } | ||
100 | continue; | ||
101 | |||
102 | default: | ||
103 | free(*new_path); | ||
104 | *new_path = NULL; | ||
105 | return -errno; | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
60 | /* Line number list operations */ | 110 | /* Line number list operations */ |
61 | 111 | ||
62 | /* Add a line to line number list */ | 112 | /* Add a line to line number list */ |
@@ -1096,11 +1146,13 @@ end: | |||
1096 | static int line_range_add_line(const char *src, unsigned int lineno, | 1146 | static int line_range_add_line(const char *src, unsigned int lineno, |
1097 | struct line_range *lr) | 1147 | struct line_range *lr) |
1098 | { | 1148 | { |
1149 | int ret; | ||
1150 | |||
1099 | /* Copy real path */ | 1151 | /* Copy real path */ |
1100 | if (!lr->path) { | 1152 | if (!lr->path) { |
1101 | lr->path = strdup(src); | 1153 | ret = get_real_path(src, &lr->path); |
1102 | if (lr->path == NULL) | 1154 | if (ret != 0) |
1103 | return -ENOMEM; | 1155 | return ret; |
1104 | } | 1156 | } |
1105 | return line_list__add_line(&lr->line_list, lineno); | 1157 | return line_list__add_line(&lr->line_list, lineno); |
1106 | } | 1158 | } |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8f83a1835766..0564a5cfb12e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -27,8 +27,10 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
27 | 27 | ||
28 | self->fd = open(self->filename, O_RDONLY); | 28 | self->fd = open(self->filename, O_RDONLY); |
29 | if (self->fd < 0) { | 29 | if (self->fd < 0) { |
30 | pr_err("failed to open file: %s", self->filename); | 30 | int err = errno; |
31 | if (!strcmp(self->filename, "perf.data")) | 31 | |
32 | pr_err("failed to open %s: %s", self->filename, strerror(err)); | ||
33 | if (err == ENOENT && !strcmp(self->filename, "perf.data")) | ||
32 | pr_err(" (try 'perf record' first)"); | 34 | pr_err(" (try 'perf record' first)"); |
33 | pr_err("\n"); | 35 | pr_err("\n"); |
34 | return -errno; | 36 | return -errno; |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2316cb5a4116..c27b4b03fbc1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -13,6 +13,7 @@ enum sort_type sort__first_dimension; | |||
13 | unsigned int dsos__col_width; | 13 | unsigned int dsos__col_width; |
14 | unsigned int comms__col_width; | 14 | unsigned int comms__col_width; |
15 | unsigned int threads__col_width; | 15 | unsigned int threads__col_width; |
16 | unsigned int cpus__col_width; | ||
16 | static unsigned int parent_symbol__col_width; | 17 | static unsigned int parent_symbol__col_width; |
17 | char * field_sep; | 18 | char * field_sep; |
18 | 19 | ||
@@ -28,6 +29,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
28 | size_t size, unsigned int width); | 29 | size_t size, unsigned int width); |
29 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | 30 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, |
30 | size_t size, unsigned int width); | 31 | size_t size, unsigned int width); |
32 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
33 | size_t size, unsigned int width); | ||
31 | 34 | ||
32 | struct sort_entry sort_thread = { | 35 | struct sort_entry sort_thread = { |
33 | .se_header = "Command: Pid", | 36 | .se_header = "Command: Pid", |
@@ -63,6 +66,13 @@ struct sort_entry sort_parent = { | |||
63 | .se_snprintf = hist_entry__parent_snprintf, | 66 | .se_snprintf = hist_entry__parent_snprintf, |
64 | .se_width = &parent_symbol__col_width, | 67 | .se_width = &parent_symbol__col_width, |
65 | }; | 68 | }; |
69 | |||
70 | struct sort_entry sort_cpu = { | ||
71 | .se_header = "CPU", | ||
72 | .se_cmp = sort__cpu_cmp, | ||
73 | .se_snprintf = hist_entry__cpu_snprintf, | ||
74 | .se_width = &cpus__col_width, | ||
75 | }; | ||
66 | 76 | ||
67 | struct sort_dimension { | 77 | struct sort_dimension { |
68 | const char *name; | 78 | const char *name; |
@@ -76,6 +86,7 @@ static struct sort_dimension sort_dimensions[] = { | |||
76 | { .name = "dso", .entry = &sort_dso, }, | 86 | { .name = "dso", .entry = &sort_dso, }, |
77 | { .name = "symbol", .entry = &sort_sym, }, | 87 | { .name = "symbol", .entry = &sort_sym, }, |
78 | { .name = "parent", .entry = &sort_parent, }, | 88 | { .name = "parent", .entry = &sort_parent, }, |
89 | { .name = "cpu", .entry = &sort_cpu, }, | ||
79 | }; | 90 | }; |
80 | 91 | ||
81 | int64_t cmp_null(void *l, void *r) | 92 | int64_t cmp_null(void *l, void *r) |
@@ -242,6 +253,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |||
242 | self->parent ? self->parent->name : "[other]"); | 253 | self->parent ? self->parent->name : "[other]"); |
243 | } | 254 | } |
244 | 255 | ||
256 | /* --sort cpu */ | ||
257 | |||
258 | int64_t | ||
259 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | ||
260 | { | ||
261 | return right->cpu - left->cpu; | ||
262 | } | ||
263 | |||
264 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
265 | size_t size, unsigned int width) | ||
266 | { | ||
267 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | ||
268 | } | ||
269 | |||
245 | int sort_dimension__add(const char *tok) | 270 | int sort_dimension__add(const char *tok) |
246 | { | 271 | { |
247 | unsigned int i; | 272 | unsigned int i; |
@@ -281,6 +306,8 @@ int sort_dimension__add(const char *tok) | |||
281 | sort__first_dimension = SORT_SYM; | 306 | sort__first_dimension = SORT_SYM; |
282 | else if (!strcmp(sd->name, "parent")) | 307 | else if (!strcmp(sd->name, "parent")) |
283 | sort__first_dimension = SORT_PARENT; | 308 | sort__first_dimension = SORT_PARENT; |
309 | else if (!strcmp(sd->name, "cpu")) | ||
310 | sort__first_dimension = SORT_CPU; | ||
284 | } | 311 | } |
285 | 312 | ||
286 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 313 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0d61c4082f43..560c855417e4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -39,6 +39,7 @@ extern struct sort_entry sort_parent; | |||
39 | extern unsigned int dsos__col_width; | 39 | extern unsigned int dsos__col_width; |
40 | extern unsigned int comms__col_width; | 40 | extern unsigned int comms__col_width; |
41 | extern unsigned int threads__col_width; | 41 | extern unsigned int threads__col_width; |
42 | extern unsigned int cpus__col_width; | ||
42 | extern enum sort_type sort__first_dimension; | 43 | extern enum sort_type sort__first_dimension; |
43 | 44 | ||
44 | struct hist_entry { | 45 | struct hist_entry { |
@@ -51,6 +52,7 @@ struct hist_entry { | |||
51 | struct map_symbol ms; | 52 | struct map_symbol ms; |
52 | struct thread *thread; | 53 | struct thread *thread; |
53 | u64 ip; | 54 | u64 ip; |
55 | s32 cpu; | ||
54 | u32 nr_events; | 56 | u32 nr_events; |
55 | char level; | 57 | char level; |
56 | u8 filtered; | 58 | u8 filtered; |
@@ -68,7 +70,8 @@ enum sort_type { | |||
68 | SORT_COMM, | 70 | SORT_COMM, |
69 | SORT_DSO, | 71 | SORT_DSO, |
70 | SORT_SYM, | 72 | SORT_SYM, |
71 | SORT_PARENT | 73 | SORT_PARENT, |
74 | SORT_CPU, | ||
72 | }; | 75 | }; |
73 | 76 | ||
74 | /* | 77 | /* |
@@ -104,6 +107,7 @@ extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | |||
104 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | 107 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); |
105 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | 108 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); |
106 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | 109 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); |
110 | int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | ||
107 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | 111 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); |
108 | extern int sort_dimension__add(const char *); | 112 | extern int sort_dimension__add(const char *); |
109 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 113 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b63e5713849f..971d0a05d6b4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -933,6 +933,25 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type | |||
933 | } | 933 | } |
934 | } | 934 | } |
935 | 935 | ||
936 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | ||
937 | { | ||
938 | Elf_Scn *sec = NULL; | ||
939 | GElf_Shdr shdr; | ||
940 | size_t cnt = 1; | ||
941 | |||
942 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
943 | gelf_getshdr(sec, &shdr); | ||
944 | |||
945 | if ((addr >= shdr.sh_addr) && | ||
946 | (addr < (shdr.sh_addr + shdr.sh_size))) | ||
947 | return cnt; | ||
948 | |||
949 | ++cnt; | ||
950 | } | ||
951 | |||
952 | return -1; | ||
953 | } | ||
954 | |||
936 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, | 955 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, |
937 | int fd, symbol_filter_t filter, int kmodule) | 956 | int fd, symbol_filter_t filter, int kmodule) |
938 | { | 957 | { |
@@ -944,12 +963,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
944 | int err = -1; | 963 | int err = -1; |
945 | uint32_t idx; | 964 | uint32_t idx; |
946 | GElf_Ehdr ehdr; | 965 | GElf_Ehdr ehdr; |
947 | GElf_Shdr shdr; | 966 | GElf_Shdr shdr, opdshdr; |
948 | Elf_Data *syms; | 967 | Elf_Data *syms, *opddata = NULL; |
949 | GElf_Sym sym; | 968 | GElf_Sym sym; |
950 | Elf_Scn *sec, *sec_strndx; | 969 | Elf_Scn *sec, *sec_strndx, *opdsec; |
951 | Elf *elf; | 970 | Elf *elf; |
952 | int nr = 0; | 971 | int nr = 0; |
972 | size_t opdidx = 0; | ||
953 | 973 | ||
954 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 974 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
955 | if (elf == NULL) { | 975 | if (elf == NULL) { |
@@ -969,6 +989,10 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
969 | goto out_elf_end; | 989 | goto out_elf_end; |
970 | } | 990 | } |
971 | 991 | ||
992 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | ||
993 | if (opdsec) | ||
994 | opddata = elf_rawdata(opdsec, NULL); | ||
995 | |||
972 | syms = elf_getdata(sec, NULL); | 996 | syms = elf_getdata(sec, NULL); |
973 | if (syms == NULL) | 997 | if (syms == NULL) |
974 | goto out_elf_end; | 998 | goto out_elf_end; |
@@ -1013,6 +1037,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1013 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 1037 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
1014 | continue; | 1038 | continue; |
1015 | 1039 | ||
1040 | if (opdsec && sym.st_shndx == opdidx) { | ||
1041 | u32 offset = sym.st_value - opdshdr.sh_addr; | ||
1042 | u64 *opd = opddata->d_buf + offset; | ||
1043 | sym.st_value = *opd; | ||
1044 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); | ||
1045 | } | ||
1046 | |||
1016 | sec = elf_getscn(elf, sym.st_shndx); | 1047 | sec = elf_getscn(elf, sym.st_shndx); |
1017 | if (!sec) | 1048 | if (!sec) |
1018 | goto out_elf_end; | 1049 | goto out_elf_end; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5e02d2c17154..80e569bbdecc 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -9,8 +9,6 @@ | |||
9 | #include <linux/rbtree.h> | 9 | #include <linux/rbtree.h> |
10 | #include <stdio.h> | 10 | #include <stdio.h> |
11 | 11 | ||
12 | #define DEBUG_CACHE_DIR ".debug" | ||
13 | |||
14 | #ifdef HAVE_CPLUS_DEMANGLE | 12 | #ifdef HAVE_CPLUS_DEMANGLE |
15 | extern char *cplus_demangle(const char *, int); | 13 | extern char *cplus_demangle(const char *, int); |
16 | 14 | ||
@@ -73,6 +71,7 @@ struct symbol_conf { | |||
73 | full_paths, | 71 | full_paths, |
74 | show_cpu_utilization; | 72 | show_cpu_utilization; |
75 | const char *vmlinux_name, | 73 | const char *vmlinux_name, |
74 | *source_prefix, | ||
76 | *field_sep; | 75 | *field_sep; |
77 | const char *default_guest_vmlinux_name, | 76 | const char *default_guest_vmlinux_name, |
78 | *default_guest_kallsyms, | 77 | *default_guest_kallsyms, |
@@ -112,7 +111,8 @@ struct addr_location { | |||
112 | u64 addr; | 111 | u64 addr; |
113 | char level; | 112 | char level; |
114 | bool filtered; | 113 | bool filtered; |
115 | unsigned int cpumode; | 114 | u8 cpumode; |
115 | s32 cpu; | ||
116 | }; | 116 | }; |
117 | 117 | ||
118 | enum dso_kernel_type { | 118 | enum dso_kernel_type { |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 4e8b6b0c551c..f380fed74359 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -89,6 +89,7 @@ | |||
89 | 89 | ||
90 | extern const char *graph_line; | 90 | extern const char *graph_line; |
91 | extern const char *graph_dotted_line; | 91 | extern const char *graph_dotted_line; |
92 | extern char buildid_dir[]; | ||
92 | 93 | ||
93 | /* On most systems <limits.h> would have given us this, but | 94 | /* On most systems <limits.h> would have given us this, but |
94 | * not on some systems (e.g. GNU/Hurd). | 95 | * not on some systems (e.g. GNU/Hurd). |
@@ -152,6 +153,8 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))) | |||
152 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 153 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
153 | 154 | ||
154 | extern int prefixcmp(const char *str, const char *prefix); | 155 | extern int prefixcmp(const char *str, const char *prefix); |
156 | extern void set_buildid_dir(void); | ||
157 | extern void disable_buildid_cache(void); | ||
155 | 158 | ||
156 | static inline const char *skip_prefix(const char *str, const char *prefix) | 159 | static inline const char *skip_prefix(const char *str, const char *prefix) |
157 | { | 160 | { |