diff options
Diffstat (limited to 'tools/perf/util')
79 files changed, 4895 insertions, 2494 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..9b5b21e7b032 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); |
| @@ -138,11 +139,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
| 138 | int max_lines); | 139 | int max_lines); |
| 139 | 140 | ||
| 140 | #ifdef NO_NEWT_SUPPORT | 141 | #ifdef NO_NEWT_SUPPORT |
| 141 | static inline int symbol__tui_annotate(struct symbol *sym __used, | 142 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, |
| 142 | struct map *map __used, | 143 | struct map *map __maybe_unused, |
| 143 | int evidx __used, | 144 | int evidx __maybe_unused, |
| 144 | void(*timer)(void *arg) __used, | 145 | void(*timer)(void *arg) __maybe_unused, |
| 145 | void *arg __used, int delay_secs __used) | 146 | void *arg __maybe_unused, |
| 147 | int delay_secs __maybe_unused) | ||
| 146 | { | 148 | { |
| 147 | return 0; | 149 | return 0; |
| 148 | } | 150 | } |
| @@ -152,5 +154,6 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
| 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..ab1769426541 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
| @@ -39,7 +39,7 @@ static inline void setup_browser(bool fallback_to_pager) | |||
| 39 | if (fallback_to_pager) | 39 | if (fallback_to_pager) |
| 40 | setup_pager(); | 40 | setup_pager(); |
| 41 | } | 41 | } |
| 42 | static inline void exit_browser(bool wait_for_ok __used) {} | 42 | static inline void exit_browser(bool wait_for_ok __maybe_unused) {} |
| 43 | #else | 43 | #else |
| 44 | void setup_browser(bool fallback_to_pager); | 44 | void setup_browser(bool fallback_to_pager); |
| 45 | void exit_browser(bool wait_for_ok); | 45 | void exit_browser(bool wait_for_ok); |
| @@ -49,7 +49,7 @@ static inline int ui__init(void) | |||
| 49 | { | 49 | { |
| 50 | return -1; | 50 | return -1; |
| 51 | } | 51 | } |
| 52 | static inline void ui__exit(bool wait_for_ok __used) {} | 52 | static inline void ui__exit(bool wait_for_ok __maybe_unused) {} |
| 53 | #else | 53 | #else |
| 54 | int ui__init(void); | 54 | int ui__init(void); |
| 55 | void ui__exit(bool wait_for_ok); | 55 | void ui__exit(bool wait_for_ok); |
| @@ -60,7 +60,7 @@ static inline int perf_gtk__init(void) | |||
| 60 | { | 60 | { |
| 61 | return -1; | 61 | return -1; |
| 62 | } | 62 | } |
| 63 | static inline void perf_gtk__exit(bool wait_for_ok __used) {} | 63 | static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} |
| 64 | #else | 64 | #else |
| 65 | int perf_gtk__init(void); | 65 | int perf_gtk__init(void); |
| 66 | void perf_gtk__exit(bool wait_for_ok); | 66 | void perf_gtk__exit(bool wait_for_ok); |
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/callchain.h b/tools/perf/util/callchain.h index 3bdb407f9cd9..eb340571e7d6 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -58,7 +58,7 @@ struct callchain_list { | |||
| 58 | /* | 58 | /* |
| 59 | * A callchain cursor is a single linked list that | 59 | * A callchain cursor is a single linked list that |
| 60 | * let one feed a callchain progressively. | 60 | * let one feed a callchain progressively. |
| 61 | * It keeps persitent allocated entries to minimize | 61 | * It keeps persistent allocated entries to minimize |
| 62 | * allocations. | 62 | * allocations. |
| 63 | */ | 63 | */ |
| 64 | struct callchain_cursor_node { | 64 | struct callchain_cursor_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..66eb3828ceb5 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); |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 015c91dbc096..bb2e7d1007ab 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; |
| @@ -15,32 +16,26 @@ 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(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) |
| 18 | static inline int ui_helpline__show_help(const char *format __used, va_list ap __used) | 19 | static inline void ui_progress__update(u64 curr __maybe_unused, |
| 19 | { | 20 | u64 total __maybe_unused, |
| 20 | return 0; | 21 | const char *title __maybe_unused) {} |
| 21 | } | ||
| 22 | |||
| 23 | static inline void ui_progress__update(u64 curr __used, u64 total __used, | ||
| 24 | const char *title __used) {} | ||
| 25 | 22 | ||
| 26 | #define ui__error(format, arg...) ui__warning(format, ##arg) | 23 | #define ui__error(format, arg...) ui__warning(format, ##arg) |
| 27 | 24 | ||
| 28 | static inline int | 25 | static inline int |
| 29 | perf_error__register(struct perf_error_ops *eops __used) | 26 | perf_error__register(struct perf_error_ops *eops __maybe_unused) |
| 30 | { | 27 | { |
| 31 | return 0; | 28 | return 0; |
| 32 | } | 29 | } |
| 33 | 30 | ||
| 34 | static inline int | 31 | static inline int |
| 35 | perf_error__unregister(struct perf_error_ops *eops __used) | 32 | perf_error__unregister(struct perf_error_ops *eops __maybe_unused) |
| 36 | { | 33 | { |
| 37 | return 0; | 34 | return 0; |
| 38 | } | 35 | } |
| 39 | 36 | ||
| 40 | #else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ | 37 | #else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ |
| 41 | 38 | ||
| 42 | extern char ui_helpline__last_msg[]; | ||
| 43 | int ui_helpline__show_help(const char *format, va_list ap); | ||
| 44 | #include "../ui/progress.h" | 39 | #include "../ui/progress.h" |
| 45 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | 40 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); |
| 46 | #include "../ui/util.h" | 41 | #include "../ui/util.h" |
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..ae89686102f4 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 = { |
| @@ -285,7 +304,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
| 285 | int cpu, thread; | 304 | int cpu, thread; |
| 286 | struct perf_evsel *pos; | 305 | struct perf_evsel *pos; |
| 287 | 306 | ||
| 288 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 307 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { |
| 289 | list_for_each_entry(pos, &evlist->entries, node) { | 308 | list_for_each_entry(pos, &evlist->entries, node) { |
| 290 | for (thread = 0; thread < evlist->threads->nr; thread++) | 309 | for (thread = 0; thread < evlist->threads->nr; thread++) |
| 291 | ioctl(FD(pos, cpu, thread), | 310 | ioctl(FD(pos, cpu, thread), |
| @@ -296,7 +315,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
| 296 | 315 | ||
| 297 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 316 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
| 298 | { | 317 | { |
| 299 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | 318 | int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries; |
| 300 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 319 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
| 301 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 320 | return evlist->pollfd != NULL ? 0 : -ENOMEM; |
| 302 | } | 321 | } |
| @@ -357,7 +376,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
| 357 | int hash; | 376 | int hash; |
| 358 | 377 | ||
| 359 | if (evlist->nr_entries == 1) | 378 | if (evlist->nr_entries == 1) |
| 360 | return list_entry(evlist->entries.next, struct perf_evsel, node); | 379 | return perf_evlist__first(evlist); |
| 361 | 380 | ||
| 362 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 381 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
| 363 | head = &evlist->heads[hash]; | 382 | head = &evlist->heads[hash]; |
| @@ -367,7 +386,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
| 367 | return sid->evsel; | 386 | return sid->evsel; |
| 368 | 387 | ||
| 369 | if (!perf_evlist__sample_id_all(evlist)) | 388 | if (!perf_evlist__sample_id_all(evlist)) |
| 370 | return list_entry(evlist->entries.next, struct perf_evsel, node); | 389 | return perf_evlist__first(evlist); |
| 371 | 390 | ||
| 372 | return NULL; | 391 | return NULL; |
| 373 | } | 392 | } |
| @@ -456,8 +475,8 @@ void perf_evlist__munmap(struct perf_evlist *evlist) | |||
| 456 | 475 | ||
| 457 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | 476 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) |
| 458 | { | 477 | { |
| 459 | evlist->nr_mmaps = evlist->cpus->nr; | 478 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); |
| 460 | if (evlist->cpus->map[0] == -1) | 479 | if (cpu_map__all(evlist->cpus)) |
| 461 | evlist->nr_mmaps = evlist->threads->nr; | 480 | evlist->nr_mmaps = evlist->threads->nr; |
| 462 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 481 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
| 463 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 482 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
| @@ -603,11 +622,11 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
| 603 | list_for_each_entry(evsel, &evlist->entries, node) { | 622 | list_for_each_entry(evsel, &evlist->entries, node) { |
| 604 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 623 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
| 605 | evsel->sample_id == NULL && | 624 | evsel->sample_id == NULL && |
| 606 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | 625 | perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) |
| 607 | return -ENOMEM; | 626 | return -ENOMEM; |
| 608 | } | 627 | } |
| 609 | 628 | ||
| 610 | if (evlist->cpus->map[0] == -1) | 629 | if (cpu_map__all(cpus)) |
| 611 | return perf_evlist__mmap_per_thread(evlist, prot, mask); | 630 | return perf_evlist__mmap_per_thread(evlist, prot, mask); |
| 612 | 631 | ||
| 613 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 632 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
| @@ -647,39 +666,44 @@ void perf_evlist__delete_maps(struct perf_evlist *evlist) | |||
| 647 | evlist->threads = NULL; | 666 | evlist->threads = NULL; |
| 648 | } | 667 | } |
| 649 | 668 | ||
| 650 | int perf_evlist__set_filters(struct perf_evlist *evlist) | 669 | int perf_evlist__apply_filters(struct perf_evlist *evlist) |
| 651 | { | 670 | { |
| 652 | const struct thread_map *threads = evlist->threads; | ||
| 653 | const struct cpu_map *cpus = evlist->cpus; | ||
| 654 | struct perf_evsel *evsel; | 671 | struct perf_evsel *evsel; |
| 655 | char *filter; | 672 | int err = 0; |
| 656 | int thread; | 673 | const int ncpus = cpu_map__nr(evlist->cpus), |
| 657 | int cpu; | 674 | nthreads = evlist->threads->nr; |
| 658 | int err; | ||
| 659 | int fd; | ||
| 660 | 675 | ||
| 661 | list_for_each_entry(evsel, &evlist->entries, node) { | 676 | list_for_each_entry(evsel, &evlist->entries, node) { |
| 662 | filter = evsel->filter; | 677 | if (evsel->filter == NULL) |
| 663 | if (!filter) | ||
| 664 | continue; | 678 | continue; |
| 665 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 679 | |
| 666 | for (thread = 0; thread < threads->nr; thread++) { | 680 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter); |
| 667 | fd = FD(evsel, cpu, thread); | 681 | if (err) |
| 668 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | 682 | break; |
| 669 | if (err) | ||
| 670 | return err; | ||
| 671 | } | ||
| 672 | } | ||
| 673 | } | 683 | } |
| 674 | 684 | ||
| 675 | return 0; | 685 | return err; |
| 676 | } | 686 | } |
| 677 | 687 | ||
| 678 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist) | 688 | int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) |
| 679 | { | 689 | { |
| 680 | struct perf_evsel *pos, *first; | 690 | struct perf_evsel *evsel; |
| 691 | int err = 0; | ||
| 692 | const int ncpus = cpu_map__nr(evlist->cpus), | ||
| 693 | nthreads = evlist->threads->nr; | ||
| 694 | |||
| 695 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 696 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); | ||
| 697 | if (err) | ||
| 698 | break; | ||
| 699 | } | ||
| 700 | |||
| 701 | return err; | ||
| 702 | } | ||
| 681 | 703 | ||
| 682 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); | 704 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) |
| 705 | { | ||
| 706 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; | ||
| 683 | 707 | ||
| 684 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 708 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
| 685 | if (first->attr.sample_type != pos->attr.sample_type) | 709 | if (first->attr.sample_type != pos->attr.sample_type) |
| @@ -689,23 +713,19 @@ bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist) | |||
| 689 | return true; | 713 | return true; |
| 690 | } | 714 | } |
| 691 | 715 | ||
| 692 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist) | 716 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) |
| 693 | { | 717 | { |
| 694 | struct perf_evsel *first; | 718 | 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; | 719 | return first->attr.sample_type; |
| 698 | } | 720 | } |
| 699 | 721 | ||
| 700 | u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist) | 722 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) |
| 701 | { | 723 | { |
| 702 | struct perf_evsel *first; | 724 | struct perf_evsel *first = perf_evlist__first(evlist); |
| 703 | struct perf_sample *data; | 725 | struct perf_sample *data; |
| 704 | u64 sample_type; | 726 | u64 sample_type; |
| 705 | u16 size = 0; | 727 | u16 size = 0; |
| 706 | 728 | ||
| 707 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 708 | |||
| 709 | if (!first->attr.sample_id_all) | 729 | if (!first->attr.sample_id_all) |
| 710 | goto out; | 730 | goto out; |
| 711 | 731 | ||
| @@ -729,11 +749,9 @@ out: | |||
| 729 | return size; | 749 | return size; |
| 730 | } | 750 | } |
| 731 | 751 | ||
| 732 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) | 752 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist) |
| 733 | { | 753 | { |
| 734 | struct perf_evsel *pos, *first; | 754 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; |
| 735 | |||
| 736 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 737 | 755 | ||
| 738 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 756 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
| 739 | if (first->attr.sample_id_all != pos->attr.sample_id_all) | 757 | if (first->attr.sample_id_all != pos->attr.sample_id_all) |
| @@ -743,11 +761,9 @@ bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) | |||
| 743 | return true; | 761 | return true; |
| 744 | } | 762 | } |
| 745 | 763 | ||
| 746 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | 764 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist) |
| 747 | { | 765 | { |
| 748 | struct perf_evsel *first; | 766 | 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; | 767 | return first->attr.sample_id_all; |
| 752 | } | 768 | } |
| 753 | 769 | ||
| @@ -757,21 +773,13 @@ void perf_evlist__set_selected(struct perf_evlist *evlist, | |||
| 757 | evlist->selected = evsel; | 773 | evlist->selected = evsel; |
| 758 | } | 774 | } |
| 759 | 775 | ||
| 760 | int perf_evlist__open(struct perf_evlist *evlist, bool group) | 776 | int perf_evlist__open(struct perf_evlist *evlist) |
| 761 | { | 777 | { |
| 762 | struct perf_evsel *evsel, *first; | 778 | struct perf_evsel *evsel; |
| 763 | int err, ncpus, nthreads; | 779 | int err, ncpus, nthreads; |
| 764 | 780 | ||
| 765 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 766 | |||
| 767 | list_for_each_entry(evsel, &evlist->entries, node) { | 781 | list_for_each_entry(evsel, &evlist->entries, node) { |
| 768 | struct xyarray *group_fd = NULL; | 782 | 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) | 783 | if (err < 0) |
| 776 | goto out_err; | 784 | goto out_err; |
| 777 | } | 785 | } |
| @@ -883,8 +891,21 @@ int perf_evlist__start_workload(struct perf_evlist *evlist) | |||
| 883 | } | 891 | } |
| 884 | 892 | ||
| 885 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 893 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
| 886 | struct perf_sample *sample, bool swapped) | 894 | struct perf_sample *sample) |
| 895 | { | ||
| 896 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 897 | return perf_evsel__parse_sample(evsel, event, sample); | ||
| 898 | } | ||
| 899 | |||
| 900 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | ||
| 887 | { | 901 | { |
| 888 | struct perf_evsel *e = list_entry(evlist->entries.next, struct perf_evsel, node); | 902 | struct perf_evsel *evsel; |
| 889 | return perf_evsel__parse_sample(e, event, sample, swapped); | 903 | size_t printed = 0; |
| 904 | |||
| 905 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 906 | printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "", | ||
| 907 | perf_evsel__name(evsel)); | ||
| 908 | } | ||
| 909 | |||
| 910 | return printed + fprintf(fp, "\n");; | ||
| 890 | } | 911 | } |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 528c1acd9298..3f1fb66be022 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, |
| @@ -73,6 +72,8 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, | |||
| 73 | #define perf_evlist__set_tracepoints_handlers_array(evlist, array) \ | 72 | #define perf_evlist__set_tracepoints_handlers_array(evlist, array) \ |
| 74 | perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) | 73 | perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) |
| 75 | 74 | ||
| 75 | int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); | ||
| 76 | |||
| 76 | struct perf_evsel * | 77 | struct perf_evsel * |
| 77 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); | 78 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); |
| 78 | 79 | ||
| @@ -85,7 +86,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
| 85 | 86 | ||
| 86 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 87 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
| 87 | 88 | ||
| 88 | int perf_evlist__open(struct perf_evlist *evlist, bool group); | 89 | int perf_evlist__open(struct perf_evlist *evlist); |
| 89 | 90 | ||
| 90 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 91 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
| 91 | struct perf_record_opts *opts); | 92 | struct perf_record_opts *opts); |
| @@ -116,20 +117,34 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
| 116 | int perf_evlist__create_maps(struct perf_evlist *evlist, | 117 | int perf_evlist__create_maps(struct perf_evlist *evlist, |
| 117 | struct perf_target *target); | 118 | struct perf_target *target); |
| 118 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 119 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
| 119 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 120 | int perf_evlist__apply_filters(struct perf_evlist *evlist); |
| 121 | |||
| 122 | void __perf_evlist__set_leader(struct list_head *list); | ||
| 123 | void perf_evlist__set_leader(struct perf_evlist *evlist); | ||
| 120 | 124 | ||
| 121 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist); | 125 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); |
| 122 | bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist); | 126 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist); |
| 123 | u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist); | 127 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist); |
| 124 | 128 | ||
| 125 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 129 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
| 126 | struct perf_sample *sample, bool swapped); | 130 | struct perf_sample *sample); |
| 127 | 131 | ||
| 128 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); | 132 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist); |
| 129 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); | 133 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist); |
| 130 | 134 | ||
| 131 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 135 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
| 132 | struct list_head *list, | 136 | struct list_head *list, |
| 133 | int nr_entries); | 137 | int nr_entries); |
| 134 | 138 | ||
| 139 | static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist) | ||
| 140 | { | ||
| 141 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 142 | } | ||
| 143 | |||
| 144 | static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | ||
| 145 | { | ||
| 146 | return list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
| 147 | } | ||
| 148 | |||
| 149 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | ||
| 135 | #endif /* __PERF_EVLIST_H */ | 150 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2eaae140def2..ffdd94e9c9c3 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/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..3ead0d59c03d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.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..389590c1ad21 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 "#ifndef NO_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 /* NO_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..99bdd3abce59 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.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..236bc9d98ff2 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,6 +114,22 @@ 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 | { |
| @@ -378,7 +394,7 @@ void hist_entry__free(struct hist_entry *he) | |||
| 378 | * collapse the histogram | 394 | * collapse the histogram |
| 379 | */ | 395 | */ |
| 380 | 396 | ||
| 381 | static bool hists__collapse_insert_entry(struct hists *hists __used, | 397 | static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, |
| 382 | struct rb_root *root, | 398 | struct rb_root *root, |
| 383 | struct hist_entry *he) | 399 | struct hist_entry *he) |
| 384 | { | 400 | { |
| @@ -394,8 +410,13 @@ static bool hists__collapse_insert_entry(struct hists *hists __used, | |||
| 394 | cmp = hist_entry__collapse(iter, he); | 410 | cmp = hist_entry__collapse(iter, he); |
| 395 | 411 | ||
| 396 | if (!cmp) { | 412 | if (!cmp) { |
| 397 | iter->period += he->period; | 413 | iter->period += he->period; |
| 398 | iter->nr_events += he->nr_events; | 414 | iter->period_sys += he->period_sys; |
| 415 | iter->period_us += he->period_us; | ||
| 416 | iter->period_guest_sys += he->period_guest_sys; | ||
| 417 | iter->period_guest_us += he->period_guest_us; | ||
| 418 | iter->nr_events += he->nr_events; | ||
| 419 | |||
| 399 | if (symbol_conf.use_callchain) { | 420 | if (symbol_conf.use_callchain) { |
| 400 | callchain_cursor_reset(&callchain_cursor); | 421 | callchain_cursor_reset(&callchain_cursor); |
| 401 | callchain_merge(&callchain_cursor, | 422 | callchain_merge(&callchain_cursor, |
| @@ -547,674 +568,6 @@ void hists__output_resort_threaded(struct hists *hists) | |||
| 547 | return __hists__output_resort(hists, true); | 568 | return __hists__output_resort(hists, true); |
| 548 | } | 569 | } |
| 549 | 570 | ||
| 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, | 571 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
| 1219 | enum hist_filter filter) | 572 | enum hist_filter filter) |
| 1220 | { | 573 | { |
| @@ -1342,25 +695,3 @@ void hists__inc_nr_events(struct hists *hists, u32 type) | |||
| 1342 | ++hists->stats.nr_events[0]; | 695 | ++hists->stats.nr_events[0]; |
| 1343 | ++hists->stats.nr_events[type]; | 696 | ++hists->stats.nr_events[type]; |
| 1344 | } | 697 | } |
| 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..f011ad4756e8 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, |
| @@ -112,25 +112,66 @@ void hists__filter_by_symbol(struct hists *hists); | |||
| 112 | u16 hists__col_len(struct hists *self, enum hist_column col); | 112 | 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); | 113 | 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); | 114 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); |
| 115 | void hists__reset_col_len(struct hists *hists); | ||
| 116 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | ||
| 117 | |||
| 118 | struct perf_hpp { | ||
| 119 | char *buf; | ||
| 120 | size_t size; | ||
| 121 | u64 total_period; | ||
| 122 | const char *sep; | ||
| 123 | long displacement; | ||
| 124 | void *ptr; | ||
| 125 | }; | ||
| 126 | |||
| 127 | struct perf_hpp_fmt { | ||
| 128 | bool cond; | ||
| 129 | int (*header)(struct perf_hpp *hpp); | ||
| 130 | int (*width)(struct perf_hpp *hpp); | ||
| 131 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); | ||
| 132 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); | ||
| 133 | }; | ||
| 134 | |||
| 135 | extern struct perf_hpp_fmt perf_hpp__format[]; | ||
| 136 | |||
| 137 | enum { | ||
| 138 | PERF_HPP__OVERHEAD, | ||
| 139 | PERF_HPP__OVERHEAD_SYS, | ||
| 140 | PERF_HPP__OVERHEAD_US, | ||
| 141 | PERF_HPP__OVERHEAD_GUEST_SYS, | ||
| 142 | PERF_HPP__OVERHEAD_GUEST_US, | ||
| 143 | PERF_HPP__SAMPLES, | ||
| 144 | PERF_HPP__PERIOD, | ||
| 145 | PERF_HPP__DELTA, | ||
| 146 | PERF_HPP__DISPL, | ||
| 147 | |||
| 148 | PERF_HPP__MAX_INDEX | ||
| 149 | }; | ||
| 150 | |||
| 151 | void perf_hpp__init(bool need_pair, bool show_displacement); | ||
| 152 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | ||
| 153 | bool color); | ||
| 115 | 154 | ||
| 116 | struct perf_evlist; | 155 | struct perf_evlist; |
| 117 | 156 | ||
| 118 | #ifdef NO_NEWT_SUPPORT | 157 | #ifdef NO_NEWT_SUPPORT |
| 119 | static inline | 158 | static inline |
| 120 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | 159 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, |
| 121 | const char *help __used, | 160 | const char *help __maybe_unused, |
| 122 | void(*timer)(void *arg) __used, | 161 | void(*timer)(void *arg) __maybe_unused, |
| 123 | void *arg __used, | 162 | void *arg __maybe_unused, |
| 124 | int refresh __used) | 163 | int refresh __maybe_unused) |
| 125 | { | 164 | { |
| 126 | return 0; | 165 | return 0; |
| 127 | } | 166 | } |
| 128 | 167 | ||
| 129 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | 168 | static inline int hist_entry__tui_annotate(struct hist_entry *self |
| 130 | int evidx __used, | 169 | __maybe_unused, |
| 131 | void(*timer)(void *arg) __used, | 170 | int evidx __maybe_unused, |
| 132 | void *arg __used, | 171 | void(*timer)(void *arg) |
| 133 | int delay_secs __used) | 172 | __maybe_unused, |
| 173 | void *arg __maybe_unused, | ||
| 174 | int delay_secs __maybe_unused) | ||
| 134 | { | 175 | { |
| 135 | return 0; | 176 | return 0; |
| 136 | } | 177 | } |
| @@ -148,11 +189,11 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | |||
| 148 | 189 | ||
| 149 | #ifdef NO_GTK2_SUPPORT | 190 | #ifdef NO_GTK2_SUPPORT |
| 150 | static inline | 191 | static inline |
| 151 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used, | 192 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, |
| 152 | const char *help __used, | 193 | const char *help __maybe_unused, |
| 153 | void(*timer)(void *arg) __used, | 194 | void(*timer)(void *arg) __maybe_unused, |
| 154 | void *arg __used, | 195 | void *arg __maybe_unused, |
| 155 | int refresh __used) | 196 | int refresh __maybe_unused) |
| 156 | { | 197 | { |
| 157 | return 0; | 198 | return 0; |
| 158 | } | 199 | } |
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/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/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..ead5316b3f89 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 | #ifndef NO_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 127d648cc548..28c18d1d52c3 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,280 @@ 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 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 517 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 518 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | ||
| 519 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 520 | |||
| 521 | return 0; | ||
| 522 | } | ||
| 523 | |||
| 524 | static int test__group2(struct perf_evlist *evlist) | ||
| 525 | { | ||
| 526 | struct perf_evsel *evsel, *leader; | ||
| 527 | |||
| 528 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | ||
| 529 | |||
| 530 | /* faults + :ku modifier */ | ||
| 531 | evsel = leader = perf_evlist__first(evlist); | ||
| 532 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); | ||
| 533 | TEST_ASSERT_VAL("wrong config", | ||
| 534 | PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config); | ||
| 535 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 536 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 537 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 538 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 539 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 540 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 541 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 542 | |||
| 543 | /* cache-references + :u modifier */ | ||
| 544 | evsel = perf_evsel__next(evsel); | ||
| 545 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 546 | TEST_ASSERT_VAL("wrong config", | ||
| 547 | PERF_COUNT_HW_CACHE_REFERENCES == evsel->attr.config); | ||
| 548 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 549 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
| 550 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 551 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 552 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 553 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 554 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 555 | |||
| 556 | /* cycles:k */ | ||
| 557 | evsel = perf_evsel__next(evsel); | ||
| 558 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 559 | TEST_ASSERT_VAL("wrong config", | ||
| 560 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 561 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
| 562 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 563 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 564 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 565 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 566 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 567 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 568 | |||
| 569 | return 0; | ||
| 570 | } | ||
| 571 | |||
| 572 | static int test__group3(struct perf_evlist *evlist __maybe_unused) | ||
| 573 | { | ||
| 574 | struct perf_evsel *evsel, *leader; | ||
| 575 | |||
| 576 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | ||
| 577 | |||
| 578 | /* group1 syscalls:sys_enter_open:H */ | ||
| 579 | evsel = leader = perf_evlist__first(evlist); | ||
| 580 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
| 581 | TEST_ASSERT_VAL("wrong sample_type", | ||
| 582 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); | ||
| 583 | TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); | ||
| 584 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 585 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 586 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 587 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
| 588 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 589 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 590 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 591 | TEST_ASSERT_VAL("wrong group name", | ||
| 592 | !strcmp(leader->group_name, "group1")); | ||
| 593 | |||
| 594 | /* group1 cycles:kppp */ | ||
| 595 | evsel = perf_evsel__next(evsel); | ||
| 596 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 597 | TEST_ASSERT_VAL("wrong config", | ||
| 598 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 599 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
| 600 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 601 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 602 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 603 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 604 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); | ||
| 605 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 606 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
| 607 | |||
| 608 | /* group2 cycles + G modifier */ | ||
| 609 | evsel = leader = perf_evsel__next(evsel); | ||
| 610 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 611 | TEST_ASSERT_VAL("wrong config", | ||
| 612 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 613 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 614 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 615 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 616 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 617 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
| 618 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 619 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 620 | TEST_ASSERT_VAL("wrong group name", | ||
| 621 | !strcmp(leader->group_name, "group2")); | ||
| 622 | |||
| 623 | /* group2 1:3 + G modifier */ | ||
| 624 | evsel = perf_evsel__next(evsel); | ||
| 625 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | ||
| 626 | TEST_ASSERT_VAL("wrong config", 3 == evsel->attr.config); | ||
| 627 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 628 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 629 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 630 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 631 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
| 632 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 633 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 634 | |||
| 635 | /* instructions:u */ | ||
| 636 | evsel = perf_evsel__next(evsel); | ||
| 637 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 638 | TEST_ASSERT_VAL("wrong config", | ||
| 639 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
| 640 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 641 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
| 642 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 643 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 644 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 645 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 646 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 647 | |||
| 648 | return 0; | ||
| 649 | } | ||
| 650 | |||
| 651 | static int test__group4(struct perf_evlist *evlist __maybe_unused) | ||
| 652 | { | ||
| 653 | struct perf_evsel *evsel, *leader; | ||
| 654 | |||
| 655 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
| 656 | |||
| 657 | /* cycles:u + p */ | ||
| 658 | evsel = leader = perf_evlist__first(evlist); | ||
| 659 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 660 | TEST_ASSERT_VAL("wrong config", | ||
| 661 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 662 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 663 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
| 664 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 665 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 666 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 667 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); | ||
| 668 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
| 669 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 670 | |||
| 671 | /* instructions:kp + p */ | ||
| 672 | evsel = perf_evsel__next(evsel); | ||
| 673 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 674 | TEST_ASSERT_VAL("wrong config", | ||
| 675 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
| 676 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
| 677 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 678 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 679 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 680 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 681 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | ||
| 682 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 683 | |||
| 684 | return 0; | ||
| 685 | } | ||
| 686 | |||
| 687 | static int test__group5(struct perf_evlist *evlist __maybe_unused) | ||
| 688 | { | ||
| 689 | struct perf_evsel *evsel, *leader; | ||
| 690 | |||
| 691 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | ||
| 692 | |||
| 693 | /* cycles + G */ | ||
| 694 | evsel = leader = perf_evlist__first(evlist); | ||
| 695 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 696 | TEST_ASSERT_VAL("wrong config", | ||
| 697 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 698 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 699 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 700 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 701 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 702 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
| 703 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 704 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
| 705 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 706 | |||
| 707 | /* instructions + G */ | ||
| 708 | evsel = perf_evsel__next(evsel); | ||
| 709 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 710 | TEST_ASSERT_VAL("wrong config", | ||
| 711 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
| 712 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 713 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 714 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 715 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 716 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
| 717 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 718 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 719 | |||
| 720 | /* cycles:G */ | ||
| 721 | evsel = leader = perf_evsel__next(evsel); | ||
| 722 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 723 | TEST_ASSERT_VAL("wrong config", | ||
| 724 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 725 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 726 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 727 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 728 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 729 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
| 730 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 731 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
| 732 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 733 | |||
| 734 | /* instructions:G */ | ||
| 735 | evsel = perf_evsel__next(evsel); | ||
| 736 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 737 | TEST_ASSERT_VAL("wrong config", | ||
| 738 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
| 739 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 740 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 741 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 742 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
| 743 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
| 744 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 745 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
| 746 | |||
| 747 | /* cycles */ | ||
| 748 | evsel = perf_evsel__next(evsel); | ||
| 749 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 750 | TEST_ASSERT_VAL("wrong config", | ||
| 751 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
| 752 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 753 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
| 754 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
| 755 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
| 756 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
| 757 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 758 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | ||
| 759 | |||
| 760 | return 0; | ||
| 761 | } | ||
| 762 | |||
| 516 | struct test__event_st { | 763 | struct test__event_st { |
| 517 | const char *name; | 764 | const char *name; |
| 518 | __u32 type; | 765 | __u32 type; |
| @@ -632,6 +879,26 @@ static struct test__event_st test__events[] = { | |||
| 632 | .name = "mem:0:rw:kp", | 879 | .name = "mem:0:rw:kp", |
| 633 | .check = test__checkevent_breakpoint_rw_modifier, | 880 | .check = test__checkevent_breakpoint_rw_modifier, |
| 634 | }, | 881 | }, |
| 882 | [28] = { | ||
| 883 | .name = "{instructions:k,cycles:upp}", | ||
| 884 | .check = test__group1, | ||
| 885 | }, | ||
| 886 | [29] = { | ||
| 887 | .name = "{faults:k,cache-references}:u,cycles:k", | ||
| 888 | .check = test__group2, | ||
| 889 | }, | ||
| 890 | [30] = { | ||
| 891 | .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u", | ||
| 892 | .check = test__group3, | ||
| 893 | }, | ||
| 894 | [31] = { | ||
| 895 | .name = "{cycles:u,instructions:kp}:p", | ||
| 896 | .check = test__group4, | ||
| 897 | }, | ||
| 898 | [32] = { | ||
| 899 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", | ||
| 900 | .check = test__group5, | ||
| 901 | }, | ||
| 635 | }; | 902 | }; |
| 636 | 903 | ||
| 637 | static struct test__event_st test__events_pmu[] = { | 904 | static struct test__event_st test__events_pmu[] = { |
| @@ -658,9 +925,6 @@ static struct test__term test__terms[] = { | |||
| 658 | }, | 925 | }, |
| 659 | }; | 926 | }; |
| 660 | 927 | ||
| 661 | #define TEST__TERMS_CNT (sizeof(test__terms) / \ | ||
| 662 | sizeof(struct test__term)) | ||
| 663 | |||
| 664 | static int test_event(struct test__event_st *e) | 928 | static int test_event(struct test__event_st *e) |
| 665 | { | 929 | { |
| 666 | struct perf_evlist *evlist; | 930 | struct perf_evlist *evlist; |
| @@ -685,19 +949,19 @@ static int test_event(struct test__event_st *e) | |||
| 685 | 949 | ||
| 686 | static int test_events(struct test__event_st *events, unsigned cnt) | 950 | static int test_events(struct test__event_st *events, unsigned cnt) |
| 687 | { | 951 | { |
| 688 | int ret = 0; | 952 | int ret1, ret2 = 0; |
| 689 | unsigned i; | 953 | unsigned i; |
| 690 | 954 | ||
| 691 | for (i = 0; i < cnt; i++) { | 955 | for (i = 0; i < cnt; i++) { |
| 692 | struct test__event_st *e = &events[i]; | 956 | struct test__event_st *e = &events[i]; |
| 693 | 957 | ||
| 694 | pr_debug("running test %d '%s'\n", i, e->name); | 958 | pr_debug("running test %d '%s'\n", i, e->name); |
| 695 | ret = test_event(e); | 959 | ret1 = test_event(e); |
| 696 | if (ret) | 960 | if (ret1) |
| 697 | break; | 961 | ret2 = ret1; |
| 698 | } | 962 | } |
| 699 | 963 | ||
| 700 | return ret; | 964 | return ret2; |
| 701 | } | 965 | } |
| 702 | 966 | ||
| 703 | static int test_term(struct test__term *t) | 967 | static int test_term(struct test__term *t) |
| @@ -752,19 +1016,19 @@ static int test_pmu(void) | |||
| 752 | 1016 | ||
| 753 | ret = stat(path, &st); | 1017 | ret = stat(path, &st); |
| 754 | if (ret) | 1018 | if (ret) |
| 755 | pr_debug("ommiting PMU cpu tests\n"); | 1019 | pr_debug("omitting PMU cpu tests\n"); |
| 756 | return !ret; | 1020 | return !ret; |
| 757 | } | 1021 | } |
| 758 | 1022 | ||
| 759 | int parse_events__test(void) | 1023 | int parse_events__test(void) |
| 760 | { | 1024 | { |
| 761 | int ret; | 1025 | int ret1, ret2 = 0; |
| 762 | 1026 | ||
| 763 | #define TEST_EVENTS(tests) \ | 1027 | #define TEST_EVENTS(tests) \ |
| 764 | do { \ | 1028 | do { \ |
| 765 | ret = test_events(tests, ARRAY_SIZE(tests)); \ | 1029 | ret1 = test_events(tests, ARRAY_SIZE(tests)); \ |
| 766 | if (ret) \ | 1030 | if (!ret2) \ |
| 767 | return ret; \ | 1031 | ret2 = ret1; \ |
| 768 | } while (0) | 1032 | } while (0) |
| 769 | 1033 | ||
| 770 | TEST_EVENTS(test__events); | 1034 | TEST_EVENTS(test__events); |
| @@ -772,5 +1036,9 @@ do { \ | |||
| 772 | if (test_pmu()) | 1036 | if (test_pmu()) |
| 773 | TEST_EVENTS(test__events_pmu); | 1037 | TEST_EVENTS(test__events_pmu); |
| 774 | 1038 | ||
| 775 | return test_terms(test__terms, ARRAY_SIZE(test__terms)); | 1039 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); |
| 1040 | if (!ret2) | ||
| 1041 | ret2 = ret1; | ||
| 1042 | |||
| 1043 | return ret2; | ||
| 776 | } | 1044 | } |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 74a5af4d33ec..aed38e4b9dfa 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') { |
| @@ -674,13 +709,51 @@ int parse_events_modifier(struct list_head *list, char *str) | |||
| 674 | if (precise > 3) | 709 | if (precise > 3) |
| 675 | return -EINVAL; | 710 | return -EINVAL; |
| 676 | 711 | ||
| 712 | mod->eu = eu; | ||
| 713 | mod->ek = ek; | ||
| 714 | mod->eh = eh; | ||
| 715 | mod->eH = eH; | ||
| 716 | mod->eG = eG; | ||
| 717 | mod->precise = precise; | ||
| 718 | mod->exclude_GH = exclude_GH; | ||
| 719 | return 0; | ||
| 720 | } | ||
| 721 | |||
| 722 | int parse_events__modifier_event(struct list_head *list, char *str, bool add) | ||
| 723 | { | ||
| 724 | struct perf_evsel *evsel; | ||
| 725 | struct event_modifier mod; | ||
| 726 | |||
| 727 | if (str == NULL) | ||
| 728 | return 0; | ||
| 729 | |||
| 730 | if (!add && get_event_modifier(&mod, str, NULL)) | ||
| 731 | return -EINVAL; | ||
| 732 | |||
| 677 | list_for_each_entry(evsel, list, node) { | 733 | list_for_each_entry(evsel, list, node) { |
| 678 | evsel->attr.exclude_user = eu; | 734 | |
| 679 | evsel->attr.exclude_kernel = ek; | 735 | if (add && get_event_modifier(&mod, str, evsel)) |
| 680 | evsel->attr.exclude_hv = eh; | 736 | return -EINVAL; |
| 681 | evsel->attr.precise_ip = precise; | 737 | |
| 682 | evsel->attr.exclude_host = eH; | 738 | evsel->attr.exclude_user = mod.eu; |
| 683 | evsel->attr.exclude_guest = eG; | 739 | evsel->attr.exclude_kernel = mod.ek; |
| 740 | evsel->attr.exclude_hv = mod.eh; | ||
| 741 | evsel->attr.precise_ip = mod.precise; | ||
| 742 | evsel->attr.exclude_host = mod.eH; | ||
| 743 | evsel->attr.exclude_guest = mod.eG; | ||
| 744 | evsel->exclude_GH = mod.exclude_GH; | ||
| 745 | } | ||
| 746 | |||
| 747 | return 0; | ||
| 748 | } | ||
| 749 | |||
| 750 | int parse_events_name(struct list_head *list, char *name) | ||
| 751 | { | ||
| 752 | struct perf_evsel *evsel; | ||
| 753 | |||
| 754 | list_for_each_entry(evsel, list, node) { | ||
| 755 | if (!evsel->name) | ||
| 756 | evsel->name = strdup(name); | ||
| 684 | } | 757 | } |
| 685 | 758 | ||
| 686 | return 0; | 759 | return 0; |
| @@ -730,7 +803,8 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
| 730 | return ret; | 803 | return ret; |
| 731 | } | 804 | } |
| 732 | 805 | ||
| 733 | int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) | 806 | int parse_events(struct perf_evlist *evlist, const char *str, |
| 807 | int unset __maybe_unused) | ||
| 734 | { | 808 | { |
| 735 | struct parse_events_data__events data = { | 809 | struct parse_events_data__events data = { |
| 736 | .list = LIST_HEAD_INIT(data.list), | 810 | .list = LIST_HEAD_INIT(data.list), |
| @@ -756,20 +830,20 @@ int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) | |||
| 756 | } | 830 | } |
| 757 | 831 | ||
| 758 | int parse_events_option(const struct option *opt, const char *str, | 832 | int parse_events_option(const struct option *opt, const char *str, |
| 759 | int unset __used) | 833 | int unset __maybe_unused) |
| 760 | { | 834 | { |
| 761 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 835 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
| 762 | return parse_events(evlist, str, unset); | 836 | return parse_events(evlist, str, unset); |
| 763 | } | 837 | } |
| 764 | 838 | ||
| 765 | int parse_filter(const struct option *opt, const char *str, | 839 | int parse_filter(const struct option *opt, const char *str, |
| 766 | int unset __used) | 840 | int unset __maybe_unused) |
| 767 | { | 841 | { |
| 768 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 842 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
| 769 | struct perf_evsel *last = NULL; | 843 | struct perf_evsel *last = NULL; |
| 770 | 844 | ||
| 771 | if (evlist->nr_entries > 0) | 845 | if (evlist->nr_entries > 0) |
| 772 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); | 846 | last = perf_evlist__last(evlist); |
| 773 | 847 | ||
| 774 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 848 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
| 775 | fprintf(stderr, | 849 | fprintf(stderr, |
| @@ -799,7 +873,8 @@ static const char * const event_type_descriptors[] = { | |||
| 799 | * Print the events from <debugfs_mount_point>/tracing/events | 873 | * Print the events from <debugfs_mount_point>/tracing/events |
| 800 | */ | 874 | */ |
| 801 | 875 | ||
| 802 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob) | 876 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob, |
| 877 | bool name_only) | ||
| 803 | { | 878 | { |
| 804 | DIR *sys_dir, *evt_dir; | 879 | DIR *sys_dir, *evt_dir; |
| 805 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 880 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
| @@ -829,6 +904,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob) | |||
| 829 | !strglobmatch(evt_dirent.d_name, event_glob)) | 904 | !strglobmatch(evt_dirent.d_name, event_glob)) |
| 830 | continue; | 905 | continue; |
| 831 | 906 | ||
| 907 | if (name_only) { | ||
| 908 | printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name); | ||
| 909 | continue; | ||
| 910 | } | ||
| 911 | |||
| 832 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 912 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
| 833 | sys_dirent.d_name, evt_dirent.d_name); | 913 | sys_dirent.d_name, evt_dirent.d_name); |
| 834 | printf(" %-50s [%s]\n", evt_path, | 914 | printf(" %-50s [%s]\n", evt_path, |
| @@ -906,7 +986,7 @@ void print_events_type(u8 type) | |||
| 906 | __print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX); | 986 | __print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX); |
| 907 | } | 987 | } |
| 908 | 988 | ||
| 909 | int print_hwcache_events(const char *event_glob) | 989 | int print_hwcache_events(const char *event_glob, bool name_only) |
| 910 | { | 990 | { |
| 911 | unsigned int type, op, i, printed = 0; | 991 | unsigned int type, op, i, printed = 0; |
| 912 | char name[64]; | 992 | char name[64]; |
| @@ -923,8 +1003,11 @@ int print_hwcache_events(const char *event_glob) | |||
| 923 | if (event_glob != NULL && !strglobmatch(name, event_glob)) | 1003 | if (event_glob != NULL && !strglobmatch(name, event_glob)) |
| 924 | continue; | 1004 | continue; |
| 925 | 1005 | ||
| 926 | printf(" %-50s [%s]\n", name, | 1006 | if (name_only) |
| 927 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | 1007 | printf("%s ", name); |
| 1008 | else | ||
| 1009 | printf(" %-50s [%s]\n", name, | ||
| 1010 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | ||
| 928 | ++printed; | 1011 | ++printed; |
| 929 | } | 1012 | } |
| 930 | } | 1013 | } |
| @@ -934,7 +1017,8 @@ int print_hwcache_events(const char *event_glob) | |||
| 934 | } | 1017 | } |
| 935 | 1018 | ||
| 936 | static void print_symbol_events(const char *event_glob, unsigned type, | 1019 | static void print_symbol_events(const char *event_glob, unsigned type, |
| 937 | struct event_symbol *syms, unsigned max) | 1020 | struct event_symbol *syms, unsigned max, |
| 1021 | bool name_only) | ||
| 938 | { | 1022 | { |
| 939 | unsigned i, printed = 0; | 1023 | unsigned i, printed = 0; |
| 940 | char name[MAX_NAME_LEN]; | 1024 | char name[MAX_NAME_LEN]; |
| @@ -946,6 +1030,11 @@ static void print_symbol_events(const char *event_glob, unsigned type, | |||
| 946 | (syms->alias && strglobmatch(syms->alias, event_glob)))) | 1030 | (syms->alias && strglobmatch(syms->alias, event_glob)))) |
| 947 | continue; | 1031 | continue; |
| 948 | 1032 | ||
| 1033 | if (name_only) { | ||
| 1034 | printf("%s ", syms->symbol); | ||
| 1035 | continue; | ||
| 1036 | } | ||
| 1037 | |||
| 949 | if (strlen(syms->alias)) | 1038 | if (strlen(syms->alias)) |
| 950 | snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); | 1039 | snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); |
| 951 | else | 1040 | else |
| @@ -963,39 +1052,42 @@ static void print_symbol_events(const char *event_glob, unsigned type, | |||
| 963 | /* | 1052 | /* |
| 964 | * Print the help text for the event symbols: | 1053 | * Print the help text for the event symbols: |
| 965 | */ | 1054 | */ |
| 966 | void print_events(const char *event_glob) | 1055 | void print_events(const char *event_glob, bool name_only) |
| 967 | { | 1056 | { |
| 968 | 1057 | if (!name_only) { | |
| 969 | printf("\n"); | 1058 | printf("\n"); |
| 970 | printf("List of pre-defined events (to be used in -e):\n"); | 1059 | printf("List of pre-defined events (to be used in -e):\n"); |
| 1060 | } | ||
| 971 | 1061 | ||
| 972 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, | 1062 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, |
| 973 | event_symbols_hw, PERF_COUNT_HW_MAX); | 1063 | event_symbols_hw, PERF_COUNT_HW_MAX, name_only); |
| 974 | 1064 | ||
| 975 | print_symbol_events(event_glob, PERF_TYPE_SOFTWARE, | 1065 | print_symbol_events(event_glob, PERF_TYPE_SOFTWARE, |
| 976 | event_symbols_sw, PERF_COUNT_SW_MAX); | 1066 | event_symbols_sw, PERF_COUNT_SW_MAX, name_only); |
| 977 | 1067 | ||
| 978 | print_hwcache_events(event_glob); | 1068 | print_hwcache_events(event_glob, name_only); |
| 979 | 1069 | ||
| 980 | if (event_glob != NULL) | 1070 | if (event_glob != NULL) |
| 981 | return; | 1071 | return; |
| 982 | 1072 | ||
| 983 | printf("\n"); | 1073 | if (!name_only) { |
| 984 | printf(" %-50s [%s]\n", | 1074 | printf("\n"); |
| 985 | "rNNN", | 1075 | printf(" %-50s [%s]\n", |
| 986 | event_type_descriptors[PERF_TYPE_RAW]); | 1076 | "rNNN", |
| 987 | printf(" %-50s [%s]\n", | 1077 | event_type_descriptors[PERF_TYPE_RAW]); |
| 988 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | 1078 | printf(" %-50s [%s]\n", |
| 989 | event_type_descriptors[PERF_TYPE_RAW]); | 1079 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", |
| 990 | printf(" (see 'perf list --help' on how to encode it)\n"); | 1080 | event_type_descriptors[PERF_TYPE_RAW]); |
| 991 | printf("\n"); | 1081 | printf(" (see 'perf list --help' on how to encode it)\n"); |
| 992 | 1082 | printf("\n"); | |
| 993 | printf(" %-50s [%s]\n", | 1083 | |
| 994 | "mem:<addr>[:access]", | 1084 | printf(" %-50s [%s]\n", |
| 1085 | "mem:<addr>[:access]", | ||
| 995 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | 1086 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); |
| 996 | printf("\n"); | 1087 | printf("\n"); |
| 1088 | } | ||
| 997 | 1089 | ||
| 998 | print_tracepoint_events(NULL, NULL); | 1090 | print_tracepoint_events(NULL, NULL, name_only); |
| 999 | } | 1091 | } |
| 1000 | 1092 | ||
| 1001 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | 1093 | int parse_events__is_hardcoded_term(struct parse_events__term *term) |
| @@ -1005,7 +1097,7 @@ int parse_events__is_hardcoded_term(struct parse_events__term *term) | |||
| 1005 | 1097 | ||
| 1006 | static int new_term(struct parse_events__term **_term, int type_val, | 1098 | static int new_term(struct parse_events__term **_term, int type_val, |
| 1007 | int type_term, char *config, | 1099 | int type_term, char *config, |
| 1008 | char *str, long num) | 1100 | char *str, u64 num) |
| 1009 | { | 1101 | { |
| 1010 | struct parse_events__term *term; | 1102 | struct parse_events__term *term; |
| 1011 | 1103 | ||
| @@ -1034,7 +1126,7 @@ static int new_term(struct parse_events__term **_term, int type_val, | |||
| 1034 | } | 1126 | } |
| 1035 | 1127 | ||
| 1036 | int parse_events__term_num(struct parse_events__term **term, | 1128 | int parse_events__term_num(struct parse_events__term **term, |
| 1037 | int type_term, char *config, long num) | 1129 | int type_term, char *config, u64 num) |
| 1038 | { | 1130 | { |
| 1039 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, | 1131 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, |
| 1040 | config, NULL, num); | 1132 | config, NULL, num); |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index ee9c218a193c..c356e443448d 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -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..443fc116512b 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
| @@ -557,7 +557,8 @@ int parse_options_usage(const char * const *usagestr, | |||
| 557 | } | 557 | } |
| 558 | 558 | ||
| 559 | 559 | ||
| 560 | int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used, | 560 | int parse_opt_verbosity_cb(const struct option *opt, |
| 561 | const char *arg __maybe_unused, | ||
| 561 | int unset) | 562 | int unset) |
| 562 | { | 563 | { |
| 563 | int *target = opt->value; | 564 | int *target = opt->value; |
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h new file mode 100644 index 000000000000..316dbe7f86ed --- /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 | #ifndef NO_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 /* NO_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..53c7794fc4be 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
| @@ -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-ext-sources b/tools/perf/util/python-ext-sources index 213362850abd..c40c2d33199e 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | # | 1 | # |
| 2 | # List of files needed by perf python extention | 2 | # List of files needed by perf python extension |
| 3 | # | 3 | # |
| 4 | # Each source file must be placed on its own line so that it can be | 4 | # Each source file must be placed on its own line so that it can be |
| 5 | # processed by Makefile and util/setup.py accordingly. | 5 | # processed by Makefile and util/setup.py accordingly. |
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..aab414fbb64b 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -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/sort.c b/tools/perf/util/sort.c index 0f5a0a496bc4..b5b1b9211960 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; |
| @@ -399,7 +400,8 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 399 | } | 400 | } |
| 400 | 401 | ||
| 401 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | 402 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, |
| 402 | size_t size, unsigned int width __used) | 403 | size_t size, |
| 404 | unsigned int width __maybe_unused) | ||
| 403 | { | 405 | { |
| 404 | struct addr_map_symbol *from = &self->branch_info->from; | 406 | struct addr_map_symbol *from = &self->branch_info->from; |
| 405 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 407 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, |
| @@ -408,7 +410,8 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | |||
| 408 | } | 410 | } |
| 409 | 411 | ||
| 410 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | 412 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, |
| 411 | size_t size, unsigned int width __used) | 413 | size_t size, |
| 414 | unsigned int width __maybe_unused) | ||
| 412 | { | 415 | { |
| 413 | struct addr_map_symbol *to = &self->branch_info->to; | 416 | struct addr_map_symbol *to = &self->branch_info->to; |
| 414 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 417 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, |
| @@ -509,6 +512,10 @@ int sort_dimension__add(const char *tok) | |||
| 509 | return -EINVAL; | 512 | return -EINVAL; |
| 510 | } | 513 | } |
| 511 | sort__has_parent = 1; | 514 | sort__has_parent = 1; |
| 515 | } else if (sd->entry == &sort_sym || | ||
| 516 | sd->entry == &sort_sym_from || | ||
| 517 | sd->entry == &sort_sym_to) { | ||
| 518 | sort__has_sym = 1; | ||
| 512 | } | 519 | } |
| 513 | 520 | ||
| 514 | if (sd->taken) | 521 | if (sd->taken) |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index e724b26acd51..12d634792de5 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; |
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..b441b07172b7 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 | #ifndef NO_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 |
| @@ -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 | #ifndef NO_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.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..a78c8b303bb5 --- /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 | #ifndef NO_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 /* NO_LIBUNWIND_SUPPORT */ | ||
| 35 | #endif /* __UNWIND_H */ | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index d03599fbe78b..2055cf38041c 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 | #ifndef NO_BACKTRACE | ||
| 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 | #ifndef NO_BACKTRACE | ||
| 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 | ||
