diff options
Diffstat (limited to 'tools/perf/util')
84 files changed, 5036 insertions, 2666 deletions
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c index b8144e80bb1e..e6d134773d0a 100644 --- a/tools/perf/util/alias.c +++ b/tools/perf/util/alias.c | |||
@@ -3,7 +3,8 @@ | |||
3 | static const char *alias_key; | 3 | static const char *alias_key; |
4 | static char *alias_val; | 4 | static char *alias_val; |
5 | 5 | ||
6 | static int alias_lookup_cb(const char *k, const char *v, void *cb __used) | 6 | static int alias_lookup_cb(const char *k, const char *v, |
7 | void *cb __maybe_unused) | ||
7 | { | 8 | { |
8 | if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) { | 9 | if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) { |
9 | if (!v) | 10 | if (!v) |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 3a282c0057d2..f0a910371377 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | 18 | ||
19 | const char *disassembler_style; | 19 | const char *disassembler_style; |
20 | const char *objdump_path; | ||
20 | 21 | ||
21 | static struct ins *ins__find(const char *name); | 22 | static struct ins *ins__find(const char *name); |
22 | static int disasm_line__parse(char *line, char **namep, char **rawp); | 23 | static int disasm_line__parse(char *line, char **namep, char **rawp); |
@@ -312,8 +313,8 @@ static struct ins_ops dec_ops = { | |||
312 | .scnprintf = dec__scnprintf, | 313 | .scnprintf = dec__scnprintf, |
313 | }; | 314 | }; |
314 | 315 | ||
315 | static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size, | 316 | static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size, |
316 | struct ins_operands *ops __used) | 317 | struct ins_operands *ops __maybe_unused) |
317 | { | 318 | { |
318 | return scnprintf(bf, size, "%-6.6s", "nop"); | 319 | return scnprintf(bf, size, "%-6.6s", "nop"); |
319 | } | 320 | } |
@@ -415,7 +416,7 @@ static struct ins *ins__find(const char *name) | |||
415 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); | 416 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); |
416 | } | 417 | } |
417 | 418 | ||
418 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) | 419 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym) |
419 | { | 420 | { |
420 | struct annotation *notes = symbol__annotation(sym); | 421 | struct annotation *notes = symbol__annotation(sym); |
421 | pthread_mutex_init(¬es->lock, NULL); | 422 | pthread_mutex_init(¬es->lock, NULL); |
@@ -820,9 +821,10 @@ fallback: | |||
820 | dso, dso->long_name, sym, sym->name); | 821 | dso, dso->long_name, sym, sym->name); |
821 | 822 | ||
822 | snprintf(command, sizeof(command), | 823 | snprintf(command, sizeof(command), |
823 | "objdump %s%s --start-address=0x%016" PRIx64 | 824 | "%s %s%s --start-address=0x%016" PRIx64 |
824 | " --stop-address=0x%016" PRIx64 | 825 | " --stop-address=0x%016" PRIx64 |
825 | " -d %s %s -C %s|grep -v %s|expand", | 826 | " -d %s %s -C %s|grep -v %s|expand", |
827 | objdump_path ? objdump_path : "objdump", | ||
826 | disassembler_style ? "-M " : "", | 828 | disassembler_style ? "-M " : "", |
827 | disassembler_style ? disassembler_style : "", | 829 | disassembler_style ? disassembler_style : "", |
828 | map__rip_2objdump(map, sym->start), | 830 | map__rip_2objdump(map, sym->start), |
@@ -982,7 +984,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
982 | int context) | 984 | int context) |
983 | { | 985 | { |
984 | struct dso *dso = map->dso; | 986 | struct dso *dso = map->dso; |
985 | const char *filename = dso->long_name, *d_filename; | 987 | char *filename; |
988 | const char *d_filename; | ||
986 | struct annotation *notes = symbol__annotation(sym); | 989 | struct annotation *notes = symbol__annotation(sym); |
987 | struct disasm_line *pos, *queue = NULL; | 990 | struct disasm_line *pos, *queue = NULL; |
988 | u64 start = map__rip_2objdump(map, sym->start); | 991 | u64 start = map__rip_2objdump(map, sym->start); |
@@ -990,6 +993,10 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
990 | int more = 0; | 993 | int more = 0; |
991 | u64 len; | 994 | u64 len; |
992 | 995 | ||
996 | filename = strdup(dso->long_name); | ||
997 | if (!filename) | ||
998 | return -ENOMEM; | ||
999 | |||
993 | if (full_paths) | 1000 | if (full_paths) |
994 | d_filename = filename; | 1001 | d_filename = filename; |
995 | else | 1002 | else |
@@ -1040,6 +1047,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1040 | } | 1047 | } |
1041 | } | 1048 | } |
1042 | 1049 | ||
1050 | free(filename); | ||
1051 | |||
1043 | return more; | 1052 | return more; |
1044 | } | 1053 | } |
1045 | 1054 | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 78a5692dd718..39242dcee8f2 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "symbol.h" | 7 | #include "symbol.h" |
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/rbtree.h> | 9 | #include <linux/rbtree.h> |
10 | #include <pthread.h> | ||
10 | 11 | ||
11 | struct ins; | 12 | struct ins; |
12 | 13 | ||
@@ -125,7 +126,7 @@ int symbol__alloc_hist(struct symbol *sym); | |||
125 | void symbol__annotate_zero_histograms(struct symbol *sym); | 126 | void symbol__annotate_zero_histograms(struct symbol *sym); |
126 | 127 | ||
127 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | 128 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); |
128 | int symbol__annotate_init(struct map *map __used, struct symbol *sym); | 129 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); |
129 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | 130 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, |
130 | bool full_paths, int min_pcnt, int max_lines, | 131 | bool full_paths, int min_pcnt, int max_lines, |
131 | int context); | 132 | int context); |
@@ -137,20 +138,22 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
137 | bool print_lines, bool full_paths, int min_pcnt, | 138 | bool print_lines, bool full_paths, int min_pcnt, |
138 | int max_lines); | 139 | int max_lines); |
139 | 140 | ||
140 | #ifdef NO_NEWT_SUPPORT | 141 | #ifdef NEWT_SUPPORT |
141 | static inline int symbol__tui_annotate(struct symbol *sym __used, | 142 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
142 | struct map *map __used, | 143 | void(*timer)(void *arg), void *arg, int delay_secs); |
143 | int evidx __used, | 144 | #else |
144 | void(*timer)(void *arg) __used, | 145 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, |
145 | void *arg __used, int delay_secs __used) | 146 | struct map *map __maybe_unused, |
147 | int evidx __maybe_unused, | ||
148 | void(*timer)(void *arg) __maybe_unused, | ||
149 | void *arg __maybe_unused, | ||
150 | int delay_secs __maybe_unused) | ||
146 | { | 151 | { |
147 | return 0; | 152 | return 0; |
148 | } | 153 | } |
149 | #else | ||
150 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | ||
151 | void(*timer)(void *arg), void *arg, int delay_secs); | ||
152 | #endif | 154 | #endif |
153 | 155 | ||
154 | extern const char *disassembler_style; | 156 | extern const char *disassembler_style; |
157 | extern const char *objdump_path; | ||
155 | 158 | ||
156 | #endif /* __PERF_ANNOTATE_H */ | 159 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index fd9a5944b627..8e3a740ddbd4 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -16,10 +16,10 @@ | |||
16 | #include "session.h" | 16 | #include "session.h" |
17 | #include "tool.h" | 17 | #include "tool.h" |
18 | 18 | ||
19 | static int build_id__mark_dso_hit(struct perf_tool *tool __used, | 19 | static int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
20 | union perf_event *event, | 20 | union perf_event *event, |
21 | struct perf_sample *sample __used, | 21 | struct perf_sample *sample __maybe_unused, |
22 | struct perf_evsel *evsel __used, | 22 | struct perf_evsel *evsel __maybe_unused, |
23 | struct machine *machine) | 23 | struct machine *machine) |
24 | { | 24 | { |
25 | struct addr_location al; | 25 | struct addr_location al; |
@@ -41,9 +41,10 @@ static int build_id__mark_dso_hit(struct perf_tool *tool __used, | |||
41 | return 0; | 41 | return 0; |
42 | } | 42 | } |
43 | 43 | ||
44 | static int perf_event__exit_del_thread(struct perf_tool *tool __used, | 44 | static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, |
45 | union perf_event *event, | 45 | union perf_event *event, |
46 | struct perf_sample *sample __used, | 46 | struct perf_sample *sample |
47 | __maybe_unused, | ||
47 | struct machine *machine) | 48 | struct machine *machine) |
48 | { | 49 | { |
49 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 50 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index cff18c617d13..2bd51370ad28 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -33,39 +33,41 @@ extern int pager_use_color; | |||
33 | 33 | ||
34 | extern int use_browser; | 34 | extern int use_browser; |
35 | 35 | ||
36 | #if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) | 36 | #if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) |
37 | static inline void setup_browser(bool fallback_to_pager) | ||
38 | { | ||
39 | if (fallback_to_pager) | ||
40 | setup_pager(); | ||
41 | } | ||
42 | static inline void exit_browser(bool wait_for_ok __used) {} | ||
43 | #else | ||
44 | void setup_browser(bool fallback_to_pager); | 37 | void setup_browser(bool fallback_to_pager); |
45 | void exit_browser(bool wait_for_ok); | 38 | void exit_browser(bool wait_for_ok); |
46 | 39 | ||
47 | #ifdef NO_NEWT_SUPPORT | 40 | #ifdef NEWT_SUPPORT |
41 | int ui__init(void); | ||
42 | void ui__exit(bool wait_for_ok); | ||
43 | #else | ||
48 | static inline int ui__init(void) | 44 | static inline int ui__init(void) |
49 | { | 45 | { |
50 | return -1; | 46 | return -1; |
51 | } | 47 | } |
52 | static inline void ui__exit(bool wait_for_ok __used) {} | 48 | static inline void ui__exit(bool wait_for_ok __maybe_unused) {} |
53 | #else | ||
54 | int ui__init(void); | ||
55 | void ui__exit(bool wait_for_ok); | ||
56 | #endif | 49 | #endif |
57 | 50 | ||
58 | #ifdef NO_GTK2_SUPPORT | 51 | #ifdef GTK2_SUPPORT |
52 | int perf_gtk__init(void); | ||
53 | void perf_gtk__exit(bool wait_for_ok); | ||
54 | #else | ||
59 | static inline int perf_gtk__init(void) | 55 | static inline int perf_gtk__init(void) |
60 | { | 56 | { |
61 | return -1; | 57 | return -1; |
62 | } | 58 | } |
63 | static inline void perf_gtk__exit(bool wait_for_ok __used) {} | 59 | static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} |
64 | #else | ||
65 | int perf_gtk__init(void); | ||
66 | void perf_gtk__exit(bool wait_for_ok); | ||
67 | #endif | 60 | #endif |
68 | #endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ | 61 | |
62 | #else /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
63 | |||
64 | static inline void setup_browser(bool fallback_to_pager) | ||
65 | { | ||
66 | if (fallback_to_pager) | ||
67 | setup_pager(); | ||
68 | } | ||
69 | static inline void exit_browser(bool wait_for_ok __maybe_unused) {} | ||
70 | #endif /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
69 | 71 | ||
70 | char *alias_lookup(const char *alias); | 72 | char *alias_lookup(const char *alias); |
71 | int split_cmdline(char *cmdline, const char ***argv); | 73 | int split_cmdline(char *cmdline, const char ***argv); |
@@ -105,7 +107,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 | |||
105 | extern char *perf_pathdup(const char *fmt, ...) | 107 | extern char *perf_pathdup(const char *fmt, ...) |
106 | __attribute__((format (printf, 1, 2))); | 108 | __attribute__((format (printf, 1, 2))); |
107 | 109 | ||
108 | #ifdef NO_STRLCPY | 110 | #ifndef HAVE_STRLCPY |
109 | extern size_t strlcpy(char *dest, const char *src, size_t size); | 111 | extern size_t strlcpy(char *dest, const char *src, size_t size); |
110 | #endif | 112 | #endif |
111 | 113 | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 3a6bff47614f..d3b3f5d82137 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -93,7 +93,7 @@ __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | |||
93 | */ | 93 | */ |
94 | static void | 94 | static void |
95 | sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, | 95 | sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, |
96 | u64 min_hit, struct callchain_param *param __used) | 96 | u64 min_hit, struct callchain_param *param __maybe_unused) |
97 | { | 97 | { |
98 | __sort_chain_flat(rb_root, &root->node, min_hit); | 98 | __sort_chain_flat(rb_root, &root->node, min_hit); |
99 | } | 99 | } |
@@ -115,7 +115,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
115 | 115 | ||
116 | static void | 116 | static void |
117 | sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, | 117 | sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, |
118 | u64 min_hit, struct callchain_param *param __used) | 118 | u64 min_hit, struct callchain_param *param __maybe_unused) |
119 | { | 119 | { |
120 | __sort_chain_graph_abs(&chain_root->node, min_hit); | 120 | __sort_chain_graph_abs(&chain_root->node, min_hit); |
121 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | 121 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
@@ -140,7 +140,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
140 | 140 | ||
141 | static void | 141 | static void |
142 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, | 142 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, |
143 | u64 min_hit __used, struct callchain_param *param) | 143 | u64 min_hit __maybe_unused, struct callchain_param *param) |
144 | { | 144 | { |
145 | __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0); | 145 | __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0); |
146 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | 146 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index dbe2f16b1a1a..96bbda1ddb83 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c | |||
@@ -138,8 +138,8 @@ void close_cgroup(struct cgroup_sel *cgrp) | |||
138 | } | 138 | } |
139 | } | 139 | } |
140 | 140 | ||
141 | int parse_cgroups(const struct option *opt __used, const char *str, | 141 | int parse_cgroups(const struct option *opt __maybe_unused, const char *str, |
142 | int unset __used) | 142 | int unset __maybe_unused) |
143 | { | 143 | { |
144 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 144 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
145 | const char *p, *e, *eos = str + strlen(str); | 145 | const char *p, *e, *eos = str + strlen(str); |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 6faa3a18bfbd..3e0fdd369ccb 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -342,13 +342,15 @@ const char *perf_config_dirname(const char *name, const char *value) | |||
342 | return value; | 342 | return value; |
343 | } | 343 | } |
344 | 344 | ||
345 | static int perf_default_core_config(const char *var __used, const char *value __used) | 345 | static int perf_default_core_config(const char *var __maybe_unused, |
346 | const char *value __maybe_unused) | ||
346 | { | 347 | { |
347 | /* Add other config variables here. */ | 348 | /* Add other config variables here. */ |
348 | return 0; | 349 | return 0; |
349 | } | 350 | } |
350 | 351 | ||
351 | int perf_default_config(const char *var, const char *value, void *dummy __used) | 352 | int perf_default_config(const char *var, const char *value, |
353 | void *dummy __maybe_unused) | ||
352 | { | 354 | { |
353 | if (!prefixcmp(var, "core.")) | 355 | if (!prefixcmp(var, "core.")) |
354 | return perf_default_core_config(var, value); | 356 | return perf_default_core_config(var, value); |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index adc72f09914d..2b32ffa9ebdb 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -38,24 +38,19 @@ static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) | |||
38 | return cpus; | 38 | return cpus; |
39 | } | 39 | } |
40 | 40 | ||
41 | static struct cpu_map *cpu_map__read_all_cpu_map(void) | 41 | struct cpu_map *cpu_map__read(FILE *file) |
42 | { | 42 | { |
43 | struct cpu_map *cpus = NULL; | 43 | struct cpu_map *cpus = NULL; |
44 | FILE *onlnf; | ||
45 | int nr_cpus = 0; | 44 | int nr_cpus = 0; |
46 | int *tmp_cpus = NULL, *tmp; | 45 | int *tmp_cpus = NULL, *tmp; |
47 | int max_entries = 0; | 46 | int max_entries = 0; |
48 | int n, cpu, prev; | 47 | int n, cpu, prev; |
49 | char sep; | 48 | char sep; |
50 | 49 | ||
51 | onlnf = fopen("/sys/devices/system/cpu/online", "r"); | ||
52 | if (!onlnf) | ||
53 | return cpu_map__default_new(); | ||
54 | |||
55 | sep = 0; | 50 | sep = 0; |
56 | prev = -1; | 51 | prev = -1; |
57 | for (;;) { | 52 | for (;;) { |
58 | n = fscanf(onlnf, "%u%c", &cpu, &sep); | 53 | n = fscanf(file, "%u%c", &cpu, &sep); |
59 | if (n <= 0) | 54 | if (n <= 0) |
60 | break; | 55 | break; |
61 | if (prev >= 0) { | 56 | if (prev >= 0) { |
@@ -95,6 +90,19 @@ static struct cpu_map *cpu_map__read_all_cpu_map(void) | |||
95 | cpus = cpu_map__default_new(); | 90 | cpus = cpu_map__default_new(); |
96 | out_free_tmp: | 91 | out_free_tmp: |
97 | free(tmp_cpus); | 92 | free(tmp_cpus); |
93 | return cpus; | ||
94 | } | ||
95 | |||
96 | static struct cpu_map *cpu_map__read_all_cpu_map(void) | ||
97 | { | ||
98 | struct cpu_map *cpus = NULL; | ||
99 | FILE *onlnf; | ||
100 | |||
101 | onlnf = fopen("/sys/devices/system/cpu/online", "r"); | ||
102 | if (!onlnf) | ||
103 | return cpu_map__default_new(); | ||
104 | |||
105 | cpus = cpu_map__read(onlnf); | ||
98 | fclose(onlnf); | 106 | fclose(onlnf); |
99 | return cpus; | 107 | return cpus; |
100 | } | 108 | } |
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index c41518573c6a..2f68a3b8c285 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_CPUMAP_H | 2 | #define __PERF_CPUMAP_H |
3 | 3 | ||
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <stdbool.h> | ||
5 | 6 | ||
6 | struct cpu_map { | 7 | struct cpu_map { |
7 | int nr; | 8 | int nr; |
@@ -11,7 +12,17 @@ struct cpu_map { | |||
11 | struct cpu_map *cpu_map__new(const char *cpu_list); | 12 | struct cpu_map *cpu_map__new(const char *cpu_list); |
12 | struct cpu_map *cpu_map__dummy_new(void); | 13 | struct cpu_map *cpu_map__dummy_new(void); |
13 | void cpu_map__delete(struct cpu_map *map); | 14 | void cpu_map__delete(struct cpu_map *map); |
14 | 15 | struct cpu_map *cpu_map__read(FILE *file); | |
15 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); | 16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); |
16 | 17 | ||
18 | static inline int cpu_map__nr(const struct cpu_map *map) | ||
19 | { | ||
20 | return map ? map->nr : 1; | ||
21 | } | ||
22 | |||
23 | static inline bool cpu_map__all(const struct cpu_map *map) | ||
24 | { | ||
25 | return map ? map->map[0] == -1 : true; | ||
26 | } | ||
27 | |||
17 | #endif /* __PERF_CPUMAP_H */ | 28 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 4dfe0bb3c322..03f830b48148 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -23,8 +23,10 @@ int eprintf(int level, const char *fmt, ...) | |||
23 | 23 | ||
24 | if (verbose >= level) { | 24 | if (verbose >= level) { |
25 | va_start(args, fmt); | 25 | va_start(args, fmt); |
26 | if (use_browser > 0) | 26 | if (use_browser == 1) |
27 | ret = ui_helpline__show_help(fmt, args); | 27 | ret = ui_helpline__show_help(fmt, args); |
28 | else if (use_browser == 2) | ||
29 | ret = perf_gtk__show_helpline(fmt, args); | ||
28 | else | 30 | else |
29 | ret = vfprintf(stderr, fmt, args); | 31 | ret = vfprintf(stderr, fmt, args); |
30 | va_end(args); | 32 | va_end(args); |
@@ -47,7 +49,7 @@ int dump_printf(const char *fmt, ...) | |||
47 | return ret; | 49 | return ret; |
48 | } | 50 | } |
49 | 51 | ||
50 | #if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) | 52 | #if !defined(NEWT_SUPPORT) && !defined(GTK2_SUPPORT) |
51 | int ui__warning(const char *format, ...) | 53 | int ui__warning(const char *format, ...) |
52 | { | 54 | { |
53 | va_list args; | 55 | va_list args; |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 015c91dbc096..dec98750b484 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include "event.h" | 6 | #include "event.h" |
7 | #include "../ui/helpline.h" | ||
7 | 8 | ||
8 | extern int verbose; | 9 | extern int verbose; |
9 | extern bool quiet, dump_trace; | 10 | extern bool quiet, dump_trace; |
@@ -14,38 +15,33 @@ void trace_event(union perf_event *event); | |||
14 | struct ui_progress; | 15 | struct ui_progress; |
15 | struct perf_error_ops; | 16 | struct perf_error_ops; |
16 | 17 | ||
17 | #if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) | 18 | #if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) |
18 | static inline int ui_helpline__show_help(const char *format __used, va_list ap __used) | 19 | |
19 | { | 20 | #include "../ui/progress.h" |
20 | return 0; | 21 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); |
21 | } | 22 | #include "../ui/util.h" |
22 | 23 | ||
23 | static inline void ui_progress__update(u64 curr __used, u64 total __used, | 24 | #else |
24 | const char *title __used) {} | 25 | |
26 | static inline void ui_progress__update(u64 curr __maybe_unused, | ||
27 | u64 total __maybe_unused, | ||
28 | const char *title __maybe_unused) {} | ||
25 | 29 | ||
26 | #define ui__error(format, arg...) ui__warning(format, ##arg) | 30 | #define ui__error(format, arg...) ui__warning(format, ##arg) |
27 | 31 | ||
28 | static inline int | 32 | static inline int |
29 | perf_error__register(struct perf_error_ops *eops __used) | 33 | perf_error__register(struct perf_error_ops *eops __maybe_unused) |
30 | { | 34 | { |
31 | return 0; | 35 | return 0; |
32 | } | 36 | } |
33 | 37 | ||
34 | static inline int | 38 | static inline int |
35 | perf_error__unregister(struct perf_error_ops *eops __used) | 39 | perf_error__unregister(struct perf_error_ops *eops __maybe_unused) |
36 | { | 40 | { |
37 | return 0; | 41 | return 0; |
38 | } | 42 | } |
39 | 43 | ||
40 | #else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ | 44 | #endif /* NEWT_SUPPORT || GTK2_SUPPORT */ |
41 | |||
42 | extern char ui_helpline__last_msg[]; | ||
43 | int ui_helpline__show_help(const char *format, va_list ap); | ||
44 | #include "../ui/progress.h" | ||
45 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | ||
46 | #include "../ui/util.h" | ||
47 | |||
48 | #endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ | ||
49 | 45 | ||
50 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | 46 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); |
51 | int ui__error_paranoid(void); | 47 | int ui__error_paranoid(void); |
diff --git a/tools/perf/util/dso-test-data.c b/tools/perf/util/dso-test-data.c index 541cdc72c7df..c6caedeb1d6b 100644 --- a/tools/perf/util/dso-test-data.c +++ b/tools/perf/util/dso-test-data.c | |||
@@ -23,7 +23,7 @@ static char *test_file(int size) | |||
23 | int fd, i; | 23 | int fd, i; |
24 | unsigned char *buf; | 24 | unsigned char *buf; |
25 | 25 | ||
26 | fd = mkostemp(templ, O_CREAT|O_WRONLY|O_TRUNC); | 26 | fd = mkstemp(templ); |
27 | 27 | ||
28 | buf = malloc(size); | 28 | buf = malloc(size); |
29 | if (!buf) { | 29 | if (!buf) { |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index ee51e9b4dc09..3e5f5430a28a 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -804,6 +804,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | |||
804 | tmp = "union "; | 804 | tmp = "union "; |
805 | else if (tag == DW_TAG_structure_type) | 805 | else if (tag == DW_TAG_structure_type) |
806 | tmp = "struct "; | 806 | tmp = "struct "; |
807 | else if (tag == DW_TAG_enumeration_type) | ||
808 | tmp = "enum "; | ||
807 | /* Write a base name */ | 809 | /* Write a base name */ |
808 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | 810 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); |
809 | return (ret >= len) ? -E2BIG : ret; | 811 | return (ret >= len) ? -E2BIG : ret; |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2a6f33cd888c..6715b1938725 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -112,7 +112,7 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
112 | event->comm.header.type = PERF_RECORD_COMM; | 112 | event->comm.header.type = PERF_RECORD_COMM; |
113 | 113 | ||
114 | size = strlen(event->comm.comm) + 1; | 114 | size = strlen(event->comm.comm) + 1; |
115 | size = ALIGN(size, sizeof(u64)); | 115 | size = PERF_ALIGN(size, sizeof(u64)); |
116 | memset(event->comm.comm + size, 0, machine->id_hdr_size); | 116 | memset(event->comm.comm + size, 0, machine->id_hdr_size); |
117 | event->comm.header.size = (sizeof(event->comm) - | 117 | event->comm.header.size = (sizeof(event->comm) - |
118 | (sizeof(event->comm.comm) - size) + | 118 | (sizeof(event->comm.comm) - size) + |
@@ -120,7 +120,9 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
120 | if (!full) { | 120 | if (!full) { |
121 | event->comm.tid = pid; | 121 | event->comm.tid = pid; |
122 | 122 | ||
123 | process(tool, event, &synth_sample, machine); | 123 | if (process(tool, event, &synth_sample, machine) != 0) |
124 | return -1; | ||
125 | |||
124 | goto out; | 126 | goto out; |
125 | } | 127 | } |
126 | 128 | ||
@@ -143,7 +145,7 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
143 | sizeof(event->comm.comm)); | 145 | sizeof(event->comm.comm)); |
144 | 146 | ||
145 | size = strlen(event->comm.comm) + 1; | 147 | size = strlen(event->comm.comm) + 1; |
146 | size = ALIGN(size, sizeof(u64)); | 148 | size = PERF_ALIGN(size, sizeof(u64)); |
147 | memset(event->comm.comm + size, 0, machine->id_hdr_size); | 149 | memset(event->comm.comm + size, 0, machine->id_hdr_size); |
148 | event->comm.header.size = (sizeof(event->comm) - | 150 | event->comm.header.size = (sizeof(event->comm) - |
149 | (sizeof(event->comm.comm) - size) + | 151 | (sizeof(event->comm.comm) - size) + |
@@ -151,7 +153,10 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
151 | 153 | ||
152 | event->comm.tid = pid; | 154 | event->comm.tid = pid; |
153 | 155 | ||
154 | process(tool, event, &synth_sample, machine); | 156 | if (process(tool, event, &synth_sample, machine) != 0) { |
157 | tgid = -1; | ||
158 | break; | ||
159 | } | ||
155 | } | 160 | } |
156 | 161 | ||
157 | closedir(tasks); | 162 | closedir(tasks); |
@@ -167,6 +172,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
167 | { | 172 | { |
168 | char filename[PATH_MAX]; | 173 | char filename[PATH_MAX]; |
169 | FILE *fp; | 174 | FILE *fp; |
175 | int rc = 0; | ||
170 | 176 | ||
171 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | 177 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); |
172 | 178 | ||
@@ -222,7 +228,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
222 | size = strlen(execname); | 228 | size = strlen(execname); |
223 | execname[size - 1] = '\0'; /* Remove \n */ | 229 | execname[size - 1] = '\0'; /* Remove \n */ |
224 | memcpy(event->mmap.filename, execname, size); | 230 | memcpy(event->mmap.filename, execname, size); |
225 | size = ALIGN(size, sizeof(u64)); | 231 | size = PERF_ALIGN(size, sizeof(u64)); |
226 | event->mmap.len -= event->mmap.start; | 232 | event->mmap.len -= event->mmap.start; |
227 | event->mmap.header.size = (sizeof(event->mmap) - | 233 | event->mmap.header.size = (sizeof(event->mmap) - |
228 | (sizeof(event->mmap.filename) - size)); | 234 | (sizeof(event->mmap.filename) - size)); |
@@ -231,18 +237,22 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
231 | event->mmap.pid = tgid; | 237 | event->mmap.pid = tgid; |
232 | event->mmap.tid = pid; | 238 | event->mmap.tid = pid; |
233 | 239 | ||
234 | process(tool, event, &synth_sample, machine); | 240 | if (process(tool, event, &synth_sample, machine) != 0) { |
241 | rc = -1; | ||
242 | break; | ||
243 | } | ||
235 | } | 244 | } |
236 | } | 245 | } |
237 | 246 | ||
238 | fclose(fp); | 247 | fclose(fp); |
239 | return 0; | 248 | return rc; |
240 | } | 249 | } |
241 | 250 | ||
242 | int perf_event__synthesize_modules(struct perf_tool *tool, | 251 | int perf_event__synthesize_modules(struct perf_tool *tool, |
243 | perf_event__handler_t process, | 252 | perf_event__handler_t process, |
244 | struct machine *machine) | 253 | struct machine *machine) |
245 | { | 254 | { |
255 | int rc = 0; | ||
246 | struct rb_node *nd; | 256 | struct rb_node *nd; |
247 | struct map_groups *kmaps = &machine->kmaps; | 257 | struct map_groups *kmaps = &machine->kmaps; |
248 | union perf_event *event = zalloc((sizeof(event->mmap) + | 258 | union perf_event *event = zalloc((sizeof(event->mmap) + |
@@ -272,7 +282,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, | |||
272 | if (pos->dso->kernel) | 282 | if (pos->dso->kernel) |
273 | continue; | 283 | continue; |
274 | 284 | ||
275 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | 285 | size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); |
276 | event->mmap.header.type = PERF_RECORD_MMAP; | 286 | event->mmap.header.type = PERF_RECORD_MMAP; |
277 | event->mmap.header.size = (sizeof(event->mmap) - | 287 | event->mmap.header.size = (sizeof(event->mmap) - |
278 | (sizeof(event->mmap.filename) - size)); | 288 | (sizeof(event->mmap.filename) - size)); |
@@ -284,11 +294,14 @@ int perf_event__synthesize_modules(struct perf_tool *tool, | |||
284 | 294 | ||
285 | memcpy(event->mmap.filename, pos->dso->long_name, | 295 | memcpy(event->mmap.filename, pos->dso->long_name, |
286 | pos->dso->long_name_len + 1); | 296 | pos->dso->long_name_len + 1); |
287 | process(tool, event, &synth_sample, machine); | 297 | if (process(tool, event, &synth_sample, machine) != 0) { |
298 | rc = -1; | ||
299 | break; | ||
300 | } | ||
288 | } | 301 | } |
289 | 302 | ||
290 | free(event); | 303 | free(event); |
291 | return 0; | 304 | return rc; |
292 | } | 305 | } |
293 | 306 | ||
294 | static int __event__synthesize_thread(union perf_event *comm_event, | 307 | static int __event__synthesize_thread(union perf_event *comm_event, |
@@ -392,12 +405,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
392 | if (*end) /* only interested in proper numerical dirents */ | 405 | if (*end) /* only interested in proper numerical dirents */ |
393 | continue; | 406 | continue; |
394 | 407 | ||
395 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, | 408 | if (__event__synthesize_thread(comm_event, mmap_event, pid, 1, |
396 | process, tool, machine); | 409 | process, tool, machine) != 0) { |
410 | err = -1; | ||
411 | goto out_closedir; | ||
412 | } | ||
397 | } | 413 | } |
398 | 414 | ||
399 | closedir(proc); | ||
400 | err = 0; | 415 | err = 0; |
416 | out_closedir: | ||
417 | closedir(proc); | ||
401 | out_free_mmap: | 418 | out_free_mmap: |
402 | free(mmap_event); | 419 | free(mmap_event); |
403 | out_free_comm: | 420 | out_free_comm: |
@@ -412,7 +429,7 @@ struct process_symbol_args { | |||
412 | }; | 429 | }; |
413 | 430 | ||
414 | static int find_symbol_cb(void *arg, const char *name, char type, | 431 | static int find_symbol_cb(void *arg, const char *name, char type, |
415 | u64 start, u64 end __used) | 432 | u64 start) |
416 | { | 433 | { |
417 | struct process_symbol_args *args = arg; | 434 | struct process_symbol_args *args = arg; |
418 | 435 | ||
@@ -477,7 +494,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
477 | map = machine->vmlinux_maps[MAP__FUNCTION]; | 494 | map = machine->vmlinux_maps[MAP__FUNCTION]; |
478 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), | 495 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), |
479 | "%s%s", mmap_name, symbol_name) + 1; | 496 | "%s%s", mmap_name, symbol_name) + 1; |
480 | size = ALIGN(size, sizeof(u64)); | 497 | size = PERF_ALIGN(size, sizeof(u64)); |
481 | event->mmap.header.type = PERF_RECORD_MMAP; | 498 | event->mmap.header.type = PERF_RECORD_MMAP; |
482 | event->mmap.header.size = (sizeof(event->mmap) - | 499 | event->mmap.header.size = (sizeof(event->mmap) - |
483 | (sizeof(event->mmap.filename) - size) + machine->id_hdr_size); | 500 | (sizeof(event->mmap.filename) - size) + machine->id_hdr_size); |
@@ -497,9 +514,9 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) | |||
497 | return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid); | 514 | return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid); |
498 | } | 515 | } |
499 | 516 | ||
500 | int perf_event__process_comm(struct perf_tool *tool __used, | 517 | int perf_event__process_comm(struct perf_tool *tool __maybe_unused, |
501 | union perf_event *event, | 518 | union perf_event *event, |
502 | struct perf_sample *sample __used, | 519 | struct perf_sample *sample __maybe_unused, |
503 | struct machine *machine) | 520 | struct machine *machine) |
504 | { | 521 | { |
505 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | 522 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); |
@@ -515,10 +532,10 @@ int perf_event__process_comm(struct perf_tool *tool __used, | |||
515 | return 0; | 532 | return 0; |
516 | } | 533 | } |
517 | 534 | ||
518 | int perf_event__process_lost(struct perf_tool *tool __used, | 535 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
519 | union perf_event *event, | 536 | union perf_event *event, |
520 | struct perf_sample *sample __used, | 537 | struct perf_sample *sample __maybe_unused, |
521 | struct machine *machine __used) | 538 | struct machine *machine __maybe_unused) |
522 | { | 539 | { |
523 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | 540 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", |
524 | event->lost.id, event->lost.lost); | 541 | event->lost.id, event->lost.lost); |
@@ -538,7 +555,8 @@ static void perf_event__set_kernel_mmap_len(union perf_event *event, | |||
538 | maps[MAP__FUNCTION]->end = ~0ULL; | 555 | maps[MAP__FUNCTION]->end = ~0ULL; |
539 | } | 556 | } |
540 | 557 | ||
541 | static int perf_event__process_kernel_mmap(struct perf_tool *tool __used, | 558 | static int perf_event__process_kernel_mmap(struct perf_tool *tool |
559 | __maybe_unused, | ||
542 | union perf_event *event, | 560 | union perf_event *event, |
543 | struct machine *machine) | 561 | struct machine *machine) |
544 | { | 562 | { |
@@ -640,7 +658,7 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | |||
640 | 658 | ||
641 | int perf_event__process_mmap(struct perf_tool *tool, | 659 | int perf_event__process_mmap(struct perf_tool *tool, |
642 | union perf_event *event, | 660 | union perf_event *event, |
643 | struct perf_sample *sample __used, | 661 | struct perf_sample *sample __maybe_unused, |
644 | struct machine *machine) | 662 | struct machine *machine) |
645 | { | 663 | { |
646 | struct thread *thread; | 664 | struct thread *thread; |
@@ -684,9 +702,9 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | |||
684 | event->fork.ppid, event->fork.ptid); | 702 | event->fork.ppid, event->fork.ptid); |
685 | } | 703 | } |
686 | 704 | ||
687 | int perf_event__process_task(struct perf_tool *tool __used, | 705 | int perf_event__process_task(struct perf_tool *tool __maybe_unused, |
688 | union perf_event *event, | 706 | union perf_event *event, |
689 | struct perf_sample *sample __used, | 707 | struct perf_sample *sample __maybe_unused, |
690 | struct machine *machine) | 708 | struct machine *machine) |
691 | { | 709 | { |
692 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 710 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); |
@@ -886,8 +904,9 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
886 | al->sym = map__find_symbol(al->map, al->addr, filter); | 904 | al->sym = map__find_symbol(al->map, al->addr, filter); |
887 | } | 905 | } |
888 | 906 | ||
889 | if (symbol_conf.sym_list && al->sym && | 907 | if (symbol_conf.sym_list && |
890 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | 908 | (!al->sym || !strlist__has_entry(symbol_conf.sym_list, |
909 | al->sym->name))) | ||
891 | goto out_filtered; | 910 | goto out_filtered; |
892 | 911 | ||
893 | return 0; | 912 | return 0; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index d84870b06426..21b99e741a87 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -69,6 +69,16 @@ struct sample_event { | |||
69 | u64 array[]; | 69 | u64 array[]; |
70 | }; | 70 | }; |
71 | 71 | ||
72 | struct regs_dump { | ||
73 | u64 *regs; | ||
74 | }; | ||
75 | |||
76 | struct stack_dump { | ||
77 | u16 offset; | ||
78 | u64 size; | ||
79 | char *data; | ||
80 | }; | ||
81 | |||
72 | struct perf_sample { | 82 | struct perf_sample { |
73 | u64 ip; | 83 | u64 ip; |
74 | u32 pid, tid; | 84 | u32 pid, tid; |
@@ -82,6 +92,8 @@ struct perf_sample { | |||
82 | void *raw_data; | 92 | void *raw_data; |
83 | struct ip_callchain *callchain; | 93 | struct ip_callchain *callchain; |
84 | struct branch_stack *branch_stack; | 94 | struct branch_stack *branch_stack; |
95 | struct regs_dump user_regs; | ||
96 | struct stack_dump user_stack; | ||
85 | }; | 97 | }; |
86 | 98 | ||
87 | #define BUILD_ID_SIZE 20 | 99 | #define BUILD_ID_SIZE 20 |
@@ -89,7 +101,7 @@ struct perf_sample { | |||
89 | struct build_id_event { | 101 | struct build_id_event { |
90 | struct perf_event_header header; | 102 | struct perf_event_header header; |
91 | pid_t pid; | 103 | pid_t pid; |
92 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | 104 | u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))]; |
93 | char filename[]; | 105 | char filename[]; |
94 | }; | 106 | }; |
95 | 107 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 9b38681add9e..186b87730396 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -57,7 +57,7 @@ void perf_evlist__config_attrs(struct perf_evlist *evlist, | |||
57 | if (evlist->cpus->map[0] < 0) | 57 | if (evlist->cpus->map[0] < 0) |
58 | opts->no_inherit = true; | 58 | opts->no_inherit = true; |
59 | 59 | ||
60 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 60 | first = perf_evlist__first(evlist); |
61 | 61 | ||
62 | list_for_each_entry(evsel, &evlist->entries, node) { | 62 | list_for_each_entry(evsel, &evlist->entries, node) { |
63 | perf_evsel__config(evsel, opts, first); | 63 | perf_evsel__config(evsel, opts, first); |
@@ -108,6 +108,25 @@ void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | |||
108 | evlist->nr_entries += nr_entries; | 108 | evlist->nr_entries += nr_entries; |
109 | } | 109 | } |
110 | 110 | ||
111 | void __perf_evlist__set_leader(struct list_head *list) | ||
112 | { | ||
113 | struct perf_evsel *evsel, *leader; | ||
114 | |||
115 | leader = list_entry(list->next, struct perf_evsel, node); | ||
116 | leader->leader = NULL; | ||
117 | |||
118 | list_for_each_entry(evsel, list, node) { | ||
119 | if (evsel != leader) | ||
120 | evsel->leader = leader; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | void perf_evlist__set_leader(struct perf_evlist *evlist) | ||
125 | { | ||
126 | if (evlist->nr_entries) | ||
127 | __perf_evlist__set_leader(&evlist->entries); | ||
128 | } | ||
129 | |||
111 | int perf_evlist__add_default(struct perf_evlist *evlist) | 130 | int perf_evlist__add_default(struct perf_evlist *evlist) |
112 | { | 131 | { |
113 | struct perf_event_attr attr = { | 132 | struct perf_event_attr attr = { |
@@ -135,8 +154,8 @@ error: | |||
135 | return -ENOMEM; | 154 | return -ENOMEM; |
136 | } | 155 | } |
137 | 156 | ||
138 | int perf_evlist__add_attrs(struct perf_evlist *evlist, | 157 | static int perf_evlist__add_attrs(struct perf_evlist *evlist, |
139 | struct perf_event_attr *attrs, size_t nr_attrs) | 158 | struct perf_event_attr *attrs, size_t nr_attrs) |
140 | { | 159 | { |
141 | struct perf_evsel *evsel, *n; | 160 | struct perf_evsel *evsel, *n; |
142 | LIST_HEAD(head); | 161 | LIST_HEAD(head); |
@@ -170,60 +189,6 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, | |||
170 | return perf_evlist__add_attrs(evlist, attrs, nr_attrs); | 189 | return perf_evlist__add_attrs(evlist, attrs, nr_attrs); |
171 | } | 190 | } |
172 | 191 | ||
173 | static int trace_event__id(const char *evname) | ||
174 | { | ||
175 | char *filename, *colon; | ||
176 | int err = -1, fd; | ||
177 | |||
178 | if (asprintf(&filename, "%s/%s/id", tracing_events_path, evname) < 0) | ||
179 | return -1; | ||
180 | |||
181 | colon = strrchr(filename, ':'); | ||
182 | if (colon != NULL) | ||
183 | *colon = '/'; | ||
184 | |||
185 | fd = open(filename, O_RDONLY); | ||
186 | if (fd >= 0) { | ||
187 | char id[16]; | ||
188 | if (read(fd, id, sizeof(id)) > 0) | ||
189 | err = atoi(id); | ||
190 | close(fd); | ||
191 | } | ||
192 | |||
193 | free(filename); | ||
194 | return err; | ||
195 | } | ||
196 | |||
197 | int perf_evlist__add_tracepoints(struct perf_evlist *evlist, | ||
198 | const char *tracepoints[], | ||
199 | size_t nr_tracepoints) | ||
200 | { | ||
201 | int err; | ||
202 | size_t i; | ||
203 | struct perf_event_attr *attrs = zalloc(nr_tracepoints * sizeof(*attrs)); | ||
204 | |||
205 | if (attrs == NULL) | ||
206 | return -1; | ||
207 | |||
208 | for (i = 0; i < nr_tracepoints; i++) { | ||
209 | err = trace_event__id(tracepoints[i]); | ||
210 | |||
211 | if (err < 0) | ||
212 | goto out_free_attrs; | ||
213 | |||
214 | attrs[i].type = PERF_TYPE_TRACEPOINT; | ||
215 | attrs[i].config = err; | ||
216 | attrs[i].sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | | ||
217 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD); | ||
218 | attrs[i].sample_period = 1; | ||
219 | } | ||
220 | |||
221 | err = perf_evlist__add_attrs(evlist, attrs, nr_tracepoints); | ||
222 | out_free_attrs: | ||
223 | free(attrs); | ||
224 | return err; | ||
225 | } | ||
226 | |||
227 | struct perf_evsel * | 192 | struct perf_evsel * |
228 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) | 193 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) |
229 | { | 194 | { |
@@ -238,32 +203,18 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) | |||
238 | return NULL; | 203 | return NULL; |
239 | } | 204 | } |
240 | 205 | ||
241 | int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, | 206 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
242 | const struct perf_evsel_str_handler *assocs, | 207 | const char *sys, const char *name, void *handler) |
243 | size_t nr_assocs) | ||
244 | { | 208 | { |
245 | struct perf_evsel *evsel; | 209 | struct perf_evsel *evsel; |
246 | int err; | ||
247 | size_t i; | ||
248 | |||
249 | for (i = 0; i < nr_assocs; i++) { | ||
250 | err = trace_event__id(assocs[i].name); | ||
251 | if (err < 0) | ||
252 | goto out; | ||
253 | 210 | ||
254 | evsel = perf_evlist__find_tracepoint_by_id(evlist, err); | 211 | evsel = perf_evsel__newtp(sys, name, evlist->nr_entries); |
255 | if (evsel == NULL) | 212 | if (evsel == NULL) |
256 | continue; | 213 | return -1; |
257 | |||
258 | err = -EEXIST; | ||
259 | if (evsel->handler.func != NULL) | ||
260 | goto out; | ||
261 | evsel->handler.func = assocs[i].handler; | ||
262 | } | ||
263 | 214 | ||
264 | err = 0; | 215 | evsel->handler.func = handler; |
265 | out: | 216 | perf_evlist__add(evlist, evsel); |
266 | return err; | 217 | return 0; |
267 | } | 218 | } |
268 | 219 | ||
269 | void perf_evlist__disable(struct perf_evlist *evlist) | 220 | void perf_evlist__disable(struct perf_evlist *evlist) |
@@ -285,7 +236,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
285 | int cpu, thread; | 236 | int cpu, thread; |
286 | struct perf_evsel *pos; | 237 | struct perf_evsel *pos; |
287 | 238 | ||
288 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 239 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { |
289 | list_for_each_entry(pos, &evlist->entries, node) { | 240 | list_for_each_entry(pos, &evlist->entries, node) { |
290 | for (thread = 0; thread < evlist->threads->nr; thread++) | 241 | for (thread = 0; thread < evlist->threads->nr; thread++) |
291 | ioctl(FD(pos, cpu, thread), | 242 | ioctl(FD(pos, cpu, thread), |
@@ -296,7 +247,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
296 | 247 | ||
297 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 248 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
298 | { | 249 | { |
299 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | 250 | int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries; |
300 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 251 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
301 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 252 | return evlist->pollfd != NULL ? 0 : -ENOMEM; |
302 | } | 253 | } |
@@ -357,7 +308,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
357 | int hash; | 308 | int hash; |
358 | 309 | ||
359 | if (evlist->nr_entries == 1) | 310 | if (evlist->nr_entries == 1) |
360 | return list_entry(evlist->entries.next, struct perf_evsel, node); | 311 | return perf_evlist__first(evlist); |
361 | 312 | ||
362 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 313 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
363 | head = &evlist->heads[hash]; | 314 | head = &evlist->heads[hash]; |
@@ -367,7 +318,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
367 | return sid->evsel; | 318 | return sid->evsel; |
368 | 319 | ||
369 | if (!perf_evlist__sample_id_all(evlist)) | 320 | if (!perf_evlist__sample_id_all(evlist)) |
370 | return list_entry(evlist->entries.next, struct perf_evsel, node); | 321 | return perf_evlist__first(evlist); |
371 | 322 | ||
372 | return NULL; | 323 | return NULL; |
373 | } | 324 | } |
@@ -456,8 +407,8 @@ void perf_evlist__munmap(struct perf_evlist *evlist) | |||
456 | 407 | ||
457 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | 408 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) |
458 | { | 409 | { |
459 | evlist->nr_mmaps = evlist->cpus->nr; | 410 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); |
460 | if (evlist->cpus->map[0] == -1) | 411 | if (cpu_map__all(evlist->cpus)) |
461 | evlist->nr_mmaps = evlist->threads->nr; | 412 | evlist->nr_mmaps = evlist->threads->nr; |
462 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 413 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
463 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 414 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
@@ -603,11 +554,11 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
603 | list_for_each_entry(evsel, &evlist->entries, node) { | 554 | list_for_each_entry(evsel, &evlist->entries, node) { |
604 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 555 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
605 | evsel->sample_id == NULL && | 556 | evsel->sample_id == NULL && |
606 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | 557 | perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) |
607 | return -ENOMEM; | 558 | return -ENOMEM; |
608 | } | 559 | } |
609 | 560 | ||
610 | if (evlist->cpus->map[0] == -1) | 561 | if (cpu_map__all(cpus)) |
611 | return perf_evlist__mmap_per_thread(evlist, prot, mask); | 562 | return perf_evlist__mmap_per_thread(evlist, prot, mask); |
612 | 563 | ||
613 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 564 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
@@ -647,39 +598,44 @@ void perf_evlist__delete_maps(struct perf_evlist *evlist) | |||
647 | evlist->threads = NULL; | 598 | evlist->threads = NULL; |
648 | } | 599 | } |
649 | 600 | ||
650 | int perf_evlist__set_filters(struct perf_evlist *evlist) | 601 | int perf_evlist__apply_filters(struct perf_evlist *evlist) |
651 | { | 602 | { |
652 | const struct thread_map *threads = evlist->threads; | ||
653 | const struct cpu_map *cpus = evlist->cpus; | ||
654 | struct perf_evsel *evsel; | 603 | struct perf_evsel *evsel; |
655 | char *filter; | 604 | int err = 0; |
656 | int thread; | 605 | const int ncpus = cpu_map__nr(evlist->cpus), |
657 | int cpu; | 606 | nthreads = evlist->threads->nr; |
658 | int err; | ||
659 | int fd; | ||
660 | 607 | ||
661 | list_for_each_entry(evsel, &evlist->entries, node) { | 608 | list_for_each_entry(evsel, &evlist->entries, node) { |
662 | filter = evsel->filter; | 609 | if (evsel->filter == NULL) |
663 | if (!filter) | ||
664 | continue; | 610 | continue; |
665 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 611 | |
666 | for (thread = 0; thread < threads->nr; thread++) { | 612 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter); |
667 | fd = FD(evsel, cpu, thread); | 613 | if (err) |
668 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | 614 | break; |
669 | if (err) | ||
670 | return err; | ||
671 | } | ||
672 | } | ||
673 | } | 615 | } |
674 | 616 | ||
675 | return 0; | 617 | return err; |
676 | } | 618 | } |
677 | 619 | ||
678 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist) | 620 | int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) |
679 | { | 621 | { |
680 | struct perf_evsel *pos, *first; | 622 | struct perf_evsel *evsel; |
623 | int err = 0; | ||
624 | const int ncpus = cpu_map__nr(evlist->cpus), | ||
625 | nthreads = evlist->threads->nr; | ||
681 | 626 | ||
682 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); | 627 | list_for_each_entry(evsel, &evlist->entries, node) { |
628 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); | ||
629 | if (err) | ||
630 | break; | ||
631 | } | ||
632 | |||
633 | return err; | ||
634 | } | ||
635 | |||
636 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) | ||
637 | { | ||
638 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; | ||
683 | 639 | ||
684 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 640 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
685 | if (first->attr.sample_type != pos->attr.sample_type) | 641 | if (first->attr.sample_type != pos->attr.sample_type) |
@@ -689,23 +645,19 @@ bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist) | |||
689 | return true; | 645 | return true; |
690 | } | 646 | } |
691 | 647 | ||
692 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist) | 648 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) |
693 | { | 649 | { |
694 | struct perf_evsel *first; | 650 | struct perf_evsel *first = perf_evlist__first(evlist); |
695 | |||
696 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
697 | return first->attr.sample_type; | 651 | return first->attr.sample_type; |
698 | } | 652 | } |
699 | 653 | ||
700 | u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist) | 654 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) |
701 | { | 655 | { |
702 | struct perf_evsel *first; | 656 | struct perf_evsel *first = perf_evlist__first(evlist); |
703 | struct perf_sample *data; | 657 | struct perf_sample *data; |
704 | u64 sample_type; | 658 | u64 sample_type; |
705 | u16 size = 0; | 659 | u16 size = 0; |
706 | 660 | ||
707 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
708 | |||
709 | if (!first->attr.sample_id_all) | 661 | if (!first->attr.sample_id_all) |
710 | goto out; | 662 | goto out; |
711 | 663 | ||
@@ -729,11 +681,9 @@ out: | |||
729 | return size; | 681 | return size; |
730 | } | 682 | } |
731 | 683 | ||
732 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) | 684 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist) |
733 | { | 685 | { |
734 | struct perf_evsel *pos, *first; | 686 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; |
735 | |||
736 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
737 | 687 | ||
738 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 688 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
739 | if (first->attr.sample_id_all != pos->attr.sample_id_all) | 689 | if (first->attr.sample_id_all != pos->attr.sample_id_all) |
@@ -743,11 +693,9 @@ bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) | |||
743 | return true; | 693 | return true; |
744 | } | 694 | } |
745 | 695 | ||
746 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | 696 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist) |
747 | { | 697 | { |
748 | struct perf_evsel *first; | 698 | struct perf_evsel *first = perf_evlist__first(evlist); |
749 | |||
750 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
751 | return first->attr.sample_id_all; | 699 | return first->attr.sample_id_all; |
752 | } | 700 | } |
753 | 701 | ||
@@ -757,21 +705,13 @@ void perf_evlist__set_selected(struct perf_evlist *evlist, | |||
757 | evlist->selected = evsel; | 705 | evlist->selected = evsel; |
758 | } | 706 | } |
759 | 707 | ||
760 | int perf_evlist__open(struct perf_evlist *evlist, bool group) | 708 | int perf_evlist__open(struct perf_evlist *evlist) |
761 | { | 709 | { |
762 | struct perf_evsel *evsel, *first; | 710 | struct perf_evsel *evsel; |
763 | int err, ncpus, nthreads; | 711 | int err, ncpus, nthreads; |
764 | 712 | ||
765 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
766 | |||
767 | list_for_each_entry(evsel, &evlist->entries, node) { | 713 | list_for_each_entry(evsel, &evlist->entries, node) { |
768 | struct xyarray *group_fd = NULL; | 714 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); |
769 | |||
770 | if (group && evsel != first) | ||
771 | group_fd = first->fd; | ||
772 | |||
773 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads, | ||
774 | group, group_fd); | ||
775 | if (err < 0) | 715 | if (err < 0) |
776 | goto out_err; | 716 | goto out_err; |
777 | } | 717 | } |
@@ -883,8 +823,21 @@ int perf_evlist__start_workload(struct perf_evlist *evlist) | |||
883 | } | 823 | } |
884 | 824 | ||
885 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 825 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
886 | struct perf_sample *sample, bool swapped) | 826 | struct perf_sample *sample) |
887 | { | 827 | { |
888 | struct perf_evsel *e = list_entry(evlist->entries.next, struct perf_evsel, node); | 828 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
889 | return perf_evsel__parse_sample(e, event, sample, swapped); | 829 | return perf_evsel__parse_sample(evsel, event, sample); |
830 | } | ||
831 | |||
832 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | ||
833 | { | ||
834 | struct perf_evsel *evsel; | ||
835 | size_t printed = 0; | ||
836 | |||
837 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
838 | printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "", | ||
839 | perf_evsel__name(evsel)); | ||
840 | } | ||
841 | |||
842 | return printed + fprintf(fp, "\n");; | ||
890 | } | 843 | } |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 528c1acd9298..56003f779e60 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include "../perf.h" | 6 | #include "../perf.h" |
7 | #include "event.h" | 7 | #include "event.h" |
8 | #include "evsel.h" | ||
8 | #include "util.h" | 9 | #include "util.h" |
9 | #include <unistd.h> | 10 | #include <unistd.h> |
10 | 11 | ||
@@ -41,8 +42,6 @@ struct perf_evsel_str_handler { | |||
41 | void *handler; | 42 | void *handler; |
42 | }; | 43 | }; |
43 | 44 | ||
44 | struct perf_evsel; | ||
45 | |||
46 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 45 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, |
47 | struct thread_map *threads); | 46 | struct thread_map *threads); |
48 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 47 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
@@ -52,26 +51,16 @@ void perf_evlist__delete(struct perf_evlist *evlist); | |||
52 | 51 | ||
53 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | 52 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); |
54 | int perf_evlist__add_default(struct perf_evlist *evlist); | 53 | int perf_evlist__add_default(struct perf_evlist *evlist); |
55 | int perf_evlist__add_attrs(struct perf_evlist *evlist, | ||
56 | struct perf_event_attr *attrs, size_t nr_attrs); | ||
57 | int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, | 54 | int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, |
58 | struct perf_event_attr *attrs, size_t nr_attrs); | 55 | struct perf_event_attr *attrs, size_t nr_attrs); |
59 | int perf_evlist__add_tracepoints(struct perf_evlist *evlist, | 56 | |
60 | const char *tracepoints[], size_t nr_tracepoints); | ||
61 | int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, | ||
62 | const struct perf_evsel_str_handler *assocs, | ||
63 | size_t nr_assocs); | ||
64 | |||
65 | #define perf_evlist__add_attrs_array(evlist, array) \ | ||
66 | perf_evlist__add_attrs(evlist, array, ARRAY_SIZE(array)) | ||
67 | #define perf_evlist__add_default_attrs(evlist, array) \ | 57 | #define perf_evlist__add_default_attrs(evlist, array) \ |
68 | __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array)) | 58 | __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array)) |
69 | 59 | ||
70 | #define perf_evlist__add_tracepoints_array(evlist, array) \ | 60 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
71 | perf_evlist__add_tracepoints(evlist, array, ARRAY_SIZE(array)) | 61 | const char *sys, const char *name, void *handler); |
72 | 62 | ||
73 | #define perf_evlist__set_tracepoints_handlers_array(evlist, array) \ | 63 | int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); |
74 | perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) | ||
75 | 64 | ||
76 | struct perf_evsel * | 65 | struct perf_evsel * |
77 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); | 66 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); |
@@ -85,7 +74,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
85 | 74 | ||
86 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 75 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
87 | 76 | ||
88 | int perf_evlist__open(struct perf_evlist *evlist, bool group); | 77 | int perf_evlist__open(struct perf_evlist *evlist); |
89 | 78 | ||
90 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 79 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
91 | struct perf_record_opts *opts); | 80 | struct perf_record_opts *opts); |
@@ -116,20 +105,34 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
116 | int perf_evlist__create_maps(struct perf_evlist *evlist, | 105 | int perf_evlist__create_maps(struct perf_evlist *evlist, |
117 | struct perf_target *target); | 106 | struct perf_target *target); |
118 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 107 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
119 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 108 | int perf_evlist__apply_filters(struct perf_evlist *evlist); |
109 | |||
110 | void __perf_evlist__set_leader(struct list_head *list); | ||
111 | void perf_evlist__set_leader(struct perf_evlist *evlist); | ||
120 | 112 | ||
121 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist); | 113 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); |
122 | bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist); | 114 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist); |
123 | u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist); | 115 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist); |
124 | 116 | ||
125 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 117 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
126 | struct perf_sample *sample, bool swapped); | 118 | struct perf_sample *sample); |
127 | 119 | ||
128 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); | 120 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist); |
129 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); | 121 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist); |
130 | 122 | ||
131 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 123 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
132 | struct list_head *list, | 124 | struct list_head *list, |
133 | int nr_entries); | 125 | int nr_entries); |
134 | 126 | ||
127 | static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist) | ||
128 | { | ||
129 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
130 | } | ||
131 | |||
132 | static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | ||
133 | { | ||
134 | return list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
135 | } | ||
136 | |||
137 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | ||
135 | #endif /* __PERF_EVLIST_H */ | 138 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2eaae140def2..618d41140abd 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -8,7 +8,10 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <byteswap.h> | 10 | #include <byteswap.h> |
11 | #include <linux/bitops.h> | ||
11 | #include "asm/bug.h" | 12 | #include "asm/bug.h" |
13 | #include "debugfs.h" | ||
14 | #include "event-parse.h" | ||
12 | #include "evsel.h" | 15 | #include "evsel.h" |
13 | #include "evlist.h" | 16 | #include "evlist.h" |
14 | #include "util.h" | 17 | #include "util.h" |
@@ -16,9 +19,10 @@ | |||
16 | #include "thread_map.h" | 19 | #include "thread_map.h" |
17 | #include "target.h" | 20 | #include "target.h" |
18 | #include "../../../include/linux/hw_breakpoint.h" | 21 | #include "../../../include/linux/hw_breakpoint.h" |
22 | #include "../../../include/uapi/linux/perf_event.h" | ||
23 | #include "perf_regs.h" | ||
19 | 24 | ||
20 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 25 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
21 | #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) | ||
22 | 26 | ||
23 | static int __perf_evsel__sample_size(u64 sample_type) | 27 | static int __perf_evsel__sample_size(u64 sample_type) |
24 | { | 28 | { |
@@ -66,7 +70,80 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | |||
66 | return evsel; | 70 | return evsel; |
67 | } | 71 | } |
68 | 72 | ||
69 | static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { | 73 | struct event_format *event_format__new(const char *sys, const char *name) |
74 | { | ||
75 | int fd, n; | ||
76 | char *filename; | ||
77 | void *bf = NULL, *nbf; | ||
78 | size_t size = 0, alloc_size = 0; | ||
79 | struct event_format *format = NULL; | ||
80 | |||
81 | if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) | ||
82 | goto out; | ||
83 | |||
84 | fd = open(filename, O_RDONLY); | ||
85 | if (fd < 0) | ||
86 | goto out_free_filename; | ||
87 | |||
88 | do { | ||
89 | if (size == alloc_size) { | ||
90 | alloc_size += BUFSIZ; | ||
91 | nbf = realloc(bf, alloc_size); | ||
92 | if (nbf == NULL) | ||
93 | goto out_free_bf; | ||
94 | bf = nbf; | ||
95 | } | ||
96 | |||
97 | n = read(fd, bf + size, BUFSIZ); | ||
98 | if (n < 0) | ||
99 | goto out_free_bf; | ||
100 | size += n; | ||
101 | } while (n > 0); | ||
102 | |||
103 | pevent_parse_format(&format, bf, size, sys); | ||
104 | |||
105 | out_free_bf: | ||
106 | free(bf); | ||
107 | close(fd); | ||
108 | out_free_filename: | ||
109 | free(filename); | ||
110 | out: | ||
111 | return format; | ||
112 | } | ||
113 | |||
114 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) | ||
115 | { | ||
116 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | ||
117 | |||
118 | if (evsel != NULL) { | ||
119 | struct perf_event_attr attr = { | ||
120 | .type = PERF_TYPE_TRACEPOINT, | ||
121 | .sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | | ||
122 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD), | ||
123 | }; | ||
124 | |||
125 | if (asprintf(&evsel->name, "%s:%s", sys, name) < 0) | ||
126 | goto out_free; | ||
127 | |||
128 | evsel->tp_format = event_format__new(sys, name); | ||
129 | if (evsel->tp_format == NULL) | ||
130 | goto out_free; | ||
131 | |||
132 | event_attr_init(&attr); | ||
133 | attr.config = evsel->tp_format->id; | ||
134 | attr.sample_period = 1; | ||
135 | perf_evsel__init(evsel, &attr, idx); | ||
136 | } | ||
137 | |||
138 | return evsel; | ||
139 | |||
140 | out_free: | ||
141 | free(evsel->name); | ||
142 | free(evsel); | ||
143 | return NULL; | ||
144 | } | ||
145 | |||
146 | const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { | ||
70 | "cycles", | 147 | "cycles", |
71 | "instructions", | 148 | "instructions", |
72 | "cache-references", | 149 | "cache-references", |
@@ -129,12 +206,12 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) | |||
129 | return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); | 206 | return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); |
130 | } | 207 | } |
131 | 208 | ||
132 | static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { | 209 | const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { |
133 | "cpu-clock", | 210 | "cpu-clock", |
134 | "task-clock", | 211 | "task-clock", |
135 | "page-faults", | 212 | "page-faults", |
136 | "context-switches", | 213 | "context-switches", |
137 | "CPU-migrations", | 214 | "cpu-migrations", |
138 | "minor-faults", | 215 | "minor-faults", |
139 | "major-faults", | 216 | "major-faults", |
140 | "alignment-faults", | 217 | "alignment-faults", |
@@ -317,7 +394,8 @@ const char *perf_evsel__name(struct perf_evsel *evsel) | |||
317 | break; | 394 | break; |
318 | 395 | ||
319 | default: | 396 | default: |
320 | scnprintf(bf, sizeof(bf), "%s", "unknown attr type"); | 397 | scnprintf(bf, sizeof(bf), "unknown attr type: %d", |
398 | evsel->attr.type); | ||
321 | break; | 399 | break; |
322 | } | 400 | } |
323 | 401 | ||
@@ -367,9 +445,18 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
367 | attr->mmap_data = track; | 445 | attr->mmap_data = track; |
368 | } | 446 | } |
369 | 447 | ||
370 | if (opts->call_graph) | 448 | if (opts->call_graph) { |
371 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 449 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
372 | 450 | ||
451 | if (opts->call_graph == CALLCHAIN_DWARF) { | ||
452 | attr->sample_type |= PERF_SAMPLE_REGS_USER | | ||
453 | PERF_SAMPLE_STACK_USER; | ||
454 | attr->sample_regs_user = PERF_REGS_MASK; | ||
455 | attr->sample_stack_user = opts->stack_dump_size; | ||
456 | attr->exclude_callchain_user = 1; | ||
457 | } | ||
458 | } | ||
459 | |||
373 | if (perf_target__has_cpu(&opts->target)) | 460 | if (perf_target__has_cpu(&opts->target)) |
374 | attr->sample_type |= PERF_SAMPLE_CPU; | 461 | attr->sample_type |= PERF_SAMPLE_CPU; |
375 | 462 | ||
@@ -421,6 +508,24 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
421 | return evsel->fd != NULL ? 0 : -ENOMEM; | 508 | return evsel->fd != NULL ? 0 : -ENOMEM; |
422 | } | 509 | } |
423 | 510 | ||
511 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | ||
512 | const char *filter) | ||
513 | { | ||
514 | int cpu, thread; | ||
515 | |||
516 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
517 | for (thread = 0; thread < nthreads; thread++) { | ||
518 | int fd = FD(evsel, cpu, thread), | ||
519 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | ||
520 | |||
521 | if (err) | ||
522 | return err; | ||
523 | } | ||
524 | } | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | |||
424 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 529 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
425 | { | 530 | { |
426 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 531 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
@@ -481,6 +586,9 @@ void perf_evsel__delete(struct perf_evsel *evsel) | |||
481 | { | 586 | { |
482 | perf_evsel__exit(evsel); | 587 | perf_evsel__exit(evsel); |
483 | close_cgroup(evsel->cgrp); | 588 | close_cgroup(evsel->cgrp); |
589 | free(evsel->group_name); | ||
590 | if (evsel->tp_format) | ||
591 | pevent_free_format(evsel->tp_format); | ||
484 | free(evsel->name); | 592 | free(evsel->name); |
485 | free(evsel); | 593 | free(evsel); |
486 | } | 594 | } |
@@ -556,9 +664,28 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
556 | return 0; | 664 | return 0; |
557 | } | 665 | } |
558 | 666 | ||
667 | static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | ||
668 | { | ||
669 | struct perf_evsel *leader = evsel->leader; | ||
670 | int fd; | ||
671 | |||
672 | if (!leader) | ||
673 | return -1; | ||
674 | |||
675 | /* | ||
676 | * Leader must be already processed/open, | ||
677 | * if not it's a bug. | ||
678 | */ | ||
679 | BUG_ON(!leader->fd); | ||
680 | |||
681 | fd = FD(leader, cpu, thread); | ||
682 | BUG_ON(fd == -1); | ||
683 | |||
684 | return fd; | ||
685 | } | ||
686 | |||
559 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 687 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
560 | struct thread_map *threads, bool group, | 688 | struct thread_map *threads) |
561 | struct xyarray *group_fds) | ||
562 | { | 689 | { |
563 | int cpu, thread; | 690 | int cpu, thread; |
564 | unsigned long flags = 0; | 691 | unsigned long flags = 0; |
@@ -574,13 +701,15 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
574 | } | 701 | } |
575 | 702 | ||
576 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 703 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
577 | int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1; | ||
578 | 704 | ||
579 | for (thread = 0; thread < threads->nr; thread++) { | 705 | for (thread = 0; thread < threads->nr; thread++) { |
706 | int group_fd; | ||
580 | 707 | ||
581 | if (!evsel->cgrp) | 708 | if (!evsel->cgrp) |
582 | pid = threads->map[thread]; | 709 | pid = threads->map[thread]; |
583 | 710 | ||
711 | group_fd = get_group_fd(evsel, cpu, thread); | ||
712 | |||
584 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 713 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
585 | pid, | 714 | pid, |
586 | cpus->map[cpu], | 715 | cpus->map[cpu], |
@@ -589,9 +718,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
589 | err = -errno; | 718 | err = -errno; |
590 | goto out_close; | 719 | goto out_close; |
591 | } | 720 | } |
592 | |||
593 | if (group && group_fd == -1) | ||
594 | group_fd = FD(evsel, cpu, thread); | ||
595 | } | 721 | } |
596 | } | 722 | } |
597 | 723 | ||
@@ -635,8 +761,7 @@ static struct { | |||
635 | }; | 761 | }; |
636 | 762 | ||
637 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 763 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
638 | struct thread_map *threads, bool group, | 764 | struct thread_map *threads) |
639 | struct xyarray *group_fd) | ||
640 | { | 765 | { |
641 | if (cpus == NULL) { | 766 | if (cpus == NULL) { |
642 | /* Work around old compiler warnings about strict aliasing */ | 767 | /* Work around old compiler warnings about strict aliasing */ |
@@ -646,30 +771,28 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
646 | if (threads == NULL) | 771 | if (threads == NULL) |
647 | threads = &empty_thread_map.map; | 772 | threads = &empty_thread_map.map; |
648 | 773 | ||
649 | return __perf_evsel__open(evsel, cpus, threads, group, group_fd); | 774 | return __perf_evsel__open(evsel, cpus, threads); |
650 | } | 775 | } |
651 | 776 | ||
652 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 777 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
653 | struct cpu_map *cpus, bool group, | 778 | struct cpu_map *cpus) |
654 | struct xyarray *group_fd) | ||
655 | { | 779 | { |
656 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, | 780 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); |
657 | group_fd); | ||
658 | } | 781 | } |
659 | 782 | ||
660 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 783 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
661 | struct thread_map *threads, bool group, | 784 | struct thread_map *threads) |
662 | struct xyarray *group_fd) | ||
663 | { | 785 | { |
664 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, | 786 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); |
665 | group_fd); | ||
666 | } | 787 | } |
667 | 788 | ||
668 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | 789 | static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, |
669 | struct perf_sample *sample, | 790 | const union perf_event *event, |
670 | bool swapped) | 791 | struct perf_sample *sample) |
671 | { | 792 | { |
793 | u64 type = evsel->attr.sample_type; | ||
672 | const u64 *array = event->sample.array; | 794 | const u64 *array = event->sample.array; |
795 | bool swapped = evsel->needs_swap; | ||
673 | union u64_swap u; | 796 | union u64_swap u; |
674 | 797 | ||
675 | array += ((event->header.size - | 798 | array += ((event->header.size - |
@@ -730,9 +853,11 @@ static bool sample_overlap(const union perf_event *event, | |||
730 | } | 853 | } |
731 | 854 | ||
732 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 855 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
733 | struct perf_sample *data, bool swapped) | 856 | struct perf_sample *data) |
734 | { | 857 | { |
735 | u64 type = evsel->attr.sample_type; | 858 | u64 type = evsel->attr.sample_type; |
859 | u64 regs_user = evsel->attr.sample_regs_user; | ||
860 | bool swapped = evsel->needs_swap; | ||
736 | const u64 *array; | 861 | const u64 *array; |
737 | 862 | ||
738 | /* | 863 | /* |
@@ -749,7 +874,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
749 | if (event->header.type != PERF_RECORD_SAMPLE) { | 874 | if (event->header.type != PERF_RECORD_SAMPLE) { |
750 | if (!evsel->attr.sample_id_all) | 875 | if (!evsel->attr.sample_id_all) |
751 | return 0; | 876 | return 0; |
752 | return perf_event__parse_id_sample(event, type, data, swapped); | 877 | return perf_evsel__parse_id_sample(evsel, event, data); |
753 | } | 878 | } |
754 | 879 | ||
755 | array = event->sample.array; | 880 | array = event->sample.array; |
@@ -869,6 +994,32 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
869 | sz /= sizeof(u64); | 994 | sz /= sizeof(u64); |
870 | array += sz; | 995 | array += sz; |
871 | } | 996 | } |
997 | |||
998 | if (type & PERF_SAMPLE_REGS_USER) { | ||
999 | /* First u64 tells us if we have any regs in sample. */ | ||
1000 | u64 avail = *array++; | ||
1001 | |||
1002 | if (avail) { | ||
1003 | data->user_regs.regs = (u64 *)array; | ||
1004 | array += hweight_long(regs_user); | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | if (type & PERF_SAMPLE_STACK_USER) { | ||
1009 | u64 size = *array++; | ||
1010 | |||
1011 | data->user_stack.offset = ((char *)(array - 1) | ||
1012 | - (char *) event); | ||
1013 | |||
1014 | if (!size) { | ||
1015 | data->user_stack.size = 0; | ||
1016 | } else { | ||
1017 | data->user_stack.data = (char *)array; | ||
1018 | array += size / sizeof(*array); | ||
1019 | data->user_stack.size = *array; | ||
1020 | } | ||
1021 | } | ||
1022 | |||
872 | return 0; | 1023 | return 0; |
873 | } | 1024 | } |
874 | 1025 | ||
@@ -947,3 +1098,72 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
947 | 1098 | ||
948 | return 0; | 1099 | return 0; |
949 | } | 1100 | } |
1101 | |||
1102 | struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name) | ||
1103 | { | ||
1104 | return pevent_find_field(evsel->tp_format, name); | ||
1105 | } | ||
1106 | |||
1107 | void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, | ||
1108 | const char *name) | ||
1109 | { | ||
1110 | struct format_field *field = perf_evsel__field(evsel, name); | ||
1111 | int offset; | ||
1112 | |||
1113 | if (!field) | ||
1114 | return NULL; | ||
1115 | |||
1116 | offset = field->offset; | ||
1117 | |||
1118 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
1119 | offset = *(int *)(sample->raw_data + field->offset); | ||
1120 | offset &= 0xffff; | ||
1121 | } | ||
1122 | |||
1123 | return sample->raw_data + offset; | ||
1124 | } | ||
1125 | |||
1126 | u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | ||
1127 | const char *name) | ||
1128 | { | ||
1129 | struct format_field *field = perf_evsel__field(evsel, name); | ||
1130 | void *ptr; | ||
1131 | u64 value; | ||
1132 | |||
1133 | if (!field) | ||
1134 | return 0; | ||
1135 | |||
1136 | ptr = sample->raw_data + field->offset; | ||
1137 | |||
1138 | switch (field->size) { | ||
1139 | case 1: | ||
1140 | return *(u8 *)ptr; | ||
1141 | case 2: | ||
1142 | value = *(u16 *)ptr; | ||
1143 | break; | ||
1144 | case 4: | ||
1145 | value = *(u32 *)ptr; | ||
1146 | break; | ||
1147 | case 8: | ||
1148 | value = *(u64 *)ptr; | ||
1149 | break; | ||
1150 | default: | ||
1151 | return 0; | ||
1152 | } | ||
1153 | |||
1154 | if (!evsel->needs_swap) | ||
1155 | return value; | ||
1156 | |||
1157 | switch (field->size) { | ||
1158 | case 2: | ||
1159 | return bswap_16(value); | ||
1160 | case 4: | ||
1161 | return bswap_32(value); | ||
1162 | case 8: | ||
1163 | return bswap_64(value); | ||
1164 | default: | ||
1165 | return 0; | ||
1166 | } | ||
1167 | |||
1168 | return 0; | ||
1169 | } | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b559929983bb..6f94d6dea00f 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include "../../../include/linux/perf_event.h" | 6 | #include "../../../include/uapi/linux/perf_event.h" |
7 | #include "types.h" | 7 | #include "types.h" |
8 | #include "xyarray.h" | 8 | #include "xyarray.h" |
9 | #include "cgroup.h" | 9 | #include "cgroup.h" |
@@ -53,9 +53,10 @@ struct perf_evsel { | |||
53 | u64 *id; | 53 | u64 *id; |
54 | struct perf_counts *counts; | 54 | struct perf_counts *counts; |
55 | int idx; | 55 | int idx; |
56 | int ids; | 56 | u32 ids; |
57 | struct hists hists; | 57 | struct hists hists; |
58 | char *name; | 58 | char *name; |
59 | struct event_format *tp_format; | ||
59 | union { | 60 | union { |
60 | void *priv; | 61 | void *priv; |
61 | off_t id_offset; | 62 | off_t id_offset; |
@@ -65,8 +66,14 @@ struct perf_evsel { | |||
65 | void *func; | 66 | void *func; |
66 | void *data; | 67 | void *data; |
67 | } handler; | 68 | } handler; |
69 | struct cpu_map *cpus; | ||
68 | unsigned int sample_size; | 70 | unsigned int sample_size; |
69 | bool supported; | 71 | bool supported; |
72 | bool needs_swap; | ||
73 | /* parse modifier helper */ | ||
74 | int exclude_GH; | ||
75 | struct perf_evsel *leader; | ||
76 | char *group_name; | ||
70 | }; | 77 | }; |
71 | 78 | ||
72 | struct cpu_map; | 79 | struct cpu_map; |
@@ -75,6 +82,10 @@ struct perf_evlist; | |||
75 | struct perf_record_opts; | 82 | struct perf_record_opts; |
76 | 83 | ||
77 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 84 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); |
85 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); | ||
86 | |||
87 | struct event_format *event_format__new(const char *sys, const char *name); | ||
88 | |||
78 | void perf_evsel__init(struct perf_evsel *evsel, | 89 | void perf_evsel__init(struct perf_evsel *evsel, |
79 | struct perf_event_attr *attr, int idx); | 90 | struct perf_event_attr *attr, int idx); |
80 | void perf_evsel__exit(struct perf_evsel *evsel); | 91 | void perf_evsel__exit(struct perf_evsel *evsel); |
@@ -92,8 +103,10 @@ extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] | |||
92 | [PERF_EVSEL__MAX_ALIASES]; | 103 | [PERF_EVSEL__MAX_ALIASES]; |
93 | extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] | 104 | extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] |
94 | [PERF_EVSEL__MAX_ALIASES]; | 105 | [PERF_EVSEL__MAX_ALIASES]; |
95 | const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] | 106 | extern const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] |
96 | [PERF_EVSEL__MAX_ALIASES]; | 107 | [PERF_EVSEL__MAX_ALIASES]; |
108 | extern const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX]; | ||
109 | extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; | ||
97 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | 110 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, |
98 | char *bf, size_t size); | 111 | char *bf, size_t size); |
99 | const char *perf_evsel__name(struct perf_evsel *evsel); | 112 | const char *perf_evsel__name(struct perf_evsel *evsel); |
@@ -105,21 +118,46 @@ void perf_evsel__free_fd(struct perf_evsel *evsel); | |||
105 | void perf_evsel__free_id(struct perf_evsel *evsel); | 118 | void perf_evsel__free_id(struct perf_evsel *evsel); |
106 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 119 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
107 | 120 | ||
121 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | ||
122 | const char *filter); | ||
123 | |||
108 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 124 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
109 | struct cpu_map *cpus, bool group, | 125 | struct cpu_map *cpus); |
110 | struct xyarray *group_fds); | ||
111 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 126 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
112 | struct thread_map *threads, bool group, | 127 | struct thread_map *threads); |
113 | struct xyarray *group_fds); | ||
114 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 128 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
115 | struct thread_map *threads, bool group, | 129 | struct thread_map *threads); |
116 | struct xyarray *group_fds); | ||
117 | void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads); | 130 | void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads); |
118 | 131 | ||
132 | struct perf_sample; | ||
133 | |||
134 | void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, | ||
135 | const char *name); | ||
136 | u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | ||
137 | const char *name); | ||
138 | |||
139 | static inline char *perf_evsel__strval(struct perf_evsel *evsel, | ||
140 | struct perf_sample *sample, | ||
141 | const char *name) | ||
142 | { | ||
143 | return perf_evsel__rawptr(evsel, sample, name); | ||
144 | } | ||
145 | |||
146 | struct format_field; | ||
147 | |||
148 | struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name); | ||
149 | |||
119 | #define perf_evsel__match(evsel, t, c) \ | 150 | #define perf_evsel__match(evsel, t, c) \ |
120 | (evsel->attr.type == PERF_TYPE_##t && \ | 151 | (evsel->attr.type == PERF_TYPE_##t && \ |
121 | evsel->attr.config == PERF_COUNT_##c) | 152 | evsel->attr.config == PERF_COUNT_##c) |
122 | 153 | ||
154 | static inline bool perf_evsel__match2(struct perf_evsel *e1, | ||
155 | struct perf_evsel *e2) | ||
156 | { | ||
157 | return (e1->attr.type == e2->attr.type) && | ||
158 | (e1->attr.config == e2->attr.config); | ||
159 | } | ||
160 | |||
123 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 161 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
124 | int cpu, int thread, bool scale); | 162 | int cpu, int thread, bool scale); |
125 | 163 | ||
@@ -181,5 +219,10 @@ static inline int perf_evsel__read_scaled(struct perf_evsel *evsel, | |||
181 | void hists__init(struct hists *hists); | 219 | void hists__init(struct hists *hists); |
182 | 220 | ||
183 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 221 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
184 | struct perf_sample *sample, bool swapped); | 222 | struct perf_sample *sample); |
223 | |||
224 | static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | ||
225 | { | ||
226 | return list_entry(evsel->node.next, struct perf_evsel, node); | ||
227 | } | ||
185 | #endif /* __PERF_EVSEL_H */ | 228 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index f06f6fd148f8..3ac38031d534 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh | |||
@@ -21,4 +21,19 @@ do | |||
21 | p | 21 | p |
22 | }' "Documentation/perf-$cmd.txt" | 22 | }' "Documentation/perf-$cmd.txt" |
23 | done | 23 | done |
24 | |||
25 | echo "#ifdef LIBELF_SUPPORT" | ||
26 | sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt | | ||
27 | sort | | ||
28 | while read cmd | ||
29 | do | ||
30 | sed -n ' | ||
31 | /^NAME/,/perf-'"$cmd"'/H | ||
32 | ${ | ||
33 | x | ||
34 | s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/ | ||
35 | p | ||
36 | }' "Documentation/perf-$cmd.txt" | ||
37 | done | ||
38 | echo "#endif /* LIBELF_SUPPORT */" | ||
24 | echo "};" | 39 | echo "};" |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 74ea3c2f8138..7daad237dea5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -20,11 +20,14 @@ | |||
20 | #include "symbol.h" | 20 | #include "symbol.h" |
21 | #include "debug.h" | 21 | #include "debug.h" |
22 | #include "cpumap.h" | 22 | #include "cpumap.h" |
23 | #include "pmu.h" | ||
24 | #include "vdso.h" | ||
25 | #include "strbuf.h" | ||
23 | 26 | ||
24 | static bool no_buildid_cache = false; | 27 | static bool no_buildid_cache = false; |
25 | 28 | ||
26 | static int event_count; | 29 | static int trace_event_count; |
27 | static struct perf_trace_event_type *events; | 30 | static struct perf_trace_event_type *trace_events; |
28 | 31 | ||
29 | static u32 header_argc; | 32 | static u32 header_argc; |
30 | static const char **header_argv; | 33 | static const char **header_argv; |
@@ -36,24 +39,24 @@ int perf_header__push_event(u64 id, const char *name) | |||
36 | if (strlen(name) > MAX_EVENT_NAME) | 39 | if (strlen(name) > MAX_EVENT_NAME) |
37 | pr_warning("Event %s will be truncated\n", name); | 40 | pr_warning("Event %s will be truncated\n", name); |
38 | 41 | ||
39 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); | 42 | nevents = realloc(trace_events, (trace_event_count + 1) * sizeof(*trace_events)); |
40 | if (nevents == NULL) | 43 | if (nevents == NULL) |
41 | return -ENOMEM; | 44 | return -ENOMEM; |
42 | events = nevents; | 45 | trace_events = nevents; |
43 | 46 | ||
44 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 47 | memset(&trace_events[trace_event_count], 0, sizeof(struct perf_trace_event_type)); |
45 | events[event_count].event_id = id; | 48 | trace_events[trace_event_count].event_id = id; |
46 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 49 | strncpy(trace_events[trace_event_count].name, name, MAX_EVENT_NAME - 1); |
47 | event_count++; | 50 | trace_event_count++; |
48 | return 0; | 51 | return 0; |
49 | } | 52 | } |
50 | 53 | ||
51 | char *perf_header__find_event(u64 id) | 54 | char *perf_header__find_event(u64 id) |
52 | { | 55 | { |
53 | int i; | 56 | int i; |
54 | for (i = 0 ; i < event_count; i++) { | 57 | for (i = 0 ; i < trace_event_count; i++) { |
55 | if (events[i].event_id == id) | 58 | if (trace_events[i].event_id == id) |
56 | return events[i].name; | 59 | return trace_events[i].name; |
57 | } | 60 | } |
58 | return NULL; | 61 | return NULL; |
59 | } | 62 | } |
@@ -128,7 +131,7 @@ static int do_write_string(int fd, const char *str) | |||
128 | int ret; | 131 | int ret; |
129 | 132 | ||
130 | olen = strlen(str) + 1; | 133 | olen = strlen(str) + 1; |
131 | len = ALIGN(olen, NAME_ALIGN); | 134 | len = PERF_ALIGN(olen, NAME_ALIGN); |
132 | 135 | ||
133 | /* write len, incl. \0 */ | 136 | /* write len, incl. \0 */ |
134 | ret = do_write(fd, &len, sizeof(len)); | 137 | ret = do_write(fd, &len, sizeof(len)); |
@@ -206,6 +209,29 @@ perf_header__set_cmdline(int argc, const char **argv) | |||
206 | continue; \ | 209 | continue; \ |
207 | else | 210 | else |
208 | 211 | ||
212 | static int write_buildid(char *name, size_t name_len, u8 *build_id, | ||
213 | pid_t pid, u16 misc, int fd) | ||
214 | { | ||
215 | int err; | ||
216 | struct build_id_event b; | ||
217 | size_t len; | ||
218 | |||
219 | len = name_len + 1; | ||
220 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
221 | |||
222 | memset(&b, 0, sizeof(b)); | ||
223 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
224 | b.pid = pid; | ||
225 | b.header.misc = misc; | ||
226 | b.header.size = sizeof(b) + len; | ||
227 | |||
228 | err = do_write(fd, &b, sizeof(b)); | ||
229 | if (err < 0) | ||
230 | return err; | ||
231 | |||
232 | return write_padded(fd, name, name_len + 1, len); | ||
233 | } | ||
234 | |||
209 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | 235 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, |
210 | u16 misc, int fd) | 236 | u16 misc, int fd) |
211 | { | 237 | { |
@@ -213,24 +239,23 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | |||
213 | 239 | ||
214 | dsos__for_each_with_build_id(pos, head) { | 240 | dsos__for_each_with_build_id(pos, head) { |
215 | int err; | 241 | int err; |
216 | struct build_id_event b; | 242 | char *name; |
217 | size_t len; | 243 | size_t name_len; |
218 | 244 | ||
219 | if (!pos->hit) | 245 | if (!pos->hit) |
220 | continue; | 246 | continue; |
221 | len = pos->long_name_len + 1; | 247 | |
222 | len = ALIGN(len, NAME_ALIGN); | 248 | if (is_vdso_map(pos->short_name)) { |
223 | memset(&b, 0, sizeof(b)); | 249 | name = (char *) VDSO__MAP_NAME; |
224 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 250 | name_len = sizeof(VDSO__MAP_NAME) + 1; |
225 | b.pid = pid; | 251 | } else { |
226 | b.header.misc = misc; | 252 | name = pos->long_name; |
227 | b.header.size = sizeof(b) + len; | 253 | name_len = pos->long_name_len + 1; |
228 | err = do_write(fd, &b, sizeof(b)); | 254 | } |
229 | if (err < 0) | 255 | |
230 | return err; | 256 | err = write_buildid(name, name_len, pos->build_id, |
231 | err = write_padded(fd, pos->long_name, | 257 | pid, misc, fd); |
232 | pos->long_name_len + 1, len); | 258 | if (err) |
233 | if (err < 0) | ||
234 | return err; | 259 | return err; |
235 | } | 260 | } |
236 | 261 | ||
@@ -276,19 +301,20 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) | |||
276 | } | 301 | } |
277 | 302 | ||
278 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 303 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
279 | const char *name, bool is_kallsyms) | 304 | const char *name, bool is_kallsyms, bool is_vdso) |
280 | { | 305 | { |
281 | const size_t size = PATH_MAX; | 306 | const size_t size = PATH_MAX; |
282 | char *realname, *filename = zalloc(size), | 307 | char *realname, *filename = zalloc(size), |
283 | *linkname = zalloc(size), *targetname; | 308 | *linkname = zalloc(size), *targetname; |
284 | int len, err = -1; | 309 | int len, err = -1; |
310 | bool slash = is_kallsyms || is_vdso; | ||
285 | 311 | ||
286 | if (is_kallsyms) { | 312 | if (is_kallsyms) { |
287 | if (symbol_conf.kptr_restrict) { | 313 | if (symbol_conf.kptr_restrict) { |
288 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | 314 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); |
289 | return 0; | 315 | return 0; |
290 | } | 316 | } |
291 | realname = (char *)name; | 317 | realname = (char *) name; |
292 | } else | 318 | } else |
293 | realname = realpath(name, NULL); | 319 | realname = realpath(name, NULL); |
294 | 320 | ||
@@ -296,7 +322,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
296 | goto out_free; | 322 | goto out_free; |
297 | 323 | ||
298 | len = scnprintf(filename, size, "%s%s%s", | 324 | len = scnprintf(filename, size, "%s%s%s", |
299 | debugdir, is_kallsyms ? "/" : "", realname); | 325 | debugdir, slash ? "/" : "", |
326 | is_vdso ? VDSO__MAP_NAME : realname); | ||
300 | if (mkdir_p(filename, 0755)) | 327 | if (mkdir_p(filename, 0755)) |
301 | goto out_free; | 328 | goto out_free; |
302 | 329 | ||
@@ -332,13 +359,14 @@ out_free: | |||
332 | 359 | ||
333 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | 360 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, |
334 | const char *name, const char *debugdir, | 361 | const char *name, const char *debugdir, |
335 | bool is_kallsyms) | 362 | bool is_kallsyms, bool is_vdso) |
336 | { | 363 | { |
337 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 364 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
338 | 365 | ||
339 | build_id__sprintf(build_id, build_id_size, sbuild_id); | 366 | build_id__sprintf(build_id, build_id_size, sbuild_id); |
340 | 367 | ||
341 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | 368 | return build_id_cache__add_s(sbuild_id, debugdir, name, |
369 | is_kallsyms, is_vdso); | ||
342 | } | 370 | } |
343 | 371 | ||
344 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | 372 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) |
@@ -382,9 +410,11 @@ out_free: | |||
382 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) | 410 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) |
383 | { | 411 | { |
384 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | 412 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; |
413 | bool is_vdso = is_vdso_map(dso->short_name); | ||
385 | 414 | ||
386 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), | 415 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), |
387 | dso->long_name, debugdir, is_kallsyms); | 416 | dso->long_name, debugdir, |
417 | is_kallsyms, is_vdso); | ||
388 | } | 418 | } |
389 | 419 | ||
390 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | 420 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) |
@@ -446,7 +476,7 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with | |||
446 | return ret; | 476 | return ret; |
447 | } | 477 | } |
448 | 478 | ||
449 | static int write_tracing_data(int fd, struct perf_header *h __used, | 479 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, |
450 | struct perf_evlist *evlist) | 480 | struct perf_evlist *evlist) |
451 | { | 481 | { |
452 | return read_tracing_data(fd, &evlist->entries); | 482 | return read_tracing_data(fd, &evlist->entries); |
@@ -454,7 +484,7 @@ static int write_tracing_data(int fd, struct perf_header *h __used, | |||
454 | 484 | ||
455 | 485 | ||
456 | static int write_build_id(int fd, struct perf_header *h, | 486 | static int write_build_id(int fd, struct perf_header *h, |
457 | struct perf_evlist *evlist __used) | 487 | struct perf_evlist *evlist __maybe_unused) |
458 | { | 488 | { |
459 | struct perf_session *session; | 489 | struct perf_session *session; |
460 | int err; | 490 | int err; |
@@ -475,8 +505,8 @@ static int write_build_id(int fd, struct perf_header *h, | |||
475 | return 0; | 505 | return 0; |
476 | } | 506 | } |
477 | 507 | ||
478 | static int write_hostname(int fd, struct perf_header *h __used, | 508 | static int write_hostname(int fd, struct perf_header *h __maybe_unused, |
479 | struct perf_evlist *evlist __used) | 509 | struct perf_evlist *evlist __maybe_unused) |
480 | { | 510 | { |
481 | struct utsname uts; | 511 | struct utsname uts; |
482 | int ret; | 512 | int ret; |
@@ -488,8 +518,8 @@ static int write_hostname(int fd, struct perf_header *h __used, | |||
488 | return do_write_string(fd, uts.nodename); | 518 | return do_write_string(fd, uts.nodename); |
489 | } | 519 | } |
490 | 520 | ||
491 | static int write_osrelease(int fd, struct perf_header *h __used, | 521 | static int write_osrelease(int fd, struct perf_header *h __maybe_unused, |
492 | struct perf_evlist *evlist __used) | 522 | struct perf_evlist *evlist __maybe_unused) |
493 | { | 523 | { |
494 | struct utsname uts; | 524 | struct utsname uts; |
495 | int ret; | 525 | int ret; |
@@ -501,8 +531,8 @@ static int write_osrelease(int fd, struct perf_header *h __used, | |||
501 | return do_write_string(fd, uts.release); | 531 | return do_write_string(fd, uts.release); |
502 | } | 532 | } |
503 | 533 | ||
504 | static int write_arch(int fd, struct perf_header *h __used, | 534 | static int write_arch(int fd, struct perf_header *h __maybe_unused, |
505 | struct perf_evlist *evlist __used) | 535 | struct perf_evlist *evlist __maybe_unused) |
506 | { | 536 | { |
507 | struct utsname uts; | 537 | struct utsname uts; |
508 | int ret; | 538 | int ret; |
@@ -514,14 +544,14 @@ static int write_arch(int fd, struct perf_header *h __used, | |||
514 | return do_write_string(fd, uts.machine); | 544 | return do_write_string(fd, uts.machine); |
515 | } | 545 | } |
516 | 546 | ||
517 | static int write_version(int fd, struct perf_header *h __used, | 547 | static int write_version(int fd, struct perf_header *h __maybe_unused, |
518 | struct perf_evlist *evlist __used) | 548 | struct perf_evlist *evlist __maybe_unused) |
519 | { | 549 | { |
520 | return do_write_string(fd, perf_version_string); | 550 | return do_write_string(fd, perf_version_string); |
521 | } | 551 | } |
522 | 552 | ||
523 | static int write_cpudesc(int fd, struct perf_header *h __used, | 553 | static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, |
524 | struct perf_evlist *evlist __used) | 554 | struct perf_evlist *evlist __maybe_unused) |
525 | { | 555 | { |
526 | #ifndef CPUINFO_PROC | 556 | #ifndef CPUINFO_PROC |
527 | #define CPUINFO_PROC NULL | 557 | #define CPUINFO_PROC NULL |
@@ -579,8 +609,8 @@ done: | |||
579 | return ret; | 609 | return ret; |
580 | } | 610 | } |
581 | 611 | ||
582 | static int write_nrcpus(int fd, struct perf_header *h __used, | 612 | static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, |
583 | struct perf_evlist *evlist __used) | 613 | struct perf_evlist *evlist __maybe_unused) |
584 | { | 614 | { |
585 | long nr; | 615 | long nr; |
586 | u32 nrc, nra; | 616 | u32 nrc, nra; |
@@ -605,15 +635,14 @@ static int write_nrcpus(int fd, struct perf_header *h __used, | |||
605 | return do_write(fd, &nra, sizeof(nra)); | 635 | return do_write(fd, &nra, sizeof(nra)); |
606 | } | 636 | } |
607 | 637 | ||
608 | static int write_event_desc(int fd, struct perf_header *h __used, | 638 | static int write_event_desc(int fd, struct perf_header *h __maybe_unused, |
609 | struct perf_evlist *evlist) | 639 | struct perf_evlist *evlist) |
610 | { | 640 | { |
611 | struct perf_evsel *attr; | 641 | struct perf_evsel *evsel; |
612 | u32 nre = 0, nri, sz; | 642 | u32 nre, nri, sz; |
613 | int ret; | 643 | int ret; |
614 | 644 | ||
615 | list_for_each_entry(attr, &evlist->entries, node) | 645 | nre = evlist->nr_entries; |
616 | nre++; | ||
617 | 646 | ||
618 | /* | 647 | /* |
619 | * write number of events | 648 | * write number of events |
@@ -625,14 +654,14 @@ static int write_event_desc(int fd, struct perf_header *h __used, | |||
625 | /* | 654 | /* |
626 | * size of perf_event_attr struct | 655 | * size of perf_event_attr struct |
627 | */ | 656 | */ |
628 | sz = (u32)sizeof(attr->attr); | 657 | sz = (u32)sizeof(evsel->attr); |
629 | ret = do_write(fd, &sz, sizeof(sz)); | 658 | ret = do_write(fd, &sz, sizeof(sz)); |
630 | if (ret < 0) | 659 | if (ret < 0) |
631 | return ret; | 660 | return ret; |
632 | 661 | ||
633 | list_for_each_entry(attr, &evlist->entries, node) { | 662 | list_for_each_entry(evsel, &evlist->entries, node) { |
634 | 663 | ||
635 | ret = do_write(fd, &attr->attr, sz); | 664 | ret = do_write(fd, &evsel->attr, sz); |
636 | if (ret < 0) | 665 | if (ret < 0) |
637 | return ret; | 666 | return ret; |
638 | /* | 667 | /* |
@@ -642,7 +671,7 @@ static int write_event_desc(int fd, struct perf_header *h __used, | |||
642 | * copy into an nri to be independent of the | 671 | * copy into an nri to be independent of the |
643 | * type of ids, | 672 | * type of ids, |
644 | */ | 673 | */ |
645 | nri = attr->ids; | 674 | nri = evsel->ids; |
646 | ret = do_write(fd, &nri, sizeof(nri)); | 675 | ret = do_write(fd, &nri, sizeof(nri)); |
647 | if (ret < 0) | 676 | if (ret < 0) |
648 | return ret; | 677 | return ret; |
@@ -650,21 +679,21 @@ static int write_event_desc(int fd, struct perf_header *h __used, | |||
650 | /* | 679 | /* |
651 | * write event string as passed on cmdline | 680 | * write event string as passed on cmdline |
652 | */ | 681 | */ |
653 | ret = do_write_string(fd, perf_evsel__name(attr)); | 682 | ret = do_write_string(fd, perf_evsel__name(evsel)); |
654 | if (ret < 0) | 683 | if (ret < 0) |
655 | return ret; | 684 | return ret; |
656 | /* | 685 | /* |
657 | * write unique ids for this event | 686 | * write unique ids for this event |
658 | */ | 687 | */ |
659 | ret = do_write(fd, attr->id, attr->ids * sizeof(u64)); | 688 | ret = do_write(fd, evsel->id, evsel->ids * sizeof(u64)); |
660 | if (ret < 0) | 689 | if (ret < 0) |
661 | return ret; | 690 | return ret; |
662 | } | 691 | } |
663 | return 0; | 692 | return 0; |
664 | } | 693 | } |
665 | 694 | ||
666 | static int write_cmdline(int fd, struct perf_header *h __used, | 695 | static int write_cmdline(int fd, struct perf_header *h __maybe_unused, |
667 | struct perf_evlist *evlist __used) | 696 | struct perf_evlist *evlist __maybe_unused) |
668 | { | 697 | { |
669 | char buf[MAXPATHLEN]; | 698 | char buf[MAXPATHLEN]; |
670 | char proc[32]; | 699 | char proc[32]; |
@@ -832,8 +861,8 @@ static struct cpu_topo *build_cpu_topology(void) | |||
832 | return tp; | 861 | return tp; |
833 | } | 862 | } |
834 | 863 | ||
835 | static int write_cpu_topology(int fd, struct perf_header *h __used, | 864 | static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused, |
836 | struct perf_evlist *evlist __used) | 865 | struct perf_evlist *evlist __maybe_unused) |
837 | { | 866 | { |
838 | struct cpu_topo *tp; | 867 | struct cpu_topo *tp; |
839 | u32 i; | 868 | u32 i; |
@@ -868,8 +897,8 @@ done: | |||
868 | 897 | ||
869 | 898 | ||
870 | 899 | ||
871 | static int write_total_mem(int fd, struct perf_header *h __used, | 900 | static int write_total_mem(int fd, struct perf_header *h __maybe_unused, |
872 | struct perf_evlist *evlist __used) | 901 | struct perf_evlist *evlist __maybe_unused) |
873 | { | 902 | { |
874 | char *buf = NULL; | 903 | char *buf = NULL; |
875 | FILE *fp; | 904 | FILE *fp; |
@@ -954,8 +983,8 @@ done: | |||
954 | return ret; | 983 | return ret; |
955 | } | 984 | } |
956 | 985 | ||
957 | static int write_numa_topology(int fd, struct perf_header *h __used, | 986 | static int write_numa_topology(int fd, struct perf_header *h __maybe_unused, |
958 | struct perf_evlist *evlist __used) | 987 | struct perf_evlist *evlist __maybe_unused) |
959 | { | 988 | { |
960 | char *buf = NULL; | 989 | char *buf = NULL; |
961 | size_t len = 0; | 990 | size_t len = 0; |
@@ -1004,16 +1033,56 @@ done: | |||
1004 | } | 1033 | } |
1005 | 1034 | ||
1006 | /* | 1035 | /* |
1036 | * File format: | ||
1037 | * | ||
1038 | * struct pmu_mappings { | ||
1039 | * u32 pmu_num; | ||
1040 | * struct pmu_map { | ||
1041 | * u32 type; | ||
1042 | * char name[]; | ||
1043 | * }[pmu_num]; | ||
1044 | * }; | ||
1045 | */ | ||
1046 | |||
1047 | static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | ||
1048 | struct perf_evlist *evlist __maybe_unused) | ||
1049 | { | ||
1050 | struct perf_pmu *pmu = NULL; | ||
1051 | off_t offset = lseek(fd, 0, SEEK_CUR); | ||
1052 | __u32 pmu_num = 0; | ||
1053 | |||
1054 | /* write real pmu_num later */ | ||
1055 | do_write(fd, &pmu_num, sizeof(pmu_num)); | ||
1056 | |||
1057 | while ((pmu = perf_pmu__scan(pmu))) { | ||
1058 | if (!pmu->name) | ||
1059 | continue; | ||
1060 | pmu_num++; | ||
1061 | do_write(fd, &pmu->type, sizeof(pmu->type)); | ||
1062 | do_write_string(fd, pmu->name); | ||
1063 | } | ||
1064 | |||
1065 | if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { | ||
1066 | /* discard all */ | ||
1067 | lseek(fd, offset, SEEK_SET); | ||
1068 | return -1; | ||
1069 | } | ||
1070 | |||
1071 | return 0; | ||
1072 | } | ||
1073 | |||
1074 | /* | ||
1007 | * default get_cpuid(): nothing gets recorded | 1075 | * default get_cpuid(): nothing gets recorded |
1008 | * actual implementation must be in arch/$(ARCH)/util/header.c | 1076 | * actual implementation must be in arch/$(ARCH)/util/header.c |
1009 | */ | 1077 | */ |
1010 | int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used) | 1078 | int __attribute__ ((weak)) get_cpuid(char *buffer __maybe_unused, |
1079 | size_t sz __maybe_unused) | ||
1011 | { | 1080 | { |
1012 | return -1; | 1081 | return -1; |
1013 | } | 1082 | } |
1014 | 1083 | ||
1015 | static int write_cpuid(int fd, struct perf_header *h __used, | 1084 | static int write_cpuid(int fd, struct perf_header *h __maybe_unused, |
1016 | struct perf_evlist *evlist __used) | 1085 | struct perf_evlist *evlist __maybe_unused) |
1017 | { | 1086 | { |
1018 | char buffer[64]; | 1087 | char buffer[64]; |
1019 | int ret; | 1088 | int ret; |
@@ -1027,133 +1096,113 @@ write_it: | |||
1027 | return do_write_string(fd, buffer); | 1096 | return do_write_string(fd, buffer); |
1028 | } | 1097 | } |
1029 | 1098 | ||
1030 | static int write_branch_stack(int fd __used, struct perf_header *h __used, | 1099 | static int write_branch_stack(int fd __maybe_unused, |
1031 | struct perf_evlist *evlist __used) | 1100 | struct perf_header *h __maybe_unused, |
1101 | struct perf_evlist *evlist __maybe_unused) | ||
1032 | { | 1102 | { |
1033 | return 0; | 1103 | return 0; |
1034 | } | 1104 | } |
1035 | 1105 | ||
1036 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | 1106 | static void print_hostname(struct perf_header *ph, int fd __maybe_unused, |
1107 | FILE *fp) | ||
1037 | { | 1108 | { |
1038 | char *str = do_read_string(fd, ph); | 1109 | fprintf(fp, "# hostname : %s\n", ph->env.hostname); |
1039 | fprintf(fp, "# hostname : %s\n", str); | ||
1040 | free(str); | ||
1041 | } | 1110 | } |
1042 | 1111 | ||
1043 | static void print_osrelease(struct perf_header *ph, int fd, FILE *fp) | 1112 | static void print_osrelease(struct perf_header *ph, int fd __maybe_unused, |
1113 | FILE *fp) | ||
1044 | { | 1114 | { |
1045 | char *str = do_read_string(fd, ph); | 1115 | fprintf(fp, "# os release : %s\n", ph->env.os_release); |
1046 | fprintf(fp, "# os release : %s\n", str); | ||
1047 | free(str); | ||
1048 | } | 1116 | } |
1049 | 1117 | ||
1050 | static void print_arch(struct perf_header *ph, int fd, FILE *fp) | 1118 | static void print_arch(struct perf_header *ph, int fd __maybe_unused, FILE *fp) |
1051 | { | 1119 | { |
1052 | char *str = do_read_string(fd, ph); | 1120 | fprintf(fp, "# arch : %s\n", ph->env.arch); |
1053 | fprintf(fp, "# arch : %s\n", str); | ||
1054 | free(str); | ||
1055 | } | 1121 | } |
1056 | 1122 | ||
1057 | static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp) | 1123 | static void print_cpudesc(struct perf_header *ph, int fd __maybe_unused, |
1124 | FILE *fp) | ||
1058 | { | 1125 | { |
1059 | char *str = do_read_string(fd, ph); | 1126 | fprintf(fp, "# cpudesc : %s\n", ph->env.cpu_desc); |
1060 | fprintf(fp, "# cpudesc : %s\n", str); | ||
1061 | free(str); | ||
1062 | } | 1127 | } |
1063 | 1128 | ||
1064 | static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp) | 1129 | static void print_nrcpus(struct perf_header *ph, int fd __maybe_unused, |
1130 | FILE *fp) | ||
1065 | { | 1131 | { |
1066 | ssize_t ret; | 1132 | fprintf(fp, "# nrcpus online : %u\n", ph->env.nr_cpus_online); |
1067 | u32 nr; | 1133 | fprintf(fp, "# nrcpus avail : %u\n", ph->env.nr_cpus_avail); |
1068 | |||
1069 | ret = read(fd, &nr, sizeof(nr)); | ||
1070 | if (ret != (ssize_t)sizeof(nr)) | ||
1071 | nr = -1; /* interpreted as error */ | ||
1072 | |||
1073 | if (ph->needs_swap) | ||
1074 | nr = bswap_32(nr); | ||
1075 | |||
1076 | fprintf(fp, "# nrcpus online : %u\n", nr); | ||
1077 | |||
1078 | ret = read(fd, &nr, sizeof(nr)); | ||
1079 | if (ret != (ssize_t)sizeof(nr)) | ||
1080 | nr = -1; /* interpreted as error */ | ||
1081 | |||
1082 | if (ph->needs_swap) | ||
1083 | nr = bswap_32(nr); | ||
1084 | |||
1085 | fprintf(fp, "# nrcpus avail : %u\n", nr); | ||
1086 | } | 1134 | } |
1087 | 1135 | ||
1088 | static void print_version(struct perf_header *ph, int fd, FILE *fp) | 1136 | static void print_version(struct perf_header *ph, int fd __maybe_unused, |
1137 | FILE *fp) | ||
1089 | { | 1138 | { |
1090 | char *str = do_read_string(fd, ph); | 1139 | fprintf(fp, "# perf version : %s\n", ph->env.version); |
1091 | fprintf(fp, "# perf version : %s\n", str); | ||
1092 | free(str); | ||
1093 | } | 1140 | } |
1094 | 1141 | ||
1095 | static void print_cmdline(struct perf_header *ph, int fd, FILE *fp) | 1142 | static void print_cmdline(struct perf_header *ph, int fd __maybe_unused, |
1143 | FILE *fp) | ||
1096 | { | 1144 | { |
1097 | ssize_t ret; | 1145 | int nr, i; |
1098 | char *str; | 1146 | char *str; |
1099 | u32 nr, i; | ||
1100 | |||
1101 | ret = read(fd, &nr, sizeof(nr)); | ||
1102 | if (ret != (ssize_t)sizeof(nr)) | ||
1103 | return; | ||
1104 | 1147 | ||
1105 | if (ph->needs_swap) | 1148 | nr = ph->env.nr_cmdline; |
1106 | nr = bswap_32(nr); | 1149 | str = ph->env.cmdline; |
1107 | 1150 | ||
1108 | fprintf(fp, "# cmdline : "); | 1151 | fprintf(fp, "# cmdline : "); |
1109 | 1152 | ||
1110 | for (i = 0; i < nr; i++) { | 1153 | for (i = 0; i < nr; i++) { |
1111 | str = do_read_string(fd, ph); | ||
1112 | fprintf(fp, "%s ", str); | 1154 | fprintf(fp, "%s ", str); |
1113 | free(str); | 1155 | str += strlen(str) + 1; |
1114 | } | 1156 | } |
1115 | fputc('\n', fp); | 1157 | fputc('\n', fp); |
1116 | } | 1158 | } |
1117 | 1159 | ||
1118 | static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) | 1160 | static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused, |
1161 | FILE *fp) | ||
1119 | { | 1162 | { |
1120 | ssize_t ret; | 1163 | int nr, i; |
1121 | u32 nr, i; | ||
1122 | char *str; | 1164 | char *str; |
1123 | 1165 | ||
1124 | ret = read(fd, &nr, sizeof(nr)); | 1166 | nr = ph->env.nr_sibling_cores; |
1125 | if (ret != (ssize_t)sizeof(nr)) | 1167 | str = ph->env.sibling_cores; |
1126 | return; | ||
1127 | |||
1128 | if (ph->needs_swap) | ||
1129 | nr = bswap_32(nr); | ||
1130 | 1168 | ||
1131 | for (i = 0; i < nr; i++) { | 1169 | for (i = 0; i < nr; i++) { |
1132 | str = do_read_string(fd, ph); | ||
1133 | fprintf(fp, "# sibling cores : %s\n", str); | 1170 | fprintf(fp, "# sibling cores : %s\n", str); |
1134 | free(str); | 1171 | str += strlen(str) + 1; |
1135 | } | 1172 | } |
1136 | 1173 | ||
1137 | ret = read(fd, &nr, sizeof(nr)); | 1174 | nr = ph->env.nr_sibling_threads; |
1138 | if (ret != (ssize_t)sizeof(nr)) | 1175 | str = ph->env.sibling_threads; |
1139 | return; | ||
1140 | |||
1141 | if (ph->needs_swap) | ||
1142 | nr = bswap_32(nr); | ||
1143 | 1176 | ||
1144 | for (i = 0; i < nr; i++) { | 1177 | for (i = 0; i < nr; i++) { |
1145 | str = do_read_string(fd, ph); | ||
1146 | fprintf(fp, "# sibling threads : %s\n", str); | 1178 | fprintf(fp, "# sibling threads : %s\n", str); |
1147 | free(str); | 1179 | str += strlen(str) + 1; |
1148 | } | 1180 | } |
1149 | } | 1181 | } |
1150 | 1182 | ||
1151 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | 1183 | static void free_event_desc(struct perf_evsel *events) |
1184 | { | ||
1185 | struct perf_evsel *evsel; | ||
1186 | |||
1187 | if (!events) | ||
1188 | return; | ||
1189 | |||
1190 | for (evsel = events; evsel->attr.size; evsel++) { | ||
1191 | if (evsel->name) | ||
1192 | free(evsel->name); | ||
1193 | if (evsel->id) | ||
1194 | free(evsel->id); | ||
1195 | } | ||
1196 | |||
1197 | free(events); | ||
1198 | } | ||
1199 | |||
1200 | static struct perf_evsel * | ||
1201 | read_event_desc(struct perf_header *ph, int fd) | ||
1152 | { | 1202 | { |
1153 | struct perf_event_attr attr; | 1203 | struct perf_evsel *evsel, *events = NULL; |
1154 | uint64_t id; | 1204 | u64 *id; |
1155 | void *buf = NULL; | 1205 | void *buf = NULL; |
1156 | char *str; | ||
1157 | u32 nre, sz, nr, i, j; | 1206 | u32 nre, sz, nr, i, j; |
1158 | ssize_t ret; | 1207 | ssize_t ret; |
1159 | size_t msz; | 1208 | size_t msz; |
@@ -1173,18 +1222,22 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1173 | if (ph->needs_swap) | 1222 | if (ph->needs_swap) |
1174 | sz = bswap_32(sz); | 1223 | sz = bswap_32(sz); |
1175 | 1224 | ||
1176 | memset(&attr, 0, sizeof(attr)); | ||
1177 | |||
1178 | /* buffer to hold on file attr struct */ | 1225 | /* buffer to hold on file attr struct */ |
1179 | buf = malloc(sz); | 1226 | buf = malloc(sz); |
1180 | if (!buf) | 1227 | if (!buf) |
1181 | goto error; | 1228 | goto error; |
1182 | 1229 | ||
1183 | msz = sizeof(attr); | 1230 | /* the last event terminates with evsel->attr.size == 0: */ |
1231 | events = calloc(nre + 1, sizeof(*events)); | ||
1232 | if (!events) | ||
1233 | goto error; | ||
1234 | |||
1235 | msz = sizeof(evsel->attr); | ||
1184 | if (sz < msz) | 1236 | if (sz < msz) |
1185 | msz = sz; | 1237 | msz = sz; |
1186 | 1238 | ||
1187 | for (i = 0 ; i < nre; i++) { | 1239 | for (i = 0, evsel = events; i < nre; evsel++, i++) { |
1240 | evsel->idx = i; | ||
1188 | 1241 | ||
1189 | /* | 1242 | /* |
1190 | * must read entire on-file attr struct to | 1243 | * must read entire on-file attr struct to |
@@ -1197,146 +1250,188 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1197 | if (ph->needs_swap) | 1250 | if (ph->needs_swap) |
1198 | perf_event__attr_swap(buf); | 1251 | perf_event__attr_swap(buf); |
1199 | 1252 | ||
1200 | memcpy(&attr, buf, msz); | 1253 | memcpy(&evsel->attr, buf, msz); |
1201 | 1254 | ||
1202 | ret = read(fd, &nr, sizeof(nr)); | 1255 | ret = read(fd, &nr, sizeof(nr)); |
1203 | if (ret != (ssize_t)sizeof(nr)) | 1256 | if (ret != (ssize_t)sizeof(nr)) |
1204 | goto error; | 1257 | goto error; |
1205 | 1258 | ||
1206 | if (ph->needs_swap) | 1259 | if (ph->needs_swap) { |
1207 | nr = bswap_32(nr); | 1260 | nr = bswap_32(nr); |
1261 | evsel->needs_swap = true; | ||
1262 | } | ||
1208 | 1263 | ||
1209 | str = do_read_string(fd, ph); | 1264 | evsel->name = do_read_string(fd, ph); |
1210 | fprintf(fp, "# event : name = %s, ", str); | 1265 | |
1211 | free(str); | 1266 | if (!nr) |
1267 | continue; | ||
1268 | |||
1269 | id = calloc(nr, sizeof(*id)); | ||
1270 | if (!id) | ||
1271 | goto error; | ||
1272 | evsel->ids = nr; | ||
1273 | evsel->id = id; | ||
1274 | |||
1275 | for (j = 0 ; j < nr; j++) { | ||
1276 | ret = read(fd, id, sizeof(*id)); | ||
1277 | if (ret != (ssize_t)sizeof(*id)) | ||
1278 | goto error; | ||
1279 | if (ph->needs_swap) | ||
1280 | *id = bswap_64(*id); | ||
1281 | id++; | ||
1282 | } | ||
1283 | } | ||
1284 | out: | ||
1285 | if (buf) | ||
1286 | free(buf); | ||
1287 | return events; | ||
1288 | error: | ||
1289 | if (events) | ||
1290 | free_event_desc(events); | ||
1291 | events = NULL; | ||
1292 | goto out; | ||
1293 | } | ||
1294 | |||
1295 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||
1296 | { | ||
1297 | struct perf_evsel *evsel, *events = read_event_desc(ph, fd); | ||
1298 | u32 j; | ||
1299 | u64 *id; | ||
1300 | |||
1301 | if (!events) { | ||
1302 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
1303 | return; | ||
1304 | } | ||
1305 | |||
1306 | for (evsel = events; evsel->attr.size; evsel++) { | ||
1307 | fprintf(fp, "# event : name = %s, ", evsel->name); | ||
1212 | 1308 | ||
1213 | fprintf(fp, "type = %d, config = 0x%"PRIx64 | 1309 | fprintf(fp, "type = %d, config = 0x%"PRIx64 |
1214 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, | 1310 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, |
1215 | attr.type, | 1311 | evsel->attr.type, |
1216 | (u64)attr.config, | 1312 | (u64)evsel->attr.config, |
1217 | (u64)attr.config1, | 1313 | (u64)evsel->attr.config1, |
1218 | (u64)attr.config2); | 1314 | (u64)evsel->attr.config2); |
1219 | 1315 | ||
1220 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", | 1316 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", |
1221 | attr.exclude_user, | 1317 | evsel->attr.exclude_user, |
1222 | attr.exclude_kernel); | 1318 | evsel->attr.exclude_kernel); |
1223 | 1319 | ||
1224 | fprintf(fp, ", excl_host = %d, excl_guest = %d", | 1320 | fprintf(fp, ", excl_host = %d, excl_guest = %d", |
1225 | attr.exclude_host, | 1321 | evsel->attr.exclude_host, |
1226 | attr.exclude_guest); | 1322 | evsel->attr.exclude_guest); |
1227 | 1323 | ||
1228 | fprintf(fp, ", precise_ip = %d", attr.precise_ip); | 1324 | fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip); |
1229 | 1325 | ||
1230 | if (nr) | 1326 | if (evsel->ids) { |
1231 | fprintf(fp, ", id = {"); | 1327 | fprintf(fp, ", id = {"); |
1232 | 1328 | for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) { | |
1233 | for (j = 0 ; j < nr; j++) { | 1329 | if (j) |
1234 | ret = read(fd, &id, sizeof(id)); | 1330 | fputc(',', fp); |
1235 | if (ret != (ssize_t)sizeof(id)) | 1331 | fprintf(fp, " %"PRIu64, *id); |
1236 | goto error; | 1332 | } |
1237 | |||
1238 | if (ph->needs_swap) | ||
1239 | id = bswap_64(id); | ||
1240 | |||
1241 | if (j) | ||
1242 | fputc(',', fp); | ||
1243 | |||
1244 | fprintf(fp, " %"PRIu64, id); | ||
1245 | } | ||
1246 | if (nr && j == nr) | ||
1247 | fprintf(fp, " }"); | 1333 | fprintf(fp, " }"); |
1334 | } | ||
1335 | |||
1248 | fputc('\n', fp); | 1336 | fputc('\n', fp); |
1249 | } | 1337 | } |
1250 | free(buf); | 1338 | |
1251 | return; | 1339 | free_event_desc(events); |
1252 | error: | ||
1253 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
1254 | } | 1340 | } |
1255 | 1341 | ||
1256 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) | 1342 | static void print_total_mem(struct perf_header *ph, int fd __maybe_unused, |
1343 | FILE *fp) | ||
1257 | { | 1344 | { |
1258 | uint64_t mem; | 1345 | fprintf(fp, "# total memory : %Lu kB\n", ph->env.total_mem); |
1259 | ssize_t ret; | ||
1260 | |||
1261 | ret = read(fd, &mem, sizeof(mem)); | ||
1262 | if (ret != sizeof(mem)) | ||
1263 | goto error; | ||
1264 | |||
1265 | if (h->needs_swap) | ||
1266 | mem = bswap_64(mem); | ||
1267 | |||
1268 | fprintf(fp, "# total memory : %"PRIu64" kB\n", mem); | ||
1269 | return; | ||
1270 | error: | ||
1271 | fprintf(fp, "# total memory : unknown\n"); | ||
1272 | } | 1346 | } |
1273 | 1347 | ||
1274 | static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp) | 1348 | static void print_numa_topology(struct perf_header *ph, int fd __maybe_unused, |
1349 | FILE *fp) | ||
1275 | { | 1350 | { |
1276 | ssize_t ret; | ||
1277 | u32 nr, c, i; | 1351 | u32 nr, c, i; |
1278 | char *str; | 1352 | char *str, *tmp; |
1279 | uint64_t mem_total, mem_free; | 1353 | uint64_t mem_total, mem_free; |
1280 | 1354 | ||
1281 | /* nr nodes */ | 1355 | /* nr nodes */ |
1282 | ret = read(fd, &nr, sizeof(nr)); | 1356 | nr = ph->env.nr_numa_nodes; |
1283 | if (ret != (ssize_t)sizeof(nr)) | 1357 | str = ph->env.numa_nodes; |
1284 | goto error; | ||
1285 | |||
1286 | if (h->needs_swap) | ||
1287 | nr = bswap_32(nr); | ||
1288 | 1358 | ||
1289 | for (i = 0; i < nr; i++) { | 1359 | for (i = 0; i < nr; i++) { |
1290 | |||
1291 | /* node number */ | 1360 | /* node number */ |
1292 | ret = read(fd, &c, sizeof(c)); | 1361 | c = strtoul(str, &tmp, 0); |
1293 | if (ret != (ssize_t)sizeof(c)) | 1362 | if (*tmp != ':') |
1294 | goto error; | 1363 | goto error; |
1295 | 1364 | ||
1296 | if (h->needs_swap) | 1365 | str = tmp + 1; |
1297 | c = bswap_32(c); | 1366 | mem_total = strtoull(str, &tmp, 0); |
1298 | 1367 | if (*tmp != ':') | |
1299 | ret = read(fd, &mem_total, sizeof(u64)); | ||
1300 | if (ret != sizeof(u64)) | ||
1301 | goto error; | 1368 | goto error; |
1302 | 1369 | ||
1303 | ret = read(fd, &mem_free, sizeof(u64)); | 1370 | str = tmp + 1; |
1304 | if (ret != sizeof(u64)) | 1371 | mem_free = strtoull(str, &tmp, 0); |
1372 | if (*tmp != ':') | ||
1305 | goto error; | 1373 | goto error; |
1306 | 1374 | ||
1307 | if (h->needs_swap) { | ||
1308 | mem_total = bswap_64(mem_total); | ||
1309 | mem_free = bswap_64(mem_free); | ||
1310 | } | ||
1311 | |||
1312 | fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," | 1375 | fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," |
1313 | " free = %"PRIu64" kB\n", | 1376 | " free = %"PRIu64" kB\n", |
1314 | c, | 1377 | c, mem_total, mem_free); |
1315 | mem_total, | ||
1316 | mem_free); | ||
1317 | 1378 | ||
1318 | str = do_read_string(fd, h); | 1379 | str = tmp + 1; |
1319 | fprintf(fp, "# node%u cpu list : %s\n", c, str); | 1380 | fprintf(fp, "# node%u cpu list : %s\n", c, str); |
1320 | free(str); | ||
1321 | } | 1381 | } |
1322 | return; | 1382 | return; |
1323 | error: | 1383 | error: |
1324 | fprintf(fp, "# numa topology : not available\n"); | 1384 | fprintf(fp, "# numa topology : not available\n"); |
1325 | } | 1385 | } |
1326 | 1386 | ||
1327 | static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | 1387 | static void print_cpuid(struct perf_header *ph, int fd __maybe_unused, FILE *fp) |
1328 | { | 1388 | { |
1329 | char *str = do_read_string(fd, ph); | 1389 | fprintf(fp, "# cpuid : %s\n", ph->env.cpuid); |
1330 | fprintf(fp, "# cpuid : %s\n", str); | ||
1331 | free(str); | ||
1332 | } | 1390 | } |
1333 | 1391 | ||
1334 | static void print_branch_stack(struct perf_header *ph __used, int fd __used, | 1392 | static void print_branch_stack(struct perf_header *ph __maybe_unused, |
1335 | FILE *fp) | 1393 | int fd __maybe_unused, FILE *fp) |
1336 | { | 1394 | { |
1337 | fprintf(fp, "# contains samples with branch stack\n"); | 1395 | fprintf(fp, "# contains samples with branch stack\n"); |
1338 | } | 1396 | } |
1339 | 1397 | ||
1398 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, | ||
1399 | FILE *fp) | ||
1400 | { | ||
1401 | const char *delimiter = "# pmu mappings: "; | ||
1402 | char *str, *tmp; | ||
1403 | u32 pmu_num; | ||
1404 | u32 type; | ||
1405 | |||
1406 | pmu_num = ph->env.nr_pmu_mappings; | ||
1407 | if (!pmu_num) { | ||
1408 | fprintf(fp, "# pmu mappings: not available\n"); | ||
1409 | return; | ||
1410 | } | ||
1411 | |||
1412 | str = ph->env.pmu_mappings; | ||
1413 | |||
1414 | while (pmu_num) { | ||
1415 | type = strtoul(str, &tmp, 0); | ||
1416 | if (*tmp != ':') | ||
1417 | goto error; | ||
1418 | |||
1419 | str = tmp + 1; | ||
1420 | fprintf(fp, "%s%s = %" PRIu32, delimiter, str, type); | ||
1421 | |||
1422 | delimiter = ", "; | ||
1423 | str += strlen(str) + 1; | ||
1424 | pmu_num--; | ||
1425 | } | ||
1426 | |||
1427 | fprintf(fp, "\n"); | ||
1428 | |||
1429 | if (!pmu_num) | ||
1430 | return; | ||
1431 | error: | ||
1432 | fprintf(fp, "# pmu mappings: unable to read\n"); | ||
1433 | } | ||
1434 | |||
1340 | static int __event_process_build_id(struct build_id_event *bev, | 1435 | static int __event_process_build_id(struct build_id_event *bev, |
1341 | char *filename, | 1436 | char *filename, |
1342 | struct perf_session *session) | 1437 | struct perf_session *session) |
@@ -1398,7 +1493,7 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | |||
1398 | struct perf_session *session = container_of(header, struct perf_session, header); | 1493 | struct perf_session *session = container_of(header, struct perf_session, header); |
1399 | struct { | 1494 | struct { |
1400 | struct perf_event_header header; | 1495 | struct perf_event_header header; |
1401 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | 1496 | u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))]; |
1402 | char filename[0]; | 1497 | char filename[0]; |
1403 | } old_bev; | 1498 | } old_bev; |
1404 | struct build_id_event bev; | 1499 | struct build_id_event bev; |
@@ -1487,28 +1582,375 @@ out: | |||
1487 | return err; | 1582 | return err; |
1488 | } | 1583 | } |
1489 | 1584 | ||
1490 | static int process_tracing_data(struct perf_file_section *section __unused, | 1585 | static int process_tracing_data(struct perf_file_section *section __maybe_unused, |
1491 | struct perf_header *ph __unused, | 1586 | struct perf_header *ph __maybe_unused, |
1492 | int feat __unused, int fd, void *data) | 1587 | int fd, void *data) |
1493 | { | 1588 | { |
1494 | trace_report(fd, data, false); | 1589 | trace_report(fd, data, false); |
1495 | return 0; | 1590 | return 0; |
1496 | } | 1591 | } |
1497 | 1592 | ||
1498 | static int process_build_id(struct perf_file_section *section, | 1593 | static int process_build_id(struct perf_file_section *section, |
1499 | struct perf_header *ph, | 1594 | struct perf_header *ph, int fd, |
1500 | int feat __unused, int fd, void *data __used) | 1595 | void *data __maybe_unused) |
1501 | { | 1596 | { |
1502 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | 1597 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
1503 | pr_debug("Failed to read buildids, continuing...\n"); | 1598 | pr_debug("Failed to read buildids, continuing...\n"); |
1504 | return 0; | 1599 | return 0; |
1505 | } | 1600 | } |
1506 | 1601 | ||
1602 | static int process_hostname(struct perf_file_section *section __maybe_unused, | ||
1603 | struct perf_header *ph, int fd, | ||
1604 | void *data __maybe_unused) | ||
1605 | { | ||
1606 | ph->env.hostname = do_read_string(fd, ph); | ||
1607 | return ph->env.hostname ? 0 : -ENOMEM; | ||
1608 | } | ||
1609 | |||
1610 | static int process_osrelease(struct perf_file_section *section __maybe_unused, | ||
1611 | struct perf_header *ph, int fd, | ||
1612 | void *data __maybe_unused) | ||
1613 | { | ||
1614 | ph->env.os_release = do_read_string(fd, ph); | ||
1615 | return ph->env.os_release ? 0 : -ENOMEM; | ||
1616 | } | ||
1617 | |||
1618 | static int process_version(struct perf_file_section *section __maybe_unused, | ||
1619 | struct perf_header *ph, int fd, | ||
1620 | void *data __maybe_unused) | ||
1621 | { | ||
1622 | ph->env.version = do_read_string(fd, ph); | ||
1623 | return ph->env.version ? 0 : -ENOMEM; | ||
1624 | } | ||
1625 | |||
1626 | static int process_arch(struct perf_file_section *section __maybe_unused, | ||
1627 | struct perf_header *ph, int fd, | ||
1628 | void *data __maybe_unused) | ||
1629 | { | ||
1630 | ph->env.arch = do_read_string(fd, ph); | ||
1631 | return ph->env.arch ? 0 : -ENOMEM; | ||
1632 | } | ||
1633 | |||
1634 | static int process_nrcpus(struct perf_file_section *section __maybe_unused, | ||
1635 | struct perf_header *ph, int fd, | ||
1636 | void *data __maybe_unused) | ||
1637 | { | ||
1638 | size_t ret; | ||
1639 | u32 nr; | ||
1640 | |||
1641 | ret = read(fd, &nr, sizeof(nr)); | ||
1642 | if (ret != sizeof(nr)) | ||
1643 | return -1; | ||
1644 | |||
1645 | if (ph->needs_swap) | ||
1646 | nr = bswap_32(nr); | ||
1647 | |||
1648 | ph->env.nr_cpus_online = nr; | ||
1649 | |||
1650 | ret = read(fd, &nr, sizeof(nr)); | ||
1651 | if (ret != sizeof(nr)) | ||
1652 | return -1; | ||
1653 | |||
1654 | if (ph->needs_swap) | ||
1655 | nr = bswap_32(nr); | ||
1656 | |||
1657 | ph->env.nr_cpus_avail = nr; | ||
1658 | return 0; | ||
1659 | } | ||
1660 | |||
1661 | static int process_cpudesc(struct perf_file_section *section __maybe_unused, | ||
1662 | struct perf_header *ph, int fd, | ||
1663 | void *data __maybe_unused) | ||
1664 | { | ||
1665 | ph->env.cpu_desc = do_read_string(fd, ph); | ||
1666 | return ph->env.cpu_desc ? 0 : -ENOMEM; | ||
1667 | } | ||
1668 | |||
1669 | static int process_cpuid(struct perf_file_section *section __maybe_unused, | ||
1670 | struct perf_header *ph, int fd, | ||
1671 | void *data __maybe_unused) | ||
1672 | { | ||
1673 | ph->env.cpuid = do_read_string(fd, ph); | ||
1674 | return ph->env.cpuid ? 0 : -ENOMEM; | ||
1675 | } | ||
1676 | |||
1677 | static int process_total_mem(struct perf_file_section *section __maybe_unused, | ||
1678 | struct perf_header *ph, int fd, | ||
1679 | void *data __maybe_unused) | ||
1680 | { | ||
1681 | uint64_t mem; | ||
1682 | size_t ret; | ||
1683 | |||
1684 | ret = read(fd, &mem, sizeof(mem)); | ||
1685 | if (ret != sizeof(mem)) | ||
1686 | return -1; | ||
1687 | |||
1688 | if (ph->needs_swap) | ||
1689 | mem = bswap_64(mem); | ||
1690 | |||
1691 | ph->env.total_mem = mem; | ||
1692 | return 0; | ||
1693 | } | ||
1694 | |||
1695 | static struct perf_evsel * | ||
1696 | perf_evlist__find_by_index(struct perf_evlist *evlist, int idx) | ||
1697 | { | ||
1698 | struct perf_evsel *evsel; | ||
1699 | |||
1700 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1701 | if (evsel->idx == idx) | ||
1702 | return evsel; | ||
1703 | } | ||
1704 | |||
1705 | return NULL; | ||
1706 | } | ||
1707 | |||
1708 | static void | ||
1709 | perf_evlist__set_event_name(struct perf_evlist *evlist, | ||
1710 | struct perf_evsel *event) | ||
1711 | { | ||
1712 | struct perf_evsel *evsel; | ||
1713 | |||
1714 | if (!event->name) | ||
1715 | return; | ||
1716 | |||
1717 | evsel = perf_evlist__find_by_index(evlist, event->idx); | ||
1718 | if (!evsel) | ||
1719 | return; | ||
1720 | |||
1721 | if (evsel->name) | ||
1722 | return; | ||
1723 | |||
1724 | evsel->name = strdup(event->name); | ||
1725 | } | ||
1726 | |||
1727 | static int | ||
1728 | process_event_desc(struct perf_file_section *section __maybe_unused, | ||
1729 | struct perf_header *header, int fd, | ||
1730 | void *data __maybe_unused) | ||
1731 | { | ||
1732 | struct perf_session *session; | ||
1733 | struct perf_evsel *evsel, *events = read_event_desc(header, fd); | ||
1734 | |||
1735 | if (!events) | ||
1736 | return 0; | ||
1737 | |||
1738 | session = container_of(header, struct perf_session, header); | ||
1739 | for (evsel = events; evsel->attr.size; evsel++) | ||
1740 | perf_evlist__set_event_name(session->evlist, evsel); | ||
1741 | |||
1742 | free_event_desc(events); | ||
1743 | |||
1744 | return 0; | ||
1745 | } | ||
1746 | |||
1747 | static int process_cmdline(struct perf_file_section *section __maybe_unused, | ||
1748 | struct perf_header *ph, int fd, | ||
1749 | void *data __maybe_unused) | ||
1750 | { | ||
1751 | size_t ret; | ||
1752 | char *str; | ||
1753 | u32 nr, i; | ||
1754 | struct strbuf sb; | ||
1755 | |||
1756 | ret = read(fd, &nr, sizeof(nr)); | ||
1757 | if (ret != sizeof(nr)) | ||
1758 | return -1; | ||
1759 | |||
1760 | if (ph->needs_swap) | ||
1761 | nr = bswap_32(nr); | ||
1762 | |||
1763 | ph->env.nr_cmdline = nr; | ||
1764 | strbuf_init(&sb, 128); | ||
1765 | |||
1766 | for (i = 0; i < nr; i++) { | ||
1767 | str = do_read_string(fd, ph); | ||
1768 | if (!str) | ||
1769 | goto error; | ||
1770 | |||
1771 | /* include a NULL character at the end */ | ||
1772 | strbuf_add(&sb, str, strlen(str) + 1); | ||
1773 | free(str); | ||
1774 | } | ||
1775 | ph->env.cmdline = strbuf_detach(&sb, NULL); | ||
1776 | return 0; | ||
1777 | |||
1778 | error: | ||
1779 | strbuf_release(&sb); | ||
1780 | return -1; | ||
1781 | } | ||
1782 | |||
1783 | static int process_cpu_topology(struct perf_file_section *section __maybe_unused, | ||
1784 | struct perf_header *ph, int fd, | ||
1785 | void *data __maybe_unused) | ||
1786 | { | ||
1787 | size_t ret; | ||
1788 | u32 nr, i; | ||
1789 | char *str; | ||
1790 | struct strbuf sb; | ||
1791 | |||
1792 | ret = read(fd, &nr, sizeof(nr)); | ||
1793 | if (ret != sizeof(nr)) | ||
1794 | return -1; | ||
1795 | |||
1796 | if (ph->needs_swap) | ||
1797 | nr = bswap_32(nr); | ||
1798 | |||
1799 | ph->env.nr_sibling_cores = nr; | ||
1800 | strbuf_init(&sb, 128); | ||
1801 | |||
1802 | for (i = 0; i < nr; i++) { | ||
1803 | str = do_read_string(fd, ph); | ||
1804 | if (!str) | ||
1805 | goto error; | ||
1806 | |||
1807 | /* include a NULL character at the end */ | ||
1808 | strbuf_add(&sb, str, strlen(str) + 1); | ||
1809 | free(str); | ||
1810 | } | ||
1811 | ph->env.sibling_cores = strbuf_detach(&sb, NULL); | ||
1812 | |||
1813 | ret = read(fd, &nr, sizeof(nr)); | ||
1814 | if (ret != sizeof(nr)) | ||
1815 | return -1; | ||
1816 | |||
1817 | if (ph->needs_swap) | ||
1818 | nr = bswap_32(nr); | ||
1819 | |||
1820 | ph->env.nr_sibling_threads = nr; | ||
1821 | |||
1822 | for (i = 0; i < nr; i++) { | ||
1823 | str = do_read_string(fd, ph); | ||
1824 | if (!str) | ||
1825 | goto error; | ||
1826 | |||
1827 | /* include a NULL character at the end */ | ||
1828 | strbuf_add(&sb, str, strlen(str) + 1); | ||
1829 | free(str); | ||
1830 | } | ||
1831 | ph->env.sibling_threads = strbuf_detach(&sb, NULL); | ||
1832 | return 0; | ||
1833 | |||
1834 | error: | ||
1835 | strbuf_release(&sb); | ||
1836 | return -1; | ||
1837 | } | ||
1838 | |||
1839 | static int process_numa_topology(struct perf_file_section *section __maybe_unused, | ||
1840 | struct perf_header *ph, int fd, | ||
1841 | void *data __maybe_unused) | ||
1842 | { | ||
1843 | size_t ret; | ||
1844 | u32 nr, node, i; | ||
1845 | char *str; | ||
1846 | uint64_t mem_total, mem_free; | ||
1847 | struct strbuf sb; | ||
1848 | |||
1849 | /* nr nodes */ | ||
1850 | ret = read(fd, &nr, sizeof(nr)); | ||
1851 | if (ret != sizeof(nr)) | ||
1852 | goto error; | ||
1853 | |||
1854 | if (ph->needs_swap) | ||
1855 | nr = bswap_32(nr); | ||
1856 | |||
1857 | ph->env.nr_numa_nodes = nr; | ||
1858 | strbuf_init(&sb, 256); | ||
1859 | |||
1860 | for (i = 0; i < nr; i++) { | ||
1861 | /* node number */ | ||
1862 | ret = read(fd, &node, sizeof(node)); | ||
1863 | if (ret != sizeof(node)) | ||
1864 | goto error; | ||
1865 | |||
1866 | ret = read(fd, &mem_total, sizeof(u64)); | ||
1867 | if (ret != sizeof(u64)) | ||
1868 | goto error; | ||
1869 | |||
1870 | ret = read(fd, &mem_free, sizeof(u64)); | ||
1871 | if (ret != sizeof(u64)) | ||
1872 | goto error; | ||
1873 | |||
1874 | if (ph->needs_swap) { | ||
1875 | node = bswap_32(node); | ||
1876 | mem_total = bswap_64(mem_total); | ||
1877 | mem_free = bswap_64(mem_free); | ||
1878 | } | ||
1879 | |||
1880 | strbuf_addf(&sb, "%u:%"PRIu64":%"PRIu64":", | ||
1881 | node, mem_total, mem_free); | ||
1882 | |||
1883 | str = do_read_string(fd, ph); | ||
1884 | if (!str) | ||
1885 | goto error; | ||
1886 | |||
1887 | /* include a NULL character at the end */ | ||
1888 | strbuf_add(&sb, str, strlen(str) + 1); | ||
1889 | free(str); | ||
1890 | } | ||
1891 | ph->env.numa_nodes = strbuf_detach(&sb, NULL); | ||
1892 | return 0; | ||
1893 | |||
1894 | error: | ||
1895 | strbuf_release(&sb); | ||
1896 | return -1; | ||
1897 | } | ||
1898 | |||
1899 | static int process_pmu_mappings(struct perf_file_section *section __maybe_unused, | ||
1900 | struct perf_header *ph, int fd, | ||
1901 | void *data __maybe_unused) | ||
1902 | { | ||
1903 | size_t ret; | ||
1904 | char *name; | ||
1905 | u32 pmu_num; | ||
1906 | u32 type; | ||
1907 | struct strbuf sb; | ||
1908 | |||
1909 | ret = read(fd, &pmu_num, sizeof(pmu_num)); | ||
1910 | if (ret != sizeof(pmu_num)) | ||
1911 | return -1; | ||
1912 | |||
1913 | if (ph->needs_swap) | ||
1914 | pmu_num = bswap_32(pmu_num); | ||
1915 | |||
1916 | if (!pmu_num) { | ||
1917 | pr_debug("pmu mappings not available\n"); | ||
1918 | return 0; | ||
1919 | } | ||
1920 | |||
1921 | ph->env.nr_pmu_mappings = pmu_num; | ||
1922 | strbuf_init(&sb, 128); | ||
1923 | |||
1924 | while (pmu_num) { | ||
1925 | if (read(fd, &type, sizeof(type)) != sizeof(type)) | ||
1926 | goto error; | ||
1927 | if (ph->needs_swap) | ||
1928 | type = bswap_32(type); | ||
1929 | |||
1930 | name = do_read_string(fd, ph); | ||
1931 | if (!name) | ||
1932 | goto error; | ||
1933 | |||
1934 | strbuf_addf(&sb, "%u:%s", type, name); | ||
1935 | /* include a NULL character at the end */ | ||
1936 | strbuf_add(&sb, "", 1); | ||
1937 | |||
1938 | free(name); | ||
1939 | pmu_num--; | ||
1940 | } | ||
1941 | ph->env.pmu_mappings = strbuf_detach(&sb, NULL); | ||
1942 | return 0; | ||
1943 | |||
1944 | error: | ||
1945 | strbuf_release(&sb); | ||
1946 | return -1; | ||
1947 | } | ||
1948 | |||
1507 | struct feature_ops { | 1949 | struct feature_ops { |
1508 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 1950 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1509 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 1951 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
1510 | int (*process)(struct perf_file_section *section, | 1952 | int (*process)(struct perf_file_section *section, |
1511 | struct perf_header *h, int feat, int fd, void *data); | 1953 | struct perf_header *h, int fd, void *data); |
1512 | const char *name; | 1954 | const char *name; |
1513 | bool full_only; | 1955 | bool full_only; |
1514 | }; | 1956 | }; |
@@ -1520,7 +1962,7 @@ struct feature_ops { | |||
1520 | .process = process_##func } | 1962 | .process = process_##func } |
1521 | #define FEAT_OPF(n, func) \ | 1963 | #define FEAT_OPF(n, func) \ |
1522 | [n] = { .name = #n, .write = write_##func, .print = print_##func, \ | 1964 | [n] = { .name = #n, .write = write_##func, .print = print_##func, \ |
1523 | .full_only = true } | 1965 | .process = process_##func, .full_only = true } |
1524 | 1966 | ||
1525 | /* feature_ops not implemented: */ | 1967 | /* feature_ops not implemented: */ |
1526 | #define print_tracing_data NULL | 1968 | #define print_tracing_data NULL |
@@ -1529,19 +1971,20 @@ struct feature_ops { | |||
1529 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | 1971 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { |
1530 | FEAT_OPP(HEADER_TRACING_DATA, tracing_data), | 1972 | FEAT_OPP(HEADER_TRACING_DATA, tracing_data), |
1531 | FEAT_OPP(HEADER_BUILD_ID, build_id), | 1973 | FEAT_OPP(HEADER_BUILD_ID, build_id), |
1532 | FEAT_OPA(HEADER_HOSTNAME, hostname), | 1974 | FEAT_OPP(HEADER_HOSTNAME, hostname), |
1533 | FEAT_OPA(HEADER_OSRELEASE, osrelease), | 1975 | FEAT_OPP(HEADER_OSRELEASE, osrelease), |
1534 | FEAT_OPA(HEADER_VERSION, version), | 1976 | FEAT_OPP(HEADER_VERSION, version), |
1535 | FEAT_OPA(HEADER_ARCH, arch), | 1977 | FEAT_OPP(HEADER_ARCH, arch), |
1536 | FEAT_OPA(HEADER_NRCPUS, nrcpus), | 1978 | FEAT_OPP(HEADER_NRCPUS, nrcpus), |
1537 | FEAT_OPA(HEADER_CPUDESC, cpudesc), | 1979 | FEAT_OPP(HEADER_CPUDESC, cpudesc), |
1538 | FEAT_OPA(HEADER_CPUID, cpuid), | 1980 | FEAT_OPP(HEADER_CPUID, cpuid), |
1539 | FEAT_OPA(HEADER_TOTAL_MEM, total_mem), | 1981 | FEAT_OPP(HEADER_TOTAL_MEM, total_mem), |
1540 | FEAT_OPA(HEADER_EVENT_DESC, event_desc), | 1982 | FEAT_OPP(HEADER_EVENT_DESC, event_desc), |
1541 | FEAT_OPA(HEADER_CMDLINE, cmdline), | 1983 | FEAT_OPP(HEADER_CMDLINE, cmdline), |
1542 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), | 1984 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), |
1543 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 1985 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
1544 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | 1986 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), |
1987 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), | ||
1545 | }; | 1988 | }; |
1546 | 1989 | ||
1547 | struct header_print_data { | 1990 | struct header_print_data { |
@@ -1683,17 +2126,17 @@ int perf_session__write_header(struct perf_session *session, | |||
1683 | struct perf_file_header f_header; | 2126 | struct perf_file_header f_header; |
1684 | struct perf_file_attr f_attr; | 2127 | struct perf_file_attr f_attr; |
1685 | struct perf_header *header = &session->header; | 2128 | struct perf_header *header = &session->header; |
1686 | struct perf_evsel *attr, *pair = NULL; | 2129 | struct perf_evsel *evsel, *pair = NULL; |
1687 | int err; | 2130 | int err; |
1688 | 2131 | ||
1689 | lseek(fd, sizeof(f_header), SEEK_SET); | 2132 | lseek(fd, sizeof(f_header), SEEK_SET); |
1690 | 2133 | ||
1691 | if (session->evlist != evlist) | 2134 | if (session->evlist != evlist) |
1692 | pair = list_entry(session->evlist->entries.next, struct perf_evsel, node); | 2135 | pair = perf_evlist__first(session->evlist); |
1693 | 2136 | ||
1694 | list_for_each_entry(attr, &evlist->entries, node) { | 2137 | list_for_each_entry(evsel, &evlist->entries, node) { |
1695 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 2138 | evsel->id_offset = lseek(fd, 0, SEEK_CUR); |
1696 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); | 2139 | err = do_write(fd, evsel->id, evsel->ids * sizeof(u64)); |
1697 | if (err < 0) { | 2140 | if (err < 0) { |
1698 | out_err_write: | 2141 | out_err_write: |
1699 | pr_debug("failed to write perf header\n"); | 2142 | pr_debug("failed to write perf header\n"); |
@@ -1703,19 +2146,19 @@ out_err_write: | |||
1703 | err = do_write(fd, pair->id, pair->ids * sizeof(u64)); | 2146 | err = do_write(fd, pair->id, pair->ids * sizeof(u64)); |
1704 | if (err < 0) | 2147 | if (err < 0) |
1705 | goto out_err_write; | 2148 | goto out_err_write; |
1706 | attr->ids += pair->ids; | 2149 | evsel->ids += pair->ids; |
1707 | pair = list_entry(pair->node.next, struct perf_evsel, node); | 2150 | pair = perf_evsel__next(pair); |
1708 | } | 2151 | } |
1709 | } | 2152 | } |
1710 | 2153 | ||
1711 | header->attr_offset = lseek(fd, 0, SEEK_CUR); | 2154 | header->attr_offset = lseek(fd, 0, SEEK_CUR); |
1712 | 2155 | ||
1713 | list_for_each_entry(attr, &evlist->entries, node) { | 2156 | list_for_each_entry(evsel, &evlist->entries, node) { |
1714 | f_attr = (struct perf_file_attr){ | 2157 | f_attr = (struct perf_file_attr){ |
1715 | .attr = attr->attr, | 2158 | .attr = evsel->attr, |
1716 | .ids = { | 2159 | .ids = { |
1717 | .offset = attr->id_offset, | 2160 | .offset = evsel->id_offset, |
1718 | .size = attr->ids * sizeof(u64), | 2161 | .size = evsel->ids * sizeof(u64), |
1719 | } | 2162 | } |
1720 | }; | 2163 | }; |
1721 | err = do_write(fd, &f_attr, sizeof(f_attr)); | 2164 | err = do_write(fd, &f_attr, sizeof(f_attr)); |
@@ -1726,9 +2169,9 @@ out_err_write: | |||
1726 | } | 2169 | } |
1727 | 2170 | ||
1728 | header->event_offset = lseek(fd, 0, SEEK_CUR); | 2171 | header->event_offset = lseek(fd, 0, SEEK_CUR); |
1729 | header->event_size = event_count * sizeof(struct perf_trace_event_type); | 2172 | header->event_size = trace_event_count * sizeof(struct perf_trace_event_type); |
1730 | if (events) { | 2173 | if (trace_events) { |
1731 | err = do_write(fd, events, header->event_size); | 2174 | err = do_write(fd, trace_events, header->event_size); |
1732 | if (err < 0) { | 2175 | if (err < 0) { |
1733 | pr_debug("failed to write perf header events\n"); | 2176 | pr_debug("failed to write perf header events\n"); |
1734 | return err; | 2177 | return err; |
@@ -1829,6 +2272,8 @@ out_free: | |||
1829 | static const int attr_file_abi_sizes[] = { | 2272 | static const int attr_file_abi_sizes[] = { |
1830 | [0] = PERF_ATTR_SIZE_VER0, | 2273 | [0] = PERF_ATTR_SIZE_VER0, |
1831 | [1] = PERF_ATTR_SIZE_VER1, | 2274 | [1] = PERF_ATTR_SIZE_VER1, |
2275 | [2] = PERF_ATTR_SIZE_VER2, | ||
2276 | [3] = PERF_ATTR_SIZE_VER3, | ||
1832 | 0, | 2277 | 0, |
1833 | }; | 2278 | }; |
1834 | 2279 | ||
@@ -2019,7 +2464,7 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
2019 | if (!feat_ops[feat].process) | 2464 | if (!feat_ops[feat].process) |
2020 | return 0; | 2465 | return 0; |
2021 | 2466 | ||
2022 | return feat_ops[feat].process(section, ph, feat, fd, data); | 2467 | return feat_ops[feat].process(section, ph, fd, data); |
2023 | } | 2468 | } |
2024 | 2469 | ||
2025 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, | 2470 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, |
@@ -2108,32 +2553,39 @@ static int read_attr(int fd, struct perf_header *ph, | |||
2108 | return ret <= 0 ? -1 : 0; | 2553 | return ret <= 0 ? -1 : 0; |
2109 | } | 2554 | } |
2110 | 2555 | ||
2111 | static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel, | 2556 | static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, |
2112 | struct pevent *pevent) | 2557 | struct pevent *pevent) |
2113 | { | 2558 | { |
2114 | struct event_format *event = pevent_find_event(pevent, | 2559 | struct event_format *event; |
2115 | evsel->attr.config); | ||
2116 | char bf[128]; | 2560 | char bf[128]; |
2117 | 2561 | ||
2562 | /* already prepared */ | ||
2563 | if (evsel->tp_format) | ||
2564 | return 0; | ||
2565 | |||
2566 | event = pevent_find_event(pevent, evsel->attr.config); | ||
2118 | if (event == NULL) | 2567 | if (event == NULL) |
2119 | return -1; | 2568 | return -1; |
2120 | 2569 | ||
2121 | snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); | 2570 | if (!evsel->name) { |
2122 | evsel->name = strdup(bf); | 2571 | snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); |
2123 | if (event->name == NULL) | 2572 | evsel->name = strdup(bf); |
2124 | return -1; | 2573 | if (evsel->name == NULL) |
2574 | return -1; | ||
2575 | } | ||
2125 | 2576 | ||
2577 | evsel->tp_format = event; | ||
2126 | return 0; | 2578 | return 0; |
2127 | } | 2579 | } |
2128 | 2580 | ||
2129 | static int perf_evlist__set_tracepoint_names(struct perf_evlist *evlist, | 2581 | static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, |
2130 | struct pevent *pevent) | 2582 | struct pevent *pevent) |
2131 | { | 2583 | { |
2132 | struct perf_evsel *pos; | 2584 | struct perf_evsel *pos; |
2133 | 2585 | ||
2134 | list_for_each_entry(pos, &evlist->entries, node) { | 2586 | list_for_each_entry(pos, &evlist->entries, node) { |
2135 | if (pos->attr.type == PERF_TYPE_TRACEPOINT && | 2587 | if (pos->attr.type == PERF_TYPE_TRACEPOINT && |
2136 | perf_evsel__set_tracepoint_name(pos, pevent)) | 2588 | perf_evsel__prepare_tracepoint_event(pos, pevent)) |
2137 | return -1; | 2589 | return -1; |
2138 | } | 2590 | } |
2139 | 2591 | ||
@@ -2176,6 +2628,8 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2176 | 2628 | ||
2177 | if (evsel == NULL) | 2629 | if (evsel == NULL) |
2178 | goto out_delete_evlist; | 2630 | goto out_delete_evlist; |
2631 | |||
2632 | evsel->needs_swap = header->needs_swap; | ||
2179 | /* | 2633 | /* |
2180 | * Do it before so that if perf_evsel__alloc_id fails, this | 2634 | * Do it before so that if perf_evsel__alloc_id fails, this |
2181 | * entry gets purged too at perf_evlist__delete(). | 2635 | * entry gets purged too at perf_evlist__delete(). |
@@ -2207,13 +2661,13 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2207 | 2661 | ||
2208 | if (f_header.event_types.size) { | 2662 | if (f_header.event_types.size) { |
2209 | lseek(fd, f_header.event_types.offset, SEEK_SET); | 2663 | lseek(fd, f_header.event_types.offset, SEEK_SET); |
2210 | events = malloc(f_header.event_types.size); | 2664 | trace_events = malloc(f_header.event_types.size); |
2211 | if (events == NULL) | 2665 | if (trace_events == NULL) |
2212 | return -ENOMEM; | 2666 | return -ENOMEM; |
2213 | if (perf_header__getbuffer64(header, fd, events, | 2667 | if (perf_header__getbuffer64(header, fd, trace_events, |
2214 | f_header.event_types.size)) | 2668 | f_header.event_types.size)) |
2215 | goto out_errno; | 2669 | goto out_errno; |
2216 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 2670 | trace_event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
2217 | } | 2671 | } |
2218 | 2672 | ||
2219 | perf_header__process_sections(header, fd, &session->pevent, | 2673 | perf_header__process_sections(header, fd, &session->pevent, |
@@ -2221,7 +2675,8 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2221 | 2675 | ||
2222 | lseek(fd, header->data_offset, SEEK_SET); | 2676 | lseek(fd, header->data_offset, SEEK_SET); |
2223 | 2677 | ||
2224 | if (perf_evlist__set_tracepoint_names(session->evlist, session->pevent)) | 2678 | if (perf_evlist__prepare_tracepoint_events(session->evlist, |
2679 | session->pevent)) | ||
2225 | goto out_delete_evlist; | 2680 | goto out_delete_evlist; |
2226 | 2681 | ||
2227 | header->frozen = 1; | 2682 | header->frozen = 1; |
@@ -2236,7 +2691,7 @@ out_delete_evlist: | |||
2236 | } | 2691 | } |
2237 | 2692 | ||
2238 | int perf_event__synthesize_attr(struct perf_tool *tool, | 2693 | int perf_event__synthesize_attr(struct perf_tool *tool, |
2239 | struct perf_event_attr *attr, u16 ids, u64 *id, | 2694 | struct perf_event_attr *attr, u32 ids, u64 *id, |
2240 | perf_event__handler_t process) | 2695 | perf_event__handler_t process) |
2241 | { | 2696 | { |
2242 | union perf_event *ev; | 2697 | union perf_event *ev; |
@@ -2244,7 +2699,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool, | |||
2244 | int err; | 2699 | int err; |
2245 | 2700 | ||
2246 | size = sizeof(struct perf_event_attr); | 2701 | size = sizeof(struct perf_event_attr); |
2247 | size = ALIGN(size, sizeof(u64)); | 2702 | size = PERF_ALIGN(size, sizeof(u64)); |
2248 | size += sizeof(struct perf_event_header); | 2703 | size += sizeof(struct perf_event_header); |
2249 | size += ids * sizeof(u64); | 2704 | size += ids * sizeof(u64); |
2250 | 2705 | ||
@@ -2257,9 +2712,12 @@ int perf_event__synthesize_attr(struct perf_tool *tool, | |||
2257 | memcpy(ev->attr.id, id, ids * sizeof(u64)); | 2712 | memcpy(ev->attr.id, id, ids * sizeof(u64)); |
2258 | 2713 | ||
2259 | ev->attr.header.type = PERF_RECORD_HEADER_ATTR; | 2714 | ev->attr.header.type = PERF_RECORD_HEADER_ATTR; |
2260 | ev->attr.header.size = size; | 2715 | ev->attr.header.size = (u16)size; |
2261 | 2716 | ||
2262 | err = process(tool, ev, NULL, NULL); | 2717 | if (ev->attr.header.size == size) |
2718 | err = process(tool, ev, NULL, NULL); | ||
2719 | else | ||
2720 | err = -E2BIG; | ||
2263 | 2721 | ||
2264 | free(ev); | 2722 | free(ev); |
2265 | 2723 | ||
@@ -2270,12 +2728,12 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, | |||
2270 | struct perf_session *session, | 2728 | struct perf_session *session, |
2271 | perf_event__handler_t process) | 2729 | perf_event__handler_t process) |
2272 | { | 2730 | { |
2273 | struct perf_evsel *attr; | 2731 | struct perf_evsel *evsel; |
2274 | int err = 0; | 2732 | int err = 0; |
2275 | 2733 | ||
2276 | list_for_each_entry(attr, &session->evlist->entries, node) { | 2734 | list_for_each_entry(evsel, &session->evlist->entries, node) { |
2277 | err = perf_event__synthesize_attr(tool, &attr->attr, attr->ids, | 2735 | err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, |
2278 | attr->id, process); | 2736 | evsel->id, process); |
2279 | if (err) { | 2737 | if (err) { |
2280 | pr_debug("failed to create perf header attribute\n"); | 2738 | pr_debug("failed to create perf header attribute\n"); |
2281 | return err; | 2739 | return err; |
@@ -2288,7 +2746,7 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, | |||
2288 | int perf_event__process_attr(union perf_event *event, | 2746 | int perf_event__process_attr(union perf_event *event, |
2289 | struct perf_evlist **pevlist) | 2747 | struct perf_evlist **pevlist) |
2290 | { | 2748 | { |
2291 | unsigned int i, ids, n_ids; | 2749 | u32 i, ids, n_ids; |
2292 | struct perf_evsel *evsel; | 2750 | struct perf_evsel *evsel; |
2293 | struct perf_evlist *evlist = *pevlist; | 2751 | struct perf_evlist *evlist = *pevlist; |
2294 | 2752 | ||
@@ -2339,7 +2797,7 @@ int perf_event__synthesize_event_type(struct perf_tool *tool, | |||
2339 | 2797 | ||
2340 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; | 2798 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; |
2341 | size = strlen(ev.event_type.event_type.name); | 2799 | size = strlen(ev.event_type.event_type.name); |
2342 | size = ALIGN(size, sizeof(u64)); | 2800 | size = PERF_ALIGN(size, sizeof(u64)); |
2343 | ev.event_type.header.size = sizeof(ev.event_type) - | 2801 | ev.event_type.header.size = sizeof(ev.event_type) - |
2344 | (sizeof(ev.event_type.event_type.name) - size); | 2802 | (sizeof(ev.event_type.event_type.name) - size); |
2345 | 2803 | ||
@@ -2355,8 +2813,8 @@ int perf_event__synthesize_event_types(struct perf_tool *tool, | |||
2355 | struct perf_trace_event_type *type; | 2813 | struct perf_trace_event_type *type; |
2356 | int i, err = 0; | 2814 | int i, err = 0; |
2357 | 2815 | ||
2358 | for (i = 0; i < event_count; i++) { | 2816 | for (i = 0; i < trace_event_count; i++) { |
2359 | type = &events[i]; | 2817 | type = &trace_events[i]; |
2360 | 2818 | ||
2361 | err = perf_event__synthesize_event_type(tool, type->event_id, | 2819 | err = perf_event__synthesize_event_type(tool, type->event_id, |
2362 | type->name, process, | 2820 | type->name, process, |
@@ -2370,7 +2828,7 @@ int perf_event__synthesize_event_types(struct perf_tool *tool, | |||
2370 | return err; | 2828 | return err; |
2371 | } | 2829 | } |
2372 | 2830 | ||
2373 | int perf_event__process_event_type(struct perf_tool *tool __unused, | 2831 | int perf_event__process_event_type(struct perf_tool *tool __maybe_unused, |
2374 | union perf_event *event) | 2832 | union perf_event *event) |
2375 | { | 2833 | { |
2376 | if (perf_header__push_event(event->event_type.event_type.event_id, | 2834 | if (perf_header__push_event(event->event_type.event_type.event_id, |
@@ -2387,7 +2845,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, | |||
2387 | union perf_event ev; | 2845 | union perf_event ev; |
2388 | struct tracing_data *tdata; | 2846 | struct tracing_data *tdata; |
2389 | ssize_t size = 0, aligned_size = 0, padding; | 2847 | ssize_t size = 0, aligned_size = 0, padding; |
2390 | int err __used = 0; | 2848 | int err __maybe_unused = 0; |
2391 | 2849 | ||
2392 | /* | 2850 | /* |
2393 | * We are going to store the size of the data followed | 2851 | * We are going to store the size of the data followed |
@@ -2408,7 +2866,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, | |||
2408 | 2866 | ||
2409 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 2867 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
2410 | size = tdata->size; | 2868 | size = tdata->size; |
2411 | aligned_size = ALIGN(size, sizeof(u64)); | 2869 | aligned_size = PERF_ALIGN(size, sizeof(u64)); |
2412 | padding = aligned_size - size; | 2870 | padding = aligned_size - size; |
2413 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 2871 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
2414 | ev.tracing_data.size = aligned_size; | 2872 | ev.tracing_data.size = aligned_size; |
@@ -2439,7 +2897,7 @@ int perf_event__process_tracing_data(union perf_event *event, | |||
2439 | 2897 | ||
2440 | size_read = trace_report(session->fd, &session->pevent, | 2898 | size_read = trace_report(session->fd, &session->pevent, |
2441 | session->repipe); | 2899 | session->repipe); |
2442 | padding = ALIGN(size_read, sizeof(u64)) - size_read; | 2900 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; |
2443 | 2901 | ||
2444 | if (read(session->fd, buf, padding) < 0) | 2902 | if (read(session->fd, buf, padding) < 0) |
2445 | die("reading input file"); | 2903 | die("reading input file"); |
@@ -2452,6 +2910,9 @@ int perf_event__process_tracing_data(union perf_event *event, | |||
2452 | if (size_read + padding != size) | 2910 | if (size_read + padding != size) |
2453 | die("tracing data size mismatch"); | 2911 | die("tracing data size mismatch"); |
2454 | 2912 | ||
2913 | perf_evlist__prepare_tracepoint_events(session->evlist, | ||
2914 | session->pevent); | ||
2915 | |||
2455 | return size_read + padding; | 2916 | return size_read + padding; |
2456 | } | 2917 | } |
2457 | 2918 | ||
@@ -2470,7 +2931,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, | |||
2470 | memset(&ev, 0, sizeof(ev)); | 2931 | memset(&ev, 0, sizeof(ev)); |
2471 | 2932 | ||
2472 | len = pos->long_name_len + 1; | 2933 | len = pos->long_name_len + 1; |
2473 | len = ALIGN(len, NAME_ALIGN); | 2934 | len = PERF_ALIGN(len, NAME_ALIGN); |
2474 | memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); | 2935 | memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); |
2475 | ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; | 2936 | ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; |
2476 | ev.build_id.header.misc = misc; | 2937 | ev.build_id.header.misc = misc; |
@@ -2483,7 +2944,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, | |||
2483 | return err; | 2944 | return err; |
2484 | } | 2945 | } |
2485 | 2946 | ||
2486 | int perf_event__process_build_id(struct perf_tool *tool __used, | 2947 | int perf_event__process_build_id(struct perf_tool *tool __maybe_unused, |
2487 | union perf_event *event, | 2948 | union perf_event *event, |
2488 | struct perf_session *session) | 2949 | struct perf_session *session) |
2489 | { | 2950 | { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 2d42b3e1826f..879d215cdac9 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_HEADER_H | 1 | #ifndef __PERF_HEADER_H |
2 | #define __PERF_HEADER_H | 2 | #define __PERF_HEADER_H |
3 | 3 | ||
4 | #include "../../../include/linux/perf_event.h" | 4 | #include "../../../include/uapi/linux/perf_event.h" |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include "types.h" |
@@ -28,6 +28,7 @@ enum { | |||
28 | HEADER_CPU_TOPOLOGY, | 28 | HEADER_CPU_TOPOLOGY, |
29 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
30 | HEADER_BRANCH_STACK, | 30 | HEADER_BRANCH_STACK, |
31 | HEADER_PMU_MAPPINGS, | ||
31 | HEADER_LAST_FEATURE, | 32 | HEADER_LAST_FEATURE, |
32 | HEADER_FEAT_BITS = 256, | 33 | HEADER_FEAT_BITS = 256, |
33 | }; | 34 | }; |
@@ -57,6 +58,29 @@ struct perf_header; | |||
57 | int perf_file_header__read(struct perf_file_header *header, | 58 | int perf_file_header__read(struct perf_file_header *header, |
58 | struct perf_header *ph, int fd); | 59 | struct perf_header *ph, int fd); |
59 | 60 | ||
61 | struct perf_session_env { | ||
62 | char *hostname; | ||
63 | char *os_release; | ||
64 | char *version; | ||
65 | char *arch; | ||
66 | int nr_cpus_online; | ||
67 | int nr_cpus_avail; | ||
68 | char *cpu_desc; | ||
69 | char *cpuid; | ||
70 | unsigned long long total_mem; | ||
71 | |||
72 | int nr_cmdline; | ||
73 | char *cmdline; | ||
74 | int nr_sibling_cores; | ||
75 | char *sibling_cores; | ||
76 | int nr_sibling_threads; | ||
77 | char *sibling_threads; | ||
78 | int nr_numa_nodes; | ||
79 | char *numa_nodes; | ||
80 | int nr_pmu_mappings; | ||
81 | char *pmu_mappings; | ||
82 | }; | ||
83 | |||
60 | struct perf_header { | 84 | struct perf_header { |
61 | int frozen; | 85 | int frozen; |
62 | bool needs_swap; | 86 | bool needs_swap; |
@@ -66,6 +90,7 @@ struct perf_header { | |||
66 | u64 event_offset; | 90 | u64 event_offset; |
67 | u64 event_size; | 91 | u64 event_size; |
68 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 92 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
93 | struct perf_session_env env; | ||
69 | }; | 94 | }; |
70 | 95 | ||
71 | struct perf_evlist; | 96 | struct perf_evlist; |
@@ -95,11 +120,11 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
95 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | 120 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); |
96 | 121 | ||
97 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 122 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
98 | const char *name, bool is_kallsyms); | 123 | const char *name, bool is_kallsyms, bool is_vdso); |
99 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | 124 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); |
100 | 125 | ||
101 | int perf_event__synthesize_attr(struct perf_tool *tool, | 126 | int perf_event__synthesize_attr(struct perf_tool *tool, |
102 | struct perf_event_attr *attr, u16 ids, u64 *id, | 127 | struct perf_event_attr *attr, u32 ids, u64 *id, |
103 | perf_event__handler_t process); | 128 | perf_event__handler_t process); |
104 | int perf_event__synthesize_attrs(struct perf_tool *tool, | 129 | int perf_event__synthesize_attrs(struct perf_tool *tool, |
105 | struct perf_session *session, | 130 | struct perf_session *session, |
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 6f2975a00358..8b1f6e891b8a 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include "exec_cmd.h" | 3 | #include "exec_cmd.h" |
4 | #include "levenshtein.h" | 4 | #include "levenshtein.h" |
5 | #include "help.h" | 5 | #include "help.h" |
6 | #include <termios.h> | ||
6 | 7 | ||
7 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) | 8 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) |
8 | { | 9 | { |
@@ -331,7 +332,8 @@ const char *help_unknown_cmd(const char *cmd) | |||
331 | exit(1); | 332 | exit(1); |
332 | } | 333 | } |
333 | 334 | ||
334 | int cmd_version(int argc __used, const char **argv __used, const char *prefix __used) | 335 | int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused, |
336 | const char *prefix __maybe_unused) | ||
335 | { | 337 | { |
336 | printf("perf version %s\n", perf_version_string); | 338 | printf("perf version %s\n", perf_version_string); |
337 | return 0; | 339 | return 0; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f247ef2789a4..277947a669b2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -45,7 +45,7 @@ bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len) | |||
45 | return false; | 45 | return false; |
46 | } | 46 | } |
47 | 47 | ||
48 | static void hists__reset_col_len(struct hists *hists) | 48 | void hists__reset_col_len(struct hists *hists) |
49 | { | 49 | { |
50 | enum hist_column col; | 50 | enum hist_column col; |
51 | 51 | ||
@@ -63,7 +63,7 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso) | |||
63 | hists__set_col_len(hists, dso, unresolved_col_width); | 63 | hists__set_col_len(hists, dso, unresolved_col_width); |
64 | } | 64 | } |
65 | 65 | ||
66 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | 66 | void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
67 | { | 67 | { |
68 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 68 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
69 | u16 len; | 69 | u16 len; |
@@ -114,36 +114,68 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
114 | } | 114 | } |
115 | } | 115 | } |
116 | 116 | ||
117 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | ||
118 | { | ||
119 | struct rb_node *next = rb_first(&hists->entries); | ||
120 | struct hist_entry *n; | ||
121 | int row = 0; | ||
122 | |||
123 | hists__reset_col_len(hists); | ||
124 | |||
125 | while (next && row++ < max_rows) { | ||
126 | n = rb_entry(next, struct hist_entry, rb_node); | ||
127 | if (!n->filtered) | ||
128 | hists__calc_col_len(hists, n); | ||
129 | next = rb_next(&n->rb_node); | ||
130 | } | ||
131 | } | ||
132 | |||
117 | static void hist_entry__add_cpumode_period(struct hist_entry *he, | 133 | static void hist_entry__add_cpumode_period(struct hist_entry *he, |
118 | unsigned int cpumode, u64 period) | 134 | unsigned int cpumode, u64 period) |
119 | { | 135 | { |
120 | switch (cpumode) { | 136 | switch (cpumode) { |
121 | case PERF_RECORD_MISC_KERNEL: | 137 | case PERF_RECORD_MISC_KERNEL: |
122 | he->period_sys += period; | 138 | he->stat.period_sys += period; |
123 | break; | 139 | break; |
124 | case PERF_RECORD_MISC_USER: | 140 | case PERF_RECORD_MISC_USER: |
125 | he->period_us += period; | 141 | he->stat.period_us += period; |
126 | break; | 142 | break; |
127 | case PERF_RECORD_MISC_GUEST_KERNEL: | 143 | case PERF_RECORD_MISC_GUEST_KERNEL: |
128 | he->period_guest_sys += period; | 144 | he->stat.period_guest_sys += period; |
129 | break; | 145 | break; |
130 | case PERF_RECORD_MISC_GUEST_USER: | 146 | case PERF_RECORD_MISC_GUEST_USER: |
131 | he->period_guest_us += period; | 147 | he->stat.period_guest_us += period; |
132 | break; | 148 | break; |
133 | default: | 149 | default: |
134 | break; | 150 | break; |
135 | } | 151 | } |
136 | } | 152 | } |
137 | 153 | ||
154 | static void he_stat__add_period(struct he_stat *he_stat, u64 period) | ||
155 | { | ||
156 | he_stat->period += period; | ||
157 | he_stat->nr_events += 1; | ||
158 | } | ||
159 | |||
160 | static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) | ||
161 | { | ||
162 | dest->period += src->period; | ||
163 | dest->period_sys += src->period_sys; | ||
164 | dest->period_us += src->period_us; | ||
165 | dest->period_guest_sys += src->period_guest_sys; | ||
166 | dest->period_guest_us += src->period_guest_us; | ||
167 | dest->nr_events += src->nr_events; | ||
168 | } | ||
169 | |||
138 | static void hist_entry__decay(struct hist_entry *he) | 170 | static void hist_entry__decay(struct hist_entry *he) |
139 | { | 171 | { |
140 | he->period = (he->period * 7) / 8; | 172 | he->stat.period = (he->stat.period * 7) / 8; |
141 | he->nr_events = (he->nr_events * 7) / 8; | 173 | he->stat.nr_events = (he->stat.nr_events * 7) / 8; |
142 | } | 174 | } |
143 | 175 | ||
144 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 176 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
145 | { | 177 | { |
146 | u64 prev_period = he->period; | 178 | u64 prev_period = he->stat.period; |
147 | 179 | ||
148 | if (prev_period == 0) | 180 | if (prev_period == 0) |
149 | return true; | 181 | return true; |
@@ -151,9 +183,9 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | |||
151 | hist_entry__decay(he); | 183 | hist_entry__decay(he); |
152 | 184 | ||
153 | if (!he->filtered) | 185 | if (!he->filtered) |
154 | hists->stats.total_period -= prev_period - he->period; | 186 | hists->stats.total_period -= prev_period - he->stat.period; |
155 | 187 | ||
156 | return he->period == 0; | 188 | return he->stat.period == 0; |
157 | } | 189 | } |
158 | 190 | ||
159 | static void __hists__decay_entries(struct hists *hists, bool zap_user, | 191 | static void __hists__decay_entries(struct hists *hists, bool zap_user, |
@@ -207,7 +239,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
207 | 239 | ||
208 | if (he != NULL) { | 240 | if (he != NULL) { |
209 | *he = *template; | 241 | *he = *template; |
210 | he->nr_events = 1; | 242 | |
211 | if (he->ms.map) | 243 | if (he->ms.map) |
212 | he->ms.map->referenced = true; | 244 | he->ms.map->referenced = true; |
213 | if (symbol_conf.use_callchain) | 245 | if (symbol_conf.use_callchain) |
@@ -222,7 +254,7 @@ static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) | |||
222 | if (!h->filtered) { | 254 | if (!h->filtered) { |
223 | hists__calc_col_len(hists, h); | 255 | hists__calc_col_len(hists, h); |
224 | ++hists->nr_entries; | 256 | ++hists->nr_entries; |
225 | hists->stats.total_period += h->period; | 257 | hists->stats.total_period += h->stat.period; |
226 | } | 258 | } |
227 | } | 259 | } |
228 | 260 | ||
@@ -254,8 +286,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
254 | cmp = hist_entry__cmp(entry, he); | 286 | cmp = hist_entry__cmp(entry, he); |
255 | 287 | ||
256 | if (!cmp) { | 288 | if (!cmp) { |
257 | he->period += period; | 289 | he_stat__add_period(&he->stat, period); |
258 | ++he->nr_events; | ||
259 | 290 | ||
260 | /* If the map of an existing hist_entry has | 291 | /* If the map of an existing hist_entry has |
261 | * become out-of-date due to an exec() or | 292 | * become out-of-date due to an exec() or |
@@ -305,10 +336,14 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, | |||
305 | .cpu = al->cpu, | 336 | .cpu = al->cpu, |
306 | .ip = bi->to.addr, | 337 | .ip = bi->to.addr, |
307 | .level = al->level, | 338 | .level = al->level, |
308 | .period = period, | 339 | .stat = { |
340 | .period = period, | ||
341 | .nr_events = 1, | ||
342 | }, | ||
309 | .parent = sym_parent, | 343 | .parent = sym_parent, |
310 | .filtered = symbol__parent_filter(sym_parent), | 344 | .filtered = symbol__parent_filter(sym_parent), |
311 | .branch_info = bi, | 345 | .branch_info = bi, |
346 | .hists = self, | ||
312 | }; | 347 | }; |
313 | 348 | ||
314 | return add_hist_entry(self, &entry, al, period); | 349 | return add_hist_entry(self, &entry, al, period); |
@@ -327,9 +362,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
327 | .cpu = al->cpu, | 362 | .cpu = al->cpu, |
328 | .ip = al->addr, | 363 | .ip = al->addr, |
329 | .level = al->level, | 364 | .level = al->level, |
330 | .period = period, | 365 | .stat = { |
366 | .period = period, | ||
367 | .nr_events = 1, | ||
368 | }, | ||
331 | .parent = sym_parent, | 369 | .parent = sym_parent, |
332 | .filtered = symbol__parent_filter(sym_parent), | 370 | .filtered = symbol__parent_filter(sym_parent), |
371 | .hists = self, | ||
333 | }; | 372 | }; |
334 | 373 | ||
335 | return add_hist_entry(self, &entry, al, period); | 374 | return add_hist_entry(self, &entry, al, period); |
@@ -378,7 +417,7 @@ void hist_entry__free(struct hist_entry *he) | |||
378 | * collapse the histogram | 417 | * collapse the histogram |
379 | */ | 418 | */ |
380 | 419 | ||
381 | static bool hists__collapse_insert_entry(struct hists *hists __used, | 420 | static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, |
382 | struct rb_root *root, | 421 | struct rb_root *root, |
383 | struct hist_entry *he) | 422 | struct hist_entry *he) |
384 | { | 423 | { |
@@ -394,8 +433,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __used, | |||
394 | cmp = hist_entry__collapse(iter, he); | 433 | cmp = hist_entry__collapse(iter, he); |
395 | 434 | ||
396 | if (!cmp) { | 435 | if (!cmp) { |
397 | iter->period += he->period; | 436 | he_stat__add_stat(&iter->stat, &he->stat); |
398 | iter->nr_events += he->nr_events; | 437 | |
399 | if (symbol_conf.use_callchain) { | 438 | if (symbol_conf.use_callchain) { |
400 | callchain_cursor_reset(&callchain_cursor); | 439 | callchain_cursor_reset(&callchain_cursor); |
401 | callchain_merge(&callchain_cursor, | 440 | callchain_merge(&callchain_cursor, |
@@ -497,7 +536,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
497 | parent = *p; | 536 | parent = *p; |
498 | iter = rb_entry(parent, struct hist_entry, rb_node); | 537 | iter = rb_entry(parent, struct hist_entry, rb_node); |
499 | 538 | ||
500 | if (he->period > iter->period) | 539 | if (he->stat.period > iter->stat.period) |
501 | p = &(*p)->rb_left; | 540 | p = &(*p)->rb_left; |
502 | else | 541 | else |
503 | p = &(*p)->rb_right; | 542 | p = &(*p)->rb_right; |
@@ -547,674 +586,6 @@ void hists__output_resort_threaded(struct hists *hists) | |||
547 | return __hists__output_resort(hists, true); | 586 | return __hists__output_resort(hists, true); |
548 | } | 587 | } |
549 | 588 | ||
550 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
551 | { | ||
552 | int i; | ||
553 | int ret = fprintf(fp, " "); | ||
554 | |||
555 | for (i = 0; i < left_margin; i++) | ||
556 | ret += fprintf(fp, " "); | ||
557 | |||
558 | return ret; | ||
559 | } | ||
560 | |||
561 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
562 | int left_margin) | ||
563 | { | ||
564 | int i; | ||
565 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | ||
566 | |||
567 | for (i = 0; i < depth; i++) | ||
568 | if (depth_mask & (1 << i)) | ||
569 | ret += fprintf(fp, "| "); | ||
570 | else | ||
571 | ret += fprintf(fp, " "); | ||
572 | |||
573 | ret += fprintf(fp, "\n"); | ||
574 | |||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | ||
579 | int depth, int depth_mask, int period, | ||
580 | u64 total_samples, u64 hits, | ||
581 | int left_margin) | ||
582 | { | ||
583 | int i; | ||
584 | size_t ret = 0; | ||
585 | |||
586 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
587 | for (i = 0; i < depth; i++) { | ||
588 | if (depth_mask & (1 << i)) | ||
589 | ret += fprintf(fp, "|"); | ||
590 | else | ||
591 | ret += fprintf(fp, " "); | ||
592 | if (!period && i == depth - 1) { | ||
593 | double percent; | ||
594 | |||
595 | percent = hits * 100.0 / total_samples; | ||
596 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
597 | } else | ||
598 | ret += fprintf(fp, "%s", " "); | ||
599 | } | ||
600 | if (chain->ms.sym) | ||
601 | ret += fprintf(fp, "%s\n", chain->ms.sym->name); | ||
602 | else | ||
603 | ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip); | ||
604 | |||
605 | return ret; | ||
606 | } | ||
607 | |||
608 | static struct symbol *rem_sq_bracket; | ||
609 | static struct callchain_list rem_hits; | ||
610 | |||
611 | static void init_rem_hits(void) | ||
612 | { | ||
613 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
614 | if (!rem_sq_bracket) { | ||
615 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
616 | return; | ||
617 | } | ||
618 | |||
619 | strcpy(rem_sq_bracket->name, "[...]"); | ||
620 | rem_hits.ms.sym = rem_sq_bracket; | ||
621 | } | ||
622 | |||
623 | static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, | ||
624 | u64 total_samples, int depth, | ||
625 | int depth_mask, int left_margin) | ||
626 | { | ||
627 | struct rb_node *node, *next; | ||
628 | struct callchain_node *child; | ||
629 | struct callchain_list *chain; | ||
630 | int new_depth_mask = depth_mask; | ||
631 | u64 remaining; | ||
632 | size_t ret = 0; | ||
633 | int i; | ||
634 | uint entries_printed = 0; | ||
635 | |||
636 | remaining = total_samples; | ||
637 | |||
638 | node = rb_first(root); | ||
639 | while (node) { | ||
640 | u64 new_total; | ||
641 | u64 cumul; | ||
642 | |||
643 | child = rb_entry(node, struct callchain_node, rb_node); | ||
644 | cumul = callchain_cumul_hits(child); | ||
645 | remaining -= cumul; | ||
646 | |||
647 | /* | ||
648 | * The depth mask manages the output of pipes that show | ||
649 | * the depth. We don't want to keep the pipes of the current | ||
650 | * level for the last child of this depth. | ||
651 | * Except if we have remaining filtered hits. They will | ||
652 | * supersede the last child | ||
653 | */ | ||
654 | next = rb_next(node); | ||
655 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
656 | new_depth_mask &= ~(1 << (depth - 1)); | ||
657 | |||
658 | /* | ||
659 | * But we keep the older depth mask for the line separator | ||
660 | * to keep the level link until we reach the last child | ||
661 | */ | ||
662 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
663 | left_margin); | ||
664 | i = 0; | ||
665 | list_for_each_entry(chain, &child->val, list) { | ||
666 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
667 | new_depth_mask, i++, | ||
668 | total_samples, | ||
669 | cumul, | ||
670 | left_margin); | ||
671 | } | ||
672 | |||
673 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
674 | new_total = child->children_hit; | ||
675 | else | ||
676 | new_total = total_samples; | ||
677 | |||
678 | ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, | ||
679 | depth + 1, | ||
680 | new_depth_mask | (1 << depth), | ||
681 | left_margin); | ||
682 | node = next; | ||
683 | if (++entries_printed == callchain_param.print_limit) | ||
684 | break; | ||
685 | } | ||
686 | |||
687 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
688 | remaining && remaining != total_samples) { | ||
689 | |||
690 | if (!rem_sq_bracket) | ||
691 | return ret; | ||
692 | |||
693 | new_depth_mask &= ~(1 << (depth - 1)); | ||
694 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
695 | new_depth_mask, 0, total_samples, | ||
696 | remaining, left_margin); | ||
697 | } | ||
698 | |||
699 | return ret; | ||
700 | } | ||
701 | |||
702 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | ||
703 | u64 total_samples, int left_margin) | ||
704 | { | ||
705 | struct callchain_node *cnode; | ||
706 | struct callchain_list *chain; | ||
707 | u32 entries_printed = 0; | ||
708 | bool printed = false; | ||
709 | struct rb_node *node; | ||
710 | int i = 0; | ||
711 | int ret = 0; | ||
712 | |||
713 | /* | ||
714 | * If have one single callchain root, don't bother printing | ||
715 | * its percentage (100 % in fractal mode and the same percentage | ||
716 | * than the hist in graph mode). This also avoid one level of column. | ||
717 | */ | ||
718 | node = rb_first(root); | ||
719 | if (node && !rb_next(node)) { | ||
720 | cnode = rb_entry(node, struct callchain_node, rb_node); | ||
721 | list_for_each_entry(chain, &cnode->val, list) { | ||
722 | /* | ||
723 | * If we sort by symbol, the first entry is the same than | ||
724 | * the symbol. No need to print it otherwise it appears as | ||
725 | * displayed twice. | ||
726 | */ | ||
727 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
728 | continue; | ||
729 | if (!printed) { | ||
730 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
731 | ret += fprintf(fp, "|\n"); | ||
732 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
733 | ret += fprintf(fp, "---"); | ||
734 | left_margin += 3; | ||
735 | printed = true; | ||
736 | } else | ||
737 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
738 | |||
739 | if (chain->ms.sym) | ||
740 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | ||
741 | else | ||
742 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
743 | |||
744 | if (++entries_printed == callchain_param.print_limit) | ||
745 | break; | ||
746 | } | ||
747 | root = &cnode->rb_root; | ||
748 | } | ||
749 | |||
750 | ret += __callchain__fprintf_graph(fp, root, total_samples, | ||
751 | 1, 1, left_margin); | ||
752 | ret += fprintf(fp, "\n"); | ||
753 | |||
754 | return ret; | ||
755 | } | ||
756 | |||
757 | static size_t __callchain__fprintf_flat(FILE *fp, | ||
758 | struct callchain_node *self, | ||
759 | u64 total_samples) | ||
760 | { | ||
761 | struct callchain_list *chain; | ||
762 | size_t ret = 0; | ||
763 | |||
764 | if (!self) | ||
765 | return 0; | ||
766 | |||
767 | ret += __callchain__fprintf_flat(fp, self->parent, total_samples); | ||
768 | |||
769 | |||
770 | list_for_each_entry(chain, &self->val, list) { | ||
771 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
772 | continue; | ||
773 | if (chain->ms.sym) | ||
774 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | ||
775 | else | ||
776 | ret += fprintf(fp, " %p\n", | ||
777 | (void *)(long)chain->ip); | ||
778 | } | ||
779 | |||
780 | return ret; | ||
781 | } | ||
782 | |||
783 | static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, | ||
784 | u64 total_samples) | ||
785 | { | ||
786 | size_t ret = 0; | ||
787 | u32 entries_printed = 0; | ||
788 | struct rb_node *rb_node; | ||
789 | struct callchain_node *chain; | ||
790 | |||
791 | rb_node = rb_first(self); | ||
792 | while (rb_node) { | ||
793 | double percent; | ||
794 | |||
795 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
796 | percent = chain->hit * 100.0 / total_samples; | ||
797 | |||
798 | ret = percent_color_fprintf(fp, " %6.2f%%\n", percent); | ||
799 | ret += __callchain__fprintf_flat(fp, chain, total_samples); | ||
800 | ret += fprintf(fp, "\n"); | ||
801 | if (++entries_printed == callchain_param.print_limit) | ||
802 | break; | ||
803 | |||
804 | rb_node = rb_next(rb_node); | ||
805 | } | ||
806 | |||
807 | return ret; | ||
808 | } | ||
809 | |||
810 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | ||
811 | u64 total_samples, int left_margin, | ||
812 | FILE *fp) | ||
813 | { | ||
814 | switch (callchain_param.mode) { | ||
815 | case CHAIN_GRAPH_REL: | ||
816 | return callchain__fprintf_graph(fp, &he->sorted_chain, he->period, | ||
817 | left_margin); | ||
818 | break; | ||
819 | case CHAIN_GRAPH_ABS: | ||
820 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, | ||
821 | left_margin); | ||
822 | break; | ||
823 | case CHAIN_FLAT: | ||
824 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); | ||
825 | break; | ||
826 | case CHAIN_NONE: | ||
827 | break; | ||
828 | default: | ||
829 | pr_err("Bad callchain mode\n"); | ||
830 | } | ||
831 | |||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | ||
836 | { | ||
837 | struct rb_node *next = rb_first(&hists->entries); | ||
838 | struct hist_entry *n; | ||
839 | int row = 0; | ||
840 | |||
841 | hists__reset_col_len(hists); | ||
842 | |||
843 | while (next && row++ < max_rows) { | ||
844 | n = rb_entry(next, struct hist_entry, rb_node); | ||
845 | if (!n->filtered) | ||
846 | hists__calc_col_len(hists, n); | ||
847 | next = rb_next(&n->rb_node); | ||
848 | } | ||
849 | } | ||
850 | |||
851 | static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, | ||
852 | size_t size, struct hists *pair_hists, | ||
853 | bool show_displacement, long displacement, | ||
854 | bool color, u64 total_period) | ||
855 | { | ||
856 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | ||
857 | u64 nr_events; | ||
858 | const char *sep = symbol_conf.field_sep; | ||
859 | int ret; | ||
860 | |||
861 | if (symbol_conf.exclude_other && !he->parent) | ||
862 | return 0; | ||
863 | |||
864 | if (pair_hists) { | ||
865 | period = he->pair ? he->pair->period : 0; | ||
866 | nr_events = he->pair ? he->pair->nr_events : 0; | ||
867 | total = pair_hists->stats.total_period; | ||
868 | period_sys = he->pair ? he->pair->period_sys : 0; | ||
869 | period_us = he->pair ? he->pair->period_us : 0; | ||
870 | period_guest_sys = he->pair ? he->pair->period_guest_sys : 0; | ||
871 | period_guest_us = he->pair ? he->pair->period_guest_us : 0; | ||
872 | } else { | ||
873 | period = he->period; | ||
874 | nr_events = he->nr_events; | ||
875 | total = total_period; | ||
876 | period_sys = he->period_sys; | ||
877 | period_us = he->period_us; | ||
878 | period_guest_sys = he->period_guest_sys; | ||
879 | period_guest_us = he->period_guest_us; | ||
880 | } | ||
881 | |||
882 | if (total) { | ||
883 | if (color) | ||
884 | ret = percent_color_snprintf(s, size, | ||
885 | sep ? "%.2f" : " %6.2f%%", | ||
886 | (period * 100.0) / total); | ||
887 | else | ||
888 | ret = scnprintf(s, size, sep ? "%.2f" : " %6.2f%%", | ||
889 | (period * 100.0) / total); | ||
890 | if (symbol_conf.show_cpu_utilization) { | ||
891 | ret += percent_color_snprintf(s + ret, size - ret, | ||
892 | sep ? "%.2f" : " %6.2f%%", | ||
893 | (period_sys * 100.0) / total); | ||
894 | ret += percent_color_snprintf(s + ret, size - ret, | ||
895 | sep ? "%.2f" : " %6.2f%%", | ||
896 | (period_us * 100.0) / total); | ||
897 | if (perf_guest) { | ||
898 | ret += percent_color_snprintf(s + ret, | ||
899 | size - ret, | ||
900 | sep ? "%.2f" : " %6.2f%%", | ||
901 | (period_guest_sys * 100.0) / | ||
902 | total); | ||
903 | ret += percent_color_snprintf(s + ret, | ||
904 | size - ret, | ||
905 | sep ? "%.2f" : " %6.2f%%", | ||
906 | (period_guest_us * 100.0) / | ||
907 | total); | ||
908 | } | ||
909 | } | ||
910 | } else | ||
911 | ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); | ||
912 | |||
913 | if (symbol_conf.show_nr_samples) { | ||
914 | if (sep) | ||
915 | ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); | ||
916 | else | ||
917 | ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events); | ||
918 | } | ||
919 | |||
920 | if (symbol_conf.show_total_period) { | ||
921 | if (sep) | ||
922 | ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); | ||
923 | else | ||
924 | ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period); | ||
925 | } | ||
926 | |||
927 | if (pair_hists) { | ||
928 | char bf[32]; | ||
929 | double old_percent = 0, new_percent = 0, diff; | ||
930 | |||
931 | if (total > 0) | ||
932 | old_percent = (period * 100.0) / total; | ||
933 | if (total_period > 0) | ||
934 | new_percent = (he->period * 100.0) / total_period; | ||
935 | |||
936 | diff = new_percent - old_percent; | ||
937 | |||
938 | if (fabs(diff) >= 0.01) | ||
939 | scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); | ||
940 | else | ||
941 | scnprintf(bf, sizeof(bf), " "); | ||
942 | |||
943 | if (sep) | ||
944 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); | ||
945 | else | ||
946 | ret += scnprintf(s + ret, size - ret, "%11.11s", bf); | ||
947 | |||
948 | if (show_displacement) { | ||
949 | if (displacement) | ||
950 | scnprintf(bf, sizeof(bf), "%+4ld", displacement); | ||
951 | else | ||
952 | scnprintf(bf, sizeof(bf), " "); | ||
953 | |||
954 | if (sep) | ||
955 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); | ||
956 | else | ||
957 | ret += scnprintf(s + ret, size - ret, "%6.6s", bf); | ||
958 | } | ||
959 | } | ||
960 | |||
961 | return ret; | ||
962 | } | ||
963 | |||
964 | int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, | ||
965 | struct hists *hists) | ||
966 | { | ||
967 | const char *sep = symbol_conf.field_sep; | ||
968 | struct sort_entry *se; | ||
969 | int ret = 0; | ||
970 | |||
971 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
972 | if (se->elide) | ||
973 | continue; | ||
974 | |||
975 | ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); | ||
976 | ret += se->se_snprintf(he, s + ret, size - ret, | ||
977 | hists__col_len(hists, se->se_width_idx)); | ||
978 | } | ||
979 | |||
980 | return ret; | ||
981 | } | ||
982 | |||
983 | static int hist_entry__fprintf(struct hist_entry *he, size_t size, | ||
984 | struct hists *hists, struct hists *pair_hists, | ||
985 | bool show_displacement, long displacement, | ||
986 | u64 total_period, FILE *fp) | ||
987 | { | ||
988 | char bf[512]; | ||
989 | int ret; | ||
990 | |||
991 | if (size == 0 || size > sizeof(bf)) | ||
992 | size = sizeof(bf); | ||
993 | |||
994 | ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, | ||
995 | show_displacement, displacement, | ||
996 | true, total_period); | ||
997 | hist_entry__snprintf(he, bf + ret, size - ret, hists); | ||
998 | return fprintf(fp, "%s\n", bf); | ||
999 | } | ||
1000 | |||
1001 | static size_t hist_entry__fprintf_callchain(struct hist_entry *he, | ||
1002 | struct hists *hists, | ||
1003 | u64 total_period, FILE *fp) | ||
1004 | { | ||
1005 | int left_margin = 0; | ||
1006 | |||
1007 | if (sort__first_dimension == SORT_COMM) { | ||
1008 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | ||
1009 | typeof(*se), list); | ||
1010 | left_margin = hists__col_len(hists, se->se_width_idx); | ||
1011 | left_margin -= thread__comm_len(he->thread); | ||
1012 | } | ||
1013 | |||
1014 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); | ||
1015 | } | ||
1016 | |||
1017 | size_t hists__fprintf(struct hists *hists, struct hists *pair, | ||
1018 | bool show_displacement, bool show_header, int max_rows, | ||
1019 | int max_cols, FILE *fp) | ||
1020 | { | ||
1021 | struct sort_entry *se; | ||
1022 | struct rb_node *nd; | ||
1023 | size_t ret = 0; | ||
1024 | u64 total_period; | ||
1025 | unsigned long position = 1; | ||
1026 | long displacement = 0; | ||
1027 | unsigned int width; | ||
1028 | const char *sep = symbol_conf.field_sep; | ||
1029 | const char *col_width = symbol_conf.col_width_list_str; | ||
1030 | int nr_rows = 0; | ||
1031 | |||
1032 | init_rem_hits(); | ||
1033 | |||
1034 | if (!show_header) | ||
1035 | goto print_entries; | ||
1036 | |||
1037 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | ||
1038 | |||
1039 | if (symbol_conf.show_cpu_utilization) { | ||
1040 | if (sep) { | ||
1041 | ret += fprintf(fp, "%csys", *sep); | ||
1042 | ret += fprintf(fp, "%cus", *sep); | ||
1043 | if (perf_guest) { | ||
1044 | ret += fprintf(fp, "%cguest sys", *sep); | ||
1045 | ret += fprintf(fp, "%cguest us", *sep); | ||
1046 | } | ||
1047 | } else { | ||
1048 | ret += fprintf(fp, " sys "); | ||
1049 | ret += fprintf(fp, " us "); | ||
1050 | if (perf_guest) { | ||
1051 | ret += fprintf(fp, " guest sys "); | ||
1052 | ret += fprintf(fp, " guest us "); | ||
1053 | } | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | if (symbol_conf.show_nr_samples) { | ||
1058 | if (sep) | ||
1059 | fprintf(fp, "%cSamples", *sep); | ||
1060 | else | ||
1061 | fputs(" Samples ", fp); | ||
1062 | } | ||
1063 | |||
1064 | if (symbol_conf.show_total_period) { | ||
1065 | if (sep) | ||
1066 | ret += fprintf(fp, "%cPeriod", *sep); | ||
1067 | else | ||
1068 | ret += fprintf(fp, " Period "); | ||
1069 | } | ||
1070 | |||
1071 | if (pair) { | ||
1072 | if (sep) | ||
1073 | ret += fprintf(fp, "%cDelta", *sep); | ||
1074 | else | ||
1075 | ret += fprintf(fp, " Delta "); | ||
1076 | |||
1077 | if (show_displacement) { | ||
1078 | if (sep) | ||
1079 | ret += fprintf(fp, "%cDisplacement", *sep); | ||
1080 | else | ||
1081 | ret += fprintf(fp, " Displ"); | ||
1082 | } | ||
1083 | } | ||
1084 | |||
1085 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
1086 | if (se->elide) | ||
1087 | continue; | ||
1088 | if (sep) { | ||
1089 | fprintf(fp, "%c%s", *sep, se->se_header); | ||
1090 | continue; | ||
1091 | } | ||
1092 | width = strlen(se->se_header); | ||
1093 | if (symbol_conf.col_width_list_str) { | ||
1094 | if (col_width) { | ||
1095 | hists__set_col_len(hists, se->se_width_idx, | ||
1096 | atoi(col_width)); | ||
1097 | col_width = strchr(col_width, ','); | ||
1098 | if (col_width) | ||
1099 | ++col_width; | ||
1100 | } | ||
1101 | } | ||
1102 | if (!hists__new_col_len(hists, se->se_width_idx, width)) | ||
1103 | width = hists__col_len(hists, se->se_width_idx); | ||
1104 | fprintf(fp, " %*s", width, se->se_header); | ||
1105 | } | ||
1106 | |||
1107 | fprintf(fp, "\n"); | ||
1108 | if (max_rows && ++nr_rows >= max_rows) | ||
1109 | goto out; | ||
1110 | |||
1111 | if (sep) | ||
1112 | goto print_entries; | ||
1113 | |||
1114 | fprintf(fp, "# ........"); | ||
1115 | if (symbol_conf.show_cpu_utilization) | ||
1116 | fprintf(fp, " ....... ......."); | ||
1117 | if (symbol_conf.show_nr_samples) | ||
1118 | fprintf(fp, " .........."); | ||
1119 | if (symbol_conf.show_total_period) | ||
1120 | fprintf(fp, " ............"); | ||
1121 | if (pair) { | ||
1122 | fprintf(fp, " .........."); | ||
1123 | if (show_displacement) | ||
1124 | fprintf(fp, " ....."); | ||
1125 | } | ||
1126 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
1127 | unsigned int i; | ||
1128 | |||
1129 | if (se->elide) | ||
1130 | continue; | ||
1131 | |||
1132 | fprintf(fp, " "); | ||
1133 | width = hists__col_len(hists, se->se_width_idx); | ||
1134 | if (width == 0) | ||
1135 | width = strlen(se->se_header); | ||
1136 | for (i = 0; i < width; i++) | ||
1137 | fprintf(fp, "."); | ||
1138 | } | ||
1139 | |||
1140 | fprintf(fp, "\n"); | ||
1141 | if (max_rows && ++nr_rows >= max_rows) | ||
1142 | goto out; | ||
1143 | |||
1144 | fprintf(fp, "#\n"); | ||
1145 | if (max_rows && ++nr_rows >= max_rows) | ||
1146 | goto out; | ||
1147 | |||
1148 | print_entries: | ||
1149 | total_period = hists->stats.total_period; | ||
1150 | |||
1151 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1152 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1153 | |||
1154 | if (h->filtered) | ||
1155 | continue; | ||
1156 | |||
1157 | if (show_displacement) { | ||
1158 | if (h->pair != NULL) | ||
1159 | displacement = ((long)h->pair->position - | ||
1160 | (long)position); | ||
1161 | else | ||
1162 | displacement = 0; | ||
1163 | ++position; | ||
1164 | } | ||
1165 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, | ||
1166 | displacement, total_period, fp); | ||
1167 | |||
1168 | if (symbol_conf.use_callchain) | ||
1169 | ret += hist_entry__fprintf_callchain(h, hists, total_period, fp); | ||
1170 | if (max_rows && ++nr_rows >= max_rows) | ||
1171 | goto out; | ||
1172 | |||
1173 | if (h->ms.map == NULL && verbose > 1) { | ||
1174 | __map_groups__fprintf_maps(&h->thread->mg, | ||
1175 | MAP__FUNCTION, verbose, fp); | ||
1176 | fprintf(fp, "%.10s end\n", graph_dotted_line); | ||
1177 | } | ||
1178 | } | ||
1179 | out: | ||
1180 | free(rem_sq_bracket); | ||
1181 | |||
1182 | return ret; | ||
1183 | } | ||
1184 | |||
1185 | /* | ||
1186 | * See hists__fprintf to match the column widths | ||
1187 | */ | ||
1188 | unsigned int hists__sort_list_width(struct hists *hists) | ||
1189 | { | ||
1190 | struct sort_entry *se; | ||
1191 | int ret = 9; /* total % */ | ||
1192 | |||
1193 | if (symbol_conf.show_cpu_utilization) { | ||
1194 | ret += 7; /* count_sys % */ | ||
1195 | ret += 6; /* count_us % */ | ||
1196 | if (perf_guest) { | ||
1197 | ret += 13; /* count_guest_sys % */ | ||
1198 | ret += 12; /* count_guest_us % */ | ||
1199 | } | ||
1200 | } | ||
1201 | |||
1202 | if (symbol_conf.show_nr_samples) | ||
1203 | ret += 11; | ||
1204 | |||
1205 | if (symbol_conf.show_total_period) | ||
1206 | ret += 13; | ||
1207 | |||
1208 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
1209 | if (!se->elide) | ||
1210 | ret += 2 + hists__col_len(hists, se->se_width_idx); | ||
1211 | |||
1212 | if (verbose) /* Addr + origin */ | ||
1213 | ret += 3 + BITS_PER_LONG / 4; | ||
1214 | |||
1215 | return ret; | ||
1216 | } | ||
1217 | |||
1218 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, | 589 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
1219 | enum hist_filter filter) | 590 | enum hist_filter filter) |
1220 | { | 591 | { |
@@ -1226,8 +597,8 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h | |||
1226 | if (h->ms.unfolded) | 597 | if (h->ms.unfolded) |
1227 | hists->nr_entries += h->nr_rows; | 598 | hists->nr_entries += h->nr_rows; |
1228 | h->row_offset = 0; | 599 | h->row_offset = 0; |
1229 | hists->stats.total_period += h->period; | 600 | hists->stats.total_period += h->stat.period; |
1230 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | 601 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; |
1231 | 602 | ||
1232 | hists__calc_col_len(hists, h); | 603 | hists__calc_col_len(hists, h); |
1233 | } | 604 | } |
@@ -1342,25 +713,3 @@ void hists__inc_nr_events(struct hists *hists, u32 type) | |||
1342 | ++hists->stats.nr_events[0]; | 713 | ++hists->stats.nr_events[0]; |
1343 | ++hists->stats.nr_events[type]; | 714 | ++hists->stats.nr_events[type]; |
1344 | } | 715 | } |
1345 | |||
1346 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | ||
1347 | { | ||
1348 | int i; | ||
1349 | size_t ret = 0; | ||
1350 | |||
1351 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | ||
1352 | const char *name; | ||
1353 | |||
1354 | if (hists->stats.nr_events[i] == 0) | ||
1355 | continue; | ||
1356 | |||
1357 | name = perf_event__name(i); | ||
1358 | if (!strcmp(name, "UNKNOWN")) | ||
1359 | continue; | ||
1360 | |||
1361 | ret += fprintf(fp, "%16s events: %10d\n", name, | ||
1362 | hists->stats.nr_events[i]); | ||
1363 | } | ||
1364 | |||
1365 | return ret; | ||
1366 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 0b096c27a419..66cb31fe81d2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -75,8 +75,8 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
75 | struct symbol *parent, u64 period); | 75 | struct symbol *parent, u64 period); |
76 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); | 76 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
77 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | 77 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
78 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 78 | int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, |
79 | struct hists *hists); | 79 | struct hists *hists); |
80 | void hist_entry__free(struct hist_entry *); | 80 | void hist_entry__free(struct hist_entry *); |
81 | 81 | ||
82 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | 82 | struct hist_entry *__hists__add_branch_entry(struct hists *self, |
@@ -98,9 +98,8 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows); | |||
98 | void hists__inc_nr_events(struct hists *self, u32 type); | 98 | void hists__inc_nr_events(struct hists *self, u32 type); |
99 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 99 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); |
100 | 100 | ||
101 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 101 | size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, |
102 | bool show_displacement, bool show_header, | 102 | int max_cols, FILE *fp); |
103 | int max_rows, int max_cols, FILE *fp); | ||
104 | 103 | ||
105 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 104 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
106 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); | 105 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
@@ -112,55 +111,95 @@ void hists__filter_by_symbol(struct hists *hists); | |||
112 | u16 hists__col_len(struct hists *self, enum hist_column col); | 111 | u16 hists__col_len(struct hists *self, enum hist_column col); |
113 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 112 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
114 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | 113 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); |
114 | void hists__reset_col_len(struct hists *hists); | ||
115 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | ||
116 | |||
117 | struct perf_hpp { | ||
118 | char *buf; | ||
119 | size_t size; | ||
120 | const char *sep; | ||
121 | void *ptr; | ||
122 | }; | ||
123 | |||
124 | struct perf_hpp_fmt { | ||
125 | bool cond; | ||
126 | int (*header)(struct perf_hpp *hpp); | ||
127 | int (*width)(struct perf_hpp *hpp); | ||
128 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); | ||
129 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); | ||
130 | }; | ||
131 | |||
132 | extern struct perf_hpp_fmt perf_hpp__format[]; | ||
133 | |||
134 | enum { | ||
135 | PERF_HPP__BASELINE, | ||
136 | PERF_HPP__OVERHEAD, | ||
137 | PERF_HPP__OVERHEAD_SYS, | ||
138 | PERF_HPP__OVERHEAD_US, | ||
139 | PERF_HPP__OVERHEAD_GUEST_SYS, | ||
140 | PERF_HPP__OVERHEAD_GUEST_US, | ||
141 | PERF_HPP__SAMPLES, | ||
142 | PERF_HPP__PERIOD, | ||
143 | PERF_HPP__DELTA, | ||
144 | PERF_HPP__DISPL, | ||
145 | |||
146 | PERF_HPP__MAX_INDEX | ||
147 | }; | ||
148 | |||
149 | void perf_hpp__init(void); | ||
150 | void perf_hpp__column_enable(unsigned col, bool enable); | ||
151 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | ||
152 | bool color); | ||
115 | 153 | ||
116 | struct perf_evlist; | 154 | struct perf_evlist; |
117 | 155 | ||
118 | #ifdef NO_NEWT_SUPPORT | 156 | #ifdef NEWT_SUPPORT |
157 | #include "../ui/keysyms.h" | ||
158 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | ||
159 | void(*timer)(void *arg), void *arg, int delay_secs); | ||
160 | |||
161 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | ||
162 | void(*timer)(void *arg), void *arg, | ||
163 | int refresh); | ||
164 | #else | ||
119 | static inline | 165 | static inline |
120 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | 166 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, |
121 | const char *help __used, | 167 | const char *help __maybe_unused, |
122 | void(*timer)(void *arg) __used, | 168 | void(*timer)(void *arg) __maybe_unused, |
123 | void *arg __used, | 169 | void *arg __maybe_unused, |
124 | int refresh __used) | 170 | int refresh __maybe_unused) |
125 | { | 171 | { |
126 | return 0; | 172 | return 0; |
127 | } | 173 | } |
128 | 174 | ||
129 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | 175 | static inline int hist_entry__tui_annotate(struct hist_entry *self |
130 | int evidx __used, | 176 | __maybe_unused, |
131 | void(*timer)(void *arg) __used, | 177 | int evidx __maybe_unused, |
132 | void *arg __used, | 178 | void(*timer)(void *arg) |
133 | int delay_secs __used) | 179 | __maybe_unused, |
180 | void *arg __maybe_unused, | ||
181 | int delay_secs __maybe_unused) | ||
134 | { | 182 | { |
135 | return 0; | 183 | return 0; |
136 | } | 184 | } |
137 | #define K_LEFT -1 | 185 | #define K_LEFT -1 |
138 | #define K_RIGHT -2 | 186 | #define K_RIGHT -2 |
139 | #else | 187 | #endif |
140 | #include "../ui/keysyms.h" | ||
141 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | ||
142 | void(*timer)(void *arg), void *arg, int delay_secs); | ||
143 | 188 | ||
144 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 189 | #ifdef GTK2_SUPPORT |
190 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | ||
145 | void(*timer)(void *arg), void *arg, | 191 | void(*timer)(void *arg), void *arg, |
146 | int refresh); | 192 | int refresh); |
147 | #endif | 193 | #else |
148 | |||
149 | #ifdef NO_GTK2_SUPPORT | ||
150 | static inline | 194 | static inline |
151 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used, | 195 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, |
152 | const char *help __used, | 196 | const char *help __maybe_unused, |
153 | void(*timer)(void *arg) __used, | 197 | void(*timer)(void *arg) __maybe_unused, |
154 | void *arg __used, | 198 | void *arg __maybe_unused, |
155 | int refresh __used) | 199 | int refresh __maybe_unused) |
156 | { | 200 | { |
157 | return 0; | 201 | return 0; |
158 | } | 202 | } |
159 | |||
160 | #else | ||
161 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | ||
162 | void(*timer)(void *arg), void *arg, | ||
163 | int refresh); | ||
164 | #endif | 203 | #endif |
165 | 204 | ||
166 | unsigned int hists__sort_list_width(struct hists *self); | 205 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h index b722abe3a626..2a9bdc066307 100644 --- a/tools/perf/util/include/asm/byteorder.h +++ b/tools/perf/util/include/asm/byteorder.h | |||
@@ -1,2 +1,2 @@ | |||
1 | #include <asm/types.h> | 1 | #include <asm/types.h> |
2 | #include "../../../../include/linux/swab.h" | 2 | #include "../../../../include/uapi/linux/swab.h" |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 587a230d2075..a55d8cf083c9 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -5,6 +5,10 @@ | |||
5 | #include <linux/compiler.h> | 5 | #include <linux/compiler.h> |
6 | #include <asm/hweight.h> | 6 | #include <asm/hweight.h> |
7 | 7 | ||
8 | #ifndef __WORDSIZE | ||
9 | #define __WORDSIZE (__SIZEOF_LONG__ * 8) | ||
10 | #endif | ||
11 | |||
8 | #define BITS_PER_LONG __WORDSIZE | 12 | #define BITS_PER_LONG __WORDSIZE |
9 | #define BITS_PER_BYTE 8 | 13 | #define BITS_PER_BYTE 8 |
10 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) | 14 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) |
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 547628e97f3d..96b919dae11c 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -9,6 +9,13 @@ | |||
9 | #define __attribute_const__ | 9 | #define __attribute_const__ |
10 | #endif | 10 | #endif |
11 | 11 | ||
12 | #define __used __attribute__((__unused__)) | 12 | #ifndef __maybe_unused |
13 | #define __maybe_unused __attribute__((unused)) | ||
14 | #endif | ||
15 | #define __packed __attribute__((__packed__)) | ||
16 | |||
17 | #ifndef __force | ||
18 | #define __force | ||
19 | #endif | ||
13 | 20 | ||
14 | #endif | 21 | #endif |
diff --git a/tools/perf/util/include/linux/const.h b/tools/perf/util/include/linux/const.h index 1b476c9ae649..c10a35e1afb8 100644 --- a/tools/perf/util/include/linux/const.h +++ b/tools/perf/util/include/linux/const.h | |||
@@ -1 +1 @@ | |||
#include "../../../../include/linux/const.h" | #include "../../../../include/uapi/linux/const.h" | ||
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index b6842c1d02a8..d8c927c868ee 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
@@ -8,8 +8,8 @@ | |||
8 | 8 | ||
9 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) | 9 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) |
10 | 10 | ||
11 | #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) | 11 | #define PERF_ALIGN(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1) |
12 | #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) | 12 | #define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) |
13 | 13 | ||
14 | #ifndef offsetof | 14 | #ifndef offsetof |
15 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | 15 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
@@ -46,9 +46,22 @@ | |||
46 | _min1 < _min2 ? _min1 : _min2; }) | 46 | _min1 < _min2 ? _min1 : _min2; }) |
47 | #endif | 47 | #endif |
48 | 48 | ||
49 | #ifndef roundup | ||
50 | #define roundup(x, y) ( \ | ||
51 | { \ | ||
52 | const typeof(y) __y = y; \ | ||
53 | (((x) + (__y - 1)) / __y) * __y; \ | ||
54 | } \ | ||
55 | ) | ||
56 | #endif | ||
57 | |||
49 | #ifndef BUG_ON | 58 | #ifndef BUG_ON |
59 | #ifdef NDEBUG | ||
60 | #define BUG_ON(cond) do { if (cond) {} } while (0) | ||
61 | #else | ||
50 | #define BUG_ON(cond) assert(!(cond)) | 62 | #define BUG_ON(cond) assert(!(cond)) |
51 | #endif | 63 | #endif |
64 | #endif | ||
52 | 65 | ||
53 | /* | 66 | /* |
54 | * Both need more care to handle endianness | 67 | * Both need more care to handle endianness |
diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h new file mode 100644 index 000000000000..58b64ed4da12 --- /dev/null +++ b/tools/perf/util/include/linux/magic.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef _PERF_LINUX_MAGIC_H_ | ||
2 | #define _PERF_LINUX_MAGIC_H_ | ||
3 | |||
4 | #ifndef DEBUGFS_MAGIC | ||
5 | #define DEBUGFS_MAGIC 0x64626720 | ||
6 | #endif | ||
7 | |||
8 | #ifndef SYSFS_MAGIC | ||
9 | #define SYSFS_MAGIC 0x62656572 | ||
10 | #endif | ||
11 | |||
12 | #endif | ||
diff --git a/tools/perf/util/include/linux/rbtree.h b/tools/perf/util/include/linux/rbtree.h index 7a243a143037..2a030c5af3aa 100644 --- a/tools/perf/util/include/linux/rbtree.h +++ b/tools/perf/util/include/linux/rbtree.h | |||
@@ -1 +1,2 @@ | |||
1 | #include <stdbool.h> | ||
1 | #include "../../../../include/linux/rbtree.h" | 2 | #include "../../../../include/linux/rbtree.h" |
diff --git a/tools/perf/util/include/linux/rbtree_augmented.h b/tools/perf/util/include/linux/rbtree_augmented.h new file mode 100644 index 000000000000..9d6fcdf1788b --- /dev/null +++ b/tools/perf/util/include/linux/rbtree_augmented.h | |||
@@ -0,0 +1,2 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include "../../../../include/linux/rbtree_augmented.h" | ||
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h index 3b2f5900276f..6f19c548ecc0 100644 --- a/tools/perf/util/include/linux/string.h +++ b/tools/perf/util/include/linux/string.h | |||
@@ -1 +1,3 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | |||
3 | void *memdup(const void *src, size_t len); | ||
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h index 12de3b8112f9..eb464786c084 100644 --- a/tools/perf/util/include/linux/types.h +++ b/tools/perf/util/include/linux/types.h | |||
@@ -3,6 +3,14 @@ | |||
3 | 3 | ||
4 | #include <asm/types.h> | 4 | #include <asm/types.h> |
5 | 5 | ||
6 | #ifndef __bitwise | ||
7 | #define __bitwise | ||
8 | #endif | ||
9 | |||
10 | #ifndef __le32 | ||
11 | typedef __u32 __bitwise __le32; | ||
12 | #endif | ||
13 | |||
6 | #define DECLARE_BITMAP(name,bits) \ | 14 | #define DECLARE_BITMAP(name,bits) \ |
7 | unsigned long name[BITS_TO_LONGS(bits)] | 15 | unsigned long name[BITS_TO_LONGS(bits)] |
8 | 16 | ||
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index fd530dced9cb..9d0740024ba8 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c | |||
@@ -11,7 +11,7 @@ | |||
11 | 11 | ||
12 | #include "intlist.h" | 12 | #include "intlist.h" |
13 | 13 | ||
14 | static struct rb_node *intlist__node_new(struct rblist *rblist __used, | 14 | static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused, |
15 | const void *entry) | 15 | const void *entry) |
16 | { | 16 | { |
17 | int i = (int)((long)entry); | 17 | int i = (int)((long)entry); |
@@ -31,7 +31,7 @@ static void int_node__delete(struct int_node *ilist) | |||
31 | free(ilist); | 31 | free(ilist); |
32 | } | 32 | } |
33 | 33 | ||
34 | static void intlist__node_delete(struct rblist *rblist __used, | 34 | static void intlist__node_delete(struct rblist *rblist __maybe_unused, |
35 | struct rb_node *rb_node) | 35 | struct rb_node *rb_node) |
36 | { | 36 | { |
37 | struct int_node *node = container_of(rb_node, struct int_node, rb_node); | 37 | struct int_node *node = container_of(rb_node, struct int_node, rb_node); |
@@ -52,9 +52,9 @@ int intlist__add(struct intlist *ilist, int i) | |||
52 | return rblist__add_node(&ilist->rblist, (void *)((long)i)); | 52 | return rblist__add_node(&ilist->rblist, (void *)((long)i)); |
53 | } | 53 | } |
54 | 54 | ||
55 | void intlist__remove(struct intlist *ilist __used, struct int_node *node) | 55 | void intlist__remove(struct intlist *ilist, struct int_node *node) |
56 | { | 56 | { |
57 | int_node__delete(node); | 57 | rblist__remove_node(&ilist->rblist, &node->rb_node); |
58 | } | 58 | } |
59 | 59 | ||
60 | struct int_node *intlist__find(struct intlist *ilist, int i) | 60 | struct int_node *intlist__find(struct intlist *ilist, int i) |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index cc33486ad9e2..6109fa4d14cd 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "map.h" | 9 | #include "map.h" |
10 | #include "thread.h" | 10 | #include "thread.h" |
11 | #include "strlist.h" | 11 | #include "strlist.h" |
12 | #include "vdso.h" | ||
12 | 13 | ||
13 | const char *map_type__name[MAP__NR_TYPES] = { | 14 | const char *map_type__name[MAP__NR_TYPES] = { |
14 | [MAP__FUNCTION] = "Functions", | 15 | [MAP__FUNCTION] = "Functions", |
@@ -23,7 +24,6 @@ static inline int is_anon_memory(const char *filename) | |||
23 | static inline int is_no_dso_memory(const char *filename) | 24 | static inline int is_no_dso_memory(const char *filename) |
24 | { | 25 | { |
25 | return !strcmp(filename, "[stack]") || | 26 | return !strcmp(filename, "[stack]") || |
26 | !strcmp(filename, "[vdso]") || | ||
27 | !strcmp(filename, "[heap]"); | 27 | !strcmp(filename, "[heap]"); |
28 | } | 28 | } |
29 | 29 | ||
@@ -52,9 +52,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
52 | if (self != NULL) { | 52 | if (self != NULL) { |
53 | char newfilename[PATH_MAX]; | 53 | char newfilename[PATH_MAX]; |
54 | struct dso *dso; | 54 | struct dso *dso; |
55 | int anon, no_dso; | 55 | int anon, no_dso, vdso; |
56 | 56 | ||
57 | anon = is_anon_memory(filename); | 57 | anon = is_anon_memory(filename); |
58 | vdso = is_vdso_map(filename); | ||
58 | no_dso = is_no_dso_memory(filename); | 59 | no_dso = is_no_dso_memory(filename); |
59 | 60 | ||
60 | if (anon) { | 61 | if (anon) { |
@@ -62,7 +63,12 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
62 | filename = newfilename; | 63 | filename = newfilename; |
63 | } | 64 | } |
64 | 65 | ||
65 | dso = __dsos__findnew(dsos__list, filename); | 66 | if (vdso) { |
67 | pgoff = 0; | ||
68 | dso = vdso__dso_findnew(dsos__list); | ||
69 | } else | ||
70 | dso = __dsos__findnew(dsos__list, filename); | ||
71 | |||
66 | if (dso == NULL) | 72 | if (dso == NULL) |
67 | goto out_delete; | 73 | goto out_delete; |
68 | 74 | ||
@@ -86,6 +92,25 @@ out_delete: | |||
86 | return NULL; | 92 | return NULL; |
87 | } | 93 | } |
88 | 94 | ||
95 | /* | ||
96 | * Constructor variant for modules (where we know from /proc/modules where | ||
97 | * they are loaded) and for vmlinux, where only after we load all the | ||
98 | * symbols we'll know where it starts and ends. | ||
99 | */ | ||
100 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | ||
101 | { | ||
102 | struct map *map = calloc(1, (sizeof(*map) + | ||
103 | (dso->kernel ? sizeof(struct kmap) : 0))); | ||
104 | if (map != NULL) { | ||
105 | /* | ||
106 | * ->end will be filled after we load all the symbols | ||
107 | */ | ||
108 | map__init(map, type, start, 0, 0, dso); | ||
109 | } | ||
110 | |||
111 | return map; | ||
112 | } | ||
113 | |||
89 | void map__delete(struct map *self) | 114 | void map__delete(struct map *self) |
90 | { | 115 | { |
91 | free(self); | 116 | free(self); |
@@ -137,6 +162,7 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
137 | pr_warning(", continuing without symbols\n"); | 162 | pr_warning(", continuing without symbols\n"); |
138 | return -1; | 163 | return -1; |
139 | } else if (nr == 0) { | 164 | } else if (nr == 0) { |
165 | #ifdef LIBELF_SUPPORT | ||
140 | const size_t len = strlen(name); | 166 | const size_t len = strlen(name); |
141 | const size_t real_len = len - sizeof(DSO__DELETED); | 167 | const size_t real_len = len - sizeof(DSO__DELETED); |
142 | 168 | ||
@@ -149,7 +175,7 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
149 | pr_warning("no symbols found in %s, maybe install " | 175 | pr_warning("no symbols found in %s, maybe install " |
150 | "a debug package?\n", name); | 176 | "a debug package?\n", name); |
151 | } | 177 | } |
152 | 178 | #endif | |
153 | return -1; | 179 | return -1; |
154 | } | 180 | } |
155 | /* | 181 | /* |
@@ -217,15 +243,14 @@ size_t map__fprintf(struct map *self, FILE *fp) | |||
217 | 243 | ||
218 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) | 244 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) |
219 | { | 245 | { |
220 | const char *dsoname; | 246 | const char *dsoname = "[unknown]"; |
221 | 247 | ||
222 | if (map && map->dso && (map->dso->name || map->dso->long_name)) { | 248 | if (map && map->dso && (map->dso->name || map->dso->long_name)) { |
223 | if (symbol_conf.show_kernel_path && map->dso->long_name) | 249 | if (symbol_conf.show_kernel_path && map->dso->long_name) |
224 | dsoname = map->dso->long_name; | 250 | dsoname = map->dso->long_name; |
225 | else if (map->dso->name) | 251 | else if (map->dso->name) |
226 | dsoname = map->dso->name; | 252 | dsoname = map->dso->name; |
227 | } else | 253 | } |
228 | dsoname = "[unknown]"; | ||
229 | 254 | ||
230 | return fprintf(fp, "%s", dsoname); | 255 | return fprintf(fp, "%s", dsoname); |
231 | } | 256 | } |
@@ -242,14 +267,6 @@ u64 map__rip_2objdump(struct map *map, u64 rip) | |||
242 | return addr; | 267 | return addr; |
243 | } | 268 | } |
244 | 269 | ||
245 | u64 map__objdump_2ip(struct map *map, u64 addr) | ||
246 | { | ||
247 | u64 ip = map->dso->adjust_symbols ? | ||
248 | addr : | ||
249 | map->unmap_ip(map, addr); /* RIP -> IP */ | ||
250 | return ip; | ||
251 | } | ||
252 | |||
253 | void map_groups__init(struct map_groups *mg) | 270 | void map_groups__init(struct map_groups *mg) |
254 | { | 271 | { |
255 | int i; | 272 | int i; |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 03a1e9b08b21..d2250fc97e25 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -96,7 +96,7 @@ static inline u64 map__unmap_ip(struct map *map, u64 ip) | |||
96 | return ip + map->start - map->pgoff; | 96 | return ip + map->start - map->pgoff; |
97 | } | 97 | } |
98 | 98 | ||
99 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | 99 | static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip) |
100 | { | 100 | { |
101 | return ip; | 101 | return ip; |
102 | } | 102 | } |
@@ -104,7 +104,6 @@ static inline u64 identity__map_ip(struct map *map __used, u64 ip) | |||
104 | 104 | ||
105 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ | 105 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ |
106 | u64 map__rip_2objdump(struct map *map, u64 rip); | 106 | u64 map__rip_2objdump(struct map *map, u64 rip); |
107 | u64 map__objdump_2ip(struct map *map, u64 addr); | ||
108 | 107 | ||
109 | struct symbol; | 108 | struct symbol; |
110 | 109 | ||
@@ -115,6 +114,7 @@ void map__init(struct map *self, enum map_type type, | |||
115 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 114 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
116 | u64 pgoff, u32 pid, char *filename, | 115 | u64 pgoff, u32 pid, char *filename, |
117 | enum map_type type); | 116 | enum map_type type); |
117 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | ||
118 | void map__delete(struct map *self); | 118 | void map__delete(struct map *self); |
119 | struct map *map__clone(struct map *self); | 119 | struct map *map__clone(struct map *self); |
120 | int map__overlap(struct map *l, struct map *r); | 120 | int map__overlap(struct map *l, struct map *r); |
@@ -157,9 +157,12 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid); | |||
157 | void machine__exit(struct machine *self); | 157 | void machine__exit(struct machine *self); |
158 | void machine__delete(struct machine *self); | 158 | void machine__delete(struct machine *self); |
159 | 159 | ||
160 | struct perf_evsel; | ||
161 | struct perf_sample; | ||
160 | int machine__resolve_callchain(struct machine *machine, | 162 | int machine__resolve_callchain(struct machine *machine, |
163 | struct perf_evsel *evsel, | ||
161 | struct thread *thread, | 164 | struct thread *thread, |
162 | struct ip_callchain *chain, | 165 | struct perf_sample *sample, |
163 | struct symbol **parent); | 166 | struct symbol **parent); |
164 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, | 167 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, |
165 | u64 addr); | 168 | u64 addr); |
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c index 607dd290b319..516ecd9ddd6e 100644 --- a/tools/perf/util/parse-events-test.c +++ b/tools/perf/util/parse-events-test.c | |||
@@ -18,8 +18,7 @@ do { \ | |||
18 | 18 | ||
19 | static int test__checkevent_tracepoint(struct perf_evlist *evlist) | 19 | static int test__checkevent_tracepoint(struct perf_evlist *evlist) |
20 | { | 20 | { |
21 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 21 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
22 | struct perf_evsel, node); | ||
23 | 22 | ||
24 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 23 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
25 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | 24 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); |
@@ -48,8 +47,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) | |||
48 | 47 | ||
49 | static int test__checkevent_raw(struct perf_evlist *evlist) | 48 | static int test__checkevent_raw(struct perf_evlist *evlist) |
50 | { | 49 | { |
51 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 50 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
52 | struct perf_evsel, node); | ||
53 | 51 | ||
54 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 52 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
55 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 53 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
@@ -59,8 +57,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist) | |||
59 | 57 | ||
60 | static int test__checkevent_numeric(struct perf_evlist *evlist) | 58 | static int test__checkevent_numeric(struct perf_evlist *evlist) |
61 | { | 59 | { |
62 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 60 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
63 | struct perf_evsel, node); | ||
64 | 61 | ||
65 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 62 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
66 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | 63 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); |
@@ -70,8 +67,7 @@ static int test__checkevent_numeric(struct perf_evlist *evlist) | |||
70 | 67 | ||
71 | static int test__checkevent_symbolic_name(struct perf_evlist *evlist) | 68 | static int test__checkevent_symbolic_name(struct perf_evlist *evlist) |
72 | { | 69 | { |
73 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 70 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
74 | struct perf_evsel, node); | ||
75 | 71 | ||
76 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 72 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
77 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | 73 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); |
@@ -82,8 +78,7 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist) | |||
82 | 78 | ||
83 | static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) | 79 | static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) |
84 | { | 80 | { |
85 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 81 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
86 | struct perf_evsel, node); | ||
87 | 82 | ||
88 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 83 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
89 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | 84 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); |
@@ -100,8 +95,7 @@ static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) | |||
100 | 95 | ||
101 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) | 96 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) |
102 | { | 97 | { |
103 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 98 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
104 | struct perf_evsel, node); | ||
105 | 99 | ||
106 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 100 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
107 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); | 101 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); |
@@ -112,8 +106,7 @@ static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) | |||
112 | 106 | ||
113 | static int test__checkevent_genhw(struct perf_evlist *evlist) | 107 | static int test__checkevent_genhw(struct perf_evlist *evlist) |
114 | { | 108 | { |
115 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 109 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
116 | struct perf_evsel, node); | ||
117 | 110 | ||
118 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 111 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
119 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); | 112 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); |
@@ -123,8 +116,7 @@ static int test__checkevent_genhw(struct perf_evlist *evlist) | |||
123 | 116 | ||
124 | static int test__checkevent_breakpoint(struct perf_evlist *evlist) | 117 | static int test__checkevent_breakpoint(struct perf_evlist *evlist) |
125 | { | 118 | { |
126 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 119 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
127 | struct perf_evsel, node); | ||
128 | 120 | ||
129 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 121 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
130 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | 122 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); |
@@ -138,8 +130,7 @@ static int test__checkevent_breakpoint(struct perf_evlist *evlist) | |||
138 | 130 | ||
139 | static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) | 131 | static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) |
140 | { | 132 | { |
141 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 133 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
142 | struct perf_evsel, node); | ||
143 | 134 | ||
144 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 135 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
145 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | 136 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); |
@@ -152,8 +143,7 @@ static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) | |||
152 | 143 | ||
153 | static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) | 144 | static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) |
154 | { | 145 | { |
155 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 146 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
156 | struct perf_evsel, node); | ||
157 | 147 | ||
158 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 148 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
159 | TEST_ASSERT_VAL("wrong type", | 149 | TEST_ASSERT_VAL("wrong type", |
@@ -168,8 +158,7 @@ static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) | |||
168 | 158 | ||
169 | static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) | 159 | static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) |
170 | { | 160 | { |
171 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 161 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
172 | struct perf_evsel, node); | ||
173 | 162 | ||
174 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 163 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
175 | TEST_ASSERT_VAL("wrong type", | 164 | TEST_ASSERT_VAL("wrong type", |
@@ -184,8 +173,7 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) | |||
184 | 173 | ||
185 | static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist) | 174 | static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist) |
186 | { | 175 | { |
187 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 176 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
188 | struct perf_evsel, node); | ||
189 | 177 | ||
190 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 178 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
191 | TEST_ASSERT_VAL("wrong type", | 179 | TEST_ASSERT_VAL("wrong type", |
@@ -200,8 +188,7 @@ static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist) | |||
200 | 188 | ||
201 | static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist) | 189 | static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist) |
202 | { | 190 | { |
203 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 191 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
204 | struct perf_evsel, node); | ||
205 | 192 | ||
206 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 193 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
207 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | 194 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); |
@@ -232,8 +219,7 @@ test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist) | |||
232 | 219 | ||
233 | static int test__checkevent_raw_modifier(struct perf_evlist *evlist) | 220 | static int test__checkevent_raw_modifier(struct perf_evlist *evlist) |
234 | { | 221 | { |
235 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 222 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
236 | struct perf_evsel, node); | ||
237 | 223 | ||
238 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 224 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
239 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | 225 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); |
@@ -245,8 +231,7 @@ static int test__checkevent_raw_modifier(struct perf_evlist *evlist) | |||
245 | 231 | ||
246 | static int test__checkevent_numeric_modifier(struct perf_evlist *evlist) | 232 | static int test__checkevent_numeric_modifier(struct perf_evlist *evlist) |
247 | { | 233 | { |
248 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 234 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
249 | struct perf_evsel, node); | ||
250 | 235 | ||
251 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 236 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
252 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 237 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
@@ -258,8 +243,7 @@ static int test__checkevent_numeric_modifier(struct perf_evlist *evlist) | |||
258 | 243 | ||
259 | static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) | 244 | static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) |
260 | { | 245 | { |
261 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 246 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
262 | struct perf_evsel, node); | ||
263 | 247 | ||
264 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 248 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
265 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 249 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
@@ -271,8 +255,7 @@ static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) | |||
271 | 255 | ||
272 | static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) | 256 | static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) |
273 | { | 257 | { |
274 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 258 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
275 | struct perf_evsel, node); | ||
276 | 259 | ||
277 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 260 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
278 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 261 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
@@ -282,8 +265,7 @@ static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) | |||
282 | 265 | ||
283 | static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) | 266 | static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) |
284 | { | 267 | { |
285 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 268 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
286 | struct perf_evsel, node); | ||
287 | 269 | ||
288 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 270 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
289 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 271 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
@@ -293,8 +275,7 @@ static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) | |||
293 | 275 | ||
294 | static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) | 276 | static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) |
295 | { | 277 | { |
296 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 278 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
297 | struct perf_evsel, node); | ||
298 | 279 | ||
299 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | 280 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); |
300 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 281 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
@@ -306,8 +287,7 @@ static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) | |||
306 | 287 | ||
307 | static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) | 288 | static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) |
308 | { | 289 | { |
309 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 290 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
310 | struct perf_evsel, node); | ||
311 | 291 | ||
312 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 292 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
313 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | 293 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); |
@@ -319,75 +299,71 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) | |||
319 | 299 | ||
320 | static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) | 300 | static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) |
321 | { | 301 | { |
322 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 302 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
323 | struct perf_evsel, node); | 303 | |
324 | 304 | ||
325 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | 305 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); |
326 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 306 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
327 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 307 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
328 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 308 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
329 | TEST_ASSERT_VAL("wrong name", | 309 | TEST_ASSERT_VAL("wrong name", |
330 | !strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u")); | 310 | !strcmp(perf_evsel__name(evsel), "mem:0:u")); |
331 | 311 | ||
332 | return test__checkevent_breakpoint(evlist); | 312 | return test__checkevent_breakpoint(evlist); |
333 | } | 313 | } |
334 | 314 | ||
335 | static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) | 315 | static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) |
336 | { | 316 | { |
337 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 317 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
338 | struct perf_evsel, node); | ||
339 | 318 | ||
340 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 319 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
341 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | 320 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); |
342 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 321 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
343 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 322 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
344 | TEST_ASSERT_VAL("wrong name", | 323 | TEST_ASSERT_VAL("wrong name", |
345 | !strcmp(perf_evsel__name(evsel), "mem:0x0:x:k")); | 324 | !strcmp(perf_evsel__name(evsel), "mem:0:x:k")); |
346 | 325 | ||
347 | return test__checkevent_breakpoint_x(evlist); | 326 | return test__checkevent_breakpoint_x(evlist); |
348 | } | 327 | } |
349 | 328 | ||
350 | static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) | 329 | static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) |
351 | { | 330 | { |
352 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 331 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
353 | struct perf_evsel, node); | ||
354 | 332 | ||
355 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 333 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
356 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 334 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
357 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | 335 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); |
358 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | 336 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); |
359 | TEST_ASSERT_VAL("wrong name", | 337 | TEST_ASSERT_VAL("wrong name", |
360 | !strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp")); | 338 | !strcmp(perf_evsel__name(evsel), "mem:0:r:hp")); |
361 | 339 | ||
362 | return test__checkevent_breakpoint_r(evlist); | 340 | return test__checkevent_breakpoint_r(evlist); |
363 | } | 341 | } |
364 | 342 | ||
365 | static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) | 343 | static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) |
366 | { | 344 | { |
367 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 345 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
368 | struct perf_evsel, node); | ||
369 | 346 | ||
370 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | 347 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); |
371 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 348 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
372 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 349 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
373 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | 350 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); |
374 | TEST_ASSERT_VAL("wrong name", | 351 | TEST_ASSERT_VAL("wrong name", |
375 | !strcmp(perf_evsel__name(evsel), "mem:0x0:w:up")); | 352 | !strcmp(perf_evsel__name(evsel), "mem:0:w:up")); |
376 | 353 | ||
377 | return test__checkevent_breakpoint_w(evlist); | 354 | return test__checkevent_breakpoint_w(evlist); |
378 | } | 355 | } |
379 | 356 | ||
380 | static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist) | 357 | static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist) |
381 | { | 358 | { |
382 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 359 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
383 | struct perf_evsel, node); | ||
384 | 360 | ||
385 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 361 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
386 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | 362 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); |
387 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 363 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
388 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | 364 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); |
389 | TEST_ASSERT_VAL("wrong name", | 365 | TEST_ASSERT_VAL("wrong name", |
390 | !strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp")); | 366 | !strcmp(perf_evsel__name(evsel), "mem:0:rw:kp")); |
391 | 367 | ||
392 | return test__checkevent_breakpoint_rw(evlist); | 368 | return test__checkevent_breakpoint_rw(evlist); |
393 | } | 369 | } |
@@ -395,8 +371,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist) | |||
395 | static int test__checkevent_pmu(struct perf_evlist *evlist) | 371 | static int test__checkevent_pmu(struct perf_evlist *evlist) |
396 | { | 372 | { |
397 | 373 | ||
398 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 374 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
399 | struct perf_evsel, node); | ||
400 | 375 | ||
401 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 376 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
402 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 377 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
@@ -410,12 +385,11 @@ static int test__checkevent_pmu(struct perf_evlist *evlist) | |||
410 | 385 | ||
411 | static int test__checkevent_list(struct perf_evlist *evlist) | 386 | static int test__checkevent_list(struct perf_evlist *evlist) |
412 | { | 387 | { |
413 | struct perf_evsel *evsel; | 388 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
414 | 389 | ||
415 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | 390 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); |
416 | 391 | ||
417 | /* r1 */ | 392 | /* r1 */ |
418 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
419 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 393 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
420 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | 394 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); |
421 | TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); | 395 | TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); |
@@ -426,7 +400,7 @@ static int test__checkevent_list(struct perf_evlist *evlist) | |||
426 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 400 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
427 | 401 | ||
428 | /* syscalls:sys_enter_open:k */ | 402 | /* syscalls:sys_enter_open:k */ |
429 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | 403 | evsel = perf_evsel__next(evsel); |
430 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | 404 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); |
431 | TEST_ASSERT_VAL("wrong sample_type", | 405 | TEST_ASSERT_VAL("wrong sample_type", |
432 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); | 406 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); |
@@ -437,7 +411,7 @@ static int test__checkevent_list(struct perf_evlist *evlist) | |||
437 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 411 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
438 | 412 | ||
439 | /* 1:1:hp */ | 413 | /* 1:1:hp */ |
440 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | 414 | evsel = perf_evsel__next(evsel); |
441 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | 415 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); |
442 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | 416 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); |
443 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | 417 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); |
@@ -450,22 +424,21 @@ static int test__checkevent_list(struct perf_evlist *evlist) | |||
450 | 424 | ||
451 | static int test__checkevent_pmu_name(struct perf_evlist *evlist) | 425 | static int test__checkevent_pmu_name(struct perf_evlist *evlist) |
452 | { | 426 | { |
453 | struct perf_evsel *evsel; | 427 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
454 | 428 | ||
455 | /* cpu/config=1,name=krava/u */ | 429 | /* cpu/config=1,name=krava/u */ |
456 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
457 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | 430 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); |
458 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 431 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
459 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | 432 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); |
460 | TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava")); | 433 | TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava")); |
461 | 434 | ||
462 | /* cpu/config=2/u" */ | 435 | /* cpu/config=2/u" */ |
463 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | 436 | evsel = perf_evsel__next(evsel); |
464 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | 437 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); |
465 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 438 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
466 | TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); | 439 | TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); |
467 | TEST_ASSERT_VAL("wrong name", | 440 | TEST_ASSERT_VAL("wrong name", |
468 | !strcmp(perf_evsel__name(evsel), "raw 0x2:u")); | 441 | !strcmp(perf_evsel__name(evsel), "cpu/config=2/u")); |
469 | 442 | ||
470 | return 0; | 443 | return 0; |
471 | } | 444 | } |
@@ -513,6 +486,284 @@ static int test__checkterms_simple(struct list_head *terms) | |||
513 | return 0; | 486 | return 0; |
514 | } | 487 | } |
515 | 488 | ||
489 | static int test__group1(struct perf_evlist *evlist) | ||
490 | { | ||
491 | struct perf_evsel *evsel, *leader; | ||
492 | |||
493 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
494 | |||
495 | /* instructions:k */ | ||
496 | evsel = leader = perf_evlist__first(evlist); | ||
497 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
498 | TEST_ASSERT_VAL("wrong config", | ||
499 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
500 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
501 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
502 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
503 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
504 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
505 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
506 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
507 | |||
508 | /* cycles:upp */ | ||
509 | evsel = perf_evsel__next(evsel); | ||
510 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
511 | TEST_ASSERT_VAL("wrong config", | ||
512 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
513 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
514 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
515 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
516 | /* use of precise requires exclude_guest */ | ||
517 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
518 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
519 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | ||
520 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | static int test__group2(struct perf_evlist *evlist) | ||
526 | { | ||
527 | struct perf_evsel *evsel, *leader; | ||
528 | |||
529 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | ||
530 | |||
531 | /* faults + :ku modifier */ | ||
532 | evsel = leader = perf_evlist__first(evlist); | ||
533 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); | ||
534 | TEST_ASSERT_VAL("wrong config", | ||
535 | PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config); | ||
536 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
537 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
538 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
539 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
540 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
541 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
542 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
543 | |||
544 | /* cache-references + :u modifier */ | ||
545 | evsel = perf_evsel__next(evsel); | ||
546 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
547 | TEST_ASSERT_VAL("wrong config", | ||
548 | PERF_COUNT_HW_CACHE_REFERENCES == evsel->attr.config); | ||
549 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
550 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
551 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
552 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
553 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
554 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
555 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
556 | |||
557 | /* cycles:k */ | ||
558 | evsel = perf_evsel__next(evsel); | ||
559 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
560 | TEST_ASSERT_VAL("wrong config", | ||
561 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
562 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
563 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
564 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
565 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
566 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
567 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
568 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
569 | |||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static int test__group3(struct perf_evlist *evlist __maybe_unused) | ||
574 | { | ||
575 | struct perf_evsel *evsel, *leader; | ||
576 | |||
577 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | ||
578 | |||
579 | /* group1 syscalls:sys_enter_open:H */ | ||
580 | evsel = leader = perf_evlist__first(evlist); | ||
581 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
582 | TEST_ASSERT_VAL("wrong sample_type", | ||
583 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); | ||
584 | TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); | ||
585 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
586 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
587 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
588 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
589 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
590 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
591 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
592 | TEST_ASSERT_VAL("wrong group name", | ||
593 | !strcmp(leader->group_name, "group1")); | ||
594 | |||
595 | /* group1 cycles:kppp */ | ||
596 | evsel = perf_evsel__next(evsel); | ||
597 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
598 | TEST_ASSERT_VAL("wrong config", | ||
599 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
600 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
601 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
602 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
603 | /* use of precise requires exclude_guest */ | ||
604 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
605 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
606 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); | ||
607 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
608 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
609 | |||
610 | /* group2 cycles + G modifier */ | ||
611 | evsel = leader = perf_evsel__next(evsel); | ||
612 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
613 | TEST_ASSERT_VAL("wrong config", | ||
614 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
615 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
616 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
617 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
618 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
619 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
620 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
621 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
622 | TEST_ASSERT_VAL("wrong group name", | ||
623 | !strcmp(leader->group_name, "group2")); | ||
624 | |||
625 | /* group2 1:3 + G modifier */ | ||
626 | evsel = perf_evsel__next(evsel); | ||
627 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | ||
628 | TEST_ASSERT_VAL("wrong config", 3 == evsel->attr.config); | ||
629 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
630 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
631 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
632 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
633 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
634 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
635 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
636 | |||
637 | /* instructions:u */ | ||
638 | evsel = perf_evsel__next(evsel); | ||
639 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
640 | TEST_ASSERT_VAL("wrong config", | ||
641 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
642 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
643 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
644 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
645 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
646 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
647 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
648 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
649 | |||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static int test__group4(struct perf_evlist *evlist __maybe_unused) | ||
654 | { | ||
655 | struct perf_evsel *evsel, *leader; | ||
656 | |||
657 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
658 | |||
659 | /* cycles:u + p */ | ||
660 | evsel = leader = perf_evlist__first(evlist); | ||
661 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
662 | TEST_ASSERT_VAL("wrong config", | ||
663 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
664 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
665 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
666 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
667 | /* use of precise requires exclude_guest */ | ||
668 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
669 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
670 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); | ||
671 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
672 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
673 | |||
674 | /* instructions:kp + p */ | ||
675 | evsel = perf_evsel__next(evsel); | ||
676 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
677 | TEST_ASSERT_VAL("wrong config", | ||
678 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
679 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
680 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
681 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
682 | /* use of precise requires exclude_guest */ | ||
683 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
684 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
685 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | ||
686 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
687 | |||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | static int test__group5(struct perf_evlist *evlist __maybe_unused) | ||
692 | { | ||
693 | struct perf_evsel *evsel, *leader; | ||
694 | |||
695 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | ||
696 | |||
697 | /* cycles + G */ | ||
698 | evsel = leader = perf_evlist__first(evlist); | ||
699 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
700 | TEST_ASSERT_VAL("wrong config", | ||
701 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
702 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
703 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
704 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
705 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
706 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
707 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
708 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
709 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
710 | |||
711 | /* instructions + G */ | ||
712 | evsel = perf_evsel__next(evsel); | ||
713 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
714 | TEST_ASSERT_VAL("wrong config", | ||
715 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
716 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
717 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
718 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
719 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
720 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
721 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
722 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
723 | |||
724 | /* cycles:G */ | ||
725 | evsel = leader = perf_evsel__next(evsel); | ||
726 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
727 | TEST_ASSERT_VAL("wrong config", | ||
728 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
729 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
730 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
731 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
732 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
733 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
734 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
735 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
736 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
737 | |||
738 | /* instructions:G */ | ||
739 | evsel = perf_evsel__next(evsel); | ||
740 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
741 | TEST_ASSERT_VAL("wrong config", | ||
742 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
743 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
744 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
745 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
746 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
747 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
748 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
749 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
750 | |||
751 | /* cycles */ | ||
752 | evsel = perf_evsel__next(evsel); | ||
753 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
754 | TEST_ASSERT_VAL("wrong config", | ||
755 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
756 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
757 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
758 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
759 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
760 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
761 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
762 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
763 | |||
764 | return 0; | ||
765 | } | ||
766 | |||
516 | struct test__event_st { | 767 | struct test__event_st { |
517 | const char *name; | 768 | const char *name; |
518 | __u32 type; | 769 | __u32 type; |
@@ -632,6 +883,26 @@ static struct test__event_st test__events[] = { | |||
632 | .name = "mem:0:rw:kp", | 883 | .name = "mem:0:rw:kp", |
633 | .check = test__checkevent_breakpoint_rw_modifier, | 884 | .check = test__checkevent_breakpoint_rw_modifier, |
634 | }, | 885 | }, |
886 | [28] = { | ||
887 | .name = "{instructions:k,cycles:upp}", | ||
888 | .check = test__group1, | ||
889 | }, | ||
890 | [29] = { | ||
891 | .name = "{faults:k,cache-references}:u,cycles:k", | ||
892 | .check = test__group2, | ||
893 | }, | ||
894 | [30] = { | ||
895 | .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u", | ||
896 | .check = test__group3, | ||
897 | }, | ||
898 | [31] = { | ||
899 | .name = "{cycles:u,instructions:kp}:p", | ||
900 | .check = test__group4, | ||
901 | }, | ||
902 | [32] = { | ||
903 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", | ||
904 | .check = test__group5, | ||
905 | }, | ||
635 | }; | 906 | }; |
636 | 907 | ||
637 | static struct test__event_st test__events_pmu[] = { | 908 | static struct test__event_st test__events_pmu[] = { |
@@ -658,9 +929,6 @@ static struct test__term test__terms[] = { | |||
658 | }, | 929 | }, |
659 | }; | 930 | }; |
660 | 931 | ||
661 | #define TEST__TERMS_CNT (sizeof(test__terms) / \ | ||
662 | sizeof(struct test__term)) | ||
663 | |||
664 | static int test_event(struct test__event_st *e) | 932 | static int test_event(struct test__event_st *e) |
665 | { | 933 | { |
666 | struct perf_evlist *evlist; | 934 | struct perf_evlist *evlist; |
@@ -685,19 +953,19 @@ static int test_event(struct test__event_st *e) | |||
685 | 953 | ||
686 | static int test_events(struct test__event_st *events, unsigned cnt) | 954 | static int test_events(struct test__event_st *events, unsigned cnt) |
687 | { | 955 | { |
688 | int ret = 0; | 956 | int ret1, ret2 = 0; |
689 | unsigned i; | 957 | unsigned i; |
690 | 958 | ||
691 | for (i = 0; i < cnt; i++) { | 959 | for (i = 0; i < cnt; i++) { |
692 | struct test__event_st *e = &events[i]; | 960 | struct test__event_st *e = &events[i]; |
693 | 961 | ||
694 | pr_debug("running test %d '%s'\n", i, e->name); | 962 | pr_debug("running test %d '%s'\n", i, e->name); |
695 | ret = test_event(e); | 963 | ret1 = test_event(e); |
696 | if (ret) | 964 | if (ret1) |
697 | break; | 965 | ret2 = ret1; |
698 | } | 966 | } |
699 | 967 | ||
700 | return ret; | 968 | return ret2; |
701 | } | 969 | } |
702 | 970 | ||
703 | static int test_term(struct test__term *t) | 971 | static int test_term(struct test__term *t) |
@@ -758,13 +1026,13 @@ static int test_pmu(void) | |||
758 | 1026 | ||
759 | int parse_events__test(void) | 1027 | int parse_events__test(void) |
760 | { | 1028 | { |
761 | int ret; | 1029 | int ret1, ret2 = 0; |
762 | 1030 | ||
763 | #define TEST_EVENTS(tests) \ | 1031 | #define TEST_EVENTS(tests) \ |
764 | do { \ | 1032 | do { \ |
765 | ret = test_events(tests, ARRAY_SIZE(tests)); \ | 1033 | ret1 = test_events(tests, ARRAY_SIZE(tests)); \ |
766 | if (ret) \ | 1034 | if (!ret2) \ |
767 | return ret; \ | 1035 | ret2 = ret1; \ |
768 | } while (0) | 1036 | } while (0) |
769 | 1037 | ||
770 | TEST_EVENTS(test__events); | 1038 | TEST_EVENTS(test__events); |
@@ -772,5 +1040,9 @@ do { \ | |||
772 | if (test_pmu()) | 1040 | if (test_pmu()) |
773 | TEST_EVENTS(test__events_pmu); | 1041 | TEST_EVENTS(test__events_pmu); |
774 | 1042 | ||
775 | return test_terms(test__terms, ARRAY_SIZE(test__terms)); | 1043 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); |
1044 | if (!ret2) | ||
1045 | ret2 = ret1; | ||
1046 | |||
1047 | return ret2; | ||
776 | } | 1048 | } |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 74a5af4d33ec..75c7b0fca6d9 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -239,8 +239,11 @@ const char *event_type(int type) | |||
239 | return "unknown"; | 239 | return "unknown"; |
240 | } | 240 | } |
241 | 241 | ||
242 | static int add_event(struct list_head **_list, int *idx, | 242 | |
243 | struct perf_event_attr *attr, char *name) | 243 | |
244 | static int __add_event(struct list_head **_list, int *idx, | ||
245 | struct perf_event_attr *attr, | ||
246 | char *name, struct cpu_map *cpus) | ||
244 | { | 247 | { |
245 | struct perf_evsel *evsel; | 248 | struct perf_evsel *evsel; |
246 | struct list_head *list = *_list; | 249 | struct list_head *list = *_list; |
@@ -260,6 +263,7 @@ static int add_event(struct list_head **_list, int *idx, | |||
260 | return -ENOMEM; | 263 | return -ENOMEM; |
261 | } | 264 | } |
262 | 265 | ||
266 | evsel->cpus = cpus; | ||
263 | if (name) | 267 | if (name) |
264 | evsel->name = strdup(name); | 268 | evsel->name = strdup(name); |
265 | list_add_tail(&evsel->node, list); | 269 | list_add_tail(&evsel->node, list); |
@@ -267,6 +271,12 @@ static int add_event(struct list_head **_list, int *idx, | |||
267 | return 0; | 271 | return 0; |
268 | } | 272 | } |
269 | 273 | ||
274 | static int add_event(struct list_head **_list, int *idx, | ||
275 | struct perf_event_attr *attr, char *name) | ||
276 | { | ||
277 | return __add_event(_list, idx, attr, name, NULL); | ||
278 | } | ||
279 | |||
270 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) | 280 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) |
271 | { | 281 | { |
272 | int i, j; | 282 | int i, j; |
@@ -308,7 +318,7 @@ int parse_events_add_cache(struct list_head **list, int *idx, | |||
308 | for (i = 0; (i < 2) && (op_result[i]); i++) { | 318 | for (i = 0; (i < 2) && (op_result[i]); i++) { |
309 | char *str = op_result[i]; | 319 | char *str = op_result[i]; |
310 | 320 | ||
311 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); | 321 | n += snprintf(name + n, MAX_NAME_LEN - n, "-%s", str); |
312 | 322 | ||
313 | if (cache_op == -1) { | 323 | if (cache_op == -1) { |
314 | cache_op = parse_aliases(str, perf_evsel__hw_cache_op, | 324 | cache_op = parse_aliases(str, perf_evsel__hw_cache_op, |
@@ -346,42 +356,28 @@ int parse_events_add_cache(struct list_head **list, int *idx, | |||
346 | return add_event(list, idx, &attr, name); | 356 | return add_event(list, idx, &attr, name); |
347 | } | 357 | } |
348 | 358 | ||
349 | static int add_tracepoint(struct list_head **list, int *idx, | 359 | static int add_tracepoint(struct list_head **listp, int *idx, |
350 | char *sys_name, char *evt_name) | 360 | char *sys_name, char *evt_name) |
351 | { | 361 | { |
352 | struct perf_event_attr attr; | 362 | struct perf_evsel *evsel; |
353 | char name[MAX_NAME_LEN]; | 363 | struct list_head *list = *listp; |
354 | char evt_path[MAXPATHLEN]; | ||
355 | char id_buf[4]; | ||
356 | u64 id; | ||
357 | int fd; | ||
358 | |||
359 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path, | ||
360 | sys_name, evt_name); | ||
361 | |||
362 | fd = open(evt_path, O_RDONLY); | ||
363 | if (fd < 0) | ||
364 | return -1; | ||
365 | 364 | ||
366 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | 365 | if (!list) { |
367 | close(fd); | 366 | list = malloc(sizeof(*list)); |
368 | return -1; | 367 | if (!list) |
368 | return -ENOMEM; | ||
369 | INIT_LIST_HEAD(list); | ||
369 | } | 370 | } |
370 | 371 | ||
371 | close(fd); | 372 | evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); |
372 | id = atoll(id_buf); | 373 | if (!evsel) { |
373 | 374 | free(list); | |
374 | memset(&attr, 0, sizeof(attr)); | 375 | return -ENOMEM; |
375 | attr.config = id; | 376 | } |
376 | attr.type = PERF_TYPE_TRACEPOINT; | ||
377 | attr.sample_type |= PERF_SAMPLE_RAW; | ||
378 | attr.sample_type |= PERF_SAMPLE_TIME; | ||
379 | attr.sample_type |= PERF_SAMPLE_CPU; | ||
380 | attr.sample_type |= PERF_SAMPLE_PERIOD; | ||
381 | attr.sample_period = 1; | ||
382 | 377 | ||
383 | snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name); | 378 | list_add_tail(&evsel->node, list); |
384 | return add_event(list, idx, &attr, name); | 379 | *listp = list; |
380 | return 0; | ||
385 | } | 381 | } |
386 | 382 | ||
387 | static int add_tracepoint_multi(struct list_head **list, int *idx, | 383 | static int add_tracepoint_multi(struct list_head **list, int *idx, |
@@ -551,7 +547,7 @@ static int config_attr(struct perf_event_attr *attr, | |||
551 | } | 547 | } |
552 | 548 | ||
553 | int parse_events_add_numeric(struct list_head **list, int *idx, | 549 | int parse_events_add_numeric(struct list_head **list, int *idx, |
554 | unsigned long type, unsigned long config, | 550 | u32 type, u64 config, |
555 | struct list_head *head_config) | 551 | struct list_head *head_config) |
556 | { | 552 | { |
557 | struct perf_event_attr attr; | 553 | struct perf_event_attr attr; |
@@ -607,8 +603,23 @@ int parse_events_add_pmu(struct list_head **list, int *idx, | |||
607 | if (perf_pmu__config(pmu, &attr, head_config)) | 603 | if (perf_pmu__config(pmu, &attr, head_config)) |
608 | return -EINVAL; | 604 | return -EINVAL; |
609 | 605 | ||
610 | return add_event(list, idx, &attr, | 606 | return __add_event(list, idx, &attr, pmu_event_name(head_config), |
611 | pmu_event_name(head_config)); | 607 | pmu->cpus); |
608 | } | ||
609 | |||
610 | int parse_events__modifier_group(struct list_head *list, | ||
611 | char *event_mod) | ||
612 | { | ||
613 | return parse_events__modifier_event(list, event_mod, true); | ||
614 | } | ||
615 | |||
616 | void parse_events__set_leader(char *name, struct list_head *list) | ||
617 | { | ||
618 | struct perf_evsel *leader; | ||
619 | |||
620 | __perf_evlist__set_leader(list); | ||
621 | leader = list_entry(list->next, struct perf_evsel, node); | ||
622 | leader->group_name = name ? strdup(name) : NULL; | ||
612 | } | 623 | } |
613 | 624 | ||
614 | void parse_events_update_lists(struct list_head *list_event, | 625 | void parse_events_update_lists(struct list_head *list_event, |
@@ -616,21 +627,45 @@ void parse_events_update_lists(struct list_head *list_event, | |||
616 | { | 627 | { |
617 | /* | 628 | /* |
618 | * Called for single event definition. Update the | 629 | * Called for single event definition. Update the |
619 | * 'all event' list, and reinit the 'signle event' | 630 | * 'all event' list, and reinit the 'single event' |
620 | * list, for next event definition. | 631 | * list, for next event definition. |
621 | */ | 632 | */ |
622 | list_splice_tail(list_event, list_all); | 633 | list_splice_tail(list_event, list_all); |
623 | free(list_event); | 634 | free(list_event); |
624 | } | 635 | } |
625 | 636 | ||
626 | int parse_events_modifier(struct list_head *list, char *str) | 637 | struct event_modifier { |
638 | int eu; | ||
639 | int ek; | ||
640 | int eh; | ||
641 | int eH; | ||
642 | int eG; | ||
643 | int precise; | ||
644 | int exclude_GH; | ||
645 | }; | ||
646 | |||
647 | static int get_event_modifier(struct event_modifier *mod, char *str, | ||
648 | struct perf_evsel *evsel) | ||
627 | { | 649 | { |
628 | struct perf_evsel *evsel; | 650 | int eu = evsel ? evsel->attr.exclude_user : 0; |
629 | int exclude = 0, exclude_GH = 0; | 651 | int ek = evsel ? evsel->attr.exclude_kernel : 0; |
630 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; | 652 | int eh = evsel ? evsel->attr.exclude_hv : 0; |
653 | int eH = evsel ? evsel->attr.exclude_host : 0; | ||
654 | int eG = evsel ? evsel->attr.exclude_guest : 0; | ||
655 | int precise = evsel ? evsel->attr.precise_ip : 0; | ||
631 | 656 | ||
632 | if (str == NULL) | 657 | int exclude = eu | ek | eh; |
633 | return 0; | 658 | int exclude_GH = evsel ? evsel->exclude_GH : 0; |
659 | |||
660 | /* | ||
661 | * We are here for group and 'GH' was not set as event | ||
662 | * modifier and whatever event/group modifier override | ||
663 | * default 'GH' setup. | ||
664 | */ | ||
665 | if (evsel && !exclude_GH) | ||
666 | eH = eG = 0; | ||
667 | |||
668 | memset(mod, 0, sizeof(*mod)); | ||
634 | 669 | ||
635 | while (*str) { | 670 | while (*str) { |
636 | if (*str == 'u') { | 671 | if (*str == 'u') { |
@@ -655,6 +690,9 @@ int parse_events_modifier(struct list_head *list, char *str) | |||
655 | eH = 0; | 690 | eH = 0; |
656 | } else if (*str == 'p') { | 691 | } else if (*str == 'p') { |
657 | precise++; | 692 | precise++; |
693 | /* use of precise requires exclude_guest */ | ||
694 | if (!exclude_GH) | ||
695 | eG = 1; | ||
658 | } else | 696 | } else |
659 | break; | 697 | break; |
660 | 698 | ||
@@ -674,13 +712,51 @@ int parse_events_modifier(struct list_head *list, char *str) | |||
674 | if (precise > 3) | 712 | if (precise > 3) |
675 | return -EINVAL; | 713 | return -EINVAL; |
676 | 714 | ||
715 | mod->eu = eu; | ||
716 | mod->ek = ek; | ||
717 | mod->eh = eh; | ||
718 | mod->eH = eH; | ||
719 | mod->eG = eG; | ||
720 | mod->precise = precise; | ||
721 | mod->exclude_GH = exclude_GH; | ||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | int parse_events__modifier_event(struct list_head *list, char *str, bool add) | ||
726 | { | ||
727 | struct perf_evsel *evsel; | ||
728 | struct event_modifier mod; | ||
729 | |||
730 | if (str == NULL) | ||
731 | return 0; | ||
732 | |||
733 | if (!add && get_event_modifier(&mod, str, NULL)) | ||
734 | return -EINVAL; | ||
735 | |||
736 | list_for_each_entry(evsel, list, node) { | ||
737 | |||
738 | if (add && get_event_modifier(&mod, str, evsel)) | ||
739 | return -EINVAL; | ||
740 | |||
741 | evsel->attr.exclude_user = mod.eu; | ||
742 | evsel->attr.exclude_kernel = mod.ek; | ||
743 | evsel->attr.exclude_hv = mod.eh; | ||
744 | evsel->attr.precise_ip = mod.precise; | ||
745 | evsel->attr.exclude_host = mod.eH; | ||
746 | evsel->attr.exclude_guest = mod.eG; | ||
747 | evsel->exclude_GH = mod.exclude_GH; | ||
748 | } | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | int parse_events_name(struct list_head *list, char *name) | ||
754 | { | ||
755 | struct perf_evsel *evsel; | ||
756 | |||
677 | list_for_each_entry(evsel, list, node) { | 757 | list_for_each_entry(evsel, list, node) { |
678 | evsel->attr.exclude_user = eu; | 758 | if (!evsel->name) |
679 | evsel->attr.exclude_kernel = ek; | 759 | evsel->name = strdup(name); |
680 | evsel->attr.exclude_hv = eh; | ||
681 | evsel->attr.precise_ip = precise; | ||
682 | evsel->attr.exclude_host = eH; | ||
683 | evsel->attr.exclude_guest = eG; | ||
684 | } | 760 | } |
685 | 761 | ||
686 | return 0; | 762 | return 0; |
@@ -730,7 +806,8 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
730 | return ret; | 806 | return ret; |
731 | } | 807 | } |
732 | 808 | ||
733 | int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) | 809 | int parse_events(struct perf_evlist *evlist, const char *str, |
810 | int unset __maybe_unused) | ||
734 | { | 811 | { |
735 | struct parse_events_data__events data = { | 812 | struct parse_events_data__events data = { |
736 | .list = LIST_HEAD_INIT(data.list), | 813 | .list = LIST_HEAD_INIT(data.list), |
@@ -756,20 +833,20 @@ int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) | |||
756 | } | 833 | } |
757 | 834 | ||
758 | int parse_events_option(const struct option *opt, const char *str, | 835 | int parse_events_option(const struct option *opt, const char *str, |
759 | int unset __used) | 836 | int unset __maybe_unused) |
760 | { | 837 | { |
761 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 838 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
762 | return parse_events(evlist, str, unset); | 839 | return parse_events(evlist, str, unset); |
763 | } | 840 | } |
764 | 841 | ||
765 | int parse_filter(const struct option *opt, const char *str, | 842 | int parse_filter(const struct option *opt, const char *str, |
766 | int unset __used) | 843 | int unset __maybe_unused) |
767 | { | 844 | { |
768 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 845 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
769 | struct perf_evsel *last = NULL; | 846 | struct perf_evsel *last = NULL; |
770 | 847 | ||
771 | if (evlist->nr_entries > 0) | 848 | if (evlist->nr_entries > 0) |
772 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); | 849 | last = perf_evlist__last(evlist); |
773 | 850 | ||
774 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 851 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
775 | fprintf(stderr, | 852 | fprintf(stderr, |
@@ -799,7 +876,8 @@ static const char * const event_type_descriptors[] = { | |||
799 | * Print the events from <debugfs_mount_point>/tracing/events | 876 | * Print the events from <debugfs_mount_point>/tracing/events |
800 | */ | 877 | */ |
801 | 878 | ||
802 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob) | 879 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob, |
880 | bool name_only) | ||
803 | { | 881 | { |
804 | DIR *sys_dir, *evt_dir; | 882 | DIR *sys_dir, *evt_dir; |
805 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 883 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
@@ -829,6 +907,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob) | |||
829 | !strglobmatch(evt_dirent.d_name, event_glob)) | 907 | !strglobmatch(evt_dirent.d_name, event_glob)) |
830 | continue; | 908 | continue; |
831 | 909 | ||
910 | if (name_only) { | ||
911 | printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name); | ||
912 | continue; | ||
913 | } | ||
914 | |||
832 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 915 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
833 | sys_dirent.d_name, evt_dirent.d_name); | 916 | sys_dirent.d_name, evt_dirent.d_name); |
834 | printf(" %-50s [%s]\n", evt_path, | 917 | printf(" %-50s [%s]\n", evt_path, |
@@ -906,7 +989,7 @@ void print_events_type(u8 type) | |||
906 | __print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX); | 989 | __print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX); |
907 | } | 990 | } |
908 | 991 | ||
909 | int print_hwcache_events(const char *event_glob) | 992 | int print_hwcache_events(const char *event_glob, bool name_only) |
910 | { | 993 | { |
911 | unsigned int type, op, i, printed = 0; | 994 | unsigned int type, op, i, printed = 0; |
912 | char name[64]; | 995 | char name[64]; |
@@ -923,8 +1006,11 @@ int print_hwcache_events(const char *event_glob) | |||
923 | if (event_glob != NULL && !strglobmatch(name, event_glob)) | 1006 | if (event_glob != NULL && !strglobmatch(name, event_glob)) |
924 | continue; | 1007 | continue; |
925 | 1008 | ||
926 | printf(" %-50s [%s]\n", name, | 1009 | if (name_only) |
927 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | 1010 | printf("%s ", name); |
1011 | else | ||
1012 | printf(" %-50s [%s]\n", name, | ||
1013 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | ||
928 | ++printed; | 1014 | ++printed; |
929 | } | 1015 | } |
930 | } | 1016 | } |
@@ -934,7 +1020,8 @@ int print_hwcache_events(const char *event_glob) | |||
934 | } | 1020 | } |
935 | 1021 | ||
936 | static void print_symbol_events(const char *event_glob, unsigned type, | 1022 | static void print_symbol_events(const char *event_glob, unsigned type, |
937 | struct event_symbol *syms, unsigned max) | 1023 | struct event_symbol *syms, unsigned max, |
1024 | bool name_only) | ||
938 | { | 1025 | { |
939 | unsigned i, printed = 0; | 1026 | unsigned i, printed = 0; |
940 | char name[MAX_NAME_LEN]; | 1027 | char name[MAX_NAME_LEN]; |
@@ -946,6 +1033,11 @@ static void print_symbol_events(const char *event_glob, unsigned type, | |||
946 | (syms->alias && strglobmatch(syms->alias, event_glob)))) | 1033 | (syms->alias && strglobmatch(syms->alias, event_glob)))) |
947 | continue; | 1034 | continue; |
948 | 1035 | ||
1036 | if (name_only) { | ||
1037 | printf("%s ", syms->symbol); | ||
1038 | continue; | ||
1039 | } | ||
1040 | |||
949 | if (strlen(syms->alias)) | 1041 | if (strlen(syms->alias)) |
950 | snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); | 1042 | snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); |
951 | else | 1043 | else |
@@ -963,39 +1055,42 @@ static void print_symbol_events(const char *event_glob, unsigned type, | |||
963 | /* | 1055 | /* |
964 | * Print the help text for the event symbols: | 1056 | * Print the help text for the event symbols: |
965 | */ | 1057 | */ |
966 | void print_events(const char *event_glob) | 1058 | void print_events(const char *event_glob, bool name_only) |
967 | { | 1059 | { |
968 | 1060 | if (!name_only) { | |
969 | printf("\n"); | 1061 | printf("\n"); |
970 | printf("List of pre-defined events (to be used in -e):\n"); | 1062 | printf("List of pre-defined events (to be used in -e):\n"); |
1063 | } | ||
971 | 1064 | ||
972 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, | 1065 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, |
973 | event_symbols_hw, PERF_COUNT_HW_MAX); | 1066 | event_symbols_hw, PERF_COUNT_HW_MAX, name_only); |
974 | 1067 | ||
975 | print_symbol_events(event_glob, PERF_TYPE_SOFTWARE, | 1068 | print_symbol_events(event_glob, PERF_TYPE_SOFTWARE, |
976 | event_symbols_sw, PERF_COUNT_SW_MAX); | 1069 | event_symbols_sw, PERF_COUNT_SW_MAX, name_only); |
977 | 1070 | ||
978 | print_hwcache_events(event_glob); | 1071 | print_hwcache_events(event_glob, name_only); |
979 | 1072 | ||
980 | if (event_glob != NULL) | 1073 | if (event_glob != NULL) |
981 | return; | 1074 | return; |
982 | 1075 | ||
983 | printf("\n"); | 1076 | if (!name_only) { |
984 | printf(" %-50s [%s]\n", | 1077 | printf("\n"); |
985 | "rNNN", | 1078 | printf(" %-50s [%s]\n", |
986 | event_type_descriptors[PERF_TYPE_RAW]); | 1079 | "rNNN", |
987 | printf(" %-50s [%s]\n", | 1080 | event_type_descriptors[PERF_TYPE_RAW]); |
988 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | 1081 | printf(" %-50s [%s]\n", |
989 | event_type_descriptors[PERF_TYPE_RAW]); | 1082 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", |
990 | printf(" (see 'perf list --help' on how to encode it)\n"); | 1083 | event_type_descriptors[PERF_TYPE_RAW]); |
991 | printf("\n"); | 1084 | printf(" (see 'perf list --help' on how to encode it)\n"); |
992 | 1085 | printf("\n"); | |
993 | printf(" %-50s [%s]\n", | 1086 | |
994 | "mem:<addr>[:access]", | 1087 | printf(" %-50s [%s]\n", |
1088 | "mem:<addr>[:access]", | ||
995 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | 1089 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); |
996 | printf("\n"); | 1090 | printf("\n"); |
1091 | } | ||
997 | 1092 | ||
998 | print_tracepoint_events(NULL, NULL); | 1093 | print_tracepoint_events(NULL, NULL, name_only); |
999 | } | 1094 | } |
1000 | 1095 | ||
1001 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | 1096 | int parse_events__is_hardcoded_term(struct parse_events__term *term) |
@@ -1005,7 +1100,7 @@ int parse_events__is_hardcoded_term(struct parse_events__term *term) | |||
1005 | 1100 | ||
1006 | static int new_term(struct parse_events__term **_term, int type_val, | 1101 | static int new_term(struct parse_events__term **_term, int type_val, |
1007 | int type_term, char *config, | 1102 | int type_term, char *config, |
1008 | char *str, long num) | 1103 | char *str, u64 num) |
1009 | { | 1104 | { |
1010 | struct parse_events__term *term; | 1105 | struct parse_events__term *term; |
1011 | 1106 | ||
@@ -1034,7 +1129,7 @@ static int new_term(struct parse_events__term **_term, int type_val, | |||
1034 | } | 1129 | } |
1035 | 1130 | ||
1036 | int parse_events__term_num(struct parse_events__term **term, | 1131 | int parse_events__term_num(struct parse_events__term **term, |
1037 | int type_term, char *config, long num) | 1132 | int type_term, char *config, u64 num) |
1038 | { | 1133 | { |
1039 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, | 1134 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, |
1040 | config, NULL, num); | 1135 | config, NULL, num); |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index ee9c218a193c..839230ceb18b 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -7,7 +7,7 @@ | |||
7 | #include <linux/list.h> | 7 | #include <linux/list.h> |
8 | #include <stdbool.h> | 8 | #include <stdbool.h> |
9 | #include "types.h" | 9 | #include "types.h" |
10 | #include "../../../include/linux/perf_event.h" | 10 | #include "../../../include/uapi/linux/perf_event.h" |
11 | #include "types.h" | 11 | #include "types.h" |
12 | 12 | ||
13 | struct list_head; | 13 | struct list_head; |
@@ -55,7 +55,7 @@ struct parse_events__term { | |||
55 | char *config; | 55 | char *config; |
56 | union { | 56 | union { |
57 | char *str; | 57 | char *str; |
58 | long num; | 58 | u64 num; |
59 | } val; | 59 | } val; |
60 | int type_val; | 60 | int type_val; |
61 | int type_term; | 61 | int type_term; |
@@ -73,17 +73,19 @@ struct parse_events_data__terms { | |||
73 | 73 | ||
74 | int parse_events__is_hardcoded_term(struct parse_events__term *term); | 74 | int parse_events__is_hardcoded_term(struct parse_events__term *term); |
75 | int parse_events__term_num(struct parse_events__term **_term, | 75 | int parse_events__term_num(struct parse_events__term **_term, |
76 | int type_term, char *config, long num); | 76 | int type_term, char *config, u64 num); |
77 | int parse_events__term_str(struct parse_events__term **_term, | 77 | int parse_events__term_str(struct parse_events__term **_term, |
78 | int type_term, char *config, char *str); | 78 | int type_term, char *config, char *str); |
79 | int parse_events__term_clone(struct parse_events__term **new, | 79 | int parse_events__term_clone(struct parse_events__term **new, |
80 | struct parse_events__term *term); | 80 | struct parse_events__term *term); |
81 | void parse_events__free_terms(struct list_head *terms); | 81 | void parse_events__free_terms(struct list_head *terms); |
82 | int parse_events_modifier(struct list_head *list, char *str); | 82 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); |
83 | int parse_events__modifier_group(struct list_head *list, char *event_mod); | ||
84 | int parse_events_name(struct list_head *list, char *name); | ||
83 | int parse_events_add_tracepoint(struct list_head **list, int *idx, | 85 | int parse_events_add_tracepoint(struct list_head **list, int *idx, |
84 | char *sys, char *event); | 86 | char *sys, char *event); |
85 | int parse_events_add_numeric(struct list_head **list, int *idx, | 87 | int parse_events_add_numeric(struct list_head **list, int *idx, |
86 | unsigned long type, unsigned long config, | 88 | u32 type, u64 config, |
87 | struct list_head *head_config); | 89 | struct list_head *head_config); |
88 | int parse_events_add_cache(struct list_head **list, int *idx, | 90 | int parse_events_add_cache(struct list_head **list, int *idx, |
89 | char *type, char *op_result1, char *op_result2); | 91 | char *type, char *op_result1, char *op_result2); |
@@ -91,15 +93,17 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx, | |||
91 | void *ptr, char *type); | 93 | void *ptr, char *type); |
92 | int parse_events_add_pmu(struct list_head **list, int *idx, | 94 | int parse_events_add_pmu(struct list_head **list, int *idx, |
93 | char *pmu , struct list_head *head_config); | 95 | char *pmu , struct list_head *head_config); |
96 | void parse_events__set_leader(char *name, struct list_head *list); | ||
94 | void parse_events_update_lists(struct list_head *list_event, | 97 | void parse_events_update_lists(struct list_head *list_event, |
95 | struct list_head *list_all); | 98 | struct list_head *list_all); |
96 | void parse_events_error(void *data, void *scanner, char const *msg); | 99 | void parse_events_error(void *data, void *scanner, char const *msg); |
97 | int parse_events__test(void); | 100 | int parse_events__test(void); |
98 | 101 | ||
99 | void print_events(const char *event_glob); | 102 | void print_events(const char *event_glob, bool name_only); |
100 | void print_events_type(u8 type); | 103 | void print_events_type(u8 type); |
101 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); | 104 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob, |
102 | int print_hwcache_events(const char *event_glob); | 105 | bool name_only); |
106 | int print_hwcache_events(const char *event_glob, bool name_only); | ||
103 | extern int is_valid_tracepoint(const char *event_string); | 107 | extern int is_valid_tracepoint(const char *event_string); |
104 | 108 | ||
105 | extern int valid_debugfs_mount(const char *debugfs); | 109 | extern int valid_debugfs_mount(const char *debugfs); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 384ca74c6b22..c87efc12579d 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -15,10 +15,10 @@ YYSTYPE *parse_events_get_lval(yyscan_t yyscanner); | |||
15 | 15 | ||
16 | static int __value(YYSTYPE *yylval, char *str, int base, int token) | 16 | static int __value(YYSTYPE *yylval, char *str, int base, int token) |
17 | { | 17 | { |
18 | long num; | 18 | u64 num; |
19 | 19 | ||
20 | errno = 0; | 20 | errno = 0; |
21 | num = strtoul(str, NULL, base); | 21 | num = strtoull(str, NULL, base); |
22 | if (errno) | 22 | if (errno) |
23 | return PE_ERROR; | 23 | return PE_ERROR; |
24 | 24 | ||
@@ -70,6 +70,12 @@ static int term(yyscan_t scanner, int type) | |||
70 | %} | 70 | %} |
71 | 71 | ||
72 | %x mem | 72 | %x mem |
73 | %s config | ||
74 | %x event | ||
75 | |||
76 | group [^,{}/]*[{][^}]*[}][^,{}/]* | ||
77 | event_pmu [^,{}/]+[/][^/]*[/][^,{}/]* | ||
78 | event [^,{}/]+ | ||
73 | 79 | ||
74 | num_dec [0-9]+ | 80 | num_dec [0-9]+ |
75 | num_hex 0x[a-fA-F0-9]+ | 81 | num_hex 0x[a-fA-F0-9]+ |
@@ -84,7 +90,13 @@ modifier_bp [rwx]{1,3} | |||
84 | { | 90 | { |
85 | int start_token; | 91 | int start_token; |
86 | 92 | ||
87 | start_token = (int) parse_events_get_extra(yyscanner); | 93 | start_token = parse_events_get_extra(yyscanner); |
94 | |||
95 | if (start_token == PE_START_TERMS) | ||
96 | BEGIN(config); | ||
97 | else if (start_token == PE_START_EVENTS) | ||
98 | BEGIN(event); | ||
99 | |||
88 | if (start_token) { | 100 | if (start_token) { |
89 | parse_events_set_extra(NULL, yyscanner); | 101 | parse_events_set_extra(NULL, yyscanner); |
90 | return start_token; | 102 | return start_token; |
@@ -92,6 +104,26 @@ modifier_bp [rwx]{1,3} | |||
92 | } | 104 | } |
93 | %} | 105 | %} |
94 | 106 | ||
107 | <event>{ | ||
108 | |||
109 | {group} { | ||
110 | BEGIN(INITIAL); yyless(0); | ||
111 | } | ||
112 | |||
113 | {event_pmu} | | ||
114 | {event} { | ||
115 | str(yyscanner, PE_EVENT_NAME); | ||
116 | BEGIN(INITIAL); yyless(0); | ||
117 | return PE_EVENT_NAME; | ||
118 | } | ||
119 | |||
120 | . | | ||
121 | <<EOF>> { | ||
122 | BEGIN(INITIAL); yyless(0); | ||
123 | } | ||
124 | |||
125 | } | ||
126 | |||
95 | cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } | 127 | cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } |
96 | stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } | 128 | stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } |
97 | stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } | 129 | stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } |
@@ -127,18 +159,16 @@ speculative-read|speculative-load | | |||
127 | refs|Reference|ops|access | | 159 | refs|Reference|ops|access | |
128 | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } | 160 | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } |
129 | 161 | ||
130 | /* | 162 | <config>{ |
131 | * These are event config hardcoded term names to be specified | ||
132 | * within xxx/.../ syntax. So far we dont clash with other names, | ||
133 | * so we can put them here directly. In case the we have a conflict | ||
134 | * in future, this needs to go into '//' condition block. | ||
135 | */ | ||
136 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } | 163 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } |
137 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } | 164 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } |
138 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } | 165 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } |
139 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } | 166 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } |
140 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | 167 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } |
141 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | 168 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } |
169 | , { return ','; } | ||
170 | "/" { BEGIN(INITIAL); return '/'; } | ||
171 | } | ||
142 | 172 | ||
143 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } | 173 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } |
144 | r{num_raw_hex} { return raw(yyscanner); } | 174 | r{num_raw_hex} { return raw(yyscanner); } |
@@ -147,10 +177,12 @@ r{num_raw_hex} { return raw(yyscanner); } | |||
147 | 177 | ||
148 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } | 178 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } |
149 | {name} { return str(yyscanner, PE_NAME); } | 179 | {name} { return str(yyscanner, PE_NAME); } |
150 | "/" { return '/'; } | 180 | "/" { BEGIN(config); return '/'; } |
151 | - { return '-'; } | 181 | - { return '-'; } |
152 | , { return ','; } | 182 | , { BEGIN(event); return ','; } |
153 | : { return ':'; } | 183 | : { return ':'; } |
184 | "{" { BEGIN(event); return '{'; } | ||
185 | "}" { return '}'; } | ||
154 | = { return '='; } | 186 | = { return '='; } |
155 | \n { } | 187 | \n { } |
156 | 188 | ||
@@ -175,7 +207,7 @@ r{num_raw_hex} { return raw(yyscanner); } | |||
175 | 207 | ||
176 | %% | 208 | %% |
177 | 209 | ||
178 | int parse_events_wrap(void *scanner __used) | 210 | int parse_events_wrap(void *scanner __maybe_unused) |
179 | { | 211 | { |
180 | return 1; | 212 | return 1; |
181 | } | 213 | } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 2bc5fbff2b5d..cd88209e3c58 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -27,10 +27,11 @@ do { \ | |||
27 | 27 | ||
28 | %token PE_START_EVENTS PE_START_TERMS | 28 | %token PE_START_EVENTS PE_START_TERMS |
29 | %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM | 29 | %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM |
30 | %token PE_EVENT_NAME | ||
30 | %token PE_NAME | 31 | %token PE_NAME |
31 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP | 32 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP |
32 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | 33 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT |
33 | %token PE_PREFIX_MEM PE_PREFIX_RAW | 34 | %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP |
34 | %token PE_ERROR | 35 | %token PE_ERROR |
35 | %type <num> PE_VALUE | 36 | %type <num> PE_VALUE |
36 | %type <num> PE_VALUE_SYM_HW | 37 | %type <num> PE_VALUE_SYM_HW |
@@ -42,6 +43,7 @@ do { \ | |||
42 | %type <str> PE_NAME_CACHE_OP_RESULT | 43 | %type <str> PE_NAME_CACHE_OP_RESULT |
43 | %type <str> PE_MODIFIER_EVENT | 44 | %type <str> PE_MODIFIER_EVENT |
44 | %type <str> PE_MODIFIER_BP | 45 | %type <str> PE_MODIFIER_BP |
46 | %type <str> PE_EVENT_NAME | ||
45 | %type <num> value_sym | 47 | %type <num> value_sym |
46 | %type <head> event_config | 48 | %type <head> event_config |
47 | %type <term> event_term | 49 | %type <term> event_term |
@@ -53,44 +55,125 @@ do { \ | |||
53 | %type <head> event_legacy_numeric | 55 | %type <head> event_legacy_numeric |
54 | %type <head> event_legacy_raw | 56 | %type <head> event_legacy_raw |
55 | %type <head> event_def | 57 | %type <head> event_def |
58 | %type <head> event_mod | ||
59 | %type <head> event_name | ||
60 | %type <head> event | ||
61 | %type <head> events | ||
62 | %type <head> group_def | ||
63 | %type <head> group | ||
64 | %type <head> groups | ||
56 | 65 | ||
57 | %union | 66 | %union |
58 | { | 67 | { |
59 | char *str; | 68 | char *str; |
60 | unsigned long num; | 69 | u64 num; |
61 | struct list_head *head; | 70 | struct list_head *head; |
62 | struct parse_events__term *term; | 71 | struct parse_events__term *term; |
63 | } | 72 | } |
64 | %% | 73 | %% |
65 | 74 | ||
66 | start: | 75 | start: |
67 | PE_START_EVENTS events | 76 | PE_START_EVENTS start_events |
68 | | | 77 | | |
69 | PE_START_TERMS terms | 78 | PE_START_TERMS start_terms |
79 | |||
80 | start_events: groups | ||
81 | { | ||
82 | struct parse_events_data__events *data = _data; | ||
83 | |||
84 | parse_events_update_lists($1, &data->list); | ||
85 | } | ||
86 | |||
87 | groups: | ||
88 | groups ',' group | ||
89 | { | ||
90 | struct list_head *list = $1; | ||
91 | struct list_head *group = $3; | ||
92 | |||
93 | parse_events_update_lists(group, list); | ||
94 | $$ = list; | ||
95 | } | ||
96 | | | ||
97 | groups ',' event | ||
98 | { | ||
99 | struct list_head *list = $1; | ||
100 | struct list_head *event = $3; | ||
101 | |||
102 | parse_events_update_lists(event, list); | ||
103 | $$ = list; | ||
104 | } | ||
105 | | | ||
106 | group | ||
107 | | | ||
108 | event | ||
109 | |||
110 | group: | ||
111 | group_def ':' PE_MODIFIER_EVENT | ||
112 | { | ||
113 | struct list_head *list = $1; | ||
114 | |||
115 | ABORT_ON(parse_events__modifier_group(list, $3)); | ||
116 | $$ = list; | ||
117 | } | ||
118 | | | ||
119 | group_def | ||
120 | |||
121 | group_def: | ||
122 | PE_NAME '{' events '}' | ||
123 | { | ||
124 | struct list_head *list = $3; | ||
125 | |||
126 | parse_events__set_leader($1, list); | ||
127 | $$ = list; | ||
128 | } | ||
129 | | | ||
130 | '{' events '}' | ||
131 | { | ||
132 | struct list_head *list = $2; | ||
133 | |||
134 | parse_events__set_leader(NULL, list); | ||
135 | $$ = list; | ||
136 | } | ||
70 | 137 | ||
71 | events: | 138 | events: |
72 | events ',' event | event | 139 | events ',' event |
140 | { | ||
141 | struct list_head *event = $3; | ||
142 | struct list_head *list = $1; | ||
73 | 143 | ||
74 | event: | 144 | parse_events_update_lists(event, list); |
75 | event_def PE_MODIFIER_EVENT | 145 | $$ = list; |
146 | } | ||
147 | | | ||
148 | event | ||
149 | |||
150 | event: event_mod | ||
151 | |||
152 | event_mod: | ||
153 | event_name PE_MODIFIER_EVENT | ||
76 | { | 154 | { |
77 | struct parse_events_data__events *data = _data; | 155 | struct list_head *list = $1; |
78 | 156 | ||
79 | /* | 157 | /* |
80 | * Apply modifier on all events added by single event definition | 158 | * Apply modifier on all events added by single event definition |
81 | * (there could be more events added for multiple tracepoint | 159 | * (there could be more events added for multiple tracepoint |
82 | * definitions via '*?'. | 160 | * definitions via '*?'. |
83 | */ | 161 | */ |
84 | ABORT_ON(parse_events_modifier($1, $2)); | 162 | ABORT_ON(parse_events__modifier_event(list, $2, false)); |
85 | parse_events_update_lists($1, &data->list); | 163 | $$ = list; |
86 | } | 164 | } |
87 | | | 165 | | |
88 | event_def | 166 | event_name |
89 | { | ||
90 | struct parse_events_data__events *data = _data; | ||
91 | 167 | ||
92 | parse_events_update_lists($1, &data->list); | 168 | event_name: |
169 | PE_EVENT_NAME event_def | ||
170 | { | ||
171 | ABORT_ON(parse_events_name($2, $1)); | ||
172 | free($1); | ||
173 | $$ = $2; | ||
93 | } | 174 | } |
175 | | | ||
176 | event_def | ||
94 | 177 | ||
95 | event_def: event_pmu | | 178 | event_def: event_pmu | |
96 | event_legacy_symbol | | 179 | event_legacy_symbol | |
@@ -207,7 +290,7 @@ PE_VALUE ':' PE_VALUE | |||
207 | struct parse_events_data__events *data = _data; | 290 | struct parse_events_data__events *data = _data; |
208 | struct list_head *list = NULL; | 291 | struct list_head *list = NULL; |
209 | 292 | ||
210 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, $1, $3, NULL)); | 293 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); |
211 | $$ = list; | 294 | $$ = list; |
212 | } | 295 | } |
213 | 296 | ||
@@ -222,7 +305,7 @@ PE_RAW | |||
222 | $$ = list; | 305 | $$ = list; |
223 | } | 306 | } |
224 | 307 | ||
225 | terms: event_config | 308 | start_terms: event_config |
226 | { | 309 | { |
227 | struct parse_events_data__terms *data = _data; | 310 | struct parse_events_data__terms *data = _data; |
228 | data->terms = $1; | 311 | data->terms = $1; |
@@ -282,7 +365,7 @@ PE_TERM '=' PE_NAME | |||
282 | { | 365 | { |
283 | struct parse_events__term *term; | 366 | struct parse_events__term *term; |
284 | 367 | ||
285 | ABORT_ON(parse_events__term_str(&term, $1, NULL, $3)); | 368 | ABORT_ON(parse_events__term_str(&term, (int)$1, NULL, $3)); |
286 | $$ = term; | 369 | $$ = term; |
287 | } | 370 | } |
288 | | | 371 | | |
@@ -290,7 +373,7 @@ PE_TERM '=' PE_VALUE | |||
290 | { | 373 | { |
291 | struct parse_events__term *term; | 374 | struct parse_events__term *term; |
292 | 375 | ||
293 | ABORT_ON(parse_events__term_num(&term, $1, NULL, $3)); | 376 | ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, $3)); |
294 | $$ = term; | 377 | $$ = term; |
295 | } | 378 | } |
296 | | | 379 | | |
@@ -298,7 +381,7 @@ PE_TERM | |||
298 | { | 381 | { |
299 | struct parse_events__term *term; | 382 | struct parse_events__term *term; |
300 | 383 | ||
301 | ABORT_ON(parse_events__term_num(&term, $1, NULL, 1)); | 384 | ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, 1)); |
302 | $$ = term; | 385 | $$ = term; |
303 | } | 386 | } |
304 | 387 | ||
@@ -308,7 +391,7 @@ sep_slash_dc: '/' | ':' | | |||
308 | 391 | ||
309 | %% | 392 | %% |
310 | 393 | ||
311 | void parse_events_error(void *data __used, void *scanner __used, | 394 | void parse_events_error(void *data __maybe_unused, void *scanner __maybe_unused, |
312 | char const *msg __used) | 395 | char const *msg __maybe_unused) |
313 | { | 396 | { |
314 | } | 397 | } |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 594f8fad5ecd..2bc9e70df7e2 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -384,6 +384,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
384 | return usage_with_options_internal(usagestr, options, 1); | 384 | return usage_with_options_internal(usagestr, options, 1); |
385 | if (internal_help && !strcmp(arg + 2, "help")) | 385 | if (internal_help && !strcmp(arg + 2, "help")) |
386 | return parse_options_usage(usagestr, options); | 386 | return parse_options_usage(usagestr, options); |
387 | if (!strcmp(arg + 2, "list-opts")) | ||
388 | return PARSE_OPT_LIST; | ||
387 | switch (parse_long_opt(ctx, arg + 2, options)) { | 389 | switch (parse_long_opt(ctx, arg + 2, options)) { |
388 | case -1: | 390 | case -1: |
389 | return parse_options_usage(usagestr, options); | 391 | return parse_options_usage(usagestr, options); |
@@ -422,6 +424,12 @@ int parse_options(int argc, const char **argv, const struct option *options, | |||
422 | exit(129); | 424 | exit(129); |
423 | case PARSE_OPT_DONE: | 425 | case PARSE_OPT_DONE: |
424 | break; | 426 | break; |
427 | case PARSE_OPT_LIST: | ||
428 | while (options->type != OPTION_END) { | ||
429 | printf("--%s ", options->long_name); | ||
430 | options++; | ||
431 | } | ||
432 | exit(130); | ||
425 | default: /* PARSE_OPT_UNKNOWN */ | 433 | default: /* PARSE_OPT_UNKNOWN */ |
426 | if (ctx.argv[0][1] == '-') { | 434 | if (ctx.argv[0][1] == '-') { |
427 | error("unknown option `%s'", ctx.argv[0] + 2); | 435 | error("unknown option `%s'", ctx.argv[0] + 2); |
@@ -557,7 +565,8 @@ int parse_options_usage(const char * const *usagestr, | |||
557 | } | 565 | } |
558 | 566 | ||
559 | 567 | ||
560 | int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used, | 568 | int parse_opt_verbosity_cb(const struct option *opt, |
569 | const char *arg __maybe_unused, | ||
561 | int unset) | 570 | int unset) |
562 | { | 571 | { |
563 | int *target = opt->value; | 572 | int *target = opt->value; |
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index abc31a1dac1a..7bb5999940ca 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -140,6 +140,7 @@ extern NORETURN void usage_with_options(const char * const *usagestr, | |||
140 | enum { | 140 | enum { |
141 | PARSE_OPT_HELP = -1, | 141 | PARSE_OPT_HELP = -1, |
142 | PARSE_OPT_DONE, | 142 | PARSE_OPT_DONE, |
143 | PARSE_OPT_LIST, | ||
143 | PARSE_OPT_UNKNOWN, | 144 | PARSE_OPT_UNKNOWN, |
144 | }; | 145 | }; |
145 | 146 | ||
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index bd7497711424..a8c49548ca48 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c | |||
@@ -22,7 +22,7 @@ static const char *get_perf_dir(void) | |||
22 | return "."; | 22 | return "."; |
23 | } | 23 | } |
24 | 24 | ||
25 | #ifdef NO_STRLCPY | 25 | #ifndef HAVE_STRLCPY |
26 | size_t strlcpy(char *dest, const char *src, size_t size) | 26 | size_t strlcpy(char *dest, const char *src, size_t size) |
27 | { | 27 | { |
28 | size_t ret = strlen(src); | 28 | size_t ret = strlen(src); |
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h new file mode 100644 index 000000000000..5a4f2b6f3738 --- /dev/null +++ b/tools/perf/util/perf_regs.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef __PERF_REGS_H | ||
2 | #define __PERF_REGS_H | ||
3 | |||
4 | #ifdef HAVE_PERF_REGS | ||
5 | #include <perf_regs.h> | ||
6 | #else | ||
7 | #define PERF_REGS_MASK 0 | ||
8 | |||
9 | static inline const char *perf_reg_name(int id __maybe_unused) | ||
10 | { | ||
11 | return NULL; | ||
12 | } | ||
13 | #endif /* HAVE_PERF_REGS */ | ||
14 | #endif /* __PERF_REGS_H */ | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 67715a42cd6d..8a2229da594f 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -9,6 +9,9 @@ | |||
9 | #include "util.h" | 9 | #include "util.h" |
10 | #include "pmu.h" | 10 | #include "pmu.h" |
11 | #include "parse-events.h" | 11 | #include "parse-events.h" |
12 | #include "cpumap.h" | ||
13 | |||
14 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" | ||
12 | 15 | ||
13 | int perf_pmu_parse(struct list_head *list, char *name); | 16 | int perf_pmu_parse(struct list_head *list, char *name); |
14 | extern FILE *perf_pmu_in; | 17 | extern FILE *perf_pmu_in; |
@@ -69,7 +72,7 @@ static int pmu_format(char *name, struct list_head *format) | |||
69 | return -1; | 72 | return -1; |
70 | 73 | ||
71 | snprintf(path, PATH_MAX, | 74 | snprintf(path, PATH_MAX, |
72 | "%s/bus/event_source/devices/%s/format", sysfs, name); | 75 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); |
73 | 76 | ||
74 | if (stat(path, &st) < 0) | 77 | if (stat(path, &st) < 0) |
75 | return 0; /* no error if format does not exist */ | 78 | return 0; /* no error if format does not exist */ |
@@ -206,7 +209,7 @@ static int pmu_type(char *name, __u32 *type) | |||
206 | return -1; | 209 | return -1; |
207 | 210 | ||
208 | snprintf(path, PATH_MAX, | 211 | snprintf(path, PATH_MAX, |
209 | "%s/bus/event_source/devices/%s/type", sysfs, name); | 212 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); |
210 | 213 | ||
211 | if (stat(path, &st) < 0) | 214 | if (stat(path, &st) < 0) |
212 | return -1; | 215 | return -1; |
@@ -222,6 +225,62 @@ static int pmu_type(char *name, __u32 *type) | |||
222 | return ret; | 225 | return ret; |
223 | } | 226 | } |
224 | 227 | ||
228 | /* Add all pmus in sysfs to pmu list: */ | ||
229 | static void pmu_read_sysfs(void) | ||
230 | { | ||
231 | char path[PATH_MAX]; | ||
232 | const char *sysfs; | ||
233 | DIR *dir; | ||
234 | struct dirent *dent; | ||
235 | |||
236 | sysfs = sysfs_find_mountpoint(); | ||
237 | if (!sysfs) | ||
238 | return; | ||
239 | |||
240 | snprintf(path, PATH_MAX, | ||
241 | "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); | ||
242 | |||
243 | dir = opendir(path); | ||
244 | if (!dir) | ||
245 | return; | ||
246 | |||
247 | while ((dent = readdir(dir))) { | ||
248 | if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) | ||
249 | continue; | ||
250 | /* add to static LIST_HEAD(pmus): */ | ||
251 | perf_pmu__find(dent->d_name); | ||
252 | } | ||
253 | |||
254 | closedir(dir); | ||
255 | } | ||
256 | |||
257 | static struct cpu_map *pmu_cpumask(char *name) | ||
258 | { | ||
259 | struct stat st; | ||
260 | char path[PATH_MAX]; | ||
261 | const char *sysfs; | ||
262 | FILE *file; | ||
263 | struct cpu_map *cpus; | ||
264 | |||
265 | sysfs = sysfs_find_mountpoint(); | ||
266 | if (!sysfs) | ||
267 | return NULL; | ||
268 | |||
269 | snprintf(path, PATH_MAX, | ||
270 | "%s/bus/event_source/devices/%s/cpumask", sysfs, name); | ||
271 | |||
272 | if (stat(path, &st) < 0) | ||
273 | return NULL; | ||
274 | |||
275 | file = fopen(path, "r"); | ||
276 | if (!file) | ||
277 | return NULL; | ||
278 | |||
279 | cpus = cpu_map__read(file); | ||
280 | fclose(file); | ||
281 | return cpus; | ||
282 | } | ||
283 | |||
225 | static struct perf_pmu *pmu_lookup(char *name) | 284 | static struct perf_pmu *pmu_lookup(char *name) |
226 | { | 285 | { |
227 | struct perf_pmu *pmu; | 286 | struct perf_pmu *pmu; |
@@ -244,6 +303,8 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
244 | if (!pmu) | 303 | if (!pmu) |
245 | return NULL; | 304 | return NULL; |
246 | 305 | ||
306 | pmu->cpus = pmu_cpumask(name); | ||
307 | |||
247 | pmu_aliases(name, &aliases); | 308 | pmu_aliases(name, &aliases); |
248 | 309 | ||
249 | INIT_LIST_HEAD(&pmu->format); | 310 | INIT_LIST_HEAD(&pmu->format); |
@@ -267,6 +328,21 @@ static struct perf_pmu *pmu_find(char *name) | |||
267 | return NULL; | 328 | return NULL; |
268 | } | 329 | } |
269 | 330 | ||
331 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) | ||
332 | { | ||
333 | /* | ||
334 | * pmu iterator: If pmu is NULL, we start at the begin, | ||
335 | * otherwise return the next pmu. Returns NULL on end. | ||
336 | */ | ||
337 | if (!pmu) { | ||
338 | pmu_read_sysfs(); | ||
339 | pmu = list_prepare_entry(pmu, &pmus, list); | ||
340 | } | ||
341 | list_for_each_entry_continue(pmu, &pmus, list) | ||
342 | return pmu; | ||
343 | return NULL; | ||
344 | } | ||
345 | |||
270 | struct perf_pmu *perf_pmu__find(char *name) | 346 | struct perf_pmu *perf_pmu__find(char *name) |
271 | { | 347 | { |
272 | struct perf_pmu *pmu; | 348 | struct perf_pmu *pmu; |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 535f2c5258ab..39f3abac7744 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #define __PMU_H | 2 | #define __PMU_H |
3 | 3 | ||
4 | #include <linux/bitops.h> | 4 | #include <linux/bitops.h> |
5 | #include "../../../include/linux/perf_event.h" | 5 | #include "../../../include/uapi/linux/perf_event.h" |
6 | 6 | ||
7 | enum { | 7 | enum { |
8 | PERF_PMU_FORMAT_VALUE_CONFIG, | 8 | PERF_PMU_FORMAT_VALUE_CONFIG, |
@@ -28,6 +28,7 @@ struct perf_pmu__alias { | |||
28 | struct perf_pmu { | 28 | struct perf_pmu { |
29 | char *name; | 29 | char *name; |
30 | __u32 type; | 30 | __u32 type; |
31 | struct cpu_map *cpus; | ||
31 | struct list_head format; | 32 | struct list_head format; |
32 | struct list_head aliases; | 33 | struct list_head aliases; |
33 | struct list_head list; | 34 | struct list_head list; |
@@ -46,5 +47,7 @@ int perf_pmu__new_format(struct list_head *list, char *name, | |||
46 | int config, unsigned long *bits); | 47 | int config, unsigned long *bits); |
47 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | 48 | void perf_pmu__set_format(unsigned long *bits, long from, long to); |
48 | 49 | ||
50 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | ||
51 | |||
49 | int perf_pmu__test(void); | 52 | int perf_pmu__test(void); |
50 | #endif /* __PMU_H */ | 53 | #endif /* __PMU_H */ |
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y index 20ea77e93169..ec898047ebb9 100644 --- a/tools/perf/util/pmu.y +++ b/tools/perf/util/pmu.y | |||
@@ -86,8 +86,8 @@ PP_VALUE | |||
86 | 86 | ||
87 | %% | 87 | %% |
88 | 88 | ||
89 | void perf_pmu_error(struct list_head *list __used, | 89 | void perf_pmu_error(struct list_head *list __maybe_unused, |
90 | char *name __used, | 90 | char *name __maybe_unused, |
91 | char const *msg __used) | 91 | char const *msg __maybe_unused) |
92 | { | 92 | { |
93 | } | 93 | } |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 0dda25d82d06..49a256e6e0a2 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -41,7 +41,7 @@ | |||
41 | #include "symbol.h" | 41 | #include "symbol.h" |
42 | #include "thread.h" | 42 | #include "thread.h" |
43 | #include "debugfs.h" | 43 | #include "debugfs.h" |
44 | #include "trace-event.h" /* For __unused */ | 44 | #include "trace-event.h" /* For __maybe_unused */ |
45 | #include "probe-event.h" | 45 | #include "probe-event.h" |
46 | #include "probe-finder.h" | 46 | #include "probe-finder.h" |
47 | #include "session.h" | 47 | #include "session.h" |
@@ -647,8 +647,8 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
647 | } | 647 | } |
648 | 648 | ||
649 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 649 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
650 | struct probe_trace_event **tevs __unused, | 650 | struct probe_trace_event **tevs __maybe_unused, |
651 | int max_tevs __unused, const char *target) | 651 | int max_tevs __maybe_unused, const char *target) |
652 | { | 652 | { |
653 | if (perf_probe_event_need_dwarf(pev)) { | 653 | if (perf_probe_event_need_dwarf(pev)) { |
654 | pr_warning("Debuginfo-analysis is not supported.\n"); | 654 | pr_warning("Debuginfo-analysis is not supported.\n"); |
@@ -661,17 +661,18 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
661 | return 0; | 661 | return 0; |
662 | } | 662 | } |
663 | 663 | ||
664 | int show_line_range(struct line_range *lr __unused, const char *module __unused) | 664 | int show_line_range(struct line_range *lr __maybe_unused, |
665 | const char *module __maybe_unused) | ||
665 | { | 666 | { |
666 | pr_warning("Debuginfo-analysis is not supported.\n"); | 667 | pr_warning("Debuginfo-analysis is not supported.\n"); |
667 | return -ENOSYS; | 668 | return -ENOSYS; |
668 | } | 669 | } |
669 | 670 | ||
670 | int show_available_vars(struct perf_probe_event *pevs __unused, | 671 | int show_available_vars(struct perf_probe_event *pevs __maybe_unused, |
671 | int npevs __unused, int max_vls __unused, | 672 | int npevs __maybe_unused, int max_vls __maybe_unused, |
672 | const char *module __unused, | 673 | const char *module __maybe_unused, |
673 | struct strfilter *filter __unused, | 674 | struct strfilter *filter __maybe_unused, |
674 | bool externs __unused) | 675 | bool externs __maybe_unused) |
675 | { | 676 | { |
676 | pr_warning("Debuginfo-analysis is not supported.\n"); | 677 | pr_warning("Debuginfo-analysis is not supported.\n"); |
677 | return -ENOSYS; | 678 | return -ENOSYS; |
@@ -1099,6 +1100,7 @@ static int parse_probe_trace_command(const char *cmd, | |||
1099 | struct probe_trace_point *tp = &tev->point; | 1100 | struct probe_trace_point *tp = &tev->point; |
1100 | char pr; | 1101 | char pr; |
1101 | char *p; | 1102 | char *p; |
1103 | char *argv0_str = NULL, *fmt, *fmt1_str, *fmt2_str, *fmt3_str; | ||
1102 | int ret, i, argc; | 1104 | int ret, i, argc; |
1103 | char **argv; | 1105 | char **argv; |
1104 | 1106 | ||
@@ -1115,14 +1117,27 @@ static int parse_probe_trace_command(const char *cmd, | |||
1115 | } | 1117 | } |
1116 | 1118 | ||
1117 | /* Scan event and group name. */ | 1119 | /* Scan event and group name. */ |
1118 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | 1120 | argv0_str = strdup(argv[0]); |
1119 | &pr, (float *)(void *)&tev->group, | 1121 | if (argv0_str == NULL) { |
1120 | (float *)(void *)&tev->event); | 1122 | ret = -ENOMEM; |
1121 | if (ret != 3) { | 1123 | goto out; |
1124 | } | ||
1125 | fmt1_str = strtok_r(argv0_str, ":", &fmt); | ||
1126 | fmt2_str = strtok_r(NULL, "/", &fmt); | ||
1127 | fmt3_str = strtok_r(NULL, " \t", &fmt); | ||
1128 | if (fmt1_str == NULL || strlen(fmt1_str) != 1 || fmt2_str == NULL | ||
1129 | || fmt3_str == NULL) { | ||
1122 | semantic_error("Failed to parse event name: %s\n", argv[0]); | 1130 | semantic_error("Failed to parse event name: %s\n", argv[0]); |
1123 | ret = -EINVAL; | 1131 | ret = -EINVAL; |
1124 | goto out; | 1132 | goto out; |
1125 | } | 1133 | } |
1134 | pr = fmt1_str[0]; | ||
1135 | tev->group = strdup(fmt2_str); | ||
1136 | tev->event = strdup(fmt3_str); | ||
1137 | if (tev->group == NULL || tev->event == NULL) { | ||
1138 | ret = -ENOMEM; | ||
1139 | goto out; | ||
1140 | } | ||
1126 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); | 1141 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); |
1127 | 1142 | ||
1128 | tp->retprobe = (pr == 'r'); | 1143 | tp->retprobe = (pr == 'r'); |
@@ -1134,10 +1149,17 @@ static int parse_probe_trace_command(const char *cmd, | |||
1134 | p++; | 1149 | p++; |
1135 | } else | 1150 | } else |
1136 | p = argv[1]; | 1151 | p = argv[1]; |
1137 | ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol, | 1152 | fmt1_str = strtok_r(p, "+", &fmt); |
1138 | &tp->offset); | 1153 | tp->symbol = strdup(fmt1_str); |
1139 | if (ret == 1) | 1154 | if (tp->symbol == NULL) { |
1155 | ret = -ENOMEM; | ||
1156 | goto out; | ||
1157 | } | ||
1158 | fmt2_str = strtok_r(NULL, "", &fmt); | ||
1159 | if (fmt2_str == NULL) | ||
1140 | tp->offset = 0; | 1160 | tp->offset = 0; |
1161 | else | ||
1162 | tp->offset = strtoul(fmt2_str, NULL, 10); | ||
1141 | 1163 | ||
1142 | tev->nargs = argc - 2; | 1164 | tev->nargs = argc - 2; |
1143 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1165 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
@@ -1161,6 +1183,7 @@ static int parse_probe_trace_command(const char *cmd, | |||
1161 | } | 1183 | } |
1162 | ret = 0; | 1184 | ret = 0; |
1163 | out: | 1185 | out: |
1186 | free(argv0_str); | ||
1164 | argv_free(argv); | 1187 | argv_free(argv); |
1165 | return ret; | 1188 | return ret; |
1166 | } | 1189 | } |
@@ -2183,7 +2206,7 @@ static struct strfilter *available_func_filter; | |||
2183 | * If a symbol corresponds to a function with global binding and | 2206 | * If a symbol corresponds to a function with global binding and |
2184 | * matches filter return 0. For all others return 1. | 2207 | * matches filter return 0. For all others return 1. |
2185 | */ | 2208 | */ |
2186 | static int filter_available_functions(struct map *map __unused, | 2209 | static int filter_available_functions(struct map *map __maybe_unused, |
2187 | struct symbol *sym) | 2210 | struct symbol *sym) |
2188 | { | 2211 | { |
2189 | if (sym->binding == STB_GLOBAL && | 2212 | if (sym->binding == STB_GLOBAL && |
@@ -2307,10 +2330,17 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) | |||
2307 | function = NULL; | 2330 | function = NULL; |
2308 | } | 2331 | } |
2309 | if (!pev->group) { | 2332 | if (!pev->group) { |
2310 | char *ptr1, *ptr2; | 2333 | char *ptr1, *ptr2, *exec_copy; |
2311 | 2334 | ||
2312 | pev->group = zalloc(sizeof(char *) * 64); | 2335 | pev->group = zalloc(sizeof(char *) * 64); |
2313 | ptr1 = strdup(basename(exec)); | 2336 | exec_copy = strdup(exec); |
2337 | if (!exec_copy) { | ||
2338 | ret = -ENOMEM; | ||
2339 | pr_warning("Failed to copy exec string.\n"); | ||
2340 | goto out; | ||
2341 | } | ||
2342 | |||
2343 | ptr1 = strdup(basename(exec_copy)); | ||
2314 | if (ptr1) { | 2344 | if (ptr1) { |
2315 | ptr2 = strpbrk(ptr1, "-._"); | 2345 | ptr2 = strpbrk(ptr1, "-._"); |
2316 | if (ptr2) | 2346 | if (ptr2) |
@@ -2319,6 +2349,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) | |||
2319 | ptr1); | 2349 | ptr1); |
2320 | free(ptr1); | 2350 | free(ptr1); |
2321 | } | 2351 | } |
2352 | free(exec_copy); | ||
2322 | } | 2353 | } |
2323 | free(pp->function); | 2354 | free(pp->function); |
2324 | pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS); | 2355 | pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d448984ed789..1daf5c14e751 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -207,7 +207,7 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, | |||
207 | #else | 207 | #else |
208 | /* With older elfutils, this just support kernel module... */ | 208 | /* With older elfutils, this just support kernel module... */ |
209 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, | 209 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
210 | Dwarf_Addr addr __used) | 210 | Dwarf_Addr addr __maybe_unused) |
211 | { | 211 | { |
212 | const char *path = kernel_get_module_path("kernel"); | 212 | const char *path = kernel_get_module_path("kernel"); |
213 | 213 | ||
@@ -525,8 +525,10 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
525 | return -ENOENT; | 525 | return -ENOENT; |
526 | } | 526 | } |
527 | /* Verify it is a data structure */ | 527 | /* Verify it is a data structure */ |
528 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | 528 | tag = dwarf_tag(&type); |
529 | pr_warning("%s is not a data structure.\n", varname); | 529 | if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { |
530 | pr_warning("%s is not a data structure nor an union.\n", | ||
531 | varname); | ||
530 | return -EINVAL; | 532 | return -EINVAL; |
531 | } | 533 | } |
532 | 534 | ||
@@ -539,8 +541,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
539 | *ref_ptr = ref; | 541 | *ref_ptr = ref; |
540 | } else { | 542 | } else { |
541 | /* Verify it is a data structure */ | 543 | /* Verify it is a data structure */ |
542 | if (tag != DW_TAG_structure_type) { | 544 | if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { |
543 | pr_warning("%s is not a data structure.\n", varname); | 545 | pr_warning("%s is not a data structure nor an union.\n", |
546 | varname); | ||
544 | return -EINVAL; | 547 | return -EINVAL; |
545 | } | 548 | } |
546 | if (field->name[0] == '[') { | 549 | if (field->name[0] == '[') { |
@@ -567,10 +570,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
567 | } | 570 | } |
568 | 571 | ||
569 | /* Get the offset of the field */ | 572 | /* Get the offset of the field */ |
570 | ret = die_get_data_member_location(die_mem, &offs); | 573 | if (tag == DW_TAG_union_type) { |
571 | if (ret < 0) { | 574 | offs = 0; |
572 | pr_warning("Failed to get the offset of %s.\n", field->name); | 575 | } else { |
573 | return ret; | 576 | ret = die_get_data_member_location(die_mem, &offs); |
577 | if (ret < 0) { | ||
578 | pr_warning("Failed to get the offset of %s.\n", | ||
579 | field->name); | ||
580 | return ret; | ||
581 | } | ||
574 | } | 582 | } |
575 | ref->offset += (long)offs; | 583 | ref->offset += (long)offs; |
576 | 584 | ||
@@ -1419,7 +1427,7 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
1419 | } | 1427 | } |
1420 | 1428 | ||
1421 | static int line_range_walk_cb(const char *fname, int lineno, | 1429 | static int line_range_walk_cb(const char *fname, int lineno, |
1422 | Dwarf_Addr addr __used, | 1430 | Dwarf_Addr addr __maybe_unused, |
1423 | void *data) | 1431 | void *data) |
1424 | { | 1432 | { |
1425 | struct line_finder *lf = data; | 1433 | struct line_finder *lf = data; |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 0688bfb6d280..9181bf212fb9 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -627,7 +627,7 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | |||
627 | * This will group just the fds for this single evsel, to group | 627 | * This will group just the fds for this single evsel, to group |
628 | * multiple events, use evlist.open(). | 628 | * multiple events, use evlist.open(). |
629 | */ | 629 | */ |
630 | if (perf_evsel__open(evsel, cpus, threads, group, NULL) < 0) { | 630 | if (perf_evsel__open(evsel, cpus, threads) < 0) { |
631 | PyErr_SetFromErrno(PyExc_OSError); | 631 | PyErr_SetFromErrno(PyExc_OSError); |
632 | return NULL; | 632 | return NULL; |
633 | } | 633 | } |
@@ -672,7 +672,7 @@ struct pyrf_evlist { | |||
672 | }; | 672 | }; |
673 | 673 | ||
674 | static int pyrf_evlist__init(struct pyrf_evlist *pevlist, | 674 | static int pyrf_evlist__init(struct pyrf_evlist *pevlist, |
675 | PyObject *args, PyObject *kwargs __used) | 675 | PyObject *args, PyObject *kwargs __maybe_unused) |
676 | { | 676 | { |
677 | PyObject *pcpus = NULL, *pthreads = NULL; | 677 | PyObject *pcpus = NULL, *pthreads = NULL; |
678 | struct cpu_map *cpus; | 678 | struct cpu_map *cpus; |
@@ -733,7 +733,8 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
733 | } | 733 | } |
734 | 734 | ||
735 | static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | 735 | static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, |
736 | PyObject *args __used, PyObject *kwargs __used) | 736 | PyObject *args __maybe_unused, |
737 | PyObject *kwargs __maybe_unused) | ||
737 | { | 738 | { |
738 | struct perf_evlist *evlist = &pevlist->evlist; | 739 | struct perf_evlist *evlist = &pevlist->evlist; |
739 | PyObject *list = PyList_New(0); | 740 | PyObject *list = PyList_New(0); |
@@ -765,7 +766,8 @@ free_list: | |||
765 | 766 | ||
766 | 767 | ||
767 | static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, | 768 | static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, |
768 | PyObject *args, PyObject *kwargs __used) | 769 | PyObject *args, |
770 | PyObject *kwargs __maybe_unused) | ||
769 | { | 771 | { |
770 | struct perf_evlist *evlist = &pevlist->evlist; | 772 | struct perf_evlist *evlist = &pevlist->evlist; |
771 | PyObject *pevsel; | 773 | PyObject *pevsel; |
@@ -803,7 +805,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
803 | if (pyevent == NULL) | 805 | if (pyevent == NULL) |
804 | return PyErr_NoMemory(); | 806 | return PyErr_NoMemory(); |
805 | 807 | ||
806 | err = perf_evlist__parse_sample(evlist, event, &pevent->sample, false); | 808 | err = perf_evlist__parse_sample(evlist, event, &pevent->sample); |
807 | if (err) | 809 | if (err) |
808 | return PyErr_Format(PyExc_OSError, | 810 | return PyErr_Format(PyExc_OSError, |
809 | "perf: can't parse sample, err=%d", err); | 811 | "perf: can't parse sample, err=%d", err); |
@@ -824,7 +826,10 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, | |||
824 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group)) | 826 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group)) |
825 | return NULL; | 827 | return NULL; |
826 | 828 | ||
827 | if (perf_evlist__open(evlist, group) < 0) { | 829 | if (group) |
830 | perf_evlist__set_leader(evlist); | ||
831 | |||
832 | if (perf_evlist__open(evlist) < 0) { | ||
828 | PyErr_SetFromErrno(PyExc_OSError); | 833 | PyErr_SetFromErrno(PyExc_OSError); |
829 | return NULL; | 834 | return NULL; |
830 | } | 835 | } |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 02dfa19a467f..f80605eb1855 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -25,16 +25,16 @@ | |||
25 | #include <ctype.h> | 25 | #include <ctype.h> |
26 | #include <errno.h> | 26 | #include <errno.h> |
27 | 27 | ||
28 | #include "../../perf.h" | ||
29 | #include "../util.h" | 28 | #include "../util.h" |
29 | #include <EXTERN.h> | ||
30 | #include <perl.h> | ||
31 | |||
32 | #include "../../perf.h" | ||
30 | #include "../thread.h" | 33 | #include "../thread.h" |
31 | #include "../event.h" | 34 | #include "../event.h" |
32 | #include "../trace-event.h" | 35 | #include "../trace-event.h" |
33 | #include "../evsel.h" | 36 | #include "../evsel.h" |
34 | 37 | ||
35 | #include <EXTERN.h> | ||
36 | #include <perl.h> | ||
37 | |||
38 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | 38 | void boot_Perf__Trace__Context(pTHX_ CV *cv); |
39 | void boot_DynaLoader(pTHX_ CV *cv); | 39 | void boot_DynaLoader(pTHX_ CV *cv); |
40 | typedef PerlInterpreter * INTERP; | 40 | typedef PerlInterpreter * INTERP; |
@@ -237,16 +237,16 @@ static void define_event_symbols(struct event_format *event, | |||
237 | define_event_symbols(event, ev_name, args->next); | 237 | define_event_symbols(event, ev_name, args->next); |
238 | } | 238 | } |
239 | 239 | ||
240 | static inline | 240 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) |
241 | struct event_format *find_cache_event(struct pevent *pevent, int type) | ||
242 | { | 241 | { |
243 | static char ev_name[256]; | 242 | static char ev_name[256]; |
244 | struct event_format *event; | 243 | struct event_format *event; |
244 | int type = evsel->attr.config; | ||
245 | 245 | ||
246 | if (events[type]) | 246 | if (events[type]) |
247 | return events[type]; | 247 | return events[type]; |
248 | 248 | ||
249 | events[type] = event = pevent_find_event(pevent, type); | 249 | events[type] = event = evsel->tp_format; |
250 | if (!event) | 250 | if (!event) |
251 | return NULL; | 251 | return NULL; |
252 | 252 | ||
@@ -257,23 +257,22 @@ struct event_format *find_cache_event(struct pevent *pevent, int type) | |||
257 | return event; | 257 | return event; |
258 | } | 258 | } |
259 | 259 | ||
260 | static void perl_process_tracepoint(union perf_event *perf_event __unused, | 260 | static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, |
261 | struct pevent *pevent, | ||
262 | struct perf_sample *sample, | 261 | struct perf_sample *sample, |
263 | struct perf_evsel *evsel, | 262 | struct perf_evsel *evsel, |
264 | struct machine *machine __unused, | 263 | struct machine *machine __maybe_unused, |
265 | struct thread *thread) | 264 | struct addr_location *al) |
266 | { | 265 | { |
267 | struct format_field *field; | 266 | struct format_field *field; |
268 | static char handler[256]; | 267 | static char handler[256]; |
269 | unsigned long long val; | 268 | unsigned long long val; |
270 | unsigned long s, ns; | 269 | unsigned long s, ns; |
271 | struct event_format *event; | 270 | struct event_format *event; |
272 | int type; | ||
273 | int pid; | 271 | int pid; |
274 | int cpu = sample->cpu; | 272 | int cpu = sample->cpu; |
275 | void *data = sample->raw_data; | 273 | void *data = sample->raw_data; |
276 | unsigned long long nsecs = sample->time; | 274 | unsigned long long nsecs = sample->time; |
275 | struct thread *thread = al->thread; | ||
277 | char *comm = thread->comm; | 276 | char *comm = thread->comm; |
278 | 277 | ||
279 | dSP; | 278 | dSP; |
@@ -281,13 +280,11 @@ static void perl_process_tracepoint(union perf_event *perf_event __unused, | |||
281 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 280 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
282 | return; | 281 | return; |
283 | 282 | ||
284 | type = trace_parse_common_type(pevent, data); | 283 | event = find_cache_event(evsel); |
285 | |||
286 | event = find_cache_event(pevent, type); | ||
287 | if (!event) | 284 | if (!event) |
288 | die("ug! no event found for type %d", type); | 285 | die("ug! no event found for type %" PRIu64, evsel->attr.config); |
289 | 286 | ||
290 | pid = trace_parse_common_pid(pevent, data); | 287 | pid = raw_field_value(event, "common_pid", data); |
291 | 288 | ||
292 | sprintf(handler, "%s::%s", event->system, event->name); | 289 | sprintf(handler, "%s::%s", event->system, event->name); |
293 | 290 | ||
@@ -320,7 +317,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __unused, | |||
320 | offset = field->offset; | 317 | offset = field->offset; |
321 | XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); | 318 | XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); |
322 | } else { /* FIELD_IS_NUMERIC */ | 319 | } else { /* FIELD_IS_NUMERIC */ |
323 | val = read_size(pevent, data + field->offset, | 320 | val = read_size(event, data + field->offset, |
324 | field->size); | 321 | field->size); |
325 | if (field->flags & FIELD_IS_SIGNED) { | 322 | if (field->flags & FIELD_IS_SIGNED) { |
326 | XPUSHs(sv_2mortal(newSViv(val))); | 323 | XPUSHs(sv_2mortal(newSViv(val))); |
@@ -349,11 +346,11 @@ static void perl_process_tracepoint(union perf_event *perf_event __unused, | |||
349 | LEAVE; | 346 | LEAVE; |
350 | } | 347 | } |
351 | 348 | ||
352 | static void perl_process_event_generic(union perf_event *pevent __unused, | 349 | static void perl_process_event_generic(union perf_event *event, |
353 | struct perf_sample *sample, | 350 | struct perf_sample *sample, |
354 | struct perf_evsel *evsel __unused, | 351 | struct perf_evsel *evsel, |
355 | struct machine *machine __unused, | 352 | struct machine *machine __maybe_unused, |
356 | struct thread *thread __unused) | 353 | struct addr_location *al __maybe_unused) |
357 | { | 354 | { |
358 | dSP; | 355 | dSP; |
359 | 356 | ||
@@ -363,7 +360,7 @@ static void perl_process_event_generic(union perf_event *pevent __unused, | |||
363 | ENTER; | 360 | ENTER; |
364 | SAVETMPS; | 361 | SAVETMPS; |
365 | PUSHMARK(SP); | 362 | PUSHMARK(SP); |
366 | XPUSHs(sv_2mortal(newSVpvn((const char *)pevent, pevent->header.size))); | 363 | XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size))); |
367 | XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->attr, sizeof(evsel->attr)))); | 364 | XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->attr, sizeof(evsel->attr)))); |
368 | XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample)))); | 365 | XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample)))); |
369 | XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size))); | 366 | XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size))); |
@@ -376,14 +373,13 @@ static void perl_process_event_generic(union perf_event *pevent __unused, | |||
376 | } | 373 | } |
377 | 374 | ||
378 | static void perl_process_event(union perf_event *event, | 375 | static void perl_process_event(union perf_event *event, |
379 | struct pevent *pevent, | ||
380 | struct perf_sample *sample, | 376 | struct perf_sample *sample, |
381 | struct perf_evsel *evsel, | 377 | struct perf_evsel *evsel, |
382 | struct machine *machine, | 378 | struct machine *machine, |
383 | struct thread *thread) | 379 | struct addr_location *al) |
384 | { | 380 | { |
385 | perl_process_tracepoint(event, pevent, sample, evsel, machine, thread); | 381 | perl_process_tracepoint(event, sample, evsel, machine, al); |
386 | perl_process_event_generic(event, sample, evsel, machine, thread); | 382 | perl_process_event_generic(event, sample, evsel, machine, al); |
387 | } | 383 | } |
388 | 384 | ||
389 | static void run_start_sub(void) | 385 | static void run_start_sub(void) |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index ce4d1b0c3862..730c6630cba5 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -27,10 +27,12 @@ | |||
27 | #include <errno.h> | 27 | #include <errno.h> |
28 | 28 | ||
29 | #include "../../perf.h" | 29 | #include "../../perf.h" |
30 | #include "../evsel.h" | ||
30 | #include "../util.h" | 31 | #include "../util.h" |
31 | #include "../event.h" | 32 | #include "../event.h" |
32 | #include "../thread.h" | 33 | #include "../thread.h" |
33 | #include "../trace-event.h" | 34 | #include "../trace-event.h" |
35 | #include "../evsel.h" | ||
34 | 36 | ||
35 | PyMODINIT_FUNC initperf_trace_context(void); | 37 | PyMODINIT_FUNC initperf_trace_context(void); |
36 | 38 | ||
@@ -194,16 +196,21 @@ static void define_event_symbols(struct event_format *event, | |||
194 | define_event_symbols(event, ev_name, args->next); | 196 | define_event_symbols(event, ev_name, args->next); |
195 | } | 197 | } |
196 | 198 | ||
197 | static inline | 199 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) |
198 | struct event_format *find_cache_event(struct pevent *pevent, int type) | ||
199 | { | 200 | { |
200 | static char ev_name[256]; | 201 | static char ev_name[256]; |
201 | struct event_format *event; | 202 | struct event_format *event; |
203 | int type = evsel->attr.config; | ||
202 | 204 | ||
205 | /* | ||
206 | * XXX: Do we really need to cache this since now we have evsel->tp_format | ||
207 | * cached already? Need to re-read this "cache" routine that as well calls | ||
208 | * define_event_symbols() :-\ | ||
209 | */ | ||
203 | if (events[type]) | 210 | if (events[type]) |
204 | return events[type]; | 211 | return events[type]; |
205 | 212 | ||
206 | events[type] = event = pevent_find_event(pevent, type); | 213 | events[type] = event = evsel->tp_format; |
207 | if (!event) | 214 | if (!event) |
208 | return NULL; | 215 | return NULL; |
209 | 216 | ||
@@ -214,12 +221,12 @@ struct event_format *find_cache_event(struct pevent *pevent, int type) | |||
214 | return event; | 221 | return event; |
215 | } | 222 | } |
216 | 223 | ||
217 | static void python_process_event(union perf_event *perf_event __unused, | 224 | static void python_process_tracepoint(union perf_event *perf_event |
218 | struct pevent *pevent, | 225 | __maybe_unused, |
219 | struct perf_sample *sample, | 226 | struct perf_sample *sample, |
220 | struct perf_evsel *evsel __unused, | 227 | struct perf_evsel *evsel, |
221 | struct machine *machine __unused, | 228 | struct machine *machine __maybe_unused, |
222 | struct thread *thread) | 229 | struct addr_location *al) |
223 | { | 230 | { |
224 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; | 231 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; |
225 | static char handler_name[256]; | 232 | static char handler_name[256]; |
@@ -228,24 +235,22 @@ static void python_process_event(union perf_event *perf_event __unused, | |||
228 | unsigned long s, ns; | 235 | unsigned long s, ns; |
229 | struct event_format *event; | 236 | struct event_format *event; |
230 | unsigned n = 0; | 237 | unsigned n = 0; |
231 | int type; | ||
232 | int pid; | 238 | int pid; |
233 | int cpu = sample->cpu; | 239 | int cpu = sample->cpu; |
234 | void *data = sample->raw_data; | 240 | void *data = sample->raw_data; |
235 | unsigned long long nsecs = sample->time; | 241 | unsigned long long nsecs = sample->time; |
242 | struct thread *thread = al->thread; | ||
236 | char *comm = thread->comm; | 243 | char *comm = thread->comm; |
237 | 244 | ||
238 | t = PyTuple_New(MAX_FIELDS); | 245 | t = PyTuple_New(MAX_FIELDS); |
239 | if (!t) | 246 | if (!t) |
240 | Py_FatalError("couldn't create Python tuple"); | 247 | Py_FatalError("couldn't create Python tuple"); |
241 | 248 | ||
242 | type = trace_parse_common_type(pevent, data); | 249 | event = find_cache_event(evsel); |
243 | |||
244 | event = find_cache_event(pevent, type); | ||
245 | if (!event) | 250 | if (!event) |
246 | die("ug! no event found for type %d", type); | 251 | die("ug! no event found for type %d", (int)evsel->attr.config); |
247 | 252 | ||
248 | pid = trace_parse_common_pid(pevent, data); | 253 | pid = raw_field_value(event, "common_pid", data); |
249 | 254 | ||
250 | sprintf(handler_name, "%s__%s", event->system, event->name); | 255 | sprintf(handler_name, "%s__%s", event->system, event->name); |
251 | 256 | ||
@@ -290,7 +295,7 @@ static void python_process_event(union perf_event *perf_event __unused, | |||
290 | offset = field->offset; | 295 | offset = field->offset; |
291 | obj = PyString_FromString((char *)data + offset); | 296 | obj = PyString_FromString((char *)data + offset); |
292 | } else { /* FIELD_IS_NUMERIC */ | 297 | } else { /* FIELD_IS_NUMERIC */ |
293 | val = read_size(pevent, data + field->offset, | 298 | val = read_size(event, data + field->offset, |
294 | field->size); | 299 | field->size); |
295 | if (field->flags & FIELD_IS_SIGNED) { | 300 | if (field->flags & FIELD_IS_SIGNED) { |
296 | if ((long long)val >= LONG_MIN && | 301 | if ((long long)val >= LONG_MIN && |
@@ -335,6 +340,84 @@ static void python_process_event(union perf_event *perf_event __unused, | |||
335 | Py_DECREF(t); | 340 | Py_DECREF(t); |
336 | } | 341 | } |
337 | 342 | ||
343 | static void python_process_general_event(union perf_event *perf_event | ||
344 | __maybe_unused, | ||
345 | struct perf_sample *sample, | ||
346 | struct perf_evsel *evsel, | ||
347 | struct machine *machine __maybe_unused, | ||
348 | struct addr_location *al) | ||
349 | { | ||
350 | PyObject *handler, *retval, *t, *dict; | ||
351 | static char handler_name[64]; | ||
352 | unsigned n = 0; | ||
353 | struct thread *thread = al->thread; | ||
354 | |||
355 | /* | ||
356 | * Use the MAX_FIELDS to make the function expandable, though | ||
357 | * currently there is only one item for the tuple. | ||
358 | */ | ||
359 | t = PyTuple_New(MAX_FIELDS); | ||
360 | if (!t) | ||
361 | Py_FatalError("couldn't create Python tuple"); | ||
362 | |||
363 | dict = PyDict_New(); | ||
364 | if (!dict) | ||
365 | Py_FatalError("couldn't create Python dictionary"); | ||
366 | |||
367 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); | ||
368 | |||
369 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
370 | if (!handler || !PyCallable_Check(handler)) | ||
371 | goto exit; | ||
372 | |||
373 | PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | ||
374 | PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize( | ||
375 | (const char *)&evsel->attr, sizeof(evsel->attr))); | ||
376 | PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize( | ||
377 | (const char *)sample, sizeof(*sample))); | ||
378 | PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize( | ||
379 | (const char *)sample->raw_data, sample->raw_size)); | ||
380 | PyDict_SetItemString(dict, "comm", | ||
381 | PyString_FromString(thread->comm)); | ||
382 | if (al->map) { | ||
383 | PyDict_SetItemString(dict, "dso", | ||
384 | PyString_FromString(al->map->dso->name)); | ||
385 | } | ||
386 | if (al->sym) { | ||
387 | PyDict_SetItemString(dict, "symbol", | ||
388 | PyString_FromString(al->sym->name)); | ||
389 | } | ||
390 | |||
391 | PyTuple_SetItem(t, n++, dict); | ||
392 | if (_PyTuple_Resize(&t, n) == -1) | ||
393 | Py_FatalError("error resizing Python tuple"); | ||
394 | |||
395 | retval = PyObject_CallObject(handler, t); | ||
396 | if (retval == NULL) | ||
397 | handler_call_die(handler_name); | ||
398 | exit: | ||
399 | Py_DECREF(dict); | ||
400 | Py_DECREF(t); | ||
401 | } | ||
402 | |||
403 | static void python_process_event(union perf_event *perf_event, | ||
404 | struct perf_sample *sample, | ||
405 | struct perf_evsel *evsel, | ||
406 | struct machine *machine, | ||
407 | struct addr_location *al) | ||
408 | { | ||
409 | switch (evsel->attr.type) { | ||
410 | case PERF_TYPE_TRACEPOINT: | ||
411 | python_process_tracepoint(perf_event, sample, evsel, | ||
412 | machine, al); | ||
413 | break; | ||
414 | /* Reserve for future process_hw/sw/raw APIs */ | ||
415 | default: | ||
416 | python_process_general_event(perf_event, sample, evsel, | ||
417 | machine, al); | ||
418 | } | ||
419 | } | ||
420 | |||
338 | static int run_start_sub(void) | 421 | static int run_start_sub(void) |
339 | { | 422 | { |
340 | PyObject *handler, *retval; | 423 | PyObject *handler, *retval; |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 2437fb0b463a..8cdd23239c90 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -15,6 +15,9 @@ | |||
15 | #include "util.h" | 15 | #include "util.h" |
16 | #include "cpumap.h" | 16 | #include "cpumap.h" |
17 | #include "event-parse.h" | 17 | #include "event-parse.h" |
18 | #include "perf_regs.h" | ||
19 | #include "unwind.h" | ||
20 | #include "vdso.h" | ||
18 | 21 | ||
19 | static int perf_session__open(struct perf_session *self, bool force) | 22 | static int perf_session__open(struct perf_session *self, bool force) |
20 | { | 23 | { |
@@ -209,6 +212,7 @@ void perf_session__delete(struct perf_session *self) | |||
209 | machine__exit(&self->host_machine); | 212 | machine__exit(&self->host_machine); |
210 | close(self->fd); | 213 | close(self->fd); |
211 | free(self); | 214 | free(self); |
215 | vdso__exit(); | ||
212 | } | 216 | } |
213 | 217 | ||
214 | void machine__remove_thread(struct machine *self, struct thread *th) | 218 | void machine__remove_thread(struct machine *self, struct thread *th) |
@@ -288,10 +292,11 @@ struct branch_info *machine__resolve_bstack(struct machine *self, | |||
288 | return bi; | 292 | return bi; |
289 | } | 293 | } |
290 | 294 | ||
291 | int machine__resolve_callchain(struct machine *self, | 295 | static int machine__resolve_callchain_sample(struct machine *machine, |
292 | struct thread *thread, | 296 | struct thread *thread, |
293 | struct ip_callchain *chain, | 297 | struct ip_callchain *chain, |
294 | struct symbol **parent) | 298 | struct symbol **parent) |
299 | |||
295 | { | 300 | { |
296 | u8 cpumode = PERF_RECORD_MISC_USER; | 301 | u8 cpumode = PERF_RECORD_MISC_USER; |
297 | unsigned int i; | 302 | unsigned int i; |
@@ -316,11 +321,14 @@ int machine__resolve_callchain(struct machine *self, | |||
316 | if (ip >= PERF_CONTEXT_MAX) { | 321 | if (ip >= PERF_CONTEXT_MAX) { |
317 | switch (ip) { | 322 | switch (ip) { |
318 | case PERF_CONTEXT_HV: | 323 | case PERF_CONTEXT_HV: |
319 | cpumode = PERF_RECORD_MISC_HYPERVISOR; break; | 324 | cpumode = PERF_RECORD_MISC_HYPERVISOR; |
325 | break; | ||
320 | case PERF_CONTEXT_KERNEL: | 326 | case PERF_CONTEXT_KERNEL: |
321 | cpumode = PERF_RECORD_MISC_KERNEL; break; | 327 | cpumode = PERF_RECORD_MISC_KERNEL; |
328 | break; | ||
322 | case PERF_CONTEXT_USER: | 329 | case PERF_CONTEXT_USER: |
323 | cpumode = PERF_RECORD_MISC_USER; break; | 330 | cpumode = PERF_RECORD_MISC_USER; |
331 | break; | ||
324 | default: | 332 | default: |
325 | pr_debug("invalid callchain context: " | 333 | pr_debug("invalid callchain context: " |
326 | "%"PRId64"\n", (s64) ip); | 334 | "%"PRId64"\n", (s64) ip); |
@@ -335,7 +343,7 @@ int machine__resolve_callchain(struct machine *self, | |||
335 | } | 343 | } |
336 | 344 | ||
337 | al.filtered = false; | 345 | al.filtered = false; |
338 | thread__find_addr_location(thread, self, cpumode, | 346 | thread__find_addr_location(thread, machine, cpumode, |
339 | MAP__FUNCTION, ip, &al, NULL); | 347 | MAP__FUNCTION, ip, &al, NULL); |
340 | if (al.sym != NULL) { | 348 | if (al.sym != NULL) { |
341 | if (sort__has_parent && !*parent && | 349 | if (sort__has_parent && !*parent && |
@@ -354,49 +362,92 @@ int machine__resolve_callchain(struct machine *self, | |||
354 | return 0; | 362 | return 0; |
355 | } | 363 | } |
356 | 364 | ||
357 | static int process_event_synth_tracing_data_stub(union perf_event *event __used, | 365 | static int unwind_entry(struct unwind_entry *entry, void *arg) |
358 | struct perf_session *session __used) | 366 | { |
367 | struct callchain_cursor *cursor = arg; | ||
368 | return callchain_cursor_append(cursor, entry->ip, | ||
369 | entry->map, entry->sym); | ||
370 | } | ||
371 | |||
372 | int machine__resolve_callchain(struct machine *machine, | ||
373 | struct perf_evsel *evsel, | ||
374 | struct thread *thread, | ||
375 | struct perf_sample *sample, | ||
376 | struct symbol **parent) | ||
377 | |||
378 | { | ||
379 | int ret; | ||
380 | |||
381 | callchain_cursor_reset(&callchain_cursor); | ||
382 | |||
383 | ret = machine__resolve_callchain_sample(machine, thread, | ||
384 | sample->callchain, parent); | ||
385 | if (ret) | ||
386 | return ret; | ||
387 | |||
388 | /* Can we do dwarf post unwind? */ | ||
389 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
390 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
391 | return 0; | ||
392 | |||
393 | /* Bail out if nothing was captured. */ | ||
394 | if ((!sample->user_regs.regs) || | ||
395 | (!sample->user_stack.size)) | ||
396 | return 0; | ||
397 | |||
398 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
399 | thread, evsel->attr.sample_regs_user, | ||
400 | sample); | ||
401 | |||
402 | } | ||
403 | |||
404 | static int process_event_synth_tracing_data_stub(union perf_event *event | ||
405 | __maybe_unused, | ||
406 | struct perf_session *session | ||
407 | __maybe_unused) | ||
359 | { | 408 | { |
360 | dump_printf(": unhandled!\n"); | 409 | dump_printf(": unhandled!\n"); |
361 | return 0; | 410 | return 0; |
362 | } | 411 | } |
363 | 412 | ||
364 | static int process_event_synth_attr_stub(union perf_event *event __used, | 413 | static int process_event_synth_attr_stub(union perf_event *event __maybe_unused, |
365 | struct perf_evlist **pevlist __used) | 414 | struct perf_evlist **pevlist |
415 | __maybe_unused) | ||
366 | { | 416 | { |
367 | dump_printf(": unhandled!\n"); | 417 | dump_printf(": unhandled!\n"); |
368 | return 0; | 418 | return 0; |
369 | } | 419 | } |
370 | 420 | ||
371 | static int process_event_sample_stub(struct perf_tool *tool __used, | 421 | static int process_event_sample_stub(struct perf_tool *tool __maybe_unused, |
372 | union perf_event *event __used, | 422 | union perf_event *event __maybe_unused, |
373 | struct perf_sample *sample __used, | 423 | struct perf_sample *sample __maybe_unused, |
374 | struct perf_evsel *evsel __used, | 424 | struct perf_evsel *evsel __maybe_unused, |
375 | struct machine *machine __used) | 425 | struct machine *machine __maybe_unused) |
376 | { | 426 | { |
377 | dump_printf(": unhandled!\n"); | 427 | dump_printf(": unhandled!\n"); |
378 | return 0; | 428 | return 0; |
379 | } | 429 | } |
380 | 430 | ||
381 | static int process_event_stub(struct perf_tool *tool __used, | 431 | static int process_event_stub(struct perf_tool *tool __maybe_unused, |
382 | union perf_event *event __used, | 432 | union perf_event *event __maybe_unused, |
383 | struct perf_sample *sample __used, | 433 | struct perf_sample *sample __maybe_unused, |
384 | struct machine *machine __used) | 434 | struct machine *machine __maybe_unused) |
385 | { | 435 | { |
386 | dump_printf(": unhandled!\n"); | 436 | dump_printf(": unhandled!\n"); |
387 | return 0; | 437 | return 0; |
388 | } | 438 | } |
389 | 439 | ||
390 | static int process_finished_round_stub(struct perf_tool *tool __used, | 440 | static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, |
391 | union perf_event *event __used, | 441 | union perf_event *event __maybe_unused, |
392 | struct perf_session *perf_session __used) | 442 | struct perf_session *perf_session |
443 | __maybe_unused) | ||
393 | { | 444 | { |
394 | dump_printf(": unhandled!\n"); | 445 | dump_printf(": unhandled!\n"); |
395 | return 0; | 446 | return 0; |
396 | } | 447 | } |
397 | 448 | ||
398 | static int process_event_type_stub(struct perf_tool *tool __used, | 449 | static int process_event_type_stub(struct perf_tool *tool __maybe_unused, |
399 | union perf_event *event __used) | 450 | union perf_event *event __maybe_unused) |
400 | { | 451 | { |
401 | dump_printf(": unhandled!\n"); | 452 | dump_printf(": unhandled!\n"); |
402 | return 0; | 453 | return 0; |
@@ -473,7 +524,7 @@ static void swap_sample_id_all(union perf_event *event, void *data) | |||
473 | } | 524 | } |
474 | 525 | ||
475 | static void perf_event__all64_swap(union perf_event *event, | 526 | static void perf_event__all64_swap(union perf_event *event, |
476 | bool sample_id_all __used) | 527 | bool sample_id_all __maybe_unused) |
477 | { | 528 | { |
478 | struct perf_event_header *hdr = &event->header; | 529 | struct perf_event_header *hdr = &event->header; |
479 | mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr)); | 530 | mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr)); |
@@ -487,7 +538,7 @@ static void perf_event__comm_swap(union perf_event *event, bool sample_id_all) | |||
487 | if (sample_id_all) { | 538 | if (sample_id_all) { |
488 | void *data = &event->comm.comm; | 539 | void *data = &event->comm.comm; |
489 | 540 | ||
490 | data += ALIGN(strlen(data) + 1, sizeof(u64)); | 541 | data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); |
491 | swap_sample_id_all(event, data); | 542 | swap_sample_id_all(event, data); |
492 | } | 543 | } |
493 | } | 544 | } |
@@ -504,7 +555,7 @@ static void perf_event__mmap_swap(union perf_event *event, | |||
504 | if (sample_id_all) { | 555 | if (sample_id_all) { |
505 | void *data = &event->mmap.filename; | 556 | void *data = &event->mmap.filename; |
506 | 557 | ||
507 | data += ALIGN(strlen(data) + 1, sizeof(u64)); | 558 | data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); |
508 | swap_sample_id_all(event, data); | 559 | swap_sample_id_all(event, data); |
509 | } | 560 | } |
510 | } | 561 | } |
@@ -584,7 +635,7 @@ void perf_event__attr_swap(struct perf_event_attr *attr) | |||
584 | } | 635 | } |
585 | 636 | ||
586 | static void perf_event__hdr_attr_swap(union perf_event *event, | 637 | static void perf_event__hdr_attr_swap(union perf_event *event, |
587 | bool sample_id_all __used) | 638 | bool sample_id_all __maybe_unused) |
588 | { | 639 | { |
589 | size_t size; | 640 | size_t size; |
590 | 641 | ||
@@ -596,14 +647,14 @@ static void perf_event__hdr_attr_swap(union perf_event *event, | |||
596 | } | 647 | } |
597 | 648 | ||
598 | static void perf_event__event_type_swap(union perf_event *event, | 649 | static void perf_event__event_type_swap(union perf_event *event, |
599 | bool sample_id_all __used) | 650 | bool sample_id_all __maybe_unused) |
600 | { | 651 | { |
601 | event->event_type.event_type.event_id = | 652 | event->event_type.event_type.event_id = |
602 | bswap_64(event->event_type.event_type.event_id); | 653 | bswap_64(event->event_type.event_type.event_id); |
603 | } | 654 | } |
604 | 655 | ||
605 | static void perf_event__tracing_data_swap(union perf_event *event, | 656 | static void perf_event__tracing_data_swap(union perf_event *event, |
606 | bool sample_id_all __used) | 657 | bool sample_id_all __maybe_unused) |
607 | { | 658 | { |
608 | event->tracing_data.size = bswap_32(event->tracing_data.size); | 659 | event->tracing_data.size = bswap_32(event->tracing_data.size); |
609 | } | 660 | } |
@@ -652,7 +703,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
652 | struct perf_tool *tool, | 703 | struct perf_tool *tool, |
653 | u64 file_offset); | 704 | u64 file_offset); |
654 | 705 | ||
655 | static void flush_sample_queue(struct perf_session *s, | 706 | static int flush_sample_queue(struct perf_session *s, |
656 | struct perf_tool *tool) | 707 | struct perf_tool *tool) |
657 | { | 708 | { |
658 | struct ordered_samples *os = &s->ordered_samples; | 709 | struct ordered_samples *os = &s->ordered_samples; |
@@ -665,19 +716,21 @@ static void flush_sample_queue(struct perf_session *s, | |||
665 | int ret; | 716 | int ret; |
666 | 717 | ||
667 | if (!tool->ordered_samples || !limit) | 718 | if (!tool->ordered_samples || !limit) |
668 | return; | 719 | return 0; |
669 | 720 | ||
670 | list_for_each_entry_safe(iter, tmp, head, list) { | 721 | list_for_each_entry_safe(iter, tmp, head, list) { |
671 | if (iter->timestamp > limit) | 722 | if (iter->timestamp > limit) |
672 | break; | 723 | break; |
673 | 724 | ||
674 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample, | 725 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); |
675 | s->header.needs_swap); | ||
676 | if (ret) | 726 | if (ret) |
677 | pr_err("Can't parse sample, err = %d\n", ret); | 727 | pr_err("Can't parse sample, err = %d\n", ret); |
678 | else | 728 | else { |
679 | perf_session_deliver_event(s, iter->event, &sample, tool, | 729 | ret = perf_session_deliver_event(s, iter->event, &sample, tool, |
680 | iter->file_offset); | 730 | iter->file_offset); |
731 | if (ret) | ||
732 | return ret; | ||
733 | } | ||
681 | 734 | ||
682 | os->last_flush = iter->timestamp; | 735 | os->last_flush = iter->timestamp; |
683 | list_del(&iter->list); | 736 | list_del(&iter->list); |
@@ -697,6 +750,8 @@ static void flush_sample_queue(struct perf_session *s, | |||
697 | } | 750 | } |
698 | 751 | ||
699 | os->nr_samples = 0; | 752 | os->nr_samples = 0; |
753 | |||
754 | return 0; | ||
700 | } | 755 | } |
701 | 756 | ||
702 | /* | 757 | /* |
@@ -739,13 +794,14 @@ static void flush_sample_queue(struct perf_session *s, | |||
739 | * etc... | 794 | * etc... |
740 | */ | 795 | */ |
741 | static int process_finished_round(struct perf_tool *tool, | 796 | static int process_finished_round(struct perf_tool *tool, |
742 | union perf_event *event __used, | 797 | union perf_event *event __maybe_unused, |
743 | struct perf_session *session) | 798 | struct perf_session *session) |
744 | { | 799 | { |
745 | flush_sample_queue(session, tool); | 800 | int ret = flush_sample_queue(session, tool); |
746 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | 801 | if (!ret) |
802 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | ||
747 | 803 | ||
748 | return 0; | 804 | return ret; |
749 | } | 805 | } |
750 | 806 | ||
751 | /* The queue is ordered by time */ | 807 | /* The queue is ordered by time */ |
@@ -860,6 +916,34 @@ static void branch_stack__printf(struct perf_sample *sample) | |||
860 | sample->branch_stack->entries[i].to); | 916 | sample->branch_stack->entries[i].to); |
861 | } | 917 | } |
862 | 918 | ||
919 | static void regs_dump__printf(u64 mask, u64 *regs) | ||
920 | { | ||
921 | unsigned rid, i = 0; | ||
922 | |||
923 | for_each_set_bit(rid, (unsigned long *) &mask, sizeof(mask) * 8) { | ||
924 | u64 val = regs[i++]; | ||
925 | |||
926 | printf(".... %-5s 0x%" PRIx64 "\n", | ||
927 | perf_reg_name(rid), val); | ||
928 | } | ||
929 | } | ||
930 | |||
931 | static void regs_user__printf(struct perf_sample *sample, u64 mask) | ||
932 | { | ||
933 | struct regs_dump *user_regs = &sample->user_regs; | ||
934 | |||
935 | if (user_regs->regs) { | ||
936 | printf("... user regs: mask 0x%" PRIx64 "\n", mask); | ||
937 | regs_dump__printf(mask, user_regs->regs); | ||
938 | } | ||
939 | } | ||
940 | |||
941 | static void stack_user__printf(struct stack_dump *dump) | ||
942 | { | ||
943 | printf("... ustack: size %" PRIu64 ", offset 0x%x\n", | ||
944 | dump->size, dump->offset); | ||
945 | } | ||
946 | |||
863 | static void perf_session__print_tstamp(struct perf_session *session, | 947 | static void perf_session__print_tstamp(struct perf_session *session, |
864 | union perf_event *event, | 948 | union perf_event *event, |
865 | struct perf_sample *sample) | 949 | struct perf_sample *sample) |
@@ -897,7 +981,7 @@ static void dump_event(struct perf_session *session, union perf_event *event, | |||
897 | event->header.size, perf_event__name(event->header.type)); | 981 | event->header.size, perf_event__name(event->header.type)); |
898 | } | 982 | } |
899 | 983 | ||
900 | static void dump_sample(struct perf_session *session, union perf_event *event, | 984 | static void dump_sample(struct perf_evsel *evsel, union perf_event *event, |
901 | struct perf_sample *sample) | 985 | struct perf_sample *sample) |
902 | { | 986 | { |
903 | u64 sample_type; | 987 | u64 sample_type; |
@@ -909,13 +993,19 @@ static void dump_sample(struct perf_session *session, union perf_event *event, | |||
909 | event->header.misc, sample->pid, sample->tid, sample->ip, | 993 | event->header.misc, sample->pid, sample->tid, sample->ip, |
910 | sample->period, sample->addr); | 994 | sample->period, sample->addr); |
911 | 995 | ||
912 | sample_type = perf_evlist__sample_type(session->evlist); | 996 | sample_type = evsel->attr.sample_type; |
913 | 997 | ||
914 | if (sample_type & PERF_SAMPLE_CALLCHAIN) | 998 | if (sample_type & PERF_SAMPLE_CALLCHAIN) |
915 | callchain__printf(sample); | 999 | callchain__printf(sample); |
916 | 1000 | ||
917 | if (sample_type & PERF_SAMPLE_BRANCH_STACK) | 1001 | if (sample_type & PERF_SAMPLE_BRANCH_STACK) |
918 | branch_stack__printf(sample); | 1002 | branch_stack__printf(sample); |
1003 | |||
1004 | if (sample_type & PERF_SAMPLE_REGS_USER) | ||
1005 | regs_user__printf(sample, evsel->attr.sample_regs_user); | ||
1006 | |||
1007 | if (sample_type & PERF_SAMPLE_STACK_USER) | ||
1008 | stack_user__printf(&sample->user_stack); | ||
919 | } | 1009 | } |
920 | 1010 | ||
921 | static struct machine * | 1011 | static struct machine * |
@@ -973,7 +1063,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
973 | 1063 | ||
974 | switch (event->header.type) { | 1064 | switch (event->header.type) { |
975 | case PERF_RECORD_SAMPLE: | 1065 | case PERF_RECORD_SAMPLE: |
976 | dump_sample(session, event, sample); | 1066 | dump_sample(evsel, event, sample); |
977 | if (evsel == NULL) { | 1067 | if (evsel == NULL) { |
978 | ++session->hists.stats.nr_unknown_id; | 1068 | ++session->hists.stats.nr_unknown_id; |
979 | return 0; | 1069 | return 0; |
@@ -1083,8 +1173,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
1083 | /* | 1173 | /* |
1084 | * For all kernel events we get the sample data | 1174 | * For all kernel events we get the sample data |
1085 | */ | 1175 | */ |
1086 | ret = perf_evlist__parse_sample(session->evlist, event, &sample, | 1176 | ret = perf_evlist__parse_sample(session->evlist, event, &sample); |
1087 | session->header.needs_swap); | ||
1088 | if (ret) | 1177 | if (ret) |
1089 | return ret; | 1178 | return ret; |
1090 | 1179 | ||
@@ -1369,7 +1458,7 @@ more: | |||
1369 | err = 0; | 1458 | err = 0; |
1370 | /* do the final flush for ordered samples */ | 1459 | /* do the final flush for ordered samples */ |
1371 | session->ordered_samples.next_flush = ULLONG_MAX; | 1460 | session->ordered_samples.next_flush = ULLONG_MAX; |
1372 | flush_sample_queue(session, tool); | 1461 | err = flush_sample_queue(session, tool); |
1373 | out_err: | 1462 | out_err: |
1374 | perf_session__warn_about_errors(session, tool); | 1463 | perf_session__warn_about_errors(session, tool); |
1375 | perf_session_free_sample_buffers(session); | 1464 | perf_session_free_sample_buffers(session); |
@@ -1498,9 +1587,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
1498 | return NULL; | 1587 | return NULL; |
1499 | } | 1588 | } |
1500 | 1589 | ||
1501 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | 1590 | void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, |
1502 | struct machine *machine, int print_sym, | 1591 | struct perf_sample *sample, struct machine *machine, |
1503 | int print_dso, int print_symoffset) | 1592 | int print_sym, int print_dso, int print_symoffset) |
1504 | { | 1593 | { |
1505 | struct addr_location al; | 1594 | struct addr_location al; |
1506 | struct callchain_cursor_node *node; | 1595 | struct callchain_cursor_node *node; |
@@ -1514,8 +1603,9 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | |||
1514 | 1603 | ||
1515 | if (symbol_conf.use_callchain && sample->callchain) { | 1604 | if (symbol_conf.use_callchain && sample->callchain) { |
1516 | 1605 | ||
1517 | if (machine__resolve_callchain(machine, al.thread, | 1606 | |
1518 | sample->callchain, NULL) != 0) { | 1607 | if (machine__resolve_callchain(machine, evsel, al.thread, |
1608 | sample, NULL) != 0) { | ||
1519 | if (verbose) | 1609 | if (verbose) |
1520 | error("Failed to resolve callchain. Skipping\n"); | 1610 | error("Failed to resolve callchain. Skipping\n"); |
1521 | return; | 1611 | return; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 1f7ec87db7d7..dd6426163ba6 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "symbol.h" | 7 | #include "symbol.h" |
8 | #include "thread.h" | 8 | #include "thread.h" |
9 | #include <linux/rbtree.h> | 9 | #include <linux/rbtree.h> |
10 | #include "../../../include/linux/perf_event.h" | 10 | #include "../../../include/uapi/linux/perf_event.h" |
11 | 11 | ||
12 | struct sample_queue; | 12 | struct sample_queue; |
13 | struct ip_callchain; | 13 | struct ip_callchain; |
@@ -36,9 +36,7 @@ struct perf_session { | |||
36 | struct pevent *pevent; | 36 | struct pevent *pevent; |
37 | /* | 37 | /* |
38 | * FIXME: Need to split this up further, we need global | 38 | * FIXME: Need to split this up further, we need global |
39 | * stats + per event stats. 'perf diff' also needs | 39 | * stats + per event stats. |
40 | * to properly support multiple events in a single | ||
41 | * perf.data file. | ||
42 | */ | 40 | */ |
43 | struct hists hists; | 41 | struct hists hists; |
44 | int fd; | 42 | int fd; |
@@ -129,9 +127,9 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); | |||
129 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 127 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
130 | unsigned int type); | 128 | unsigned int type); |
131 | 129 | ||
132 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | 130 | void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, |
133 | struct machine *machine, int print_sym, | 131 | struct perf_sample *sample, struct machine *machine, |
134 | int print_dso, int print_symoffset); | 132 | int print_sym, int print_dso, int print_symoffset); |
135 | 133 | ||
136 | int perf_session__cpu_bitmap(struct perf_session *session, | 134 | int perf_session__cpu_bitmap(struct perf_session *session, |
137 | const char *cpu_list, unsigned long *cpu_bitmap); | 135 | const char *cpu_list, unsigned long *cpu_bitmap); |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index d0f9f29cf181..73d510269784 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -23,6 +23,7 @@ cflags += getenv('CFLAGS', '').split() | |||
23 | 23 | ||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | 24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') |
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | 25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') |
26 | libtraceevent = getenv('LIBTRACEEVENT') | ||
26 | 27 | ||
27 | ext_sources = [f.strip() for f in file('util/python-ext-sources') | 28 | ext_sources = [f.strip() for f in file('util/python-ext-sources') |
28 | if len(f.strip()) > 0 and f[0] != '#'] | 29 | if len(f.strip()) > 0 and f[0] != '#'] |
@@ -31,6 +32,7 @@ perf = Extension('perf', | |||
31 | sources = ext_sources, | 32 | sources = ext_sources, |
32 | include_dirs = ['util/include'], | 33 | include_dirs = ['util/include'], |
33 | extra_compile_args = cflags, | 34 | extra_compile_args = cflags, |
35 | extra_objects = [libtraceevent], | ||
34 | ) | 36 | ) |
35 | 37 | ||
36 | setup(name='perf', | 38 | setup(name='perf', |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 0f5a0a496bc4..cfd1c0feb32d 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -8,12 +8,11 @@ const char default_sort_order[] = "comm,dso,symbol"; | |||
8 | const char *sort_order = default_sort_order; | 8 | const char *sort_order = default_sort_order; |
9 | int sort__need_collapse = 0; | 9 | int sort__need_collapse = 0; |
10 | int sort__has_parent = 0; | 10 | int sort__has_parent = 0; |
11 | int sort__has_sym = 0; | ||
11 | int sort__branch_mode = -1; /* -1 = means not set */ | 12 | int sort__branch_mode = -1; /* -1 = means not set */ |
12 | 13 | ||
13 | enum sort_type sort__first_dimension; | 14 | enum sort_type sort__first_dimension; |
14 | 15 | ||
15 | char * field_sep; | ||
16 | |||
17 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_HEAD(hist_entry__sort_list); |
18 | 17 | ||
19 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | 18 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
@@ -23,11 +22,11 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
23 | 22 | ||
24 | va_start(ap, fmt); | 23 | va_start(ap, fmt); |
25 | n = vsnprintf(bf, size, fmt, ap); | 24 | n = vsnprintf(bf, size, fmt, ap); |
26 | if (field_sep && n > 0) { | 25 | if (symbol_conf.field_sep && n > 0) { |
27 | char *sep = bf; | 26 | char *sep = bf; |
28 | 27 | ||
29 | while (1) { | 28 | while (1) { |
30 | sep = strchr(sep, *field_sep); | 29 | sep = strchr(sep, *symbol_conf.field_sep); |
31 | if (sep == NULL) | 30 | if (sep == NULL) |
32 | break; | 31 | break; |
33 | *sep = '.'; | 32 | *sep = '.'; |
@@ -172,7 +171,7 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
172 | 171 | ||
173 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | 172 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
174 | u64 ip, char level, char *bf, size_t size, | 173 | u64 ip, char level, char *bf, size_t size, |
175 | unsigned int width __used) | 174 | unsigned int width __maybe_unused) |
176 | { | 175 | { |
177 | size_t ret = 0; | 176 | size_t ret = 0; |
178 | 177 | ||
@@ -207,7 +206,8 @@ struct sort_entry sort_dso = { | |||
207 | }; | 206 | }; |
208 | 207 | ||
209 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | 208 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
210 | size_t size, unsigned int width __used) | 209 | size_t size, |
210 | unsigned int width __maybe_unused) | ||
211 | { | 211 | { |
212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | 212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, |
213 | self->level, bf, size, width); | 213 | self->level, bf, size, width); |
@@ -250,7 +250,8 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
250 | } | 250 | } |
251 | 251 | ||
252 | static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | 252 | static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, |
253 | size_t size, unsigned int width __used) | 253 | size_t size, |
254 | unsigned int width __maybe_unused) | ||
254 | { | 255 | { |
255 | FILE *fp; | 256 | FILE *fp; |
256 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; | 257 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; |
@@ -259,6 +260,12 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
259 | if (path != NULL) | 260 | if (path != NULL) |
260 | goto out_path; | 261 | goto out_path; |
261 | 262 | ||
263 | if (!self->ms.map) | ||
264 | goto out_ip; | ||
265 | |||
266 | if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) | ||
267 | goto out_ip; | ||
268 | |||
262 | snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, | 269 | snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, |
263 | self->ms.map->dso->long_name, self->ip); | 270 | self->ms.map->dso->long_name, self->ip); |
264 | fp = popen(cmd, "r"); | 271 | fp = popen(cmd, "r"); |
@@ -399,7 +406,8 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
399 | } | 406 | } |
400 | 407 | ||
401 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | 408 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, |
402 | size_t size, unsigned int width __used) | 409 | size_t size, |
410 | unsigned int width __maybe_unused) | ||
403 | { | 411 | { |
404 | struct addr_map_symbol *from = &self->branch_info->from; | 412 | struct addr_map_symbol *from = &self->branch_info->from; |
405 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 413 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, |
@@ -408,7 +416,8 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | |||
408 | } | 416 | } |
409 | 417 | ||
410 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | 418 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, |
411 | size_t size, unsigned int width __used) | 419 | size_t size, |
420 | unsigned int width __maybe_unused) | ||
412 | { | 421 | { |
413 | struct addr_map_symbol *to = &self->branch_info->to; | 422 | struct addr_map_symbol *to = &self->branch_info->to; |
414 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 423 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, |
@@ -509,6 +518,10 @@ int sort_dimension__add(const char *tok) | |||
509 | return -EINVAL; | 518 | return -EINVAL; |
510 | } | 519 | } |
511 | sort__has_parent = 1; | 520 | sort__has_parent = 1; |
521 | } else if (sd->entry == &sort_sym || | ||
522 | sd->entry == &sort_sym_from || | ||
523 | sd->entry == &sort_sym_to) { | ||
524 | sort__has_sym = 1; | ||
512 | } | 525 | } |
513 | 526 | ||
514 | if (sd->taken) | 527 | if (sd->taken) |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index e724b26acd51..5786f323b597 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -31,8 +31,8 @@ extern const char *parent_pattern; | |||
31 | extern const char default_sort_order[]; | 31 | extern const char default_sort_order[]; |
32 | extern int sort__need_collapse; | 32 | extern int sort__need_collapse; |
33 | extern int sort__has_parent; | 33 | extern int sort__has_parent; |
34 | extern int sort__has_sym; | ||
34 | extern int sort__branch_mode; | 35 | extern int sort__branch_mode; |
35 | extern char *field_sep; | ||
36 | extern struct sort_entry sort_comm; | 36 | extern struct sort_entry sort_comm; |
37 | extern struct sort_entry sort_dso; | 37 | extern struct sort_entry sort_dso; |
38 | extern struct sort_entry sort_sym; | 38 | extern struct sort_entry sort_sym; |
@@ -43,6 +43,15 @@ extern struct sort_entry sort_sym_from; | |||
43 | extern struct sort_entry sort_sym_to; | 43 | extern struct sort_entry sort_sym_to; |
44 | extern enum sort_type sort__first_dimension; | 44 | extern enum sort_type sort__first_dimension; |
45 | 45 | ||
46 | struct he_stat { | ||
47 | u64 period; | ||
48 | u64 period_sys; | ||
49 | u64 period_us; | ||
50 | u64 period_guest_sys; | ||
51 | u64 period_guest_us; | ||
52 | u32 nr_events; | ||
53 | }; | ||
54 | |||
46 | /** | 55 | /** |
47 | * struct hist_entry - histogram entry | 56 | * struct hist_entry - histogram entry |
48 | * | 57 | * |
@@ -52,16 +61,11 @@ extern enum sort_type sort__first_dimension; | |||
52 | struct hist_entry { | 61 | struct hist_entry { |
53 | struct rb_node rb_node_in; | 62 | struct rb_node rb_node_in; |
54 | struct rb_node rb_node; | 63 | struct rb_node rb_node; |
55 | u64 period; | 64 | struct he_stat stat; |
56 | u64 period_sys; | ||
57 | u64 period_us; | ||
58 | u64 period_guest_sys; | ||
59 | u64 period_guest_us; | ||
60 | struct map_symbol ms; | 65 | struct map_symbol ms; |
61 | struct thread *thread; | 66 | struct thread *thread; |
62 | u64 ip; | 67 | u64 ip; |
63 | s32 cpu; | 68 | s32 cpu; |
64 | u32 nr_events; | ||
65 | 69 | ||
66 | /* XXX These two should move to some tree widget lib */ | 70 | /* XXX These two should move to some tree widget lib */ |
67 | u16 row_offset; | 71 | u16 row_offset; |
@@ -73,12 +77,13 @@ struct hist_entry { | |||
73 | u8 filtered; | 77 | u8 filtered; |
74 | char *srcline; | 78 | char *srcline; |
75 | struct symbol *parent; | 79 | struct symbol *parent; |
80 | unsigned long position; | ||
76 | union { | 81 | union { |
77 | unsigned long position; | ||
78 | struct hist_entry *pair; | 82 | struct hist_entry *pair; |
79 | struct rb_root sorted_chain; | 83 | struct rb_root sorted_chain; |
80 | }; | 84 | }; |
81 | struct branch_info *branch_info; | 85 | struct branch_info *branch_info; |
86 | struct hists *hists; | ||
82 | struct callchain_root callchain[0]; | 87 | struct callchain_root callchain[0]; |
83 | }; | 88 | }; |
84 | 89 | ||
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c new file mode 100644 index 000000000000..23742126f47c --- /dev/null +++ b/tools/perf/util/stat.c | |||
@@ -0,0 +1,57 @@ | |||
1 | #include <math.h> | ||
2 | |||
3 | #include "stat.h" | ||
4 | |||
5 | void update_stats(struct stats *stats, u64 val) | ||
6 | { | ||
7 | double delta; | ||
8 | |||
9 | stats->n++; | ||
10 | delta = val - stats->mean; | ||
11 | stats->mean += delta / stats->n; | ||
12 | stats->M2 += delta*(val - stats->mean); | ||
13 | } | ||
14 | |||
15 | double avg_stats(struct stats *stats) | ||
16 | { | ||
17 | return stats->mean; | ||
18 | } | ||
19 | |||
20 | /* | ||
21 | * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance | ||
22 | * | ||
23 | * (\Sum n_i^2) - ((\Sum n_i)^2)/n | ||
24 | * s^2 = ------------------------------- | ||
25 | * n - 1 | ||
26 | * | ||
27 | * http://en.wikipedia.org/wiki/Stddev | ||
28 | * | ||
29 | * The std dev of the mean is related to the std dev by: | ||
30 | * | ||
31 | * s | ||
32 | * s_mean = ------- | ||
33 | * sqrt(n) | ||
34 | * | ||
35 | */ | ||
36 | double stddev_stats(struct stats *stats) | ||
37 | { | ||
38 | double variance, variance_mean; | ||
39 | |||
40 | if (!stats->n) | ||
41 | return 0.0; | ||
42 | |||
43 | variance = stats->M2 / (stats->n - 1); | ||
44 | variance_mean = variance / stats->n; | ||
45 | |||
46 | return sqrt(variance_mean); | ||
47 | } | ||
48 | |||
49 | double rel_stddev_stats(double stddev, double avg) | ||
50 | { | ||
51 | double pct = 0.0; | ||
52 | |||
53 | if (avg) | ||
54 | pct = 100.0 * stddev/avg; | ||
55 | |||
56 | return pct; | ||
57 | } | ||
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h new file mode 100644 index 000000000000..588367c3c767 --- /dev/null +++ b/tools/perf/util/stat.h | |||
@@ -0,0 +1,16 @@ | |||
1 | #ifndef __PERF_STATS_H | ||
2 | #define __PERF_STATS_H | ||
3 | |||
4 | #include "types.h" | ||
5 | |||
6 | struct stats | ||
7 | { | ||
8 | double n, mean, M2; | ||
9 | }; | ||
10 | |||
11 | void update_stats(struct stats *stats, u64 val); | ||
12 | double avg_stats(struct stats *stats); | ||
13 | double stddev_stats(struct stats *stats); | ||
14 | double rel_stddev_stats(double stddev, double avg); | ||
15 | |||
16 | #endif | ||
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 199bc4d8905d..32170590892d 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "string.h" | 2 | #include "linux/string.h" |
3 | 3 | ||
4 | #define K 1024LL | 4 | #define K 1024LL |
5 | /* | 5 | /* |
@@ -335,3 +335,19 @@ char *rtrim(char *s) | |||
335 | 335 | ||
336 | return s; | 336 | return s; |
337 | } | 337 | } |
338 | |||
339 | /** | ||
340 | * memdup - duplicate region of memory | ||
341 | * @src: memory region to duplicate | ||
342 | * @len: memory region length | ||
343 | */ | ||
344 | void *memdup(const void *src, size_t len) | ||
345 | { | ||
346 | void *p; | ||
347 | |||
348 | p = malloc(len); | ||
349 | if (p) | ||
350 | memcpy(p, src, len); | ||
351 | |||
352 | return p; | ||
353 | } | ||
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 95856ff3dda4..155d8b7078a7 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
@@ -93,7 +93,7 @@ out: | |||
93 | 93 | ||
94 | void strlist__remove(struct strlist *slist, struct str_node *snode) | 94 | void strlist__remove(struct strlist *slist, struct str_node *snode) |
95 | { | 95 | { |
96 | str_node__delete(snode, slist->dupstr); | 96 | rblist__remove_node(&slist->rblist, &snode->rb_node); |
97 | } | 97 | } |
98 | 98 | ||
99 | struct str_node *strlist__find(struct strlist *slist, const char *entry) | 99 | struct str_node *strlist__find(struct strlist *slist, const char *entry) |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c new file mode 100644 index 000000000000..db0cc92cf2ea --- /dev/null +++ b/tools/perf/util/symbol-elf.c | |||
@@ -0,0 +1,841 @@ | |||
1 | #include <libelf.h> | ||
2 | #include <gelf.h> | ||
3 | #include <elf.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <stdio.h> | ||
6 | #include <errno.h> | ||
7 | #include <string.h> | ||
8 | #include <unistd.h> | ||
9 | #include <inttypes.h> | ||
10 | |||
11 | #include "symbol.h" | ||
12 | #include "debug.h" | ||
13 | |||
14 | #ifndef NT_GNU_BUILD_ID | ||
15 | #define NT_GNU_BUILD_ID 3 | ||
16 | #endif | ||
17 | |||
18 | /** | ||
19 | * elf_symtab__for_each_symbol - iterate thru all the symbols | ||
20 | * | ||
21 | * @syms: struct elf_symtab instance to iterate | ||
22 | * @idx: uint32_t idx | ||
23 | * @sym: GElf_Sym iterator | ||
24 | */ | ||
25 | #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ | ||
26 | for (idx = 0, gelf_getsym(syms, idx, &sym);\ | ||
27 | idx < nr_syms; \ | ||
28 | idx++, gelf_getsym(syms, idx, &sym)) | ||
29 | |||
30 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | ||
31 | { | ||
32 | return GELF_ST_TYPE(sym->st_info); | ||
33 | } | ||
34 | |||
35 | static inline int elf_sym__is_function(const GElf_Sym *sym) | ||
36 | { | ||
37 | return elf_sym__type(sym) == STT_FUNC && | ||
38 | sym->st_name != 0 && | ||
39 | sym->st_shndx != SHN_UNDEF; | ||
40 | } | ||
41 | |||
42 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
43 | { | ||
44 | return elf_sym__type(sym) == STT_OBJECT && | ||
45 | sym->st_name != 0 && | ||
46 | sym->st_shndx != SHN_UNDEF; | ||
47 | } | ||
48 | |||
49 | static inline int elf_sym__is_label(const GElf_Sym *sym) | ||
50 | { | ||
51 | return elf_sym__type(sym) == STT_NOTYPE && | ||
52 | sym->st_name != 0 && | ||
53 | sym->st_shndx != SHN_UNDEF && | ||
54 | sym->st_shndx != SHN_ABS; | ||
55 | } | ||
56 | |||
57 | static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) | ||
58 | { | ||
59 | switch (type) { | ||
60 | case MAP__FUNCTION: | ||
61 | return elf_sym__is_function(sym); | ||
62 | case MAP__VARIABLE: | ||
63 | return elf_sym__is_object(sym); | ||
64 | default: | ||
65 | return false; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static inline const char *elf_sym__name(const GElf_Sym *sym, | ||
70 | const Elf_Data *symstrs) | ||
71 | { | ||
72 | return symstrs->d_buf + sym->st_name; | ||
73 | } | ||
74 | |||
75 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | ||
76 | const Elf_Data *secstrs) | ||
77 | { | ||
78 | return secstrs->d_buf + shdr->sh_name; | ||
79 | } | ||
80 | |||
81 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | ||
82 | const Elf_Data *secstrs) | ||
83 | { | ||
84 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | ||
85 | } | ||
86 | |||
87 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
88 | const Elf_Data *secstrs) | ||
89 | { | ||
90 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
91 | } | ||
92 | |||
93 | static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, | ||
94 | enum map_type type) | ||
95 | { | ||
96 | switch (type) { | ||
97 | case MAP__FUNCTION: | ||
98 | return elf_sec__is_text(shdr, secstrs); | ||
99 | case MAP__VARIABLE: | ||
100 | return elf_sec__is_data(shdr, secstrs); | ||
101 | default: | ||
102 | return false; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | ||
107 | { | ||
108 | Elf_Scn *sec = NULL; | ||
109 | GElf_Shdr shdr; | ||
110 | size_t cnt = 1; | ||
111 | |||
112 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
113 | gelf_getshdr(sec, &shdr); | ||
114 | |||
115 | if ((addr >= shdr.sh_addr) && | ||
116 | (addr < (shdr.sh_addr + shdr.sh_size))) | ||
117 | return cnt; | ||
118 | |||
119 | ++cnt; | ||
120 | } | ||
121 | |||
122 | return -1; | ||
123 | } | ||
124 | |||
125 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
126 | GElf_Shdr *shp, const char *name, | ||
127 | size_t *idx) | ||
128 | { | ||
129 | Elf_Scn *sec = NULL; | ||
130 | size_t cnt = 1; | ||
131 | |||
132 | /* Elf is corrupted/truncated, avoid calling elf_strptr. */ | ||
133 | if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) | ||
134 | return NULL; | ||
135 | |||
136 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
137 | char *str; | ||
138 | |||
139 | gelf_getshdr(sec, shp); | ||
140 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
141 | if (!strcmp(name, str)) { | ||
142 | if (idx) | ||
143 | *idx = cnt; | ||
144 | break; | ||
145 | } | ||
146 | ++cnt; | ||
147 | } | ||
148 | |||
149 | return sec; | ||
150 | } | ||
151 | |||
152 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | ||
153 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | ||
154 | idx < nr_entries; \ | ||
155 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | ||
156 | |||
157 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | ||
158 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | ||
159 | idx < nr_entries; \ | ||
160 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | ||
161 | |||
162 | /* | ||
163 | * We need to check if we have a .dynsym, so that we can handle the | ||
164 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
165 | * .dynsym or .symtab). | ||
166 | * And always look at the original dso, not at debuginfo packages, that | ||
167 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
168 | */ | ||
169 | int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map *map, | ||
170 | symbol_filter_t filter) | ||
171 | { | ||
172 | uint32_t nr_rel_entries, idx; | ||
173 | GElf_Sym sym; | ||
174 | u64 plt_offset; | ||
175 | GElf_Shdr shdr_plt; | ||
176 | struct symbol *f; | ||
177 | GElf_Shdr shdr_rel_plt, shdr_dynsym; | ||
178 | Elf_Data *reldata, *syms, *symstrs; | ||
179 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; | ||
180 | size_t dynsym_idx; | ||
181 | GElf_Ehdr ehdr; | ||
182 | char sympltname[1024]; | ||
183 | Elf *elf; | ||
184 | int nr = 0, symidx, err = 0; | ||
185 | |||
186 | if (!ss->dynsym) | ||
187 | return 0; | ||
188 | |||
189 | elf = ss->elf; | ||
190 | ehdr = ss->ehdr; | ||
191 | |||
192 | scn_dynsym = ss->dynsym; | ||
193 | shdr_dynsym = ss->dynshdr; | ||
194 | dynsym_idx = ss->dynsym_idx; | ||
195 | |||
196 | if (scn_dynsym == NULL) | ||
197 | goto out_elf_end; | ||
198 | |||
199 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
200 | ".rela.plt", NULL); | ||
201 | if (scn_plt_rel == NULL) { | ||
202 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
203 | ".rel.plt", NULL); | ||
204 | if (scn_plt_rel == NULL) | ||
205 | goto out_elf_end; | ||
206 | } | ||
207 | |||
208 | err = -1; | ||
209 | |||
210 | if (shdr_rel_plt.sh_link != dynsym_idx) | ||
211 | goto out_elf_end; | ||
212 | |||
213 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) | ||
214 | goto out_elf_end; | ||
215 | |||
216 | /* | ||
217 | * Fetch the relocation section to find the idxes to the GOT | ||
218 | * and the symbols in the .dynsym they refer to. | ||
219 | */ | ||
220 | reldata = elf_getdata(scn_plt_rel, NULL); | ||
221 | if (reldata == NULL) | ||
222 | goto out_elf_end; | ||
223 | |||
224 | syms = elf_getdata(scn_dynsym, NULL); | ||
225 | if (syms == NULL) | ||
226 | goto out_elf_end; | ||
227 | |||
228 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); | ||
229 | if (scn_symstrs == NULL) | ||
230 | goto out_elf_end; | ||
231 | |||
232 | symstrs = elf_getdata(scn_symstrs, NULL); | ||
233 | if (symstrs == NULL) | ||
234 | goto out_elf_end; | ||
235 | |||
236 | if (symstrs->d_size == 0) | ||
237 | goto out_elf_end; | ||
238 | |||
239 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | ||
240 | plt_offset = shdr_plt.sh_offset; | ||
241 | |||
242 | if (shdr_rel_plt.sh_type == SHT_RELA) { | ||
243 | GElf_Rela pos_mem, *pos; | ||
244 | |||
245 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | ||
246 | nr_rel_entries) { | ||
247 | symidx = GELF_R_SYM(pos->r_info); | ||
248 | plt_offset += shdr_plt.sh_entsize; | ||
249 | gelf_getsym(syms, symidx, &sym); | ||
250 | snprintf(sympltname, sizeof(sympltname), | ||
251 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
252 | |||
253 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
254 | STB_GLOBAL, sympltname); | ||
255 | if (!f) | ||
256 | goto out_elf_end; | ||
257 | |||
258 | if (filter && filter(map, f)) | ||
259 | symbol__delete(f); | ||
260 | else { | ||
261 | symbols__insert(&dso->symbols[map->type], f); | ||
262 | ++nr; | ||
263 | } | ||
264 | } | ||
265 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | ||
266 | GElf_Rel pos_mem, *pos; | ||
267 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | ||
268 | nr_rel_entries) { | ||
269 | symidx = GELF_R_SYM(pos->r_info); | ||
270 | plt_offset += shdr_plt.sh_entsize; | ||
271 | gelf_getsym(syms, symidx, &sym); | ||
272 | snprintf(sympltname, sizeof(sympltname), | ||
273 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
274 | |||
275 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
276 | STB_GLOBAL, sympltname); | ||
277 | if (!f) | ||
278 | goto out_elf_end; | ||
279 | |||
280 | if (filter && filter(map, f)) | ||
281 | symbol__delete(f); | ||
282 | else { | ||
283 | symbols__insert(&dso->symbols[map->type], f); | ||
284 | ++nr; | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | |||
289 | err = 0; | ||
290 | out_elf_end: | ||
291 | if (err == 0) | ||
292 | return nr; | ||
293 | pr_debug("%s: problems reading %s PLT info.\n", | ||
294 | __func__, dso->long_name); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | /* | ||
299 | * Align offset to 4 bytes as needed for note name and descriptor data. | ||
300 | */ | ||
301 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | ||
302 | |||
303 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) | ||
304 | { | ||
305 | int err = -1; | ||
306 | GElf_Ehdr ehdr; | ||
307 | GElf_Shdr shdr; | ||
308 | Elf_Data *data; | ||
309 | Elf_Scn *sec; | ||
310 | Elf_Kind ek; | ||
311 | void *ptr; | ||
312 | |||
313 | if (size < BUILD_ID_SIZE) | ||
314 | goto out; | ||
315 | |||
316 | ek = elf_kind(elf); | ||
317 | if (ek != ELF_K_ELF) | ||
318 | goto out; | ||
319 | |||
320 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
321 | pr_err("%s: cannot get elf header.\n", __func__); | ||
322 | goto out; | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * Check following sections for notes: | ||
327 | * '.note.gnu.build-id' | ||
328 | * '.notes' | ||
329 | * '.note' (VDSO specific) | ||
330 | */ | ||
331 | do { | ||
332 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
333 | ".note.gnu.build-id", NULL); | ||
334 | if (sec) | ||
335 | break; | ||
336 | |||
337 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
338 | ".notes", NULL); | ||
339 | if (sec) | ||
340 | break; | ||
341 | |||
342 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
343 | ".note", NULL); | ||
344 | if (sec) | ||
345 | break; | ||
346 | |||
347 | return err; | ||
348 | |||
349 | } while (0); | ||
350 | |||
351 | data = elf_getdata(sec, NULL); | ||
352 | if (data == NULL) | ||
353 | goto out; | ||
354 | |||
355 | ptr = data->d_buf; | ||
356 | while (ptr < (data->d_buf + data->d_size)) { | ||
357 | GElf_Nhdr *nhdr = ptr; | ||
358 | size_t namesz = NOTE_ALIGN(nhdr->n_namesz), | ||
359 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
360 | const char *name; | ||
361 | |||
362 | ptr += sizeof(*nhdr); | ||
363 | name = ptr; | ||
364 | ptr += namesz; | ||
365 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
366 | nhdr->n_namesz == sizeof("GNU")) { | ||
367 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
368 | size_t sz = min(size, descsz); | ||
369 | memcpy(bf, ptr, sz); | ||
370 | memset(bf + sz, 0, size - sz); | ||
371 | err = descsz; | ||
372 | break; | ||
373 | } | ||
374 | } | ||
375 | ptr += descsz; | ||
376 | } | ||
377 | |||
378 | out: | ||
379 | return err; | ||
380 | } | ||
381 | |||
382 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
383 | { | ||
384 | int fd, err = -1; | ||
385 | Elf *elf; | ||
386 | |||
387 | if (size < BUILD_ID_SIZE) | ||
388 | goto out; | ||
389 | |||
390 | fd = open(filename, O_RDONLY); | ||
391 | if (fd < 0) | ||
392 | goto out; | ||
393 | |||
394 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
395 | if (elf == NULL) { | ||
396 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
397 | goto out_close; | ||
398 | } | ||
399 | |||
400 | err = elf_read_build_id(elf, bf, size); | ||
401 | |||
402 | elf_end(elf); | ||
403 | out_close: | ||
404 | close(fd); | ||
405 | out: | ||
406 | return err; | ||
407 | } | ||
408 | |||
409 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
410 | { | ||
411 | int fd, err = -1; | ||
412 | |||
413 | if (size < BUILD_ID_SIZE) | ||
414 | goto out; | ||
415 | |||
416 | fd = open(filename, O_RDONLY); | ||
417 | if (fd < 0) | ||
418 | goto out; | ||
419 | |||
420 | while (1) { | ||
421 | char bf[BUFSIZ]; | ||
422 | GElf_Nhdr nhdr; | ||
423 | size_t namesz, descsz; | ||
424 | |||
425 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | ||
426 | break; | ||
427 | |||
428 | namesz = NOTE_ALIGN(nhdr.n_namesz); | ||
429 | descsz = NOTE_ALIGN(nhdr.n_descsz); | ||
430 | if (nhdr.n_type == NT_GNU_BUILD_ID && | ||
431 | nhdr.n_namesz == sizeof("GNU")) { | ||
432 | if (read(fd, bf, namesz) != (ssize_t)namesz) | ||
433 | break; | ||
434 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | ||
435 | size_t sz = min(descsz, size); | ||
436 | if (read(fd, build_id, sz) == (ssize_t)sz) { | ||
437 | memset(build_id + sz, 0, size - sz); | ||
438 | err = 0; | ||
439 | break; | ||
440 | } | ||
441 | } else if (read(fd, bf, descsz) != (ssize_t)descsz) | ||
442 | break; | ||
443 | } else { | ||
444 | int n = namesz + descsz; | ||
445 | if (read(fd, bf, n) != n) | ||
446 | break; | ||
447 | } | ||
448 | } | ||
449 | close(fd); | ||
450 | out: | ||
451 | return err; | ||
452 | } | ||
453 | |||
454 | int filename__read_debuglink(const char *filename, char *debuglink, | ||
455 | size_t size) | ||
456 | { | ||
457 | int fd, err = -1; | ||
458 | Elf *elf; | ||
459 | GElf_Ehdr ehdr; | ||
460 | GElf_Shdr shdr; | ||
461 | Elf_Data *data; | ||
462 | Elf_Scn *sec; | ||
463 | Elf_Kind ek; | ||
464 | |||
465 | fd = open(filename, O_RDONLY); | ||
466 | if (fd < 0) | ||
467 | goto out; | ||
468 | |||
469 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
470 | if (elf == NULL) { | ||
471 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
472 | goto out_close; | ||
473 | } | ||
474 | |||
475 | ek = elf_kind(elf); | ||
476 | if (ek != ELF_K_ELF) | ||
477 | goto out_close; | ||
478 | |||
479 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
480 | pr_err("%s: cannot get elf header.\n", __func__); | ||
481 | goto out_close; | ||
482 | } | ||
483 | |||
484 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
485 | ".gnu_debuglink", NULL); | ||
486 | if (sec == NULL) | ||
487 | goto out_close; | ||
488 | |||
489 | data = elf_getdata(sec, NULL); | ||
490 | if (data == NULL) | ||
491 | goto out_close; | ||
492 | |||
493 | /* the start of this section is a zero-terminated string */ | ||
494 | strncpy(debuglink, data->d_buf, size); | ||
495 | |||
496 | elf_end(elf); | ||
497 | |||
498 | out_close: | ||
499 | close(fd); | ||
500 | out: | ||
501 | return err; | ||
502 | } | ||
503 | |||
504 | static int dso__swap_init(struct dso *dso, unsigned char eidata) | ||
505 | { | ||
506 | static unsigned int const endian = 1; | ||
507 | |||
508 | dso->needs_swap = DSO_SWAP__NO; | ||
509 | |||
510 | switch (eidata) { | ||
511 | case ELFDATA2LSB: | ||
512 | /* We are big endian, DSO is little endian. */ | ||
513 | if (*(unsigned char const *)&endian != 1) | ||
514 | dso->needs_swap = DSO_SWAP__YES; | ||
515 | break; | ||
516 | |||
517 | case ELFDATA2MSB: | ||
518 | /* We are little endian, DSO is big endian. */ | ||
519 | if (*(unsigned char const *)&endian != 0) | ||
520 | dso->needs_swap = DSO_SWAP__YES; | ||
521 | break; | ||
522 | |||
523 | default: | ||
524 | pr_err("unrecognized DSO data encoding %d\n", eidata); | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | bool symsrc__possibly_runtime(struct symsrc *ss) | ||
532 | { | ||
533 | return ss->dynsym || ss->opdsec; | ||
534 | } | ||
535 | |||
536 | bool symsrc__has_symtab(struct symsrc *ss) | ||
537 | { | ||
538 | return ss->symtab != NULL; | ||
539 | } | ||
540 | |||
541 | void symsrc__destroy(struct symsrc *ss) | ||
542 | { | ||
543 | free(ss->name); | ||
544 | elf_end(ss->elf); | ||
545 | close(ss->fd); | ||
546 | } | ||
547 | |||
548 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | ||
549 | enum dso_binary_type type) | ||
550 | { | ||
551 | int err = -1; | ||
552 | GElf_Ehdr ehdr; | ||
553 | Elf *elf; | ||
554 | int fd; | ||
555 | |||
556 | fd = open(name, O_RDONLY); | ||
557 | if (fd < 0) | ||
558 | return -1; | ||
559 | |||
560 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
561 | if (elf == NULL) { | ||
562 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); | ||
563 | goto out_close; | ||
564 | } | ||
565 | |||
566 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
567 | pr_debug("%s: cannot get elf header.\n", __func__); | ||
568 | goto out_elf_end; | ||
569 | } | ||
570 | |||
571 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) | ||
572 | goto out_elf_end; | ||
573 | |||
574 | /* Always reject images with a mismatched build-id: */ | ||
575 | if (dso->has_build_id) { | ||
576 | u8 build_id[BUILD_ID_SIZE]; | ||
577 | |||
578 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) | ||
579 | goto out_elf_end; | ||
580 | |||
581 | if (!dso__build_id_equal(dso, build_id)) | ||
582 | goto out_elf_end; | ||
583 | } | ||
584 | |||
585 | ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab", | ||
586 | NULL); | ||
587 | if (ss->symshdr.sh_type != SHT_SYMTAB) | ||
588 | ss->symtab = NULL; | ||
589 | |||
590 | ss->dynsym_idx = 0; | ||
591 | ss->dynsym = elf_section_by_name(elf, &ehdr, &ss->dynshdr, ".dynsym", | ||
592 | &ss->dynsym_idx); | ||
593 | if (ss->dynshdr.sh_type != SHT_DYNSYM) | ||
594 | ss->dynsym = NULL; | ||
595 | |||
596 | ss->opdidx = 0; | ||
597 | ss->opdsec = elf_section_by_name(elf, &ehdr, &ss->opdshdr, ".opd", | ||
598 | &ss->opdidx); | ||
599 | if (ss->opdshdr.sh_type != SHT_PROGBITS) | ||
600 | ss->opdsec = NULL; | ||
601 | |||
602 | if (dso->kernel == DSO_TYPE_USER) { | ||
603 | GElf_Shdr shdr; | ||
604 | ss->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
605 | elf_section_by_name(elf, &ehdr, &shdr, | ||
606 | ".gnu.prelink_undo", | ||
607 | NULL) != NULL); | ||
608 | } else { | ||
609 | ss->adjust_symbols = 0; | ||
610 | } | ||
611 | |||
612 | ss->name = strdup(name); | ||
613 | if (!ss->name) | ||
614 | goto out_elf_end; | ||
615 | |||
616 | ss->elf = elf; | ||
617 | ss->fd = fd; | ||
618 | ss->ehdr = ehdr; | ||
619 | ss->type = type; | ||
620 | |||
621 | return 0; | ||
622 | |||
623 | out_elf_end: | ||
624 | elf_end(elf); | ||
625 | out_close: | ||
626 | close(fd); | ||
627 | return err; | ||
628 | } | ||
629 | |||
630 | int dso__load_sym(struct dso *dso, struct map *map, | ||
631 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | ||
632 | symbol_filter_t filter, int kmodule) | ||
633 | { | ||
634 | struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL; | ||
635 | struct map *curr_map = map; | ||
636 | struct dso *curr_dso = dso; | ||
637 | Elf_Data *symstrs, *secstrs; | ||
638 | uint32_t nr_syms; | ||
639 | int err = -1; | ||
640 | uint32_t idx; | ||
641 | GElf_Ehdr ehdr; | ||
642 | GElf_Shdr shdr; | ||
643 | Elf_Data *syms, *opddata = NULL; | ||
644 | GElf_Sym sym; | ||
645 | Elf_Scn *sec, *sec_strndx; | ||
646 | Elf *elf; | ||
647 | int nr = 0; | ||
648 | |||
649 | dso->symtab_type = syms_ss->type; | ||
650 | |||
651 | if (!syms_ss->symtab) { | ||
652 | syms_ss->symtab = syms_ss->dynsym; | ||
653 | syms_ss->symshdr = syms_ss->dynshdr; | ||
654 | } | ||
655 | |||
656 | elf = syms_ss->elf; | ||
657 | ehdr = syms_ss->ehdr; | ||
658 | sec = syms_ss->symtab; | ||
659 | shdr = syms_ss->symshdr; | ||
660 | |||
661 | if (runtime_ss->opdsec) | ||
662 | opddata = elf_rawdata(runtime_ss->opdsec, NULL); | ||
663 | |||
664 | syms = elf_getdata(sec, NULL); | ||
665 | if (syms == NULL) | ||
666 | goto out_elf_end; | ||
667 | |||
668 | sec = elf_getscn(elf, shdr.sh_link); | ||
669 | if (sec == NULL) | ||
670 | goto out_elf_end; | ||
671 | |||
672 | symstrs = elf_getdata(sec, NULL); | ||
673 | if (symstrs == NULL) | ||
674 | goto out_elf_end; | ||
675 | |||
676 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | ||
677 | if (sec_strndx == NULL) | ||
678 | goto out_elf_end; | ||
679 | |||
680 | secstrs = elf_getdata(sec_strndx, NULL); | ||
681 | if (secstrs == NULL) | ||
682 | goto out_elf_end; | ||
683 | |||
684 | nr_syms = shdr.sh_size / shdr.sh_entsize; | ||
685 | |||
686 | memset(&sym, 0, sizeof(sym)); | ||
687 | dso->adjust_symbols = runtime_ss->adjust_symbols; | ||
688 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | ||
689 | struct symbol *f; | ||
690 | const char *elf_name = elf_sym__name(&sym, symstrs); | ||
691 | char *demangled = NULL; | ||
692 | int is_label = elf_sym__is_label(&sym); | ||
693 | const char *section_name; | ||
694 | bool used_opd = false; | ||
695 | |||
696 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
697 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
698 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
699 | |||
700 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | ||
701 | continue; | ||
702 | |||
703 | /* Reject ARM ELF "mapping symbols": these aren't unique and | ||
704 | * don't identify functions, so will confuse the profile | ||
705 | * output: */ | ||
706 | if (ehdr.e_machine == EM_ARM) { | ||
707 | if (!strcmp(elf_name, "$a") || | ||
708 | !strcmp(elf_name, "$d") || | ||
709 | !strcmp(elf_name, "$t")) | ||
710 | continue; | ||
711 | } | ||
712 | |||
713 | if (runtime_ss->opdsec && sym.st_shndx == runtime_ss->opdidx) { | ||
714 | u32 offset = sym.st_value - syms_ss->opdshdr.sh_addr; | ||
715 | u64 *opd = opddata->d_buf + offset; | ||
716 | sym.st_value = DSO__SWAP(dso, u64, *opd); | ||
717 | sym.st_shndx = elf_addr_to_index(runtime_ss->elf, | ||
718 | sym.st_value); | ||
719 | used_opd = true; | ||
720 | } | ||
721 | |||
722 | sec = elf_getscn(runtime_ss->elf, sym.st_shndx); | ||
723 | if (!sec) | ||
724 | goto out_elf_end; | ||
725 | |||
726 | gelf_getshdr(sec, &shdr); | ||
727 | |||
728 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) | ||
729 | continue; | ||
730 | |||
731 | section_name = elf_sec__name(&shdr, secstrs); | ||
732 | |||
733 | /* On ARM, symbols for thumb functions have 1 added to | ||
734 | * the symbol address as a flag - remove it */ | ||
735 | if ((ehdr.e_machine == EM_ARM) && | ||
736 | (map->type == MAP__FUNCTION) && | ||
737 | (sym.st_value & 1)) | ||
738 | --sym.st_value; | ||
739 | |||
740 | if (dso->kernel != DSO_TYPE_USER || kmodule) { | ||
741 | char dso_name[PATH_MAX]; | ||
742 | |||
743 | if (strcmp(section_name, | ||
744 | (curr_dso->short_name + | ||
745 | dso->short_name_len)) == 0) | ||
746 | goto new_symbol; | ||
747 | |||
748 | if (strcmp(section_name, ".text") == 0) { | ||
749 | curr_map = map; | ||
750 | curr_dso = dso; | ||
751 | goto new_symbol; | ||
752 | } | ||
753 | |||
754 | snprintf(dso_name, sizeof(dso_name), | ||
755 | "%s%s", dso->short_name, section_name); | ||
756 | |||
757 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); | ||
758 | if (curr_map == NULL) { | ||
759 | u64 start = sym.st_value; | ||
760 | |||
761 | if (kmodule) | ||
762 | start += map->start + shdr.sh_offset; | ||
763 | |||
764 | curr_dso = dso__new(dso_name); | ||
765 | if (curr_dso == NULL) | ||
766 | goto out_elf_end; | ||
767 | curr_dso->kernel = dso->kernel; | ||
768 | curr_dso->long_name = dso->long_name; | ||
769 | curr_dso->long_name_len = dso->long_name_len; | ||
770 | curr_map = map__new2(start, curr_dso, | ||
771 | map->type); | ||
772 | if (curr_map == NULL) { | ||
773 | dso__delete(curr_dso); | ||
774 | goto out_elf_end; | ||
775 | } | ||
776 | curr_map->map_ip = identity__map_ip; | ||
777 | curr_map->unmap_ip = identity__map_ip; | ||
778 | curr_dso->symtab_type = dso->symtab_type; | ||
779 | map_groups__insert(kmap->kmaps, curr_map); | ||
780 | dsos__add(&dso->node, curr_dso); | ||
781 | dso__set_loaded(curr_dso, map->type); | ||
782 | } else | ||
783 | curr_dso = curr_map->dso; | ||
784 | |||
785 | goto new_symbol; | ||
786 | } | ||
787 | |||
788 | if ((used_opd && runtime_ss->adjust_symbols) | ||
789 | || (!used_opd && syms_ss->adjust_symbols)) { | ||
790 | pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " | ||
791 | "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, | ||
792 | (u64)sym.st_value, (u64)shdr.sh_addr, | ||
793 | (u64)shdr.sh_offset); | ||
794 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
795 | } | ||
796 | /* | ||
797 | * We need to figure out if the object was created from C++ sources | ||
798 | * DWARF DW_compile_unit has this, but we don't always have access | ||
799 | * to it... | ||
800 | */ | ||
801 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | ||
802 | if (demangled != NULL) | ||
803 | elf_name = demangled; | ||
804 | new_symbol: | ||
805 | f = symbol__new(sym.st_value, sym.st_size, | ||
806 | GELF_ST_BIND(sym.st_info), elf_name); | ||
807 | free(demangled); | ||
808 | if (!f) | ||
809 | goto out_elf_end; | ||
810 | |||
811 | if (filter && filter(curr_map, f)) | ||
812 | symbol__delete(f); | ||
813 | else { | ||
814 | symbols__insert(&curr_dso->symbols[curr_map->type], f); | ||
815 | nr++; | ||
816 | } | ||
817 | } | ||
818 | |||
819 | /* | ||
820 | * For misannotated, zeroed, ASM function sizes. | ||
821 | */ | ||
822 | if (nr > 0) { | ||
823 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
824 | symbols__fixup_end(&dso->symbols[map->type]); | ||
825 | if (kmap) { | ||
826 | /* | ||
827 | * We need to fixup this here too because we create new | ||
828 | * maps here, for things like vsyscall sections. | ||
829 | */ | ||
830 | __map_groups__fixup_end(kmap->kmaps, map->type); | ||
831 | } | ||
832 | } | ||
833 | err = nr; | ||
834 | out_elf_end: | ||
835 | return err; | ||
836 | } | ||
837 | |||
838 | void symbol__elf_init(void) | ||
839 | { | ||
840 | elf_version(EV_CURRENT); | ||
841 | } | ||
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c new file mode 100644 index 000000000000..259f8f2ea9c9 --- /dev/null +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -0,0 +1,307 @@ | |||
1 | #include "symbol.h" | ||
2 | |||
3 | #include <elf.h> | ||
4 | #include <stdio.h> | ||
5 | #include <fcntl.h> | ||
6 | #include <string.h> | ||
7 | #include <byteswap.h> | ||
8 | #include <sys/stat.h> | ||
9 | |||
10 | |||
11 | static bool check_need_swap(int file_endian) | ||
12 | { | ||
13 | const int data = 1; | ||
14 | u8 *check = (u8 *)&data; | ||
15 | int host_endian; | ||
16 | |||
17 | if (check[0] == 1) | ||
18 | host_endian = ELFDATA2LSB; | ||
19 | else | ||
20 | host_endian = ELFDATA2MSB; | ||
21 | |||
22 | return host_endian != file_endian; | ||
23 | } | ||
24 | |||
25 | #define NOTE_ALIGN(sz) (((sz) + 3) & ~3) | ||
26 | |||
27 | #define NT_GNU_BUILD_ID 3 | ||
28 | |||
29 | static int read_build_id(void *note_data, size_t note_len, void *bf, | ||
30 | size_t size, bool need_swap) | ||
31 | { | ||
32 | struct { | ||
33 | u32 n_namesz; | ||
34 | u32 n_descsz; | ||
35 | u32 n_type; | ||
36 | } *nhdr; | ||
37 | void *ptr; | ||
38 | |||
39 | ptr = note_data; | ||
40 | while (ptr < (note_data + note_len)) { | ||
41 | const char *name; | ||
42 | size_t namesz, descsz; | ||
43 | |||
44 | nhdr = ptr; | ||
45 | if (need_swap) { | ||
46 | nhdr->n_namesz = bswap_32(nhdr->n_namesz); | ||
47 | nhdr->n_descsz = bswap_32(nhdr->n_descsz); | ||
48 | nhdr->n_type = bswap_32(nhdr->n_type); | ||
49 | } | ||
50 | |||
51 | namesz = NOTE_ALIGN(nhdr->n_namesz); | ||
52 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
53 | |||
54 | ptr += sizeof(*nhdr); | ||
55 | name = ptr; | ||
56 | ptr += namesz; | ||
57 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
58 | nhdr->n_namesz == sizeof("GNU")) { | ||
59 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
60 | size_t sz = min(size, descsz); | ||
61 | memcpy(bf, ptr, sz); | ||
62 | memset(bf + sz, 0, size - sz); | ||
63 | return 0; | ||
64 | } | ||
65 | } | ||
66 | ptr += descsz; | ||
67 | } | ||
68 | |||
69 | return -1; | ||
70 | } | ||
71 | |||
72 | int filename__read_debuglink(const char *filename __maybe_unused, | ||
73 | char *debuglink __maybe_unused, | ||
74 | size_t size __maybe_unused) | ||
75 | { | ||
76 | return -1; | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Just try PT_NOTE header otherwise fails | ||
81 | */ | ||
82 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
83 | { | ||
84 | FILE *fp; | ||
85 | int ret = -1; | ||
86 | bool need_swap = false; | ||
87 | u8 e_ident[EI_NIDENT]; | ||
88 | size_t buf_size; | ||
89 | void *buf; | ||
90 | int i; | ||
91 | |||
92 | fp = fopen(filename, "r"); | ||
93 | if (fp == NULL) | ||
94 | return -1; | ||
95 | |||
96 | if (fread(e_ident, sizeof(e_ident), 1, fp) != 1) | ||
97 | goto out; | ||
98 | |||
99 | if (memcmp(e_ident, ELFMAG, SELFMAG) || | ||
100 | e_ident[EI_VERSION] != EV_CURRENT) | ||
101 | goto out; | ||
102 | |||
103 | need_swap = check_need_swap(e_ident[EI_DATA]); | ||
104 | |||
105 | /* for simplicity */ | ||
106 | fseek(fp, 0, SEEK_SET); | ||
107 | |||
108 | if (e_ident[EI_CLASS] == ELFCLASS32) { | ||
109 | Elf32_Ehdr ehdr; | ||
110 | Elf32_Phdr *phdr; | ||
111 | |||
112 | if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) | ||
113 | goto out; | ||
114 | |||
115 | if (need_swap) { | ||
116 | ehdr.e_phoff = bswap_32(ehdr.e_phoff); | ||
117 | ehdr.e_phentsize = bswap_16(ehdr.e_phentsize); | ||
118 | ehdr.e_phnum = bswap_16(ehdr.e_phnum); | ||
119 | } | ||
120 | |||
121 | buf_size = ehdr.e_phentsize * ehdr.e_phnum; | ||
122 | buf = malloc(buf_size); | ||
123 | if (buf == NULL) | ||
124 | goto out; | ||
125 | |||
126 | fseek(fp, ehdr.e_phoff, SEEK_SET); | ||
127 | if (fread(buf, buf_size, 1, fp) != 1) | ||
128 | goto out_free; | ||
129 | |||
130 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { | ||
131 | void *tmp; | ||
132 | |||
133 | if (need_swap) { | ||
134 | phdr->p_type = bswap_32(phdr->p_type); | ||
135 | phdr->p_offset = bswap_32(phdr->p_offset); | ||
136 | phdr->p_filesz = bswap_32(phdr->p_filesz); | ||
137 | } | ||
138 | |||
139 | if (phdr->p_type != PT_NOTE) | ||
140 | continue; | ||
141 | |||
142 | buf_size = phdr->p_filesz; | ||
143 | tmp = realloc(buf, buf_size); | ||
144 | if (tmp == NULL) | ||
145 | goto out_free; | ||
146 | |||
147 | buf = tmp; | ||
148 | fseek(fp, phdr->p_offset, SEEK_SET); | ||
149 | if (fread(buf, buf_size, 1, fp) != 1) | ||
150 | goto out_free; | ||
151 | |||
152 | ret = read_build_id(buf, buf_size, bf, size, need_swap); | ||
153 | if (ret == 0) | ||
154 | ret = size; | ||
155 | break; | ||
156 | } | ||
157 | } else { | ||
158 | Elf64_Ehdr ehdr; | ||
159 | Elf64_Phdr *phdr; | ||
160 | |||
161 | if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) | ||
162 | goto out; | ||
163 | |||
164 | if (need_swap) { | ||
165 | ehdr.e_phoff = bswap_64(ehdr.e_phoff); | ||
166 | ehdr.e_phentsize = bswap_16(ehdr.e_phentsize); | ||
167 | ehdr.e_phnum = bswap_16(ehdr.e_phnum); | ||
168 | } | ||
169 | |||
170 | buf_size = ehdr.e_phentsize * ehdr.e_phnum; | ||
171 | buf = malloc(buf_size); | ||
172 | if (buf == NULL) | ||
173 | goto out; | ||
174 | |||
175 | fseek(fp, ehdr.e_phoff, SEEK_SET); | ||
176 | if (fread(buf, buf_size, 1, fp) != 1) | ||
177 | goto out_free; | ||
178 | |||
179 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { | ||
180 | void *tmp; | ||
181 | |||
182 | if (need_swap) { | ||
183 | phdr->p_type = bswap_32(phdr->p_type); | ||
184 | phdr->p_offset = bswap_64(phdr->p_offset); | ||
185 | phdr->p_filesz = bswap_64(phdr->p_filesz); | ||
186 | } | ||
187 | |||
188 | if (phdr->p_type != PT_NOTE) | ||
189 | continue; | ||
190 | |||
191 | buf_size = phdr->p_filesz; | ||
192 | tmp = realloc(buf, buf_size); | ||
193 | if (tmp == NULL) | ||
194 | goto out_free; | ||
195 | |||
196 | buf = tmp; | ||
197 | fseek(fp, phdr->p_offset, SEEK_SET); | ||
198 | if (fread(buf, buf_size, 1, fp) != 1) | ||
199 | goto out_free; | ||
200 | |||
201 | ret = read_build_id(buf, buf_size, bf, size, need_swap); | ||
202 | if (ret == 0) | ||
203 | ret = size; | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | out_free: | ||
208 | free(buf); | ||
209 | out: | ||
210 | fclose(fp); | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
215 | { | ||
216 | int fd; | ||
217 | int ret = -1; | ||
218 | struct stat stbuf; | ||
219 | size_t buf_size; | ||
220 | void *buf; | ||
221 | |||
222 | fd = open(filename, O_RDONLY); | ||
223 | if (fd < 0) | ||
224 | return -1; | ||
225 | |||
226 | if (fstat(fd, &stbuf) < 0) | ||
227 | goto out; | ||
228 | |||
229 | buf_size = stbuf.st_size; | ||
230 | buf = malloc(buf_size); | ||
231 | if (buf == NULL) | ||
232 | goto out; | ||
233 | |||
234 | if (read(fd, buf, buf_size) != (ssize_t) buf_size) | ||
235 | goto out_free; | ||
236 | |||
237 | ret = read_build_id(buf, buf_size, build_id, size, false); | ||
238 | out_free: | ||
239 | free(buf); | ||
240 | out: | ||
241 | close(fd); | ||
242 | return ret; | ||
243 | } | ||
244 | |||
245 | int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, | ||
246 | const char *name, | ||
247 | enum dso_binary_type type) | ||
248 | { | ||
249 | int fd = open(name, O_RDONLY); | ||
250 | if (fd < 0) | ||
251 | return -1; | ||
252 | |||
253 | ss->name = strdup(name); | ||
254 | if (!ss->name) | ||
255 | goto out_close; | ||
256 | |||
257 | ss->type = type; | ||
258 | |||
259 | return 0; | ||
260 | out_close: | ||
261 | close(fd); | ||
262 | return -1; | ||
263 | } | ||
264 | |||
265 | bool symsrc__possibly_runtime(struct symsrc *ss __maybe_unused) | ||
266 | { | ||
267 | /* Assume all sym sources could be a runtime image. */ | ||
268 | return true; | ||
269 | } | ||
270 | |||
271 | bool symsrc__has_symtab(struct symsrc *ss __maybe_unused) | ||
272 | { | ||
273 | return false; | ||
274 | } | ||
275 | |||
276 | void symsrc__destroy(struct symsrc *ss) | ||
277 | { | ||
278 | free(ss->name); | ||
279 | close(ss->fd); | ||
280 | } | ||
281 | |||
282 | int dso__synthesize_plt_symbols(struct dso *dso __maybe_unused, | ||
283 | struct symsrc *ss __maybe_unused, | ||
284 | struct map *map __maybe_unused, | ||
285 | symbol_filter_t filter __maybe_unused) | ||
286 | { | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, | ||
291 | struct symsrc *ss, | ||
292 | struct symsrc *runtime_ss __maybe_unused, | ||
293 | symbol_filter_t filter __maybe_unused, | ||
294 | int kmodule __maybe_unused) | ||
295 | { | ||
296 | unsigned char *build_id[BUILD_ID_SIZE]; | ||
297 | |||
298 | if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { | ||
299 | dso__set_build_id(dso, build_id); | ||
300 | return 1; | ||
301 | } | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | void symbol__elf_init(void) | ||
306 | { | ||
307 | } | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 8b63b678e127..e2e8c697cffe 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -15,8 +15,6 @@ | |||
15 | #include "symbol.h" | 15 | #include "symbol.h" |
16 | #include "strlist.h" | 16 | #include "strlist.h" |
17 | 17 | ||
18 | #include <libelf.h> | ||
19 | #include <gelf.h> | ||
20 | #include <elf.h> | 18 | #include <elf.h> |
21 | #include <limits.h> | 19 | #include <limits.h> |
22 | #include <sys/utsname.h> | 20 | #include <sys/utsname.h> |
@@ -25,15 +23,7 @@ | |||
25 | #define KSYM_NAME_LEN 256 | 23 | #define KSYM_NAME_LEN 256 |
26 | #endif | 24 | #endif |
27 | 25 | ||
28 | #ifndef NT_GNU_BUILD_ID | ||
29 | #define NT_GNU_BUILD_ID 3 | ||
30 | #endif | ||
31 | |||
32 | static void dso_cache__free(struct rb_root *root); | 26 | static void dso_cache__free(struct rb_root *root); |
33 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
34 | static int elf_read_build_id(Elf *elf, void *bf, size_t size); | ||
35 | static void dsos__add(struct list_head *head, struct dso *dso); | ||
36 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | ||
37 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 27 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
38 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
39 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
@@ -170,7 +160,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
170 | return SYMBOL_B; | 160 | return SYMBOL_B; |
171 | } | 161 | } |
172 | 162 | ||
173 | static void symbols__fixup_duplicate(struct rb_root *symbols) | 163 | void symbols__fixup_duplicate(struct rb_root *symbols) |
174 | { | 164 | { |
175 | struct rb_node *nd; | 165 | struct rb_node *nd; |
176 | struct symbol *curr, *next; | 166 | struct symbol *curr, *next; |
@@ -199,7 +189,7 @@ again: | |||
199 | } | 189 | } |
200 | } | 190 | } |
201 | 191 | ||
202 | static void symbols__fixup_end(struct rb_root *symbols) | 192 | void symbols__fixup_end(struct rb_root *symbols) |
203 | { | 193 | { |
204 | struct rb_node *nd, *prevnd = rb_first(symbols); | 194 | struct rb_node *nd, *prevnd = rb_first(symbols); |
205 | struct symbol *curr, *prev; | 195 | struct symbol *curr, *prev; |
@@ -222,7 +212,7 @@ static void symbols__fixup_end(struct rb_root *symbols) | |||
222 | curr->end = roundup(curr->start, 4096); | 212 | curr->end = roundup(curr->start, 4096); |
223 | } | 213 | } |
224 | 214 | ||
225 | static void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | 215 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) |
226 | { | 216 | { |
227 | struct map *prev, *curr; | 217 | struct map *prev, *curr; |
228 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); | 218 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); |
@@ -252,8 +242,7 @@ static void map_groups__fixup_end(struct map_groups *mg) | |||
252 | __map_groups__fixup_end(mg, i); | 242 | __map_groups__fixup_end(mg, i); |
253 | } | 243 | } |
254 | 244 | ||
255 | static struct symbol *symbol__new(u64 start, u64 len, u8 binding, | 245 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) |
256 | const char *name) | ||
257 | { | 246 | { |
258 | size_t namelen = strlen(name) + 1; | 247 | size_t namelen = strlen(name) + 1; |
259 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + | 248 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + |
@@ -390,7 +379,7 @@ void dso__set_build_id(struct dso *dso, void *build_id) | |||
390 | dso->has_build_id = 1; | 379 | dso->has_build_id = 1; |
391 | } | 380 | } |
392 | 381 | ||
393 | static void symbols__insert(struct rb_root *symbols, struct symbol *sym) | 382 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) |
394 | { | 383 | { |
395 | struct rb_node **p = &symbols->rb_node; | 384 | struct rb_node **p = &symbols->rb_node; |
396 | struct rb_node *parent = NULL; | 385 | struct rb_node *parent = NULL; |
@@ -574,7 +563,7 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | |||
574 | 563 | ||
575 | int kallsyms__parse(const char *filename, void *arg, | 564 | int kallsyms__parse(const char *filename, void *arg, |
576 | int (*process_symbol)(void *arg, const char *name, | 565 | int (*process_symbol)(void *arg, const char *name, |
577 | char type, u64 start, u64 end)) | 566 | char type, u64 start)) |
578 | { | 567 | { |
579 | char *line = NULL; | 568 | char *line = NULL; |
580 | size_t n; | 569 | size_t n; |
@@ -614,13 +603,8 @@ int kallsyms__parse(const char *filename, void *arg, | |||
614 | break; | 603 | break; |
615 | } | 604 | } |
616 | 605 | ||
617 | /* | ||
618 | * module symbols are not sorted so we add all | ||
619 | * symbols with zero length and rely on | ||
620 | * symbols__fixup_end() to fix it up. | ||
621 | */ | ||
622 | err = process_symbol(arg, symbol_name, | 606 | err = process_symbol(arg, symbol_name, |
623 | symbol_type, start, start); | 607 | symbol_type, start); |
624 | if (err) | 608 | if (err) |
625 | break; | 609 | break; |
626 | } | 610 | } |
@@ -647,7 +631,7 @@ static u8 kallsyms2elf_type(char type) | |||
647 | } | 631 | } |
648 | 632 | ||
649 | static int map__process_kallsym_symbol(void *arg, const char *name, | 633 | static int map__process_kallsym_symbol(void *arg, const char *name, |
650 | char type, u64 start, u64 end) | 634 | char type, u64 start) |
651 | { | 635 | { |
652 | struct symbol *sym; | 636 | struct symbol *sym; |
653 | struct process_kallsyms_args *a = arg; | 637 | struct process_kallsyms_args *a = arg; |
@@ -656,8 +640,12 @@ static int map__process_kallsym_symbol(void *arg, const char *name, | |||
656 | if (!symbol_type__is_a(type, a->map->type)) | 640 | if (!symbol_type__is_a(type, a->map->type)) |
657 | return 0; | 641 | return 0; |
658 | 642 | ||
659 | sym = symbol__new(start, end - start + 1, | 643 | /* |
660 | kallsyms2elf_type(type), name); | 644 | * module symbols are not sorted so we add all |
645 | * symbols, setting length to 0, and rely on | ||
646 | * symbols__fixup_end() to fix it up. | ||
647 | */ | ||
648 | sym = symbol__new(start, 0, kallsyms2elf_type(type), name); | ||
661 | if (sym == NULL) | 649 | if (sym == NULL) |
662 | return -ENOMEM; | 650 | return -ENOMEM; |
663 | /* | 651 | /* |
@@ -904,556 +892,7 @@ out_failure: | |||
904 | return -1; | 892 | return -1; |
905 | } | 893 | } |
906 | 894 | ||
907 | /** | 895 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) |
908 | * elf_symtab__for_each_symbol - iterate thru all the symbols | ||
909 | * | ||
910 | * @syms: struct elf_symtab instance to iterate | ||
911 | * @idx: uint32_t idx | ||
912 | * @sym: GElf_Sym iterator | ||
913 | */ | ||
914 | #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ | ||
915 | for (idx = 0, gelf_getsym(syms, idx, &sym);\ | ||
916 | idx < nr_syms; \ | ||
917 | idx++, gelf_getsym(syms, idx, &sym)) | ||
918 | |||
919 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | ||
920 | { | ||
921 | return GELF_ST_TYPE(sym->st_info); | ||
922 | } | ||
923 | |||
924 | static inline int elf_sym__is_function(const GElf_Sym *sym) | ||
925 | { | ||
926 | return elf_sym__type(sym) == STT_FUNC && | ||
927 | sym->st_name != 0 && | ||
928 | sym->st_shndx != SHN_UNDEF; | ||
929 | } | ||
930 | |||
931 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
932 | { | ||
933 | return elf_sym__type(sym) == STT_OBJECT && | ||
934 | sym->st_name != 0 && | ||
935 | sym->st_shndx != SHN_UNDEF; | ||
936 | } | ||
937 | |||
938 | static inline int elf_sym__is_label(const GElf_Sym *sym) | ||
939 | { | ||
940 | return elf_sym__type(sym) == STT_NOTYPE && | ||
941 | sym->st_name != 0 && | ||
942 | sym->st_shndx != SHN_UNDEF && | ||
943 | sym->st_shndx != SHN_ABS; | ||
944 | } | ||
945 | |||
946 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | ||
947 | const Elf_Data *secstrs) | ||
948 | { | ||
949 | return secstrs->d_buf + shdr->sh_name; | ||
950 | } | ||
951 | |||
952 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | ||
953 | const Elf_Data *secstrs) | ||
954 | { | ||
955 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | ||
956 | } | ||
957 | |||
958 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
959 | const Elf_Data *secstrs) | ||
960 | { | ||
961 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
962 | } | ||
963 | |||
964 | static inline const char *elf_sym__name(const GElf_Sym *sym, | ||
965 | const Elf_Data *symstrs) | ||
966 | { | ||
967 | return symstrs->d_buf + sym->st_name; | ||
968 | } | ||
969 | |||
970 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
971 | GElf_Shdr *shp, const char *name, | ||
972 | size_t *idx) | ||
973 | { | ||
974 | Elf_Scn *sec = NULL; | ||
975 | size_t cnt = 1; | ||
976 | |||
977 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
978 | char *str; | ||
979 | |||
980 | gelf_getshdr(sec, shp); | ||
981 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
982 | if (!strcmp(name, str)) { | ||
983 | if (idx) | ||
984 | *idx = cnt; | ||
985 | break; | ||
986 | } | ||
987 | ++cnt; | ||
988 | } | ||
989 | |||
990 | return sec; | ||
991 | } | ||
992 | |||
993 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | ||
994 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | ||
995 | idx < nr_entries; \ | ||
996 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | ||
997 | |||
998 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | ||
999 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | ||
1000 | idx < nr_entries; \ | ||
1001 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | ||
1002 | |||
1003 | /* | ||
1004 | * We need to check if we have a .dynsym, so that we can handle the | ||
1005 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
1006 | * .dynsym or .symtab). | ||
1007 | * And always look at the original dso, not at debuginfo packages, that | ||
1008 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
1009 | */ | ||
1010 | static int | ||
1011 | dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map, | ||
1012 | symbol_filter_t filter) | ||
1013 | { | ||
1014 | uint32_t nr_rel_entries, idx; | ||
1015 | GElf_Sym sym; | ||
1016 | u64 plt_offset; | ||
1017 | GElf_Shdr shdr_plt; | ||
1018 | struct symbol *f; | ||
1019 | GElf_Shdr shdr_rel_plt, shdr_dynsym; | ||
1020 | Elf_Data *reldata, *syms, *symstrs; | ||
1021 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; | ||
1022 | size_t dynsym_idx; | ||
1023 | GElf_Ehdr ehdr; | ||
1024 | char sympltname[1024]; | ||
1025 | Elf *elf; | ||
1026 | int nr = 0, symidx, fd, err = 0; | ||
1027 | |||
1028 | fd = open(name, O_RDONLY); | ||
1029 | if (fd < 0) | ||
1030 | goto out; | ||
1031 | |||
1032 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1033 | if (elf == NULL) | ||
1034 | goto out_close; | ||
1035 | |||
1036 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
1037 | goto out_elf_end; | ||
1038 | |||
1039 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
1040 | ".dynsym", &dynsym_idx); | ||
1041 | if (scn_dynsym == NULL) | ||
1042 | goto out_elf_end; | ||
1043 | |||
1044 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
1045 | ".rela.plt", NULL); | ||
1046 | if (scn_plt_rel == NULL) { | ||
1047 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
1048 | ".rel.plt", NULL); | ||
1049 | if (scn_plt_rel == NULL) | ||
1050 | goto out_elf_end; | ||
1051 | } | ||
1052 | |||
1053 | err = -1; | ||
1054 | |||
1055 | if (shdr_rel_plt.sh_link != dynsym_idx) | ||
1056 | goto out_elf_end; | ||
1057 | |||
1058 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) | ||
1059 | goto out_elf_end; | ||
1060 | |||
1061 | /* | ||
1062 | * Fetch the relocation section to find the idxes to the GOT | ||
1063 | * and the symbols in the .dynsym they refer to. | ||
1064 | */ | ||
1065 | reldata = elf_getdata(scn_plt_rel, NULL); | ||
1066 | if (reldata == NULL) | ||
1067 | goto out_elf_end; | ||
1068 | |||
1069 | syms = elf_getdata(scn_dynsym, NULL); | ||
1070 | if (syms == NULL) | ||
1071 | goto out_elf_end; | ||
1072 | |||
1073 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); | ||
1074 | if (scn_symstrs == NULL) | ||
1075 | goto out_elf_end; | ||
1076 | |||
1077 | symstrs = elf_getdata(scn_symstrs, NULL); | ||
1078 | if (symstrs == NULL) | ||
1079 | goto out_elf_end; | ||
1080 | |||
1081 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | ||
1082 | plt_offset = shdr_plt.sh_offset; | ||
1083 | |||
1084 | if (shdr_rel_plt.sh_type == SHT_RELA) { | ||
1085 | GElf_Rela pos_mem, *pos; | ||
1086 | |||
1087 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | ||
1088 | nr_rel_entries) { | ||
1089 | symidx = GELF_R_SYM(pos->r_info); | ||
1090 | plt_offset += shdr_plt.sh_entsize; | ||
1091 | gelf_getsym(syms, symidx, &sym); | ||
1092 | snprintf(sympltname, sizeof(sympltname), | ||
1093 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
1094 | |||
1095 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
1096 | STB_GLOBAL, sympltname); | ||
1097 | if (!f) | ||
1098 | goto out_elf_end; | ||
1099 | |||
1100 | if (filter && filter(map, f)) | ||
1101 | symbol__delete(f); | ||
1102 | else { | ||
1103 | symbols__insert(&dso->symbols[map->type], f); | ||
1104 | ++nr; | ||
1105 | } | ||
1106 | } | ||
1107 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | ||
1108 | GElf_Rel pos_mem, *pos; | ||
1109 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | ||
1110 | nr_rel_entries) { | ||
1111 | symidx = GELF_R_SYM(pos->r_info); | ||
1112 | plt_offset += shdr_plt.sh_entsize; | ||
1113 | gelf_getsym(syms, symidx, &sym); | ||
1114 | snprintf(sympltname, sizeof(sympltname), | ||
1115 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
1116 | |||
1117 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
1118 | STB_GLOBAL, sympltname); | ||
1119 | if (!f) | ||
1120 | goto out_elf_end; | ||
1121 | |||
1122 | if (filter && filter(map, f)) | ||
1123 | symbol__delete(f); | ||
1124 | else { | ||
1125 | symbols__insert(&dso->symbols[map->type], f); | ||
1126 | ++nr; | ||
1127 | } | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | err = 0; | ||
1132 | out_elf_end: | ||
1133 | elf_end(elf); | ||
1134 | out_close: | ||
1135 | close(fd); | ||
1136 | |||
1137 | if (err == 0) | ||
1138 | return nr; | ||
1139 | out: | ||
1140 | pr_debug("%s: problems reading %s PLT info.\n", | ||
1141 | __func__, dso->long_name); | ||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) | ||
1146 | { | ||
1147 | switch (type) { | ||
1148 | case MAP__FUNCTION: | ||
1149 | return elf_sym__is_function(sym); | ||
1150 | case MAP__VARIABLE: | ||
1151 | return elf_sym__is_object(sym); | ||
1152 | default: | ||
1153 | return false; | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, | ||
1158 | enum map_type type) | ||
1159 | { | ||
1160 | switch (type) { | ||
1161 | case MAP__FUNCTION: | ||
1162 | return elf_sec__is_text(shdr, secstrs); | ||
1163 | case MAP__VARIABLE: | ||
1164 | return elf_sec__is_data(shdr, secstrs); | ||
1165 | default: | ||
1166 | return false; | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | ||
1171 | { | ||
1172 | Elf_Scn *sec = NULL; | ||
1173 | GElf_Shdr shdr; | ||
1174 | size_t cnt = 1; | ||
1175 | |||
1176 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
1177 | gelf_getshdr(sec, &shdr); | ||
1178 | |||
1179 | if ((addr >= shdr.sh_addr) && | ||
1180 | (addr < (shdr.sh_addr + shdr.sh_size))) | ||
1181 | return cnt; | ||
1182 | |||
1183 | ++cnt; | ||
1184 | } | ||
1185 | |||
1186 | return -1; | ||
1187 | } | ||
1188 | |||
1189 | static int dso__swap_init(struct dso *dso, unsigned char eidata) | ||
1190 | { | ||
1191 | static unsigned int const endian = 1; | ||
1192 | |||
1193 | dso->needs_swap = DSO_SWAP__NO; | ||
1194 | |||
1195 | switch (eidata) { | ||
1196 | case ELFDATA2LSB: | ||
1197 | /* We are big endian, DSO is little endian. */ | ||
1198 | if (*(unsigned char const *)&endian != 1) | ||
1199 | dso->needs_swap = DSO_SWAP__YES; | ||
1200 | break; | ||
1201 | |||
1202 | case ELFDATA2MSB: | ||
1203 | /* We are little endian, DSO is big endian. */ | ||
1204 | if (*(unsigned char const *)&endian != 0) | ||
1205 | dso->needs_swap = DSO_SWAP__YES; | ||
1206 | break; | ||
1207 | |||
1208 | default: | ||
1209 | pr_err("unrecognized DSO data encoding %d\n", eidata); | ||
1210 | return -EINVAL; | ||
1211 | } | ||
1212 | |||
1213 | return 0; | ||
1214 | } | ||
1215 | |||
1216 | static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | ||
1217 | int fd, symbol_filter_t filter, int kmodule, | ||
1218 | int want_symtab) | ||
1219 | { | ||
1220 | struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL; | ||
1221 | struct map *curr_map = map; | ||
1222 | struct dso *curr_dso = dso; | ||
1223 | Elf_Data *symstrs, *secstrs; | ||
1224 | uint32_t nr_syms; | ||
1225 | int err = -1; | ||
1226 | uint32_t idx; | ||
1227 | GElf_Ehdr ehdr; | ||
1228 | GElf_Shdr shdr, opdshdr; | ||
1229 | Elf_Data *syms, *opddata = NULL; | ||
1230 | GElf_Sym sym; | ||
1231 | Elf_Scn *sec, *sec_strndx, *opdsec; | ||
1232 | Elf *elf; | ||
1233 | int nr = 0; | ||
1234 | size_t opdidx = 0; | ||
1235 | |||
1236 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1237 | if (elf == NULL) { | ||
1238 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); | ||
1239 | goto out_close; | ||
1240 | } | ||
1241 | |||
1242 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1243 | pr_debug("%s: cannot get elf header.\n", __func__); | ||
1244 | goto out_elf_end; | ||
1245 | } | ||
1246 | |||
1247 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) | ||
1248 | goto out_elf_end; | ||
1249 | |||
1250 | /* Always reject images with a mismatched build-id: */ | ||
1251 | if (dso->has_build_id) { | ||
1252 | u8 build_id[BUILD_ID_SIZE]; | ||
1253 | |||
1254 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) | ||
1255 | goto out_elf_end; | ||
1256 | |||
1257 | if (!dso__build_id_equal(dso, build_id)) | ||
1258 | goto out_elf_end; | ||
1259 | } | ||
1260 | |||
1261 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | ||
1262 | if (sec == NULL) { | ||
1263 | if (want_symtab) | ||
1264 | goto out_elf_end; | ||
1265 | |||
1266 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | ||
1267 | if (sec == NULL) | ||
1268 | goto out_elf_end; | ||
1269 | } | ||
1270 | |||
1271 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | ||
1272 | if (opdshdr.sh_type != SHT_PROGBITS) | ||
1273 | opdsec = NULL; | ||
1274 | if (opdsec) | ||
1275 | opddata = elf_rawdata(opdsec, NULL); | ||
1276 | |||
1277 | syms = elf_getdata(sec, NULL); | ||
1278 | if (syms == NULL) | ||
1279 | goto out_elf_end; | ||
1280 | |||
1281 | sec = elf_getscn(elf, shdr.sh_link); | ||
1282 | if (sec == NULL) | ||
1283 | goto out_elf_end; | ||
1284 | |||
1285 | symstrs = elf_getdata(sec, NULL); | ||
1286 | if (symstrs == NULL) | ||
1287 | goto out_elf_end; | ||
1288 | |||
1289 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | ||
1290 | if (sec_strndx == NULL) | ||
1291 | goto out_elf_end; | ||
1292 | |||
1293 | secstrs = elf_getdata(sec_strndx, NULL); | ||
1294 | if (secstrs == NULL) | ||
1295 | goto out_elf_end; | ||
1296 | |||
1297 | nr_syms = shdr.sh_size / shdr.sh_entsize; | ||
1298 | |||
1299 | memset(&sym, 0, sizeof(sym)); | ||
1300 | if (dso->kernel == DSO_TYPE_USER) { | ||
1301 | dso->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
1302 | elf_section_by_name(elf, &ehdr, &shdr, | ||
1303 | ".gnu.prelink_undo", | ||
1304 | NULL) != NULL); | ||
1305 | } else { | ||
1306 | dso->adjust_symbols = 0; | ||
1307 | } | ||
1308 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | ||
1309 | struct symbol *f; | ||
1310 | const char *elf_name = elf_sym__name(&sym, symstrs); | ||
1311 | char *demangled = NULL; | ||
1312 | int is_label = elf_sym__is_label(&sym); | ||
1313 | const char *section_name; | ||
1314 | |||
1315 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
1316 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
1317 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
1318 | |||
1319 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | ||
1320 | continue; | ||
1321 | |||
1322 | /* Reject ARM ELF "mapping symbols": these aren't unique and | ||
1323 | * don't identify functions, so will confuse the profile | ||
1324 | * output: */ | ||
1325 | if (ehdr.e_machine == EM_ARM) { | ||
1326 | if (!strcmp(elf_name, "$a") || | ||
1327 | !strcmp(elf_name, "$d") || | ||
1328 | !strcmp(elf_name, "$t")) | ||
1329 | continue; | ||
1330 | } | ||
1331 | |||
1332 | if (opdsec && sym.st_shndx == opdidx) { | ||
1333 | u32 offset = sym.st_value - opdshdr.sh_addr; | ||
1334 | u64 *opd = opddata->d_buf + offset; | ||
1335 | sym.st_value = DSO__SWAP(dso, u64, *opd); | ||
1336 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); | ||
1337 | } | ||
1338 | |||
1339 | sec = elf_getscn(elf, sym.st_shndx); | ||
1340 | if (!sec) | ||
1341 | goto out_elf_end; | ||
1342 | |||
1343 | gelf_getshdr(sec, &shdr); | ||
1344 | |||
1345 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) | ||
1346 | continue; | ||
1347 | |||
1348 | section_name = elf_sec__name(&shdr, secstrs); | ||
1349 | |||
1350 | /* On ARM, symbols for thumb functions have 1 added to | ||
1351 | * the symbol address as a flag - remove it */ | ||
1352 | if ((ehdr.e_machine == EM_ARM) && | ||
1353 | (map->type == MAP__FUNCTION) && | ||
1354 | (sym.st_value & 1)) | ||
1355 | --sym.st_value; | ||
1356 | |||
1357 | if (dso->kernel != DSO_TYPE_USER || kmodule) { | ||
1358 | char dso_name[PATH_MAX]; | ||
1359 | |||
1360 | if (strcmp(section_name, | ||
1361 | (curr_dso->short_name + | ||
1362 | dso->short_name_len)) == 0) | ||
1363 | goto new_symbol; | ||
1364 | |||
1365 | if (strcmp(section_name, ".text") == 0) { | ||
1366 | curr_map = map; | ||
1367 | curr_dso = dso; | ||
1368 | goto new_symbol; | ||
1369 | } | ||
1370 | |||
1371 | snprintf(dso_name, sizeof(dso_name), | ||
1372 | "%s%s", dso->short_name, section_name); | ||
1373 | |||
1374 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); | ||
1375 | if (curr_map == NULL) { | ||
1376 | u64 start = sym.st_value; | ||
1377 | |||
1378 | if (kmodule) | ||
1379 | start += map->start + shdr.sh_offset; | ||
1380 | |||
1381 | curr_dso = dso__new(dso_name); | ||
1382 | if (curr_dso == NULL) | ||
1383 | goto out_elf_end; | ||
1384 | curr_dso->kernel = dso->kernel; | ||
1385 | curr_dso->long_name = dso->long_name; | ||
1386 | curr_dso->long_name_len = dso->long_name_len; | ||
1387 | curr_map = map__new2(start, curr_dso, | ||
1388 | map->type); | ||
1389 | if (curr_map == NULL) { | ||
1390 | dso__delete(curr_dso); | ||
1391 | goto out_elf_end; | ||
1392 | } | ||
1393 | curr_map->map_ip = identity__map_ip; | ||
1394 | curr_map->unmap_ip = identity__map_ip; | ||
1395 | curr_dso->symtab_type = dso->symtab_type; | ||
1396 | map_groups__insert(kmap->kmaps, curr_map); | ||
1397 | dsos__add(&dso->node, curr_dso); | ||
1398 | dso__set_loaded(curr_dso, map->type); | ||
1399 | } else | ||
1400 | curr_dso = curr_map->dso; | ||
1401 | |||
1402 | goto new_symbol; | ||
1403 | } | ||
1404 | |||
1405 | if (curr_dso->adjust_symbols) { | ||
1406 | pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " | ||
1407 | "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, | ||
1408 | (u64)sym.st_value, (u64)shdr.sh_addr, | ||
1409 | (u64)shdr.sh_offset); | ||
1410 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
1411 | } | ||
1412 | /* | ||
1413 | * We need to figure out if the object was created from C++ sources | ||
1414 | * DWARF DW_compile_unit has this, but we don't always have access | ||
1415 | * to it... | ||
1416 | */ | ||
1417 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | ||
1418 | if (demangled != NULL) | ||
1419 | elf_name = demangled; | ||
1420 | new_symbol: | ||
1421 | f = symbol__new(sym.st_value, sym.st_size, | ||
1422 | GELF_ST_BIND(sym.st_info), elf_name); | ||
1423 | free(demangled); | ||
1424 | if (!f) | ||
1425 | goto out_elf_end; | ||
1426 | |||
1427 | if (filter && filter(curr_map, f)) | ||
1428 | symbol__delete(f); | ||
1429 | else { | ||
1430 | symbols__insert(&curr_dso->symbols[curr_map->type], f); | ||
1431 | nr++; | ||
1432 | } | ||
1433 | } | ||
1434 | |||
1435 | /* | ||
1436 | * For misannotated, zeroed, ASM function sizes. | ||
1437 | */ | ||
1438 | if (nr > 0) { | ||
1439 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
1440 | symbols__fixup_end(&dso->symbols[map->type]); | ||
1441 | if (kmap) { | ||
1442 | /* | ||
1443 | * We need to fixup this here too because we create new | ||
1444 | * maps here, for things like vsyscall sections. | ||
1445 | */ | ||
1446 | __map_groups__fixup_end(kmap->kmaps, map->type); | ||
1447 | } | ||
1448 | } | ||
1449 | err = nr; | ||
1450 | out_elf_end: | ||
1451 | elf_end(elf); | ||
1452 | out_close: | ||
1453 | return err; | ||
1454 | } | ||
1455 | |||
1456 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
1457 | { | 896 | { |
1458 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | 897 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; |
1459 | } | 898 | } |
@@ -1480,216 +919,11 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
1480 | return have_build_id; | 919 | return have_build_id; |
1481 | } | 920 | } |
1482 | 921 | ||
1483 | /* | ||
1484 | * Align offset to 4 bytes as needed for note name and descriptor data. | ||
1485 | */ | ||
1486 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | ||
1487 | |||
1488 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) | ||
1489 | { | ||
1490 | int err = -1; | ||
1491 | GElf_Ehdr ehdr; | ||
1492 | GElf_Shdr shdr; | ||
1493 | Elf_Data *data; | ||
1494 | Elf_Scn *sec; | ||
1495 | Elf_Kind ek; | ||
1496 | void *ptr; | ||
1497 | |||
1498 | if (size < BUILD_ID_SIZE) | ||
1499 | goto out; | ||
1500 | |||
1501 | ek = elf_kind(elf); | ||
1502 | if (ek != ELF_K_ELF) | ||
1503 | goto out; | ||
1504 | |||
1505 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1506 | pr_err("%s: cannot get elf header.\n", __func__); | ||
1507 | goto out; | ||
1508 | } | ||
1509 | |||
1510 | /* | ||
1511 | * Check following sections for notes: | ||
1512 | * '.note.gnu.build-id' | ||
1513 | * '.notes' | ||
1514 | * '.note' (VDSO specific) | ||
1515 | */ | ||
1516 | do { | ||
1517 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1518 | ".note.gnu.build-id", NULL); | ||
1519 | if (sec) | ||
1520 | break; | ||
1521 | |||
1522 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1523 | ".notes", NULL); | ||
1524 | if (sec) | ||
1525 | break; | ||
1526 | |||
1527 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1528 | ".note", NULL); | ||
1529 | if (sec) | ||
1530 | break; | ||
1531 | |||
1532 | return err; | ||
1533 | |||
1534 | } while (0); | ||
1535 | |||
1536 | data = elf_getdata(sec, NULL); | ||
1537 | if (data == NULL) | ||
1538 | goto out; | ||
1539 | |||
1540 | ptr = data->d_buf; | ||
1541 | while (ptr < (data->d_buf + data->d_size)) { | ||
1542 | GElf_Nhdr *nhdr = ptr; | ||
1543 | size_t namesz = NOTE_ALIGN(nhdr->n_namesz), | ||
1544 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
1545 | const char *name; | ||
1546 | |||
1547 | ptr += sizeof(*nhdr); | ||
1548 | name = ptr; | ||
1549 | ptr += namesz; | ||
1550 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
1551 | nhdr->n_namesz == sizeof("GNU")) { | ||
1552 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
1553 | size_t sz = min(size, descsz); | ||
1554 | memcpy(bf, ptr, sz); | ||
1555 | memset(bf + sz, 0, size - sz); | ||
1556 | err = descsz; | ||
1557 | break; | ||
1558 | } | ||
1559 | } | ||
1560 | ptr += descsz; | ||
1561 | } | ||
1562 | |||
1563 | out: | ||
1564 | return err; | ||
1565 | } | ||
1566 | |||
1567 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
1568 | { | ||
1569 | int fd, err = -1; | ||
1570 | Elf *elf; | ||
1571 | |||
1572 | if (size < BUILD_ID_SIZE) | ||
1573 | goto out; | ||
1574 | |||
1575 | fd = open(filename, O_RDONLY); | ||
1576 | if (fd < 0) | ||
1577 | goto out; | ||
1578 | |||
1579 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1580 | if (elf == NULL) { | ||
1581 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1582 | goto out_close; | ||
1583 | } | ||
1584 | |||
1585 | err = elf_read_build_id(elf, bf, size); | ||
1586 | |||
1587 | elf_end(elf); | ||
1588 | out_close: | ||
1589 | close(fd); | ||
1590 | out: | ||
1591 | return err; | ||
1592 | } | ||
1593 | |||
1594 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
1595 | { | ||
1596 | int fd, err = -1; | ||
1597 | |||
1598 | if (size < BUILD_ID_SIZE) | ||
1599 | goto out; | ||
1600 | |||
1601 | fd = open(filename, O_RDONLY); | ||
1602 | if (fd < 0) | ||
1603 | goto out; | ||
1604 | |||
1605 | while (1) { | ||
1606 | char bf[BUFSIZ]; | ||
1607 | GElf_Nhdr nhdr; | ||
1608 | size_t namesz, descsz; | ||
1609 | |||
1610 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | ||
1611 | break; | ||
1612 | |||
1613 | namesz = NOTE_ALIGN(nhdr.n_namesz); | ||
1614 | descsz = NOTE_ALIGN(nhdr.n_descsz); | ||
1615 | if (nhdr.n_type == NT_GNU_BUILD_ID && | ||
1616 | nhdr.n_namesz == sizeof("GNU")) { | ||
1617 | if (read(fd, bf, namesz) != (ssize_t)namesz) | ||
1618 | break; | ||
1619 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | ||
1620 | size_t sz = min(descsz, size); | ||
1621 | if (read(fd, build_id, sz) == (ssize_t)sz) { | ||
1622 | memset(build_id + sz, 0, size - sz); | ||
1623 | err = 0; | ||
1624 | break; | ||
1625 | } | ||
1626 | } else if (read(fd, bf, descsz) != (ssize_t)descsz) | ||
1627 | break; | ||
1628 | } else { | ||
1629 | int n = namesz + descsz; | ||
1630 | if (read(fd, bf, n) != n) | ||
1631 | break; | ||
1632 | } | ||
1633 | } | ||
1634 | close(fd); | ||
1635 | out: | ||
1636 | return err; | ||
1637 | } | ||
1638 | |||
1639 | static int filename__read_debuglink(const char *filename, | ||
1640 | char *debuglink, size_t size) | ||
1641 | { | ||
1642 | int fd, err = -1; | ||
1643 | Elf *elf; | ||
1644 | GElf_Ehdr ehdr; | ||
1645 | GElf_Shdr shdr; | ||
1646 | Elf_Data *data; | ||
1647 | Elf_Scn *sec; | ||
1648 | Elf_Kind ek; | ||
1649 | |||
1650 | fd = open(filename, O_RDONLY); | ||
1651 | if (fd < 0) | ||
1652 | goto out; | ||
1653 | |||
1654 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1655 | if (elf == NULL) { | ||
1656 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1657 | goto out_close; | ||
1658 | } | ||
1659 | |||
1660 | ek = elf_kind(elf); | ||
1661 | if (ek != ELF_K_ELF) | ||
1662 | goto out_close; | ||
1663 | |||
1664 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1665 | pr_err("%s: cannot get elf header.\n", __func__); | ||
1666 | goto out_close; | ||
1667 | } | ||
1668 | |||
1669 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1670 | ".gnu_debuglink", NULL); | ||
1671 | if (sec == NULL) | ||
1672 | goto out_close; | ||
1673 | |||
1674 | data = elf_getdata(sec, NULL); | ||
1675 | if (data == NULL) | ||
1676 | goto out_close; | ||
1677 | |||
1678 | /* the start of this section is a zero-terminated string */ | ||
1679 | strncpy(debuglink, data->d_buf, size); | ||
1680 | |||
1681 | elf_end(elf); | ||
1682 | |||
1683 | out_close: | ||
1684 | close(fd); | ||
1685 | out: | ||
1686 | return err; | ||
1687 | } | ||
1688 | |||
1689 | char dso__symtab_origin(const struct dso *dso) | 922 | char dso__symtab_origin(const struct dso *dso) |
1690 | { | 923 | { |
1691 | static const char origin[] = { | 924 | static const char origin[] = { |
1692 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | 925 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', |
926 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | ||
1693 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | 927 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', |
1694 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | 928 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', |
1695 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | 929 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', |
@@ -1700,6 +934,7 @@ char dso__symtab_origin(const struct dso *dso) | |||
1700 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | 934 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', |
1701 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | 935 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', |
1702 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | 936 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', |
937 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
1703 | }; | 938 | }; |
1704 | 939 | ||
1705 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | 940 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) |
@@ -1775,7 +1010,9 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | |||
1775 | 1010 | ||
1776 | default: | 1011 | default: |
1777 | case DSO_BINARY_TYPE__KALLSYMS: | 1012 | case DSO_BINARY_TYPE__KALLSYMS: |
1013 | case DSO_BINARY_TYPE__VMLINUX: | ||
1778 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | 1014 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: |
1015 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
1779 | case DSO_BINARY_TYPE__JAVA_JIT: | 1016 | case DSO_BINARY_TYPE__JAVA_JIT: |
1780 | case DSO_BINARY_TYPE__NOT_FOUND: | 1017 | case DSO_BINARY_TYPE__NOT_FOUND: |
1781 | ret = -1; | 1018 | ret = -1; |
@@ -1789,11 +1026,12 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1789 | { | 1026 | { |
1790 | char *name; | 1027 | char *name; |
1791 | int ret = -1; | 1028 | int ret = -1; |
1792 | int fd; | ||
1793 | u_int i; | 1029 | u_int i; |
1794 | struct machine *machine; | 1030 | struct machine *machine; |
1795 | char *root_dir = (char *) ""; | 1031 | char *root_dir = (char *) ""; |
1796 | int want_symtab; | 1032 | int ss_pos = 0; |
1033 | struct symsrc ss_[2]; | ||
1034 | struct symsrc *syms_ss = NULL, *runtime_ss = NULL; | ||
1797 | 1035 | ||
1798 | dso__set_loaded(dso, map->type); | 1036 | dso__set_loaded(dso, map->type); |
1799 | 1037 | ||
@@ -1835,54 +1073,69 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1835 | root_dir = machine->root_dir; | 1073 | root_dir = machine->root_dir; |
1836 | 1074 | ||
1837 | /* Iterate over candidate debug images. | 1075 | /* Iterate over candidate debug images. |
1838 | * On the first pass, only load images if they have a full symtab. | 1076 | * Keep track of "interesting" ones (those which have a symtab, dynsym, |
1839 | * Failing that, do a second pass where we accept .dynsym also | 1077 | * and/or opd section) for processing. |
1840 | */ | 1078 | */ |
1841 | want_symtab = 1; | ||
1842 | restart: | ||
1843 | for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) { | 1079 | for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) { |
1080 | struct symsrc *ss = &ss_[ss_pos]; | ||
1081 | bool next_slot = false; | ||
1844 | 1082 | ||
1845 | dso->symtab_type = binary_type_symtab[i]; | 1083 | enum dso_binary_type symtab_type = binary_type_symtab[i]; |
1846 | 1084 | ||
1847 | if (dso__binary_type_file(dso, dso->symtab_type, | 1085 | if (dso__binary_type_file(dso, symtab_type, |
1848 | root_dir, name, PATH_MAX)) | 1086 | root_dir, name, PATH_MAX)) |
1849 | continue; | 1087 | continue; |
1850 | 1088 | ||
1851 | /* Name is now the name of the next image to try */ | 1089 | /* Name is now the name of the next image to try */ |
1852 | fd = open(name, O_RDONLY); | 1090 | if (symsrc__init(ss, dso, name, symtab_type) < 0) |
1853 | if (fd < 0) | ||
1854 | continue; | 1091 | continue; |
1855 | 1092 | ||
1856 | ret = dso__load_sym(dso, map, name, fd, filter, 0, | 1093 | if (!syms_ss && symsrc__has_symtab(ss)) { |
1857 | want_symtab); | 1094 | syms_ss = ss; |
1858 | close(fd); | 1095 | next_slot = true; |
1096 | } | ||
1859 | 1097 | ||
1860 | /* | 1098 | if (!runtime_ss && symsrc__possibly_runtime(ss)) { |
1861 | * Some people seem to have debuginfo files _WITHOUT_ debug | 1099 | runtime_ss = ss; |
1862 | * info!?!? | 1100 | next_slot = true; |
1863 | */ | 1101 | } |
1864 | if (!ret) | ||
1865 | continue; | ||
1866 | 1102 | ||
1867 | if (ret > 0) { | 1103 | if (next_slot) { |
1868 | int nr_plt; | 1104 | ss_pos++; |
1869 | 1105 | ||
1870 | nr_plt = dso__synthesize_plt_symbols(dso, name, map, filter); | 1106 | if (syms_ss && runtime_ss) |
1871 | if (nr_plt > 0) | 1107 | break; |
1872 | ret += nr_plt; | ||
1873 | break; | ||
1874 | } | 1108 | } |
1109 | |||
1875 | } | 1110 | } |
1876 | 1111 | ||
1877 | /* | 1112 | if (!runtime_ss && !syms_ss) |
1878 | * If we wanted a full symtab but no image had one, | 1113 | goto out_free; |
1879 | * relax our requirements and repeat the search. | 1114 | |
1880 | */ | 1115 | if (runtime_ss && !syms_ss) { |
1881 | if (ret <= 0 && want_symtab) { | 1116 | syms_ss = runtime_ss; |
1882 | want_symtab = 0; | 1117 | } |
1883 | goto restart; | 1118 | |
1119 | /* We'll have to hope for the best */ | ||
1120 | if (!runtime_ss && syms_ss) | ||
1121 | runtime_ss = syms_ss; | ||
1122 | |||
1123 | if (syms_ss) | ||
1124 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, 0); | ||
1125 | else | ||
1126 | ret = -1; | ||
1127 | |||
1128 | if (ret > 0) { | ||
1129 | int nr_plt; | ||
1130 | |||
1131 | nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss, map, filter); | ||
1132 | if (nr_plt > 0) | ||
1133 | ret += nr_plt; | ||
1884 | } | 1134 | } |
1885 | 1135 | ||
1136 | for (; ss_pos > 0; ss_pos--) | ||
1137 | symsrc__destroy(&ss_[ss_pos - 1]); | ||
1138 | out_free: | ||
1886 | free(name); | 1139 | free(name); |
1887 | if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) | 1140 | if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) |
1888 | return 0; | 1141 | return 0; |
@@ -2030,25 +1283,6 @@ static int machine__set_modules_path(struct machine *machine) | |||
2030 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | 1283 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); |
2031 | } | 1284 | } |
2032 | 1285 | ||
2033 | /* | ||
2034 | * Constructor variant for modules (where we know from /proc/modules where | ||
2035 | * they are loaded) and for vmlinux, where only after we load all the | ||
2036 | * symbols we'll know where it starts and ends. | ||
2037 | */ | ||
2038 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | ||
2039 | { | ||
2040 | struct map *map = calloc(1, (sizeof(*map) + | ||
2041 | (dso->kernel ? sizeof(struct kmap) : 0))); | ||
2042 | if (map != NULL) { | ||
2043 | /* | ||
2044 | * ->end will be filled after we load all the symbols | ||
2045 | */ | ||
2046 | map__init(map, type, start, 0, 0, dso); | ||
2047 | } | ||
2048 | |||
2049 | return map; | ||
2050 | } | ||
2051 | |||
2052 | struct map *machine__new_module(struct machine *machine, u64 start, | 1286 | struct map *machine__new_module(struct machine *machine, u64 start, |
2053 | const char *filename) | 1287 | const char *filename) |
2054 | { | 1288 | { |
@@ -2141,22 +1375,30 @@ out_failure: | |||
2141 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 1375 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
2142 | const char *vmlinux, symbol_filter_t filter) | 1376 | const char *vmlinux, symbol_filter_t filter) |
2143 | { | 1377 | { |
2144 | int err = -1, fd; | 1378 | int err = -1; |
1379 | struct symsrc ss; | ||
2145 | char symfs_vmlinux[PATH_MAX]; | 1380 | char symfs_vmlinux[PATH_MAX]; |
1381 | enum dso_binary_type symtab_type; | ||
2146 | 1382 | ||
2147 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1383 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", |
2148 | symbol_conf.symfs, vmlinux); | 1384 | symbol_conf.symfs, vmlinux); |
2149 | fd = open(symfs_vmlinux, O_RDONLY); | 1385 | |
2150 | if (fd < 0) | 1386 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1387 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | ||
1388 | else | ||
1389 | symtab_type = DSO_BINARY_TYPE__VMLINUX; | ||
1390 | |||
1391 | if (symsrc__init(&ss, dso, symfs_vmlinux, symtab_type)) | ||
2151 | return -1; | 1392 | return -1; |
2152 | 1393 | ||
2153 | dso__set_long_name(dso, (char *)vmlinux); | 1394 | err = dso__load_sym(dso, map, &ss, &ss, filter, 0); |
2154 | dso__set_loaded(dso, map->type); | 1395 | symsrc__destroy(&ss); |
2155 | err = dso__load_sym(dso, map, symfs_vmlinux, fd, filter, 0, 0); | ||
2156 | close(fd); | ||
2157 | 1396 | ||
2158 | if (err > 0) | 1397 | if (err > 0) { |
1398 | dso__set_long_name(dso, (char *)vmlinux); | ||
1399 | dso__set_loaded(dso, map->type); | ||
2159 | pr_debug("Using %s for symbols\n", symfs_vmlinux); | 1400 | pr_debug("Using %s for symbols\n", symfs_vmlinux); |
1401 | } | ||
2160 | 1402 | ||
2161 | return err; | 1403 | return err; |
2162 | } | 1404 | } |
@@ -2173,10 +1415,8 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
2173 | filename = dso__build_id_filename(dso, NULL, 0); | 1415 | filename = dso__build_id_filename(dso, NULL, 0); |
2174 | if (filename != NULL) { | 1416 | if (filename != NULL) { |
2175 | err = dso__load_vmlinux(dso, map, filename, filter); | 1417 | err = dso__load_vmlinux(dso, map, filename, filter); |
2176 | if (err > 0) { | 1418 | if (err > 0) |
2177 | dso__set_long_name(dso, filename); | ||
2178 | goto out; | 1419 | goto out; |
2179 | } | ||
2180 | free(filename); | 1420 | free(filename); |
2181 | } | 1421 | } |
2182 | 1422 | ||
@@ -2291,9 +1531,8 @@ do_kallsyms: | |||
2291 | free(kallsyms_allocated_filename); | 1531 | free(kallsyms_allocated_filename); |
2292 | 1532 | ||
2293 | if (err > 0) { | 1533 | if (err > 0) { |
1534 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); | ||
2294 | out_fixup: | 1535 | out_fixup: |
2295 | if (kallsyms_filename != NULL) | ||
2296 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); | ||
2297 | map__fixup_start(map); | 1536 | map__fixup_start(map); |
2298 | map__fixup_end(map); | 1537 | map__fixup_end(map); |
2299 | } | 1538 | } |
@@ -2352,12 +1591,12 @@ out_try_fixup: | |||
2352 | return err; | 1591 | return err; |
2353 | } | 1592 | } |
2354 | 1593 | ||
2355 | static void dsos__add(struct list_head *head, struct dso *dso) | 1594 | void dsos__add(struct list_head *head, struct dso *dso) |
2356 | { | 1595 | { |
2357 | list_add_tail(&dso->node, head); | 1596 | list_add_tail(&dso->node, head); |
2358 | } | 1597 | } |
2359 | 1598 | ||
2360 | static struct dso *dsos__find(struct list_head *head, const char *name) | 1599 | struct dso *dsos__find(struct list_head *head, const char *name) |
2361 | { | 1600 | { |
2362 | struct dso *pos; | 1601 | struct dso *pos; |
2363 | 1602 | ||
@@ -2516,7 +1755,7 @@ struct process_args { | |||
2516 | }; | 1755 | }; |
2517 | 1756 | ||
2518 | static int symbol__in_kernel(void *arg, const char *name, | 1757 | static int symbol__in_kernel(void *arg, const char *name, |
2519 | char type __used, u64 start, u64 end __used) | 1758 | char type __maybe_unused, u64 start) |
2520 | { | 1759 | { |
2521 | struct process_args *args = arg; | 1760 | struct process_args *args = arg; |
2522 | 1761 | ||
@@ -2752,9 +1991,10 @@ int symbol__init(void) | |||
2752 | if (symbol_conf.initialized) | 1991 | if (symbol_conf.initialized) |
2753 | return 0; | 1992 | return 0; |
2754 | 1993 | ||
2755 | symbol_conf.priv_size = ALIGN(symbol_conf.priv_size, sizeof(u64)); | 1994 | symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64)); |
1995 | |||
1996 | symbol__elf_init(); | ||
2756 | 1997 | ||
2757 | elf_version(EV_CURRENT); | ||
2758 | if (symbol_conf.sort_by_name) | 1998 | if (symbol_conf.sort_by_name) |
2759 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 1999 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
2760 | sizeof(struct symbol)); | 2000 | sizeof(struct symbol)); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 1fe733a1e21f..8b6ef7fac745 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -10,22 +10,31 @@ | |||
10 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
11 | #include <stdio.h> | 11 | #include <stdio.h> |
12 | #include <byteswap.h> | 12 | #include <byteswap.h> |
13 | #include <libgen.h> | ||
14 | |||
15 | #ifdef LIBELF_SUPPORT | ||
16 | #include <libelf.h> | ||
17 | #include <gelf.h> | ||
18 | #include <elf.h> | ||
19 | #endif | ||
13 | 20 | ||
14 | #ifdef HAVE_CPLUS_DEMANGLE | 21 | #ifdef HAVE_CPLUS_DEMANGLE |
15 | extern char *cplus_demangle(const char *, int); | 22 | extern char *cplus_demangle(const char *, int); |
16 | 23 | ||
17 | static inline char *bfd_demangle(void __used *v, const char *c, int i) | 24 | static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) |
18 | { | 25 | { |
19 | return cplus_demangle(c, i); | 26 | return cplus_demangle(c, i); |
20 | } | 27 | } |
21 | #else | 28 | #else |
22 | #ifdef NO_DEMANGLE | 29 | #ifdef NO_DEMANGLE |
23 | static inline char *bfd_demangle(void __used *v, const char __used *c, | 30 | static inline char *bfd_demangle(void __maybe_unused *v, |
24 | int __used i) | 31 | const char __maybe_unused *c, |
32 | int __maybe_unused i) | ||
25 | { | 33 | { |
26 | return NULL; | 34 | return NULL; |
27 | } | 35 | } |
28 | #else | 36 | #else |
37 | #define PACKAGE 'perf' | ||
29 | #include <bfd.h> | 38 | #include <bfd.h> |
30 | #endif | 39 | #endif |
31 | #endif | 40 | #endif |
@@ -37,10 +46,10 @@ char *strxfrchar(char *s, char from, char to); | |||
37 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
38 | * for newer versions we can use mmap to reduce memory usage: | 47 | * for newer versions we can use mmap to reduce memory usage: |
39 | */ | 48 | */ |
40 | #ifdef LIBELF_NO_MMAP | 49 | #ifdef LIBELF_MMAP |
41 | # define PERF_ELF_C_READ_MMAP ELF_C_READ | ||
42 | #else | ||
43 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP | 50 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP |
51 | #else | ||
52 | # define PERF_ELF_C_READ_MMAP ELF_C_READ | ||
44 | #endif | 53 | #endif |
45 | 54 | ||
46 | #ifndef DMGL_PARAMS | 55 | #ifndef DMGL_PARAMS |
@@ -158,6 +167,8 @@ struct addr_location { | |||
158 | enum dso_binary_type { | 167 | enum dso_binary_type { |
159 | DSO_BINARY_TYPE__KALLSYMS = 0, | 168 | DSO_BINARY_TYPE__KALLSYMS = 0, |
160 | DSO_BINARY_TYPE__GUEST_KALLSYMS, | 169 | DSO_BINARY_TYPE__GUEST_KALLSYMS, |
170 | DSO_BINARY_TYPE__VMLINUX, | ||
171 | DSO_BINARY_TYPE__GUEST_VMLINUX, | ||
161 | DSO_BINARY_TYPE__JAVA_JIT, | 172 | DSO_BINARY_TYPE__JAVA_JIT, |
162 | DSO_BINARY_TYPE__DEBUGLINK, | 173 | DSO_BINARY_TYPE__DEBUGLINK, |
163 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | 174 | DSO_BINARY_TYPE__BUILD_ID_CACHE, |
@@ -217,6 +228,36 @@ struct dso { | |||
217 | char name[0]; | 228 | char name[0]; |
218 | }; | 229 | }; |
219 | 230 | ||
231 | struct symsrc { | ||
232 | char *name; | ||
233 | int fd; | ||
234 | enum dso_binary_type type; | ||
235 | |||
236 | #ifdef LIBELF_SUPPORT | ||
237 | Elf *elf; | ||
238 | GElf_Ehdr ehdr; | ||
239 | |||
240 | Elf_Scn *opdsec; | ||
241 | size_t opdidx; | ||
242 | GElf_Shdr opdshdr; | ||
243 | |||
244 | Elf_Scn *symtab; | ||
245 | GElf_Shdr symshdr; | ||
246 | |||
247 | Elf_Scn *dynsym; | ||
248 | size_t dynsym_idx; | ||
249 | GElf_Shdr dynshdr; | ||
250 | |||
251 | bool adjust_symbols; | ||
252 | #endif | ||
253 | }; | ||
254 | |||
255 | void symsrc__destroy(struct symsrc *ss); | ||
256 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | ||
257 | enum dso_binary_type type); | ||
258 | bool symsrc__has_symtab(struct symsrc *ss); | ||
259 | bool symsrc__possibly_runtime(struct symsrc *ss); | ||
260 | |||
220 | #define DSO__SWAP(dso, type, val) \ | 261 | #define DSO__SWAP(dso, type, val) \ |
221 | ({ \ | 262 | ({ \ |
222 | type ____r = val; \ | 263 | type ____r = val; \ |
@@ -254,6 +295,8 @@ static inline void dso__set_loaded(struct dso *dso, enum map_type type) | |||
254 | 295 | ||
255 | void dso__sort_by_name(struct dso *dso, enum map_type type); | 296 | void dso__sort_by_name(struct dso *dso, enum map_type type); |
256 | 297 | ||
298 | void dsos__add(struct list_head *head, struct dso *dso); | ||
299 | struct dso *dsos__find(struct list_head *head, const char *name); | ||
257 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | 300 | struct dso *__dsos__findnew(struct list_head *head, const char *name); |
258 | 301 | ||
259 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); | 302 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); |
@@ -283,6 +326,7 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); | |||
283 | char dso__symtab_origin(const struct dso *dso); | 326 | char dso__symtab_origin(const struct dso *dso); |
284 | void dso__set_long_name(struct dso *dso, char *name); | 327 | void dso__set_long_name(struct dso *dso, char *name); |
285 | void dso__set_build_id(struct dso *dso, void *build_id); | 328 | void dso__set_build_id(struct dso *dso, void *build_id); |
329 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
286 | void dso__read_running_kernel_build_id(struct dso *dso, | 330 | void dso__read_running_kernel_build_id(struct dso *dso, |
287 | struct machine *machine); | 331 | struct machine *machine); |
288 | struct map *dso__new_map(const char *name); | 332 | struct map *dso__new_map(const char *name); |
@@ -297,7 +341,9 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | |||
297 | int build_id__sprintf(const u8 *build_id, int len, char *bf); | 341 | int build_id__sprintf(const u8 *build_id, int len, char *bf); |
298 | int kallsyms__parse(const char *filename, void *arg, | 342 | int kallsyms__parse(const char *filename, void *arg, |
299 | int (*process_symbol)(void *arg, const char *name, | 343 | int (*process_symbol)(void *arg, const char *name, |
300 | char type, u64 start, u64 end)); | 344 | char type, u64 start)); |
345 | int filename__read_debuglink(const char *filename, char *debuglink, | ||
346 | size_t size); | ||
301 | 347 | ||
302 | void machine__destroy_kernel_maps(struct machine *machine); | 348 | void machine__destroy_kernel_maps(struct machine *machine); |
303 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); | 349 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); |
@@ -309,6 +355,8 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines); | |||
309 | 355 | ||
310 | int symbol__init(void); | 356 | int symbol__init(void); |
311 | void symbol__exit(void); | 357 | void symbol__exit(void); |
358 | void symbol__elf_init(void); | ||
359 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | ||
312 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | 360 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, |
313 | const struct addr_location *al, FILE *fp); | 361 | const struct addr_location *al, FILE *fp); |
314 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | 362 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); |
@@ -326,4 +374,15 @@ ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | |||
326 | struct machine *machine, u64 addr, | 374 | struct machine *machine, u64 addr, |
327 | u8 *data, ssize_t size); | 375 | u8 *data, ssize_t size); |
328 | int dso__test_data(void); | 376 | int dso__test_data(void); |
377 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | ||
378 | struct symsrc *runtime_ss, symbol_filter_t filter, | ||
379 | int kmodule); | ||
380 | int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, | ||
381 | struct map *map, symbol_filter_t filter); | ||
382 | |||
383 | void symbols__insert(struct rb_root *symbols, struct symbol *sym); | ||
384 | void symbols__fixup_duplicate(struct rb_root *symbols); | ||
385 | void symbols__fixup_end(struct rb_root *symbols); | ||
386 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); | ||
387 | |||
329 | #endif /* __PERF_SYMBOL */ | 388 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 051eaa68095e..065528b7563e 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c | |||
@@ -117,8 +117,8 @@ int perf_target__strerror(struct perf_target *target, int errnum, | |||
117 | 117 | ||
118 | if (err != buf) { | 118 | if (err != buf) { |
119 | size_t len = strlen(err); | 119 | size_t len = strlen(err); |
120 | char *c = mempcpy(buf, err, min(buflen - 1, len)); | 120 | memcpy(buf, err, min(buflen - 1, len)); |
121 | *c = '\0'; | 121 | *(buf + min(buflen - 1, len)) = '\0'; |
122 | } | 122 | } |
123 | 123 | ||
124 | return 0; | 124 | return 0; |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index fb4b7ea6752f..8b3e5939afb6 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -39,7 +39,6 @@ int thread__set_comm(struct thread *self, const char *comm) | |||
39 | err = self->comm == NULL ? -ENOMEM : 0; | 39 | err = self->comm == NULL ? -ENOMEM : 0; |
40 | if (!err) { | 40 | if (!err) { |
41 | self->comm_set = true; | 41 | self->comm_set = true; |
42 | map_groups__flush(&self->mg); | ||
43 | } | 42 | } |
44 | return err; | 43 | return err; |
45 | } | 44 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 70c2c13ff679..f66610b7bacf 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -16,6 +16,8 @@ struct thread { | |||
16 | bool comm_set; | 16 | bool comm_set; |
17 | char *comm; | 17 | char *comm; |
18 | int comm_len; | 18 | int comm_len; |
19 | |||
20 | void *priv; | ||
19 | }; | 21 | }; |
20 | 22 | ||
21 | struct machine; | 23 | struct machine; |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 7eeebcee291c..884dde9b9bc1 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -58,8 +58,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
58 | } | 58 | } |
59 | 59 | ||
60 | if (top->evlist->nr_entries == 1) { | 60 | if (top->evlist->nr_entries == 1) { |
61 | struct perf_evsel *first; | 61 | struct perf_evsel *first = perf_evlist__first(top->evlist); |
62 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | ||
63 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | 62 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", |
64 | (uint64_t)first->attr.sample_period, | 63 | (uint64_t)first->attr.sample_period, |
65 | top->freq ? "Hz" : ""); | 64 | top->freq ? "Hz" : ""); |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 33347ca89ee4..86ff1b15059b 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "types.h" | 5 | #include "types.h" |
6 | #include <stddef.h> | 6 | #include <stddef.h> |
7 | #include <stdbool.h> | 7 | #include <stdbool.h> |
8 | #include <termios.h> | ||
8 | 9 | ||
9 | struct perf_evlist; | 10 | struct perf_evlist; |
10 | struct perf_evsel; | 11 | struct perf_evsel; |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 0715c843c2e7..3aabcd687cd5 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -162,25 +162,16 @@ int trace_parse_common_pid(struct pevent *pevent, void *data) | |||
162 | return pevent_data_pid(pevent, &record); | 162 | return pevent_data_pid(pevent, &record); |
163 | } | 163 | } |
164 | 164 | ||
165 | unsigned long long read_size(struct pevent *pevent, void *ptr, int size) | 165 | unsigned long long read_size(struct event_format *event, void *ptr, int size) |
166 | { | 166 | { |
167 | return pevent_read_number(pevent, ptr, size); | 167 | return pevent_read_number(event->pevent, ptr, size); |
168 | } | 168 | } |
169 | 169 | ||
170 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size) | 170 | void event_format__print(struct event_format *event, |
171 | int cpu, void *data, int size) | ||
171 | { | 172 | { |
172 | struct event_format *event; | ||
173 | struct pevent_record record; | 173 | struct pevent_record record; |
174 | struct trace_seq s; | 174 | struct trace_seq s; |
175 | int type; | ||
176 | |||
177 | type = trace_parse_common_type(pevent, data); | ||
178 | |||
179 | event = pevent_find_event(pevent, type); | ||
180 | if (!event) { | ||
181 | warning("ug! no event found for type %d", type); | ||
182 | return; | ||
183 | } | ||
184 | 175 | ||
185 | memset(&record, 0, sizeof(record)); | 176 | memset(&record, 0, sizeof(record)); |
186 | record.cpu = cpu; | 177 | record.cpu = cpu; |
@@ -192,6 +183,19 @@ void print_trace_event(struct pevent *pevent, int cpu, void *data, int size) | |||
192 | trace_seq_do_printf(&s); | 183 | trace_seq_do_printf(&s); |
193 | } | 184 | } |
194 | 185 | ||
186 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size) | ||
187 | { | ||
188 | int type = trace_parse_common_type(pevent, data); | ||
189 | struct event_format *event = pevent_find_event(pevent, type); | ||
190 | |||
191 | if (!event) { | ||
192 | warning("ug! no event found for type %d", type); | ||
193 | return; | ||
194 | } | ||
195 | |||
196 | event_format__print(event, cpu, data, size); | ||
197 | } | ||
198 | |||
195 | void print_event(struct pevent *pevent, int cpu, void *data, int size, | 199 | void print_event(struct pevent *pevent, int cpu, void *data, int size, |
196 | unsigned long long nsecs, char *comm) | 200 | unsigned long long nsecs, char *comm) |
197 | { | 201 | { |
@@ -217,7 +221,7 @@ void print_event(struct pevent *pevent, int cpu, void *data, int size, | |||
217 | } | 221 | } |
218 | 222 | ||
219 | void parse_proc_kallsyms(struct pevent *pevent, | 223 | void parse_proc_kallsyms(struct pevent *pevent, |
220 | char *file, unsigned int size __unused) | 224 | char *file, unsigned int size __maybe_unused) |
221 | { | 225 | { |
222 | unsigned long long addr; | 226 | unsigned long long addr; |
223 | char *func; | 227 | char *func; |
@@ -225,31 +229,29 @@ void parse_proc_kallsyms(struct pevent *pevent, | |||
225 | char *next = NULL; | 229 | char *next = NULL; |
226 | char *addr_str; | 230 | char *addr_str; |
227 | char *mod; | 231 | char *mod; |
228 | char ch; | 232 | char *fmt; |
229 | 233 | ||
230 | line = strtok_r(file, "\n", &next); | 234 | line = strtok_r(file, "\n", &next); |
231 | while (line) { | 235 | while (line) { |
232 | mod = NULL; | 236 | mod = NULL; |
233 | sscanf(line, "%as %c %as\t[%as", | 237 | addr_str = strtok_r(line, " ", &fmt); |
234 | (float *)(void *)&addr_str, /* workaround gcc warning */ | ||
235 | &ch, (float *)(void *)&func, (float *)(void *)&mod); | ||
236 | addr = strtoull(addr_str, NULL, 16); | 238 | addr = strtoull(addr_str, NULL, 16); |
237 | free(addr_str); | 239 | /* skip character */ |
238 | 240 | strtok_r(NULL, " ", &fmt); | |
239 | /* truncate the extra ']' */ | 241 | func = strtok_r(NULL, "\t", &fmt); |
242 | mod = strtok_r(NULL, "]", &fmt); | ||
243 | /* truncate the extra '[' */ | ||
240 | if (mod) | 244 | if (mod) |
241 | mod[strlen(mod) - 1] = 0; | 245 | mod = mod + 1; |
242 | 246 | ||
243 | pevent_register_function(pevent, func, addr, mod); | 247 | pevent_register_function(pevent, func, addr, mod); |
244 | free(func); | ||
245 | free(mod); | ||
246 | 248 | ||
247 | line = strtok_r(NULL, "\n", &next); | 249 | line = strtok_r(NULL, "\n", &next); |
248 | } | 250 | } |
249 | } | 251 | } |
250 | 252 | ||
251 | void parse_ftrace_printk(struct pevent *pevent, | 253 | void parse_ftrace_printk(struct pevent *pevent, |
252 | char *file, unsigned int size __unused) | 254 | char *file, unsigned int size __maybe_unused) |
253 | { | 255 | { |
254 | unsigned long long addr; | 256 | unsigned long long addr; |
255 | char *printk; | 257 | char *printk; |
@@ -289,7 +291,7 @@ struct event_format *trace_find_next_event(struct pevent *pevent, | |||
289 | { | 291 | { |
290 | static int idx; | 292 | static int idx; |
291 | 293 | ||
292 | if (!pevent->events) | 294 | if (!pevent || !pevent->events) |
293 | return NULL; | 295 | return NULL; |
294 | 296 | ||
295 | if (!event) { | 297 | if (!event) { |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 474aa7a7df43..8715a1006d00 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -35,12 +35,11 @@ static int stop_script_unsupported(void) | |||
35 | return 0; | 35 | return 0; |
36 | } | 36 | } |
37 | 37 | ||
38 | static void process_event_unsupported(union perf_event *event __unused, | 38 | static void process_event_unsupported(union perf_event *event __maybe_unused, |
39 | struct pevent *pevent __unused, | 39 | struct perf_sample *sample __maybe_unused, |
40 | struct perf_sample *sample __unused, | 40 | struct perf_evsel *evsel __maybe_unused, |
41 | struct perf_evsel *evsel __unused, | 41 | struct machine *machine __maybe_unused, |
42 | struct machine *machine __unused, | 42 | struct addr_location *al __maybe_unused) |
43 | struct thread *thread __unused) | ||
44 | { | 43 | { |
45 | } | 44 | } |
46 | 45 | ||
@@ -53,17 +52,19 @@ static void print_python_unsupported_msg(void) | |||
53 | "\n etc.\n"); | 52 | "\n etc.\n"); |
54 | } | 53 | } |
55 | 54 | ||
56 | static int python_start_script_unsupported(const char *script __unused, | 55 | static int python_start_script_unsupported(const char *script __maybe_unused, |
57 | int argc __unused, | 56 | int argc __maybe_unused, |
58 | const char **argv __unused) | 57 | const char **argv __maybe_unused) |
59 | { | 58 | { |
60 | print_python_unsupported_msg(); | 59 | print_python_unsupported_msg(); |
61 | 60 | ||
62 | return -1; | 61 | return -1; |
63 | } | 62 | } |
64 | 63 | ||
65 | static int python_generate_script_unsupported(struct pevent *pevent __unused, | 64 | static int python_generate_script_unsupported(struct pevent *pevent |
66 | const char *outfile __unused) | 65 | __maybe_unused, |
66 | const char *outfile | ||
67 | __maybe_unused) | ||
67 | { | 68 | { |
68 | print_python_unsupported_msg(); | 69 | print_python_unsupported_msg(); |
69 | 70 | ||
@@ -115,17 +116,18 @@ static void print_perl_unsupported_msg(void) | |||
115 | "\n etc.\n"); | 116 | "\n etc.\n"); |
116 | } | 117 | } |
117 | 118 | ||
118 | static int perl_start_script_unsupported(const char *script __unused, | 119 | static int perl_start_script_unsupported(const char *script __maybe_unused, |
119 | int argc __unused, | 120 | int argc __maybe_unused, |
120 | const char **argv __unused) | 121 | const char **argv __maybe_unused) |
121 | { | 122 | { |
122 | print_perl_unsupported_msg(); | 123 | print_perl_unsupported_msg(); |
123 | 124 | ||
124 | return -1; | 125 | return -1; |
125 | } | 126 | } |
126 | 127 | ||
127 | static int perl_generate_script_unsupported(struct pevent *pevent __unused, | 128 | static int perl_generate_script_unsupported(struct pevent *pevent |
128 | const char *outfile __unused) | 129 | __maybe_unused, |
130 | const char *outfile __maybe_unused) | ||
129 | { | 131 | { |
130 | print_perl_unsupported_msg(); | 132 | print_perl_unsupported_msg(); |
131 | 133 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 8fef1d6687b7..a55fd37ffea1 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -9,7 +9,6 @@ struct machine; | |||
9 | struct perf_sample; | 9 | struct perf_sample; |
10 | union perf_event; | 10 | union perf_event; |
11 | struct perf_tool; | 11 | struct perf_tool; |
12 | struct thread; | ||
13 | 12 | ||
14 | extern int header_page_size_size; | 13 | extern int header_page_size_size; |
15 | extern int header_page_ts_size; | 14 | extern int header_page_ts_size; |
@@ -32,6 +31,8 @@ int bigendian(void); | |||
32 | 31 | ||
33 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); | 32 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); |
34 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size); | 33 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size); |
34 | void event_format__print(struct event_format *event, | ||
35 | int cpu, void *data, int size); | ||
35 | 36 | ||
36 | void print_event(struct pevent *pevent, int cpu, void *data, int size, | 37 | void print_event(struct pevent *pevent, int cpu, void *data, int size, |
37 | unsigned long long nsecs, char *comm); | 38 | unsigned long long nsecs, char *comm); |
@@ -56,7 +57,7 @@ int trace_parse_common_pid(struct pevent *pevent, void *data); | |||
56 | 57 | ||
57 | struct event_format *trace_find_next_event(struct pevent *pevent, | 58 | struct event_format *trace_find_next_event(struct pevent *pevent, |
58 | struct event_format *event); | 59 | struct event_format *event); |
59 | unsigned long long read_size(struct pevent *pevent, void *ptr, int size); | 60 | unsigned long long read_size(struct event_format *event, void *ptr, int size); |
60 | unsigned long long eval_flag(const char *flag); | 61 | unsigned long long eval_flag(const char *flag); |
61 | 62 | ||
62 | struct pevent_record *trace_read_data(struct pevent *pevent, int cpu); | 63 | struct pevent_record *trace_read_data(struct pevent *pevent, int cpu); |
@@ -74,16 +75,19 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
74 | void tracing_data_put(struct tracing_data *tdata); | 75 | void tracing_data_put(struct tracing_data *tdata); |
75 | 76 | ||
76 | 77 | ||
78 | struct addr_location; | ||
79 | |||
80 | struct perf_session; | ||
81 | |||
77 | struct scripting_ops { | 82 | struct scripting_ops { |
78 | const char *name; | 83 | const char *name; |
79 | int (*start_script) (const char *script, int argc, const char **argv); | 84 | int (*start_script) (const char *script, int argc, const char **argv); |
80 | int (*stop_script) (void); | 85 | int (*stop_script) (void); |
81 | void (*process_event) (union perf_event *event, | 86 | void (*process_event) (union perf_event *event, |
82 | struct pevent *pevent, | ||
83 | struct perf_sample *sample, | 87 | struct perf_sample *sample, |
84 | struct perf_evsel *evsel, | 88 | struct perf_evsel *evsel, |
85 | struct machine *machine, | 89 | struct machine *machine, |
86 | struct thread *thread); | 90 | struct addr_location *al); |
87 | int (*generate_script) (struct pevent *pevent, const char *outfile); | 91 | int (*generate_script) (struct pevent *pevent, const char *outfile); |
88 | }; | 92 | }; |
89 | 93 | ||
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c new file mode 100644 index 000000000000..958723ba3d2e --- /dev/null +++ b/tools/perf/util/unwind.c | |||
@@ -0,0 +1,571 @@ | |||
1 | /* | ||
2 | * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. | ||
3 | * | ||
4 | * Lots of this code have been borrowed or heavily inspired from parts of | ||
5 | * the libunwind 0.99 code which are (amongst other contributors I may have | ||
6 | * forgotten): | ||
7 | * | ||
8 | * Copyright (C) 2002-2007 Hewlett-Packard Co | ||
9 | * Contributed by David Mosberger-Tang <davidm@hpl.hp.com> | ||
10 | * | ||
11 | * And the bugs have been added by: | ||
12 | * | ||
13 | * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com> | ||
14 | * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com> | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <elf.h> | ||
19 | #include <gelf.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <string.h> | ||
22 | #include <unistd.h> | ||
23 | #include <sys/mman.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <libunwind.h> | ||
26 | #include <libunwind-ptrace.h> | ||
27 | #include "thread.h" | ||
28 | #include "session.h" | ||
29 | #include "perf_regs.h" | ||
30 | #include "unwind.h" | ||
31 | #include "util.h" | ||
32 | |||
33 | extern int | ||
34 | UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, | ||
35 | unw_word_t ip, | ||
36 | unw_dyn_info_t *di, | ||
37 | unw_proc_info_t *pi, | ||
38 | int need_unwind_info, void *arg); | ||
39 | |||
40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) | ||
41 | |||
42 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ | ||
43 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ | ||
44 | |||
45 | /* Pointer-encoding formats: */ | ||
46 | #define DW_EH_PE_omit 0xff | ||
47 | #define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ | ||
48 | #define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ | ||
49 | #define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ | ||
50 | #define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ | ||
51 | #define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ | ||
52 | |||
53 | /* Pointer-encoding application: */ | ||
54 | #define DW_EH_PE_absptr 0x00 /* absolute value */ | ||
55 | #define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ | ||
56 | |||
57 | /* | ||
58 | * The following are not documented by LSB v1.3, yet they are used by | ||
59 | * GCC, presumably they aren't documented by LSB since they aren't | ||
60 | * used on Linux: | ||
61 | */ | ||
62 | #define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ | ||
63 | #define DW_EH_PE_aligned 0x50 /* aligned pointer */ | ||
64 | |||
65 | /* Flags intentionaly not handled, since they're not needed: | ||
66 | * #define DW_EH_PE_indirect 0x80 | ||
67 | * #define DW_EH_PE_uleb128 0x01 | ||
68 | * #define DW_EH_PE_udata2 0x02 | ||
69 | * #define DW_EH_PE_sleb128 0x09 | ||
70 | * #define DW_EH_PE_sdata2 0x0a | ||
71 | * #define DW_EH_PE_textrel 0x20 | ||
72 | * #define DW_EH_PE_datarel 0x30 | ||
73 | */ | ||
74 | |||
75 | struct unwind_info { | ||
76 | struct perf_sample *sample; | ||
77 | struct machine *machine; | ||
78 | struct thread *thread; | ||
79 | u64 sample_uregs; | ||
80 | }; | ||
81 | |||
82 | #define dw_read(ptr, type, end) ({ \ | ||
83 | type *__p = (type *) ptr; \ | ||
84 | type __v; \ | ||
85 | if ((__p + 1) > (type *) end) \ | ||
86 | return -EINVAL; \ | ||
87 | __v = *__p++; \ | ||
88 | ptr = (typeof(ptr)) __p; \ | ||
89 | __v; \ | ||
90 | }) | ||
91 | |||
92 | static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, | ||
93 | u8 encoding) | ||
94 | { | ||
95 | u8 *cur = *p; | ||
96 | *val = 0; | ||
97 | |||
98 | switch (encoding) { | ||
99 | case DW_EH_PE_omit: | ||
100 | *val = 0; | ||
101 | goto out; | ||
102 | case DW_EH_PE_ptr: | ||
103 | *val = dw_read(cur, unsigned long, end); | ||
104 | goto out; | ||
105 | default: | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | switch (encoding & DW_EH_PE_APPL_MASK) { | ||
110 | case DW_EH_PE_absptr: | ||
111 | break; | ||
112 | case DW_EH_PE_pcrel: | ||
113 | *val = (unsigned long) cur; | ||
114 | break; | ||
115 | default: | ||
116 | return -EINVAL; | ||
117 | } | ||
118 | |||
119 | if ((encoding & 0x07) == 0x00) | ||
120 | encoding |= DW_EH_PE_udata4; | ||
121 | |||
122 | switch (encoding & DW_EH_PE_FORMAT_MASK) { | ||
123 | case DW_EH_PE_sdata4: | ||
124 | *val += dw_read(cur, s32, end); | ||
125 | break; | ||
126 | case DW_EH_PE_udata4: | ||
127 | *val += dw_read(cur, u32, end); | ||
128 | break; | ||
129 | case DW_EH_PE_sdata8: | ||
130 | *val += dw_read(cur, s64, end); | ||
131 | break; | ||
132 | case DW_EH_PE_udata8: | ||
133 | *val += dw_read(cur, u64, end); | ||
134 | break; | ||
135 | default: | ||
136 | return -EINVAL; | ||
137 | } | ||
138 | |||
139 | out: | ||
140 | *p = cur; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | #define dw_read_encoded_value(ptr, end, enc) ({ \ | ||
145 | u64 __v; \ | ||
146 | if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ | ||
147 | return -EINVAL; \ | ||
148 | } \ | ||
149 | __v; \ | ||
150 | }) | ||
151 | |||
152 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
153 | GElf_Shdr *shp, const char *name) | ||
154 | { | ||
155 | Elf_Scn *sec = NULL; | ||
156 | |||
157 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
158 | char *str; | ||
159 | |||
160 | gelf_getshdr(sec, shp); | ||
161 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
162 | if (!strcmp(name, str)) | ||
163 | break; | ||
164 | } | ||
165 | |||
166 | return sec; | ||
167 | } | ||
168 | |||
169 | static u64 elf_section_offset(int fd, const char *name) | ||
170 | { | ||
171 | Elf *elf; | ||
172 | GElf_Ehdr ehdr; | ||
173 | GElf_Shdr shdr; | ||
174 | u64 offset = 0; | ||
175 | |||
176 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
177 | if (elf == NULL) | ||
178 | return 0; | ||
179 | |||
180 | do { | ||
181 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
182 | break; | ||
183 | |||
184 | if (!elf_section_by_name(elf, &ehdr, &shdr, name)) | ||
185 | break; | ||
186 | |||
187 | offset = shdr.sh_offset; | ||
188 | } while (0); | ||
189 | |||
190 | elf_end(elf); | ||
191 | return offset; | ||
192 | } | ||
193 | |||
194 | struct table_entry { | ||
195 | u32 start_ip_offset; | ||
196 | u32 fde_offset; | ||
197 | }; | ||
198 | |||
199 | struct eh_frame_hdr { | ||
200 | unsigned char version; | ||
201 | unsigned char eh_frame_ptr_enc; | ||
202 | unsigned char fde_count_enc; | ||
203 | unsigned char table_enc; | ||
204 | |||
205 | /* | ||
206 | * The rest of the header is variable-length and consists of the | ||
207 | * following members: | ||
208 | * | ||
209 | * encoded_t eh_frame_ptr; | ||
210 | * encoded_t fde_count; | ||
211 | */ | ||
212 | |||
213 | /* A single encoded pointer should not be more than 8 bytes. */ | ||
214 | u64 enc[2]; | ||
215 | |||
216 | /* | ||
217 | * struct { | ||
218 | * encoded_t start_ip; | ||
219 | * encoded_t fde_addr; | ||
220 | * } binary_search_table[fde_count]; | ||
221 | */ | ||
222 | char data[0]; | ||
223 | } __packed; | ||
224 | |||
225 | static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, | ||
226 | u64 offset, u64 *table_data, u64 *segbase, | ||
227 | u64 *fde_count) | ||
228 | { | ||
229 | struct eh_frame_hdr hdr; | ||
230 | u8 *enc = (u8 *) &hdr.enc; | ||
231 | u8 *end = (u8 *) &hdr.data; | ||
232 | ssize_t r; | ||
233 | |||
234 | r = dso__data_read_offset(dso, machine, offset, | ||
235 | (u8 *) &hdr, sizeof(hdr)); | ||
236 | if (r != sizeof(hdr)) | ||
237 | return -EINVAL; | ||
238 | |||
239 | /* We dont need eh_frame_ptr, just skip it. */ | ||
240 | dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); | ||
241 | |||
242 | *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); | ||
243 | *segbase = offset; | ||
244 | *table_data = (enc - (u8 *) &hdr) + offset; | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static int read_unwind_spec(struct dso *dso, struct machine *machine, | ||
249 | u64 *table_data, u64 *segbase, u64 *fde_count) | ||
250 | { | ||
251 | int ret = -EINVAL, fd; | ||
252 | u64 offset; | ||
253 | |||
254 | fd = dso__data_fd(dso, machine); | ||
255 | if (fd < 0) | ||
256 | return -EINVAL; | ||
257 | |||
258 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | ||
259 | close(fd); | ||
260 | |||
261 | if (offset) | ||
262 | ret = unwind_spec_ehframe(dso, machine, offset, | ||
263 | table_data, segbase, | ||
264 | fde_count); | ||
265 | |||
266 | /* TODO .debug_frame check if eh_frame_hdr fails */ | ||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | ||
271 | { | ||
272 | struct addr_location al; | ||
273 | |||
274 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | ||
275 | MAP__FUNCTION, ip, &al); | ||
276 | return al.map; | ||
277 | } | ||
278 | |||
279 | static int | ||
280 | find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | ||
281 | int need_unwind_info, void *arg) | ||
282 | { | ||
283 | struct unwind_info *ui = arg; | ||
284 | struct map *map; | ||
285 | unw_dyn_info_t di; | ||
286 | u64 table_data, segbase, fde_count; | ||
287 | |||
288 | map = find_map(ip, ui); | ||
289 | if (!map || !map->dso) | ||
290 | return -EINVAL; | ||
291 | |||
292 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); | ||
293 | |||
294 | if (read_unwind_spec(map->dso, ui->machine, | ||
295 | &table_data, &segbase, &fde_count)) | ||
296 | return -EINVAL; | ||
297 | |||
298 | memset(&di, 0, sizeof(di)); | ||
299 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
300 | di.start_ip = map->start; | ||
301 | di.end_ip = map->end; | ||
302 | di.u.rti.segbase = map->start + segbase; | ||
303 | di.u.rti.table_data = map->start + table_data; | ||
304 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
305 | / sizeof(unw_word_t); | ||
306 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
307 | need_unwind_info, arg); | ||
308 | } | ||
309 | |||
310 | static int access_fpreg(unw_addr_space_t __maybe_unused as, | ||
311 | unw_regnum_t __maybe_unused num, | ||
312 | unw_fpreg_t __maybe_unused *val, | ||
313 | int __maybe_unused __write, | ||
314 | void __maybe_unused *arg) | ||
315 | { | ||
316 | pr_err("unwind: access_fpreg unsupported\n"); | ||
317 | return -UNW_EINVAL; | ||
318 | } | ||
319 | |||
320 | static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, | ||
321 | unw_word_t __maybe_unused *dil_addr, | ||
322 | void __maybe_unused *arg) | ||
323 | { | ||
324 | return -UNW_ENOINFO; | ||
325 | } | ||
326 | |||
327 | static int resume(unw_addr_space_t __maybe_unused as, | ||
328 | unw_cursor_t __maybe_unused *cu, | ||
329 | void __maybe_unused *arg) | ||
330 | { | ||
331 | pr_err("unwind: resume unsupported\n"); | ||
332 | return -UNW_EINVAL; | ||
333 | } | ||
334 | |||
335 | static int | ||
336 | get_proc_name(unw_addr_space_t __maybe_unused as, | ||
337 | unw_word_t __maybe_unused addr, | ||
338 | char __maybe_unused *bufp, size_t __maybe_unused buf_len, | ||
339 | unw_word_t __maybe_unused *offp, void __maybe_unused *arg) | ||
340 | { | ||
341 | pr_err("unwind: get_proc_name unsupported\n"); | ||
342 | return -UNW_EINVAL; | ||
343 | } | ||
344 | |||
345 | static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, | ||
346 | unw_word_t *data) | ||
347 | { | ||
348 | struct addr_location al; | ||
349 | ssize_t size; | ||
350 | |||
351 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | ||
352 | MAP__FUNCTION, addr, &al); | ||
353 | if (!al.map) { | ||
354 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | if (!al.map->dso) | ||
359 | return -1; | ||
360 | |||
361 | size = dso__data_read_addr(al.map->dso, al.map, ui->machine, | ||
362 | addr, (u8 *) data, sizeof(*data)); | ||
363 | |||
364 | return !(size == sizeof(*data)); | ||
365 | } | ||
366 | |||
367 | static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id, | ||
368 | u64 sample_regs) | ||
369 | { | ||
370 | int i, idx = 0; | ||
371 | |||
372 | if (!(sample_regs & (1 << id))) | ||
373 | return -EINVAL; | ||
374 | |||
375 | for (i = 0; i < id; i++) { | ||
376 | if (sample_regs & (1 << i)) | ||
377 | idx++; | ||
378 | } | ||
379 | |||
380 | *valp = regs->regs[idx]; | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | static int access_mem(unw_addr_space_t __maybe_unused as, | ||
385 | unw_word_t addr, unw_word_t *valp, | ||
386 | int __write, void *arg) | ||
387 | { | ||
388 | struct unwind_info *ui = arg; | ||
389 | struct stack_dump *stack = &ui->sample->user_stack; | ||
390 | unw_word_t start, end; | ||
391 | int offset; | ||
392 | int ret; | ||
393 | |||
394 | /* Don't support write, probably not needed. */ | ||
395 | if (__write || !stack || !ui->sample->user_regs.regs) { | ||
396 | *valp = 0; | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP, | ||
401 | ui->sample_uregs); | ||
402 | if (ret) | ||
403 | return ret; | ||
404 | |||
405 | end = start + stack->size; | ||
406 | |||
407 | /* Check overflow. */ | ||
408 | if (addr + sizeof(unw_word_t) < addr) | ||
409 | return -EINVAL; | ||
410 | |||
411 | if (addr < start || addr + sizeof(unw_word_t) >= end) { | ||
412 | ret = access_dso_mem(ui, addr, valp); | ||
413 | if (ret) { | ||
414 | pr_debug("unwind: access_mem %p not inside range %p-%p\n", | ||
415 | (void *)addr, (void *)start, (void *)end); | ||
416 | *valp = 0; | ||
417 | return ret; | ||
418 | } | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | offset = addr - start; | ||
423 | *valp = *(unw_word_t *)&stack->data[offset]; | ||
424 | pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n", | ||
425 | (void *)addr, (unsigned long)*valp, offset); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static int access_reg(unw_addr_space_t __maybe_unused as, | ||
430 | unw_regnum_t regnum, unw_word_t *valp, | ||
431 | int __write, void *arg) | ||
432 | { | ||
433 | struct unwind_info *ui = arg; | ||
434 | int id, ret; | ||
435 | |||
436 | /* Don't support write, I suspect we don't need it. */ | ||
437 | if (__write) { | ||
438 | pr_err("unwind: access_reg w %d\n", regnum); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | if (!ui->sample->user_regs.regs) { | ||
443 | *valp = 0; | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | id = unwind__arch_reg_id(regnum); | ||
448 | if (id < 0) | ||
449 | return -EINVAL; | ||
450 | |||
451 | ret = reg_value(valp, &ui->sample->user_regs, id, ui->sample_uregs); | ||
452 | if (ret) { | ||
453 | pr_err("unwind: can't read reg %d\n", regnum); | ||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static void put_unwind_info(unw_addr_space_t __maybe_unused as, | ||
462 | unw_proc_info_t *pi __maybe_unused, | ||
463 | void *arg __maybe_unused) | ||
464 | { | ||
465 | pr_debug("unwind: put_unwind_info called\n"); | ||
466 | } | ||
467 | |||
468 | static int entry(u64 ip, struct thread *thread, struct machine *machine, | ||
469 | unwind_entry_cb_t cb, void *arg) | ||
470 | { | ||
471 | struct unwind_entry e; | ||
472 | struct addr_location al; | ||
473 | |||
474 | thread__find_addr_location(thread, machine, | ||
475 | PERF_RECORD_MISC_USER, | ||
476 | MAP__FUNCTION, ip, &al, NULL); | ||
477 | |||
478 | e.ip = ip; | ||
479 | e.map = al.map; | ||
480 | e.sym = al.sym; | ||
481 | |||
482 | pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", | ||
483 | al.sym ? al.sym->name : "''", | ||
484 | ip, | ||
485 | al.map ? al.map->map_ip(al.map, ip) : (u64) 0); | ||
486 | |||
487 | return cb(&e, arg); | ||
488 | } | ||
489 | |||
490 | static void display_error(int err) | ||
491 | { | ||
492 | switch (err) { | ||
493 | case UNW_EINVAL: | ||
494 | pr_err("unwind: Only supports local.\n"); | ||
495 | break; | ||
496 | case UNW_EUNSPEC: | ||
497 | pr_err("unwind: Unspecified error.\n"); | ||
498 | break; | ||
499 | case UNW_EBADREG: | ||
500 | pr_err("unwind: Register unavailable.\n"); | ||
501 | break; | ||
502 | default: | ||
503 | break; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | static unw_accessors_t accessors = { | ||
508 | .find_proc_info = find_proc_info, | ||
509 | .put_unwind_info = put_unwind_info, | ||
510 | .get_dyn_info_list_addr = get_dyn_info_list_addr, | ||
511 | .access_mem = access_mem, | ||
512 | .access_reg = access_reg, | ||
513 | .access_fpreg = access_fpreg, | ||
514 | .resume = resume, | ||
515 | .get_proc_name = get_proc_name, | ||
516 | }; | ||
517 | |||
518 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | ||
519 | void *arg) | ||
520 | { | ||
521 | unw_addr_space_t addr_space; | ||
522 | unw_cursor_t c; | ||
523 | int ret; | ||
524 | |||
525 | addr_space = unw_create_addr_space(&accessors, 0); | ||
526 | if (!addr_space) { | ||
527 | pr_err("unwind: Can't create unwind address space.\n"); | ||
528 | return -ENOMEM; | ||
529 | } | ||
530 | |||
531 | ret = unw_init_remote(&c, addr_space, ui); | ||
532 | if (ret) | ||
533 | display_error(ret); | ||
534 | |||
535 | while (!ret && (unw_step(&c) > 0)) { | ||
536 | unw_word_t ip; | ||
537 | |||
538 | unw_get_reg(&c, UNW_REG_IP, &ip); | ||
539 | ret = entry(ip, ui->thread, ui->machine, cb, arg); | ||
540 | } | ||
541 | |||
542 | unw_destroy_addr_space(addr_space); | ||
543 | return ret; | ||
544 | } | ||
545 | |||
546 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | ||
547 | struct machine *machine, struct thread *thread, | ||
548 | u64 sample_uregs, struct perf_sample *data) | ||
549 | { | ||
550 | unw_word_t ip; | ||
551 | struct unwind_info ui = { | ||
552 | .sample = data, | ||
553 | .sample_uregs = sample_uregs, | ||
554 | .thread = thread, | ||
555 | .machine = machine, | ||
556 | }; | ||
557 | int ret; | ||
558 | |||
559 | if (!data->user_regs.regs) | ||
560 | return -EINVAL; | ||
561 | |||
562 | ret = reg_value(&ip, &data->user_regs, PERF_REG_IP, sample_uregs); | ||
563 | if (ret) | ||
564 | return ret; | ||
565 | |||
566 | ret = entry(ip, thread, machine, cb, arg); | ||
567 | if (ret) | ||
568 | return -ENOMEM; | ||
569 | |||
570 | return get_entries(&ui, cb, arg); | ||
571 | } | ||
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h new file mode 100644 index 000000000000..cb6bc503a792 --- /dev/null +++ b/tools/perf/util/unwind.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef __UNWIND_H | ||
2 | #define __UNWIND_H | ||
3 | |||
4 | #include "types.h" | ||
5 | #include "event.h" | ||
6 | #include "symbol.h" | ||
7 | |||
8 | struct unwind_entry { | ||
9 | struct map *map; | ||
10 | struct symbol *sym; | ||
11 | u64 ip; | ||
12 | }; | ||
13 | |||
14 | typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); | ||
15 | |||
16 | #ifdef LIBUNWIND_SUPPORT | ||
17 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | ||
18 | struct machine *machine, | ||
19 | struct thread *thread, | ||
20 | u64 sample_uregs, | ||
21 | struct perf_sample *data); | ||
22 | int unwind__arch_reg_id(int regnum); | ||
23 | #else | ||
24 | static inline int | ||
25 | unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | ||
26 | void *arg __maybe_unused, | ||
27 | struct machine *machine __maybe_unused, | ||
28 | struct thread *thread __maybe_unused, | ||
29 | u64 sample_uregs __maybe_unused, | ||
30 | struct perf_sample *data __maybe_unused) | ||
31 | { | ||
32 | return 0; | ||
33 | } | ||
34 | #endif /* LIBUNWIND_SUPPORT */ | ||
35 | #endif /* __UNWIND_H */ | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index d03599fbe78b..99664598bc1a 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -1,6 +1,11 @@ | |||
1 | #include "../perf.h" | 1 | #include "../perf.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include <sys/mman.h> | 3 | #include <sys/mman.h> |
4 | #ifdef BACKTRACE_SUPPORT | ||
5 | #include <execinfo.h> | ||
6 | #endif | ||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
4 | 9 | ||
5 | /* | 10 | /* |
6 | * XXX We need to find a better place for these things... | 11 | * XXX We need to find a better place for these things... |
@@ -158,3 +163,23 @@ size_t hex_width(u64 v) | |||
158 | 163 | ||
159 | return n; | 164 | return n; |
160 | } | 165 | } |
166 | |||
167 | /* Obtain a backtrace and print it to stdout. */ | ||
168 | #ifdef BACKTRACE_SUPPORT | ||
169 | void dump_stack(void) | ||
170 | { | ||
171 | void *array[16]; | ||
172 | size_t size = backtrace(array, ARRAY_SIZE(array)); | ||
173 | char **strings = backtrace_symbols(array, size); | ||
174 | size_t i; | ||
175 | |||
176 | printf("Obtained %zd stack frames.\n", size); | ||
177 | |||
178 | for (i = 0; i < size; i++) | ||
179 | printf("%s\n", strings[i]); | ||
180 | |||
181 | free(strings); | ||
182 | } | ||
183 | #else | ||
184 | void dump_stack(void) {} | ||
185 | #endif | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b13c7331eaf8..70fa70b535b2 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -69,13 +69,8 @@ | |||
69 | #include <sys/poll.h> | 69 | #include <sys/poll.h> |
70 | #include <sys/socket.h> | 70 | #include <sys/socket.h> |
71 | #include <sys/ioctl.h> | 71 | #include <sys/ioctl.h> |
72 | #include <sys/select.h> | ||
73 | #include <netinet/in.h> | ||
74 | #include <netinet/tcp.h> | ||
75 | #include <arpa/inet.h> | ||
76 | #include <netdb.h> | ||
77 | #include <inttypes.h> | 72 | #include <inttypes.h> |
78 | #include "../../../include/linux/magic.h" | 73 | #include <linux/magic.h> |
79 | #include "types.h" | 74 | #include "types.h" |
80 | #include <sys/ttydefaults.h> | 75 | #include <sys/ttydefaults.h> |
81 | 76 | ||
@@ -266,4 +261,6 @@ size_t hex_width(u64 v); | |||
266 | 261 | ||
267 | char *rtrim(char *s); | 262 | char *rtrim(char *s); |
268 | 263 | ||
264 | void dump_stack(void); | ||
265 | |||
269 | #endif | 266 | #endif |
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c new file mode 100644 index 000000000000..e60951fcdb12 --- /dev/null +++ b/tools/perf/util/vdso.c | |||
@@ -0,0 +1,111 @@ | |||
1 | |||
2 | #include <unistd.h> | ||
3 | #include <stdio.h> | ||
4 | #include <string.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <sys/stat.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <linux/kernel.h> | ||
10 | |||
11 | #include "vdso.h" | ||
12 | #include "util.h" | ||
13 | #include "symbol.h" | ||
14 | #include "linux/string.h" | ||
15 | |||
16 | static bool vdso_found; | ||
17 | static char vdso_file[] = "/tmp/perf-vdso.so-XXXXXX"; | ||
18 | |||
19 | static int find_vdso_map(void **start, void **end) | ||
20 | { | ||
21 | FILE *maps; | ||
22 | char line[128]; | ||
23 | int found = 0; | ||
24 | |||
25 | maps = fopen("/proc/self/maps", "r"); | ||
26 | if (!maps) { | ||
27 | pr_err("vdso: cannot open maps\n"); | ||
28 | return -1; | ||
29 | } | ||
30 | |||
31 | while (!found && fgets(line, sizeof(line), maps)) { | ||
32 | int m = -1; | ||
33 | |||
34 | /* We care only about private r-x mappings. */ | ||
35 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | ||
36 | start, end, &m)) | ||
37 | continue; | ||
38 | if (m < 0) | ||
39 | continue; | ||
40 | |||
41 | if (!strncmp(&line[m], VDSO__MAP_NAME, | ||
42 | sizeof(VDSO__MAP_NAME) - 1)) | ||
43 | found = 1; | ||
44 | } | ||
45 | |||
46 | fclose(maps); | ||
47 | return !found; | ||
48 | } | ||
49 | |||
50 | static char *get_file(void) | ||
51 | { | ||
52 | char *vdso = NULL; | ||
53 | char *buf = NULL; | ||
54 | void *start, *end; | ||
55 | size_t size; | ||
56 | int fd; | ||
57 | |||
58 | if (vdso_found) | ||
59 | return vdso_file; | ||
60 | |||
61 | if (find_vdso_map(&start, &end)) | ||
62 | return NULL; | ||
63 | |||
64 | size = end - start; | ||
65 | |||
66 | buf = memdup(start, size); | ||
67 | if (!buf) | ||
68 | return NULL; | ||
69 | |||
70 | fd = mkstemp(vdso_file); | ||
71 | if (fd < 0) | ||
72 | goto out; | ||
73 | |||
74 | if (size == (size_t) write(fd, buf, size)) | ||
75 | vdso = vdso_file; | ||
76 | |||
77 | close(fd); | ||
78 | |||
79 | out: | ||
80 | free(buf); | ||
81 | |||
82 | vdso_found = (vdso != NULL); | ||
83 | return vdso; | ||
84 | } | ||
85 | |||
86 | void vdso__exit(void) | ||
87 | { | ||
88 | if (vdso_found) | ||
89 | unlink(vdso_file); | ||
90 | } | ||
91 | |||
92 | struct dso *vdso__dso_findnew(struct list_head *head) | ||
93 | { | ||
94 | struct dso *dso = dsos__find(head, VDSO__MAP_NAME); | ||
95 | |||
96 | if (!dso) { | ||
97 | char *file; | ||
98 | |||
99 | file = get_file(); | ||
100 | if (!file) | ||
101 | return NULL; | ||
102 | |||
103 | dso = dso__new(VDSO__MAP_NAME); | ||
104 | if (dso != NULL) { | ||
105 | dsos__add(head, dso); | ||
106 | dso__set_long_name(dso, file); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | return dso; | ||
111 | } | ||
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h new file mode 100644 index 000000000000..0f76e7caf6f8 --- /dev/null +++ b/tools/perf/util/vdso.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef __PERF_VDSO__ | ||
2 | #define __PERF_VDSO__ | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <string.h> | ||
6 | #include <stdbool.h> | ||
7 | |||
8 | #define VDSO__MAP_NAME "[vdso]" | ||
9 | |||
10 | static inline bool is_vdso_map(const char *filename) | ||
11 | { | ||
12 | return !strcmp(filename, VDSO__MAP_NAME); | ||
13 | } | ||
14 | |||
15 | struct dso *vdso__dso_findnew(struct list_head *head); | ||
16 | void vdso__exit(void); | ||
17 | |||
18 | #endif /* __PERF_VDSO__ */ | ||
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c index 73e900edb5a2..19f15b650703 100644 --- a/tools/perf/util/wrapper.c +++ b/tools/perf/util/wrapper.c | |||
@@ -7,7 +7,8 @@ | |||
7 | * There's no pack memory to release - but stay close to the Git | 7 | * There's no pack memory to release - but stay close to the Git |
8 | * version so wrap this away: | 8 | * version so wrap this away: |
9 | */ | 9 | */ |
10 | static inline void release_pack_memory(size_t size __used, int flag __used) | 10 | static inline void release_pack_memory(size_t size __maybe_unused, |
11 | int flag __maybe_unused) | ||
11 | { | 12 | { |
12 | } | 13 | } |
13 | 14 | ||