aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/build-id.c10
-rw-r--r--tools/perf/util/cache.h1
-rw-r--r--tools/perf/util/callchain.c2
-rw-r--r--tools/perf/util/callchain.h2
-rw-r--r--tools/perf/util/config.c64
-rw-r--r--tools/perf/util/cpumap.c57
-rw-r--r--tools/perf/util/cpumap.h2
-rw-r--r--tools/perf/util/debug.c10
-rw-r--r--tools/perf/util/event.c37
-rw-r--r--tools/perf/util/event.h5
-rw-r--r--tools/perf/util/header.c13
-rw-r--r--tools/perf/util/hist.c3
-rw-r--r--tools/perf/util/probe-finder.c58
-rw-r--r--tools/perf/util/session.c6
-rw-r--r--tools/perf/util/sort.c27
-rw-r--r--tools/perf/util/sort.h6
-rw-r--r--tools/perf/util/symbol.c37
-rw-r--r--tools/perf/util/symbol.h6
-rw-r--r--tools/perf/util/util.h3
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 = {
43char *dso__build_id_filename(struct dso *self, char *bf, size_t size) 43char *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 *);
23extern int perf_config_int(const char *, const char *); 23extern int perf_config_int(const char *, const char *);
24extern int perf_config_bool(const char *, const char *); 24extern int perf_config_bool(const char *, const char *);
25extern int config_error_nonbool(const char *); 25extern int config_error_nonbool(const char *);
26extern const char *perf_config_dirname(const char *, const char *);
26 27
27/* pager.c */ 28/* pager.c */
28extern void setup_pager(void); 29extern 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
21bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) 21bool 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);
60int append_chain(struct callchain_node *root, struct ip_callchain *chain, 60int append_chain(struct callchain_node *root, struct ip_callchain *chain,
61 struct map_symbol *syms); 61 struct map_symbol *syms);
62 62
63bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); 63bool 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
17char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
18
14static FILE *config_file; 19static FILE *config_file;
15static const char *config_file_name; 20static const char *config_file_name;
16static int config_linenr; 21static 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
335const char *perf_config_dirname(const char *name, const char *value)
336{
337 if (!name)
338 return NULL;
339 return value;
340}
341
330static int perf_default_core_config(const char *var __used, const char *value __used) 342static 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
444struct buildid_dir_config {
445 char *dir;
446};
447
448static 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
465static 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
472void 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
23int read_cpu_map(void) 23static 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
61int 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();
112invalid:
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
4extern int read_cpu_map(void); 4extern int read_cpu_map(const char *cpu_list);
5extern int cpumap[]; 5extern 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
657int event__preprocess_sample(const event_t *self, struct perf_session *session, 657int 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
727int event__parse_sample(event_t *event, u64 type, struct sample_data *data) 753int 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
158struct addr_location; 158struct addr_location;
159int event__preprocess_sample(const event_t *self, struct perf_session *session, 159int 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,
161int event__parse_sample(event_t *event, u64 type, struct sample_data *data); 161 symbol_filter_t filter);
162int event__parse_sample(const event_t *event, u64 type, struct sample_data *data);
162 163
163extern const char *event__name[]; 164extern 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
19static 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
1196void 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 */
67static 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:
1096static int line_range_add_line(const char *src, unsigned int lineno, 1146static 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;
13unsigned int dsos__col_width; 13unsigned int dsos__col_width;
14unsigned int comms__col_width; 14unsigned int comms__col_width;
15unsigned int threads__col_width; 15unsigned int threads__col_width;
16unsigned int cpus__col_width;
16static unsigned int parent_symbol__col_width; 17static unsigned int parent_symbol__col_width;
17char * field_sep; 18char * 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);
29static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 30static 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);
32static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
33 size_t size, unsigned int width);
31 34
32struct sort_entry sort_thread = { 35struct 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
70struct 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
67struct sort_dimension { 77struct 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
81int64_t cmp_null(void *l, void *r) 92int64_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
258int64_t
259sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
260{
261 return right->cpu - left->cpu;
262}
263
264static 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
245int sort_dimension__add(const char *tok) 270int 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;
39extern unsigned int dsos__col_width; 39extern unsigned int dsos__col_width;
40extern unsigned int comms__col_width; 40extern unsigned int comms__col_width;
41extern unsigned int threads__col_width; 41extern unsigned int threads__col_width;
42extern unsigned int cpus__col_width;
42extern enum sort_type sort__first_dimension; 43extern enum sort_type sort__first_dimension;
43 44
44struct hist_entry { 45struct 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 *);
104extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); 107extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
105extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); 108extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
106extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); 109extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
110int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
107extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); 111extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
108extern int sort_dimension__add(const char *); 112extern int sort_dimension__add(const char *);
109void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 113void 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
936static 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
936static int dso__load_sym(struct dso *self, struct map *map, const char *name, 955static 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
15extern char *cplus_demangle(const char *, int); 13extern 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
118enum dso_kernel_type { 118enum 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
90extern const char *graph_line; 90extern const char *graph_line;
91extern const char *graph_dotted_line; 91extern const char *graph_dotted_line;
92extern 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)))
152extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); 153extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
153 154
154extern int prefixcmp(const char *str, const char *prefix); 155extern int prefixcmp(const char *str, const char *prefix);
156extern void set_buildid_dir(void);
157extern void disable_buildid_cache(void);
155 158
156static inline const char *skip_prefix(const char *str, const char *prefix) 159static inline const char *skip_prefix(const char *str, const char *prefix)
157{ 160{