diff options
Diffstat (limited to 'tools/perf/util')
82 files changed, 3896 insertions, 1417 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 15a77b7c0e36..39f17507578d 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN | |||
@@ -19,6 +19,9 @@ if test -d ../../.git -o -f ../../.git | |||
19 | then | 19 | then |
20 | TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null ) | 20 | TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null ) |
21 | CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID" | 21 | CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID" |
22 | elif test -f ../../PERF-VERSION-FILE | ||
23 | then | ||
24 | TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g') | ||
22 | fi | 25 | fi |
23 | if test -z "$TAG" | 26 | if test -z "$TAG" |
24 | then | 27 | then |
@@ -40,7 +43,7 @@ else | |||
40 | VC=unset | 43 | VC=unset |
41 | fi | 44 | fi |
42 | test "$VN" = "$VC" || { | 45 | test "$VN" = "$VC" || { |
43 | echo >&2 "PERF_VERSION = $VN" | 46 | echo >&2 " PERF_VERSION = $VN" |
44 | echo "#define PERF_VERSION \"$VN\"" >$GVF | 47 | echo "#define PERF_VERSION \"$VN\"" >$GVF |
45 | } | 48 | } |
46 | 49 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 7eae5488ecea..cf6242c92ee2 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -825,20 +825,16 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
825 | dl->ops.target.offset = dl->ops.target.addr - | 825 | dl->ops.target.offset = dl->ops.target.addr - |
826 | map__rip_2objdump(map, sym->start); | 826 | map__rip_2objdump(map, sym->start); |
827 | 827 | ||
828 | /* | 828 | /* kcore has no symbols, so add the call target name */ |
829 | * kcore has no symbols, so add the call target name if it is on the | ||
830 | * same map. | ||
831 | */ | ||
832 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { | 829 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { |
833 | struct symbol *s; | 830 | struct addr_map_symbol target = { |
834 | u64 ip = dl->ops.target.addr; | 831 | .map = map, |
835 | 832 | .addr = dl->ops.target.addr, | |
836 | if (ip >= map->start && ip <= map->end) { | 833 | }; |
837 | ip = map->map_ip(map, ip); | 834 | |
838 | s = map__find_symbol(map, ip, NULL); | 835 | if (!map_groups__find_ams(&target, NULL) && |
839 | if (s && s->start == ip) | 836 | target.sym->start == target.al_addr) |
840 | dl->ops.target.name = strdup(s->name); | 837 | dl->ops.target.name = strdup(target.sym->name); |
841 | } | ||
842 | } | 838 | } |
843 | 839 | ||
844 | disasm__add(¬es->src->source, dl); | 840 | disasm__add(¬es->src->source, dl); |
@@ -879,6 +875,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
879 | FILE *file; | 875 | FILE *file; |
880 | int err = 0; | 876 | int err = 0; |
881 | char symfs_filename[PATH_MAX]; | 877 | char symfs_filename[PATH_MAX]; |
878 | struct kcore_extract kce; | ||
879 | bool delete_extract = false; | ||
882 | 880 | ||
883 | if (filename) { | 881 | if (filename) { |
884 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 882 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", |
@@ -940,6 +938,23 @@ fallback: | |||
940 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | 938 | pr_debug("annotating [%p] %30s : [%p] %30s\n", |
941 | dso, dso->long_name, sym, sym->name); | 939 | dso, dso->long_name, sym, sym->name); |
942 | 940 | ||
941 | if (dso__is_kcore(dso)) { | ||
942 | kce.kcore_filename = symfs_filename; | ||
943 | kce.addr = map__rip_2objdump(map, sym->start); | ||
944 | kce.offs = sym->start; | ||
945 | kce.len = sym->end + 1 - sym->start; | ||
946 | if (!kcore_extract__create(&kce)) { | ||
947 | delete_extract = true; | ||
948 | strlcpy(symfs_filename, kce.extract_filename, | ||
949 | sizeof(symfs_filename)); | ||
950 | if (free_filename) { | ||
951 | free(filename); | ||
952 | free_filename = false; | ||
953 | } | ||
954 | filename = symfs_filename; | ||
955 | } | ||
956 | } | ||
957 | |||
943 | snprintf(command, sizeof(command), | 958 | snprintf(command, sizeof(command), |
944 | "%s %s%s --start-address=0x%016" PRIx64 | 959 | "%s %s%s --start-address=0x%016" PRIx64 |
945 | " --stop-address=0x%016" PRIx64 | 960 | " --stop-address=0x%016" PRIx64 |
@@ -972,6 +987,8 @@ fallback: | |||
972 | 987 | ||
973 | pclose(file); | 988 | pclose(file); |
974 | out_free_filename: | 989 | out_free_filename: |
990 | if (delete_extract) | ||
991 | kcore_extract__delete(&kce); | ||
975 | if (free_filename) | 992 | if (free_filename) |
976 | free(filename); | 993 | free(filename); |
977 | return err; | 994 | return err; |
@@ -1070,7 +1087,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) | |||
1070 | (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); | 1087 | (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); |
1071 | 1088 | ||
1072 | for (i = 0; i < len; i++) { | 1089 | for (i = 0; i < len; i++) { |
1073 | free(src_line->path); | 1090 | free_srcline(src_line->path); |
1074 | src_line = (void *)src_line + sizeof_src_line; | 1091 | src_line = (void *)src_line + sizeof_src_line; |
1075 | } | 1092 | } |
1076 | 1093 | ||
@@ -1081,13 +1098,11 @@ static void symbol__free_source_line(struct symbol *sym, int len) | |||
1081 | /* Get the filename:line for the colored entries */ | 1098 | /* Get the filename:line for the colored entries */ |
1082 | static int symbol__get_source_line(struct symbol *sym, struct map *map, | 1099 | static int symbol__get_source_line(struct symbol *sym, struct map *map, |
1083 | struct perf_evsel *evsel, | 1100 | struct perf_evsel *evsel, |
1084 | struct rb_root *root, int len, | 1101 | struct rb_root *root, int len) |
1085 | const char *filename) | ||
1086 | { | 1102 | { |
1087 | u64 start; | 1103 | u64 start; |
1088 | int i, k; | 1104 | int i, k; |
1089 | int evidx = evsel->idx; | 1105 | int evidx = evsel->idx; |
1090 | char cmd[PATH_MAX * 2]; | ||
1091 | struct source_line *src_line; | 1106 | struct source_line *src_line; |
1092 | struct annotation *notes = symbol__annotation(sym); | 1107 | struct annotation *notes = symbol__annotation(sym); |
1093 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1108 | struct sym_hist *h = annotation__histogram(notes, evidx); |
@@ -1115,10 +1130,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
1115 | start = map__rip_2objdump(map, sym->start); | 1130 | start = map__rip_2objdump(map, sym->start); |
1116 | 1131 | ||
1117 | for (i = 0; i < len; i++) { | 1132 | for (i = 0; i < len; i++) { |
1118 | char *path = NULL; | ||
1119 | size_t line_len; | ||
1120 | u64 offset; | 1133 | u64 offset; |
1121 | FILE *fp; | ||
1122 | double percent_max = 0.0; | 1134 | double percent_max = 0.0; |
1123 | 1135 | ||
1124 | src_line->nr_pcnt = nr_pcnt; | 1136 | src_line->nr_pcnt = nr_pcnt; |
@@ -1135,23 +1147,9 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
1135 | goto next; | 1147 | goto next; |
1136 | 1148 | ||
1137 | offset = start + i; | 1149 | offset = start + i; |
1138 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | 1150 | src_line->path = get_srcline(map->dso, offset); |
1139 | fp = popen(cmd, "r"); | ||
1140 | if (!fp) | ||
1141 | goto next; | ||
1142 | |||
1143 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
1144 | goto next_close; | ||
1145 | |||
1146 | src_line->path = malloc(sizeof(char) * line_len + 1); | ||
1147 | if (!src_line->path) | ||
1148 | goto next_close; | ||
1149 | |||
1150 | strcpy(src_line->path, path); | ||
1151 | insert_source_line(&tmp_root, src_line); | 1151 | insert_source_line(&tmp_root, src_line); |
1152 | 1152 | ||
1153 | next_close: | ||
1154 | pclose(fp); | ||
1155 | next: | 1153 | next: |
1156 | src_line = (void *)src_line + sizeof_src_line; | 1154 | src_line = (void *)src_line + sizeof_src_line; |
1157 | } | 1155 | } |
@@ -1192,7 +1190,7 @@ static void print_summary(struct rb_root *root, const char *filename) | |||
1192 | 1190 | ||
1193 | path = src_line->path; | 1191 | path = src_line->path; |
1194 | color = get_percent_color(percent_max); | 1192 | color = get_percent_color(percent_max); |
1195 | color_fprintf(stdout, color, " %s", path); | 1193 | color_fprintf(stdout, color, " %s\n", path); |
1196 | 1194 | ||
1197 | node = rb_next(node); | 1195 | node = rb_next(node); |
1198 | } | 1196 | } |
@@ -1356,7 +1354,6 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
1356 | bool full_paths, int min_pcnt, int max_lines) | 1354 | bool full_paths, int min_pcnt, int max_lines) |
1357 | { | 1355 | { |
1358 | struct dso *dso = map->dso; | 1356 | struct dso *dso = map->dso; |
1359 | const char *filename = dso->long_name; | ||
1360 | struct rb_root source_line = RB_ROOT; | 1357 | struct rb_root source_line = RB_ROOT; |
1361 | u64 len; | 1358 | u64 len; |
1362 | 1359 | ||
@@ -1366,9 +1363,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
1366 | len = symbol__size(sym); | 1363 | len = symbol__size(sym); |
1367 | 1364 | ||
1368 | if (print_lines) { | 1365 | if (print_lines) { |
1369 | symbol__get_source_line(sym, map, evsel, &source_line, | 1366 | symbol__get_source_line(sym, map, evsel, &source_line, len); |
1370 | len, filename); | 1367 | print_summary(&source_line, dso->long_name); |
1371 | print_summary(&source_line, filename); | ||
1372 | } | 1368 | } |
1373 | 1369 | ||
1374 | symbol__annotate_printf(sym, map, evsel, full_paths, | 1370 | symbol__annotate_printf(sym, map, evsel, full_paths, |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index af755156d278..834b7b57b788 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -150,7 +150,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
150 | struct perf_evsel *evsel, bool print_lines, | 150 | struct perf_evsel *evsel, bool print_lines, |
151 | bool full_paths, int min_pcnt, int max_lines); | 151 | bool full_paths, int min_pcnt, int max_lines); |
152 | 152 | ||
153 | #ifdef SLANG_SUPPORT | 153 | #ifdef HAVE_SLANG_SUPPORT |
154 | int symbol__tui_annotate(struct symbol *sym, struct map *map, | 154 | int symbol__tui_annotate(struct symbol *sym, struct map *map, |
155 | struct perf_evsel *evsel, | 155 | struct perf_evsel *evsel, |
156 | struct hist_browser_timer *hbt); | 156 | struct hist_browser_timer *hbt); |
@@ -165,30 +165,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, | |||
165 | } | 165 | } |
166 | #endif | 166 | #endif |
167 | 167 | ||
168 | #ifdef GTK2_SUPPORT | ||
169 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, | ||
170 | struct perf_evsel *evsel, | ||
171 | struct hist_browser_timer *hbt); | ||
172 | |||
173 | static inline int hist_entry__gtk_annotate(struct hist_entry *he, | ||
174 | struct perf_evsel *evsel, | ||
175 | struct hist_browser_timer *hbt) | ||
176 | { | ||
177 | return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); | ||
178 | } | ||
179 | |||
180 | void perf_gtk__show_annotations(void); | ||
181 | #else | ||
182 | static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, | ||
183 | struct perf_evsel *evsel __maybe_unused, | ||
184 | struct hist_browser_timer *hbt __maybe_unused) | ||
185 | { | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static inline void perf_gtk__show_annotations(void) {} | ||
190 | #endif | ||
191 | |||
192 | extern const char *disassembler_style; | 168 | extern const char *disassembler_style; |
193 | 169 | ||
194 | #endif /* __PERF_ANNOTATE_H */ | 170 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 7ded71d19d75..a92770c98cc7 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -89,14 +89,14 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf) | |||
89 | return raw - build_id; | 89 | return raw - build_id; |
90 | } | 90 | } |
91 | 91 | ||
92 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 92 | char *dso__build_id_filename(struct dso *dso, char *bf, size_t size) |
93 | { | 93 | { |
94 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 94 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
95 | 95 | ||
96 | if (!self->has_build_id) | 96 | if (!dso->has_build_id) |
97 | return NULL; | 97 | return NULL; |
98 | 98 | ||
99 | build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); | 99 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); |
100 | if (bf == NULL) { | 100 | if (bf == NULL) { |
101 | if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, | 101 | if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, |
102 | build_id_hex, build_id_hex + 2) < 0) | 102 | build_id_hex, build_id_hex + 2) < 0) |
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a811f5c62e18..929f28a7c14d 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -10,10 +10,9 @@ extern struct perf_tool build_id__mark_dso_hit_ops; | |||
10 | struct dso; | 10 | struct dso; |
11 | 11 | ||
12 | int build_id__sprintf(const u8 *build_id, int len, char *bf); | 12 | int build_id__sprintf(const u8 *build_id, int len, char *bf); |
13 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size); | 13 | char *dso__build_id_filename(struct dso *dso, char *bf, size_t size); |
14 | 14 | ||
15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | 15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, |
16 | struct perf_sample *sample, struct perf_evsel *evsel, | 16 | struct perf_sample *sample, struct perf_evsel *evsel, |
17 | struct machine *machine); | 17 | struct machine *machine); |
18 | |||
19 | #endif | 18 | #endif |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 26e367239873..7b176dd02e1a 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -70,8 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 | |||
70 | extern char *perf_pathdup(const char *fmt, ...) | 70 | extern char *perf_pathdup(const char *fmt, ...) |
71 | __attribute__((format (printf, 1, 2))); | 71 | __attribute__((format (printf, 1, 2))); |
72 | 72 | ||
73 | #ifndef HAVE_STRLCPY | 73 | /* Matches the libc/libbsd function attribute so we declare this unconditionally: */ |
74 | extern size_t strlcpy(char *dest, const char *src, size_t size); | 74 | extern size_t strlcpy(char *dest, const char *src, size_t size); |
75 | #endif | ||
76 | 75 | ||
77 | #endif /* __PERF_CACHE_H */ | 76 | #endif /* __PERF_CACHE_H */ |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 482f68081cd8..e3970e3eaacf 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -21,12 +21,6 @@ | |||
21 | 21 | ||
22 | __thread struct callchain_cursor callchain_cursor; | 22 | __thread struct callchain_cursor callchain_cursor; |
23 | 23 | ||
24 | #define chain_for_each_child(child, parent) \ | ||
25 | list_for_each_entry(child, &parent->children, siblings) | ||
26 | |||
27 | #define chain_for_each_child_safe(child, next, parent) \ | ||
28 | list_for_each_entry_safe(child, next, &parent->children, siblings) | ||
29 | |||
30 | static void | 24 | static void |
31 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 25 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
32 | enum chain_mode mode) | 26 | enum chain_mode mode) |
@@ -71,10 +65,16 @@ static void | |||
71 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | 65 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, |
72 | u64 min_hit) | 66 | u64 min_hit) |
73 | { | 67 | { |
68 | struct rb_node *n; | ||
74 | struct callchain_node *child; | 69 | struct callchain_node *child; |
75 | 70 | ||
76 | chain_for_each_child(child, node) | 71 | n = rb_first(&node->rb_root_in); |
72 | while (n) { | ||
73 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
74 | n = rb_next(n); | ||
75 | |||
77 | __sort_chain_flat(rb_root, child, min_hit); | 76 | __sort_chain_flat(rb_root, child, min_hit); |
77 | } | ||
78 | 78 | ||
79 | if (node->hit && node->hit >= min_hit) | 79 | if (node->hit && node->hit >= min_hit) |
80 | rb_insert_callchain(rb_root, node, CHAIN_FLAT); | 80 | rb_insert_callchain(rb_root, node, CHAIN_FLAT); |
@@ -94,11 +94,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, | |||
94 | static void __sort_chain_graph_abs(struct callchain_node *node, | 94 | static void __sort_chain_graph_abs(struct callchain_node *node, |
95 | u64 min_hit) | 95 | u64 min_hit) |
96 | { | 96 | { |
97 | struct rb_node *n; | ||
97 | struct callchain_node *child; | 98 | struct callchain_node *child; |
98 | 99 | ||
99 | node->rb_root = RB_ROOT; | 100 | node->rb_root = RB_ROOT; |
101 | n = rb_first(&node->rb_root_in); | ||
102 | |||
103 | while (n) { | ||
104 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
105 | n = rb_next(n); | ||
100 | 106 | ||
101 | chain_for_each_child(child, node) { | ||
102 | __sort_chain_graph_abs(child, min_hit); | 107 | __sort_chain_graph_abs(child, min_hit); |
103 | if (callchain_cumul_hits(child) >= min_hit) | 108 | if (callchain_cumul_hits(child) >= min_hit) |
104 | rb_insert_callchain(&node->rb_root, child, | 109 | rb_insert_callchain(&node->rb_root, child, |
@@ -117,13 +122,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, | |||
117 | static void __sort_chain_graph_rel(struct callchain_node *node, | 122 | static void __sort_chain_graph_rel(struct callchain_node *node, |
118 | double min_percent) | 123 | double min_percent) |
119 | { | 124 | { |
125 | struct rb_node *n; | ||
120 | struct callchain_node *child; | 126 | struct callchain_node *child; |
121 | u64 min_hit; | 127 | u64 min_hit; |
122 | 128 | ||
123 | node->rb_root = RB_ROOT; | 129 | node->rb_root = RB_ROOT; |
124 | min_hit = ceil(node->children_hit * min_percent); | 130 | min_hit = ceil(node->children_hit * min_percent); |
125 | 131 | ||
126 | chain_for_each_child(child, node) { | 132 | n = rb_first(&node->rb_root_in); |
133 | while (n) { | ||
134 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
135 | n = rb_next(n); | ||
136 | |||
127 | __sort_chain_graph_rel(child, min_percent); | 137 | __sort_chain_graph_rel(child, min_percent); |
128 | if (callchain_cumul_hits(child) >= min_hit) | 138 | if (callchain_cumul_hits(child) >= min_hit) |
129 | rb_insert_callchain(&node->rb_root, child, | 139 | rb_insert_callchain(&node->rb_root, child, |
@@ -173,19 +183,26 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
173 | return NULL; | 183 | return NULL; |
174 | } | 184 | } |
175 | new->parent = parent; | 185 | new->parent = parent; |
176 | INIT_LIST_HEAD(&new->children); | ||
177 | INIT_LIST_HEAD(&new->val); | 186 | INIT_LIST_HEAD(&new->val); |
178 | 187 | ||
179 | if (inherit_children) { | 188 | if (inherit_children) { |
180 | struct callchain_node *next; | 189 | struct rb_node *n; |
190 | struct callchain_node *child; | ||
191 | |||
192 | new->rb_root_in = parent->rb_root_in; | ||
193 | parent->rb_root_in = RB_ROOT; | ||
181 | 194 | ||
182 | list_splice(&parent->children, &new->children); | 195 | n = rb_first(&new->rb_root_in); |
183 | INIT_LIST_HEAD(&parent->children); | 196 | while (n) { |
197 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
198 | child->parent = new; | ||
199 | n = rb_next(n); | ||
200 | } | ||
184 | 201 | ||
185 | chain_for_each_child(next, new) | 202 | /* make it the first child */ |
186 | next->parent = new; | 203 | rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); |
204 | rb_insert_color(&new->rb_node_in, &parent->rb_root_in); | ||
187 | } | 205 | } |
188 | list_add_tail(&new->siblings, &parent->children); | ||
189 | 206 | ||
190 | return new; | 207 | return new; |
191 | } | 208 | } |
@@ -223,7 +240,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | |||
223 | } | 240 | } |
224 | } | 241 | } |
225 | 242 | ||
226 | static void | 243 | static struct callchain_node * |
227 | add_child(struct callchain_node *parent, | 244 | add_child(struct callchain_node *parent, |
228 | struct callchain_cursor *cursor, | 245 | struct callchain_cursor *cursor, |
229 | u64 period) | 246 | u64 period) |
@@ -235,6 +252,19 @@ add_child(struct callchain_node *parent, | |||
235 | 252 | ||
236 | new->children_hit = 0; | 253 | new->children_hit = 0; |
237 | new->hit = period; | 254 | new->hit = period; |
255 | return new; | ||
256 | } | ||
257 | |||
258 | static s64 match_chain(struct callchain_cursor_node *node, | ||
259 | struct callchain_list *cnode) | ||
260 | { | ||
261 | struct symbol *sym = node->sym; | ||
262 | |||
263 | if (cnode->ms.sym && sym && | ||
264 | callchain_param.key == CCKEY_FUNCTION) | ||
265 | return cnode->ms.sym->start - sym->start; | ||
266 | else | ||
267 | return cnode->ip - node->ip; | ||
238 | } | 268 | } |
239 | 269 | ||
240 | /* | 270 | /* |
@@ -272,9 +302,33 @@ split_add_child(struct callchain_node *parent, | |||
272 | 302 | ||
273 | /* create a new child for the new branch if any */ | 303 | /* create a new child for the new branch if any */ |
274 | if (idx_total < cursor->nr) { | 304 | if (idx_total < cursor->nr) { |
305 | struct callchain_node *first; | ||
306 | struct callchain_list *cnode; | ||
307 | struct callchain_cursor_node *node; | ||
308 | struct rb_node *p, **pp; | ||
309 | |||
275 | parent->hit = 0; | 310 | parent->hit = 0; |
276 | add_child(parent, cursor, period); | ||
277 | parent->children_hit += period; | 311 | parent->children_hit += period; |
312 | |||
313 | node = callchain_cursor_current(cursor); | ||
314 | new = add_child(parent, cursor, period); | ||
315 | |||
316 | /* | ||
317 | * This is second child since we moved parent's children | ||
318 | * to new (first) child above. | ||
319 | */ | ||
320 | p = parent->rb_root_in.rb_node; | ||
321 | first = rb_entry(p, struct callchain_node, rb_node_in); | ||
322 | cnode = list_first_entry(&first->val, struct callchain_list, | ||
323 | list); | ||
324 | |||
325 | if (match_chain(node, cnode) < 0) | ||
326 | pp = &p->rb_left; | ||
327 | else | ||
328 | pp = &p->rb_right; | ||
329 | |||
330 | rb_link_node(&new->rb_node_in, p, pp); | ||
331 | rb_insert_color(&new->rb_node_in, &parent->rb_root_in); | ||
278 | } else { | 332 | } else { |
279 | parent->hit = period; | 333 | parent->hit = period; |
280 | } | 334 | } |
@@ -291,16 +345,40 @@ append_chain_children(struct callchain_node *root, | |||
291 | u64 period) | 345 | u64 period) |
292 | { | 346 | { |
293 | struct callchain_node *rnode; | 347 | struct callchain_node *rnode; |
348 | struct callchain_cursor_node *node; | ||
349 | struct rb_node **p = &root->rb_root_in.rb_node; | ||
350 | struct rb_node *parent = NULL; | ||
351 | |||
352 | node = callchain_cursor_current(cursor); | ||
353 | if (!node) | ||
354 | return; | ||
294 | 355 | ||
295 | /* lookup in childrens */ | 356 | /* lookup in childrens */ |
296 | chain_for_each_child(rnode, root) { | 357 | while (*p) { |
297 | unsigned int ret = append_chain(rnode, cursor, period); | 358 | s64 ret; |
359 | struct callchain_list *cnode; | ||
298 | 360 | ||
299 | if (!ret) | 361 | parent = *p; |
362 | rnode = rb_entry(parent, struct callchain_node, rb_node_in); | ||
363 | cnode = list_first_entry(&rnode->val, struct callchain_list, | ||
364 | list); | ||
365 | |||
366 | /* just check first entry */ | ||
367 | ret = match_chain(node, cnode); | ||
368 | if (ret == 0) { | ||
369 | append_chain(rnode, cursor, period); | ||
300 | goto inc_children_hit; | 370 | goto inc_children_hit; |
371 | } | ||
372 | |||
373 | if (ret < 0) | ||
374 | p = &parent->rb_left; | ||
375 | else | ||
376 | p = &parent->rb_right; | ||
301 | } | 377 | } |
302 | /* nothing in children, add to the current node */ | 378 | /* nothing in children, add to the current node */ |
303 | add_child(root, cursor, period); | 379 | rnode = add_child(root, cursor, period); |
380 | rb_link_node(&rnode->rb_node_in, parent, p); | ||
381 | rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); | ||
304 | 382 | ||
305 | inc_children_hit: | 383 | inc_children_hit: |
306 | root->children_hit += period; | 384 | root->children_hit += period; |
@@ -325,28 +403,20 @@ append_chain(struct callchain_node *root, | |||
325 | */ | 403 | */ |
326 | list_for_each_entry(cnode, &root->val, list) { | 404 | list_for_each_entry(cnode, &root->val, list) { |
327 | struct callchain_cursor_node *node; | 405 | struct callchain_cursor_node *node; |
328 | struct symbol *sym; | ||
329 | 406 | ||
330 | node = callchain_cursor_current(cursor); | 407 | node = callchain_cursor_current(cursor); |
331 | if (!node) | 408 | if (!node) |
332 | break; | 409 | break; |
333 | 410 | ||
334 | sym = node->sym; | 411 | if (match_chain(node, cnode) != 0) |
335 | |||
336 | if (cnode->ms.sym && sym && | ||
337 | callchain_param.key == CCKEY_FUNCTION) { | ||
338 | if (cnode->ms.sym->start != sym->start) | ||
339 | break; | ||
340 | } else if (cnode->ip != node->ip) | ||
341 | break; | 412 | break; |
342 | 413 | ||
343 | if (!found) | 414 | found = true; |
344 | found = true; | ||
345 | 415 | ||
346 | callchain_cursor_advance(cursor); | 416 | callchain_cursor_advance(cursor); |
347 | } | 417 | } |
348 | 418 | ||
349 | /* matches not, relay on the parent */ | 419 | /* matches not, relay no the parent */ |
350 | if (!found) { | 420 | if (!found) { |
351 | cursor->curr = curr_snap; | 421 | cursor->curr = curr_snap; |
352 | cursor->pos = start; | 422 | cursor->pos = start; |
@@ -395,8 +465,9 @@ merge_chain_branch(struct callchain_cursor *cursor, | |||
395 | struct callchain_node *dst, struct callchain_node *src) | 465 | struct callchain_node *dst, struct callchain_node *src) |
396 | { | 466 | { |
397 | struct callchain_cursor_node **old_last = cursor->last; | 467 | struct callchain_cursor_node **old_last = cursor->last; |
398 | struct callchain_node *child, *next_child; | 468 | struct callchain_node *child; |
399 | struct callchain_list *list, *next_list; | 469 | struct callchain_list *list, *next_list; |
470 | struct rb_node *n; | ||
400 | int old_pos = cursor->nr; | 471 | int old_pos = cursor->nr; |
401 | int err = 0; | 472 | int err = 0; |
402 | 473 | ||
@@ -412,12 +483,16 @@ merge_chain_branch(struct callchain_cursor *cursor, | |||
412 | append_chain_children(dst, cursor, src->hit); | 483 | append_chain_children(dst, cursor, src->hit); |
413 | } | 484 | } |
414 | 485 | ||
415 | chain_for_each_child_safe(child, next_child, src) { | 486 | n = rb_first(&src->rb_root_in); |
487 | while (n) { | ||
488 | child = container_of(n, struct callchain_node, rb_node_in); | ||
489 | n = rb_next(n); | ||
490 | rb_erase(&child->rb_node_in, &src->rb_root_in); | ||
491 | |||
416 | err = merge_chain_branch(cursor, dst, child); | 492 | err = merge_chain_branch(cursor, dst, child); |
417 | if (err) | 493 | if (err) |
418 | break; | 494 | break; |
419 | 495 | ||
420 | list_del(&child->siblings); | ||
421 | free(child); | 496 | free(child); |
422 | } | 497 | } |
423 | 498 | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2b585bc308cf..4f7f989876ec 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -21,11 +21,11 @@ enum chain_order { | |||
21 | 21 | ||
22 | struct callchain_node { | 22 | struct callchain_node { |
23 | struct callchain_node *parent; | 23 | struct callchain_node *parent; |
24 | struct list_head siblings; | ||
25 | struct list_head children; | ||
26 | struct list_head val; | 24 | struct list_head val; |
27 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | 25 | struct rb_node rb_node_in; /* to insert nodes in an rbtree */ |
28 | struct rb_root rb_root; /* sorted tree of children */ | 26 | struct rb_node rb_node; /* to sort nodes in an output tree */ |
27 | struct rb_root rb_root_in; /* input tree of children */ | ||
28 | struct rb_root rb_root; /* sorted output tree of children */ | ||
29 | unsigned int val_nr; | 29 | unsigned int val_nr; |
30 | u64 hit; | 30 | u64 hit; |
31 | u64 children_hit; | 31 | u64 children_hit; |
@@ -86,13 +86,12 @@ extern __thread struct callchain_cursor callchain_cursor; | |||
86 | 86 | ||
87 | static inline void callchain_init(struct callchain_root *root) | 87 | static inline void callchain_init(struct callchain_root *root) |
88 | { | 88 | { |
89 | INIT_LIST_HEAD(&root->node.siblings); | ||
90 | INIT_LIST_HEAD(&root->node.children); | ||
91 | INIT_LIST_HEAD(&root->node.val); | 89 | INIT_LIST_HEAD(&root->node.val); |
92 | 90 | ||
93 | root->node.parent = NULL; | 91 | root->node.parent = NULL; |
94 | root->node.hit = 0; | 92 | root->node.hit = 0; |
95 | root->node.children_hit = 0; | 93 | root->node.children_hit = 0; |
94 | root->node.rb_root_in = RB_ROOT; | ||
96 | root->max_depth = 0; | 95 | root->max_depth = 0; |
97 | } | 96 | } |
98 | 97 | ||
@@ -147,6 +146,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
147 | 146 | ||
148 | struct option; | 147 | struct option; |
149 | 148 | ||
149 | int record_parse_callchain(const char *arg, struct perf_record_opts *opts); | ||
150 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | 150 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); |
151 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); | ||
152 | |||
151 | extern const char record_callchain_help[]; | 153 | extern const char record_callchain_help[]; |
152 | #endif /* __PERF_CALLCHAIN_H */ | 154 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 11e46da17bbb..66e44a5019d5 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -318,8 +318,15 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) | |||
318 | return r; | 318 | return r; |
319 | } | 319 | } |
320 | 320 | ||
321 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) | 321 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) |
322 | { | 322 | { |
323 | const char *color = get_percent_color(percent); | 323 | va_list args; |
324 | double percent; | ||
325 | const char *color; | ||
326 | |||
327 | va_start(args, fmt); | ||
328 | percent = va_arg(args, double); | ||
329 | va_end(args); | ||
330 | color = get_percent_color(percent); | ||
324 | return color_snprintf(bf, size, color, fmt, percent); | 331 | return color_snprintf(bf, size, color, fmt, percent); |
325 | } | 332 | } |
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index dea082b79602..fced3840e99c 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
@@ -39,7 +39,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); | |||
39 | int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); | 39 | int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); |
40 | int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); | 40 | int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); |
41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); | 41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); |
42 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); | 42 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); |
43 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 43 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
44 | const char *get_percent_color(double percent); | 44 | const char *get_percent_color(double percent); |
45 | 45 | ||
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c new file mode 100644 index 000000000000..ee0df0e24cdb --- /dev/null +++ b/tools/perf/util/comm.c | |||
@@ -0,0 +1,121 @@ | |||
1 | #include "comm.h" | ||
2 | #include "util.h" | ||
3 | #include <stdlib.h> | ||
4 | #include <stdio.h> | ||
5 | |||
6 | struct comm_str { | ||
7 | char *str; | ||
8 | struct rb_node rb_node; | ||
9 | int ref; | ||
10 | }; | ||
11 | |||
12 | /* Should perhaps be moved to struct machine */ | ||
13 | static struct rb_root comm_str_root; | ||
14 | |||
15 | static void comm_str__get(struct comm_str *cs) | ||
16 | { | ||
17 | cs->ref++; | ||
18 | } | ||
19 | |||
20 | static void comm_str__put(struct comm_str *cs) | ||
21 | { | ||
22 | if (!--cs->ref) { | ||
23 | rb_erase(&cs->rb_node, &comm_str_root); | ||
24 | free(cs->str); | ||
25 | free(cs); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | static struct comm_str *comm_str__alloc(const char *str) | ||
30 | { | ||
31 | struct comm_str *cs; | ||
32 | |||
33 | cs = zalloc(sizeof(*cs)); | ||
34 | if (!cs) | ||
35 | return NULL; | ||
36 | |||
37 | cs->str = strdup(str); | ||
38 | if (!cs->str) { | ||
39 | free(cs); | ||
40 | return NULL; | ||
41 | } | ||
42 | |||
43 | return cs; | ||
44 | } | ||
45 | |||
46 | static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) | ||
47 | { | ||
48 | struct rb_node **p = &root->rb_node; | ||
49 | struct rb_node *parent = NULL; | ||
50 | struct comm_str *iter, *new; | ||
51 | int cmp; | ||
52 | |||
53 | while (*p != NULL) { | ||
54 | parent = *p; | ||
55 | iter = rb_entry(parent, struct comm_str, rb_node); | ||
56 | |||
57 | cmp = strcmp(str, iter->str); | ||
58 | if (!cmp) | ||
59 | return iter; | ||
60 | |||
61 | if (cmp < 0) | ||
62 | p = &(*p)->rb_left; | ||
63 | else | ||
64 | p = &(*p)->rb_right; | ||
65 | } | ||
66 | |||
67 | new = comm_str__alloc(str); | ||
68 | if (!new) | ||
69 | return NULL; | ||
70 | |||
71 | rb_link_node(&new->rb_node, parent, p); | ||
72 | rb_insert_color(&new->rb_node, root); | ||
73 | |||
74 | return new; | ||
75 | } | ||
76 | |||
77 | struct comm *comm__new(const char *str, u64 timestamp) | ||
78 | { | ||
79 | struct comm *comm = zalloc(sizeof(*comm)); | ||
80 | |||
81 | if (!comm) | ||
82 | return NULL; | ||
83 | |||
84 | comm->start = timestamp; | ||
85 | |||
86 | comm->comm_str = comm_str__findnew(str, &comm_str_root); | ||
87 | if (!comm->comm_str) { | ||
88 | free(comm); | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | comm_str__get(comm->comm_str); | ||
93 | |||
94 | return comm; | ||
95 | } | ||
96 | |||
97 | void comm__override(struct comm *comm, const char *str, u64 timestamp) | ||
98 | { | ||
99 | struct comm_str *old = comm->comm_str; | ||
100 | |||
101 | comm->comm_str = comm_str__findnew(str, &comm_str_root); | ||
102 | if (!comm->comm_str) { | ||
103 | comm->comm_str = old; | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | comm->start = timestamp; | ||
108 | comm_str__get(comm->comm_str); | ||
109 | comm_str__put(old); | ||
110 | } | ||
111 | |||
112 | void comm__free(struct comm *comm) | ||
113 | { | ||
114 | comm_str__put(comm->comm_str); | ||
115 | free(comm); | ||
116 | } | ||
117 | |||
118 | const char *comm__str(const struct comm *comm) | ||
119 | { | ||
120 | return comm->comm_str->str; | ||
121 | } | ||
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h new file mode 100644 index 000000000000..7a86e5656710 --- /dev/null +++ b/tools/perf/util/comm.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef __PERF_COMM_H | ||
2 | #define __PERF_COMM_H | ||
3 | |||
4 | #include "../perf.h" | ||
5 | #include <linux/rbtree.h> | ||
6 | #include <linux/list.h> | ||
7 | |||
8 | struct comm_str; | ||
9 | |||
10 | struct comm { | ||
11 | struct comm_str *comm_str; | ||
12 | u64 start; | ||
13 | struct list_head list; | ||
14 | }; | ||
15 | |||
16 | void comm__free(struct comm *comm); | ||
17 | struct comm *comm__new(const char *str, u64 timestamp); | ||
18 | const char *comm__str(const struct comm *comm); | ||
19 | void comm__override(struct comm *comm, const char *str, u64 timestamp); | ||
20 | |||
21 | #endif /* __PERF_COMM_H */ | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index beb8cf9f9976..a9b48c42e81e 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "sysfs.h" | 2 | #include "fs.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "cpumap.h" | 4 | #include "cpumap.h" |
5 | #include <assert.h> | 5 | #include <assert.h> |
@@ -216,7 +216,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) | |||
216 | 216 | ||
217 | cpu = map->map[idx]; | 217 | cpu = map->map[idx]; |
218 | 218 | ||
219 | mnt = sysfs_find_mountpoint(); | 219 | mnt = sysfs__mountpoint(); |
220 | if (!mnt) | 220 | if (!mnt) |
221 | return -1; | 221 | return -1; |
222 | 222 | ||
@@ -279,7 +279,7 @@ int cpu_map__get_core(struct cpu_map *map, int idx) | |||
279 | 279 | ||
280 | cpu = map->map[idx]; | 280 | cpu = map->map[idx]; |
281 | 281 | ||
282 | mnt = sysfs_find_mountpoint(); | 282 | mnt = sysfs__mountpoint(); |
283 | if (!mnt) | 283 | if (!mnt) |
284 | return -1; | 284 | return -1; |
285 | 285 | ||
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 000000000000..7d09faf85cf1 --- /dev/null +++ b/tools/perf/util/data.c | |||
@@ -0,0 +1,120 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <unistd.h> | ||
6 | #include <string.h> | ||
7 | |||
8 | #include "data.h" | ||
9 | #include "util.h" | ||
10 | |||
11 | static bool check_pipe(struct perf_data_file *file) | ||
12 | { | ||
13 | struct stat st; | ||
14 | bool is_pipe = false; | ||
15 | int fd = perf_data_file__is_read(file) ? | ||
16 | STDIN_FILENO : STDOUT_FILENO; | ||
17 | |||
18 | if (!file->path) { | ||
19 | if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) | ||
20 | is_pipe = true; | ||
21 | } else { | ||
22 | if (!strcmp(file->path, "-")) | ||
23 | is_pipe = true; | ||
24 | } | ||
25 | |||
26 | if (is_pipe) | ||
27 | file->fd = fd; | ||
28 | |||
29 | return file->is_pipe = is_pipe; | ||
30 | } | ||
31 | |||
32 | static int check_backup(struct perf_data_file *file) | ||
33 | { | ||
34 | struct stat st; | ||
35 | |||
36 | if (!stat(file->path, &st) && st.st_size) { | ||
37 | /* TODO check errors properly */ | ||
38 | char oldname[PATH_MAX]; | ||
39 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
40 | file->path); | ||
41 | unlink(oldname); | ||
42 | rename(file->path, oldname); | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int open_file_read(struct perf_data_file *file) | ||
49 | { | ||
50 | struct stat st; | ||
51 | int fd; | ||
52 | |||
53 | fd = open(file->path, O_RDONLY); | ||
54 | if (fd < 0) { | ||
55 | int err = errno; | ||
56 | |||
57 | pr_err("failed to open %s: %s", file->path, strerror(err)); | ||
58 | if (err == ENOENT && !strcmp(file->path, "perf.data")) | ||
59 | pr_err(" (try 'perf record' first)"); | ||
60 | pr_err("\n"); | ||
61 | return -err; | ||
62 | } | ||
63 | |||
64 | if (fstat(fd, &st) < 0) | ||
65 | goto out_close; | ||
66 | |||
67 | if (!file->force && st.st_uid && (st.st_uid != geteuid())) { | ||
68 | pr_err("file %s not owned by current user or root\n", | ||
69 | file->path); | ||
70 | goto out_close; | ||
71 | } | ||
72 | |||
73 | if (!st.st_size) { | ||
74 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
75 | file->path); | ||
76 | goto out_close; | ||
77 | } | ||
78 | |||
79 | file->size = st.st_size; | ||
80 | return fd; | ||
81 | |||
82 | out_close: | ||
83 | close(fd); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | static int open_file_write(struct perf_data_file *file) | ||
88 | { | ||
89 | if (check_backup(file)) | ||
90 | return -1; | ||
91 | |||
92 | return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); | ||
93 | } | ||
94 | |||
95 | static int open_file(struct perf_data_file *file) | ||
96 | { | ||
97 | int fd; | ||
98 | |||
99 | fd = perf_data_file__is_read(file) ? | ||
100 | open_file_read(file) : open_file_write(file); | ||
101 | |||
102 | file->fd = fd; | ||
103 | return fd < 0 ? -1 : 0; | ||
104 | } | ||
105 | |||
106 | int perf_data_file__open(struct perf_data_file *file) | ||
107 | { | ||
108 | if (check_pipe(file)) | ||
109 | return 0; | ||
110 | |||
111 | if (!file->path) | ||
112 | file->path = "perf.data"; | ||
113 | |||
114 | return open_file(file); | ||
115 | } | ||
116 | |||
117 | void perf_data_file__close(struct perf_data_file *file) | ||
118 | { | ||
119 | close(file->fd); | ||
120 | } | ||
diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h new file mode 100644 index 000000000000..8c2df80152a5 --- /dev/null +++ b/tools/perf/util/data.h | |||
@@ -0,0 +1,48 @@ | |||
1 | #ifndef __PERF_DATA_H | ||
2 | #define __PERF_DATA_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | |||
6 | enum perf_data_mode { | ||
7 | PERF_DATA_MODE_WRITE, | ||
8 | PERF_DATA_MODE_READ, | ||
9 | }; | ||
10 | |||
11 | struct perf_data_file { | ||
12 | const char *path; | ||
13 | int fd; | ||
14 | bool is_pipe; | ||
15 | bool force; | ||
16 | unsigned long size; | ||
17 | enum perf_data_mode mode; | ||
18 | }; | ||
19 | |||
20 | static inline bool perf_data_file__is_read(struct perf_data_file *file) | ||
21 | { | ||
22 | return file->mode == PERF_DATA_MODE_READ; | ||
23 | } | ||
24 | |||
25 | static inline bool perf_data_file__is_write(struct perf_data_file *file) | ||
26 | { | ||
27 | return file->mode == PERF_DATA_MODE_WRITE; | ||
28 | } | ||
29 | |||
30 | static inline int perf_data_file__is_pipe(struct perf_data_file *file) | ||
31 | { | ||
32 | return file->is_pipe; | ||
33 | } | ||
34 | |||
35 | static inline int perf_data_file__fd(struct perf_data_file *file) | ||
36 | { | ||
37 | return file->fd; | ||
38 | } | ||
39 | |||
40 | static inline unsigned long perf_data_file__size(struct perf_data_file *file) | ||
41 | { | ||
42 | return file->size; | ||
43 | } | ||
44 | |||
45 | int perf_data_file__open(struct perf_data_file *file); | ||
46 | void perf_data_file__close(struct perf_data_file *file); | ||
47 | |||
48 | #endif /* __PERF_DATA_H */ | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e3c1ff8512c8..af4c687cc49b 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -7,19 +7,20 @@ | |||
7 | char dso__symtab_origin(const struct dso *dso) | 7 | char dso__symtab_origin(const struct dso *dso) |
8 | { | 8 | { |
9 | static const char origin[] = { | 9 | static const char origin[] = { |
10 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | 10 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', |
11 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | 11 | [DSO_BINARY_TYPE__VMLINUX] = 'v', |
12 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | 12 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', |
13 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | 13 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', |
14 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | 14 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', |
15 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', | 15 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', |
16 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', | 16 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', |
17 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | 17 | [DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO] = 'o', |
18 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | 18 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', |
19 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | 19 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', |
20 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | 20 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', |
21 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | 21 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', |
22 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | 22 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', |
23 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
23 | }; | 24 | }; |
24 | 25 | ||
25 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | 26 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) |
@@ -64,6 +65,28 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | |||
64 | symbol_conf.symfs, dso->long_name); | 65 | symbol_conf.symfs, dso->long_name); |
65 | break; | 66 | break; |
66 | 67 | ||
68 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: | ||
69 | { | ||
70 | char *last_slash; | ||
71 | size_t len; | ||
72 | size_t dir_size; | ||
73 | |||
74 | last_slash = dso->long_name + dso->long_name_len; | ||
75 | while (last_slash != dso->long_name && *last_slash != '/') | ||
76 | last_slash--; | ||
77 | |||
78 | len = scnprintf(file, size, "%s", symbol_conf.symfs); | ||
79 | dir_size = last_slash - dso->long_name + 2; | ||
80 | if (dir_size > (size - len)) { | ||
81 | ret = -1; | ||
82 | break; | ||
83 | } | ||
84 | len += scnprintf(file + len, dir_size, "%s", dso->long_name); | ||
85 | len += scnprintf(file + len , size - len, ".debug%s", | ||
86 | last_slash); | ||
87 | break; | ||
88 | } | ||
89 | |||
67 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | 90 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: |
68 | if (!dso->has_build_id) { | 91 | if (!dso->has_build_id) { |
69 | ret = -1; | 92 | ret = -1; |
@@ -427,6 +450,7 @@ struct dso *dso__new(const char *name) | |||
427 | dso->rel = 0; | 450 | dso->rel = 0; |
428 | dso->sorted_by_name = 0; | 451 | dso->sorted_by_name = 0; |
429 | dso->has_build_id = 0; | 452 | dso->has_build_id = 0; |
453 | dso->has_srcline = 1; | ||
430 | dso->kernel = DSO_TYPE_USER; | 454 | dso->kernel = DSO_TYPE_USER; |
431 | dso->needs_swap = DSO_SWAP__UNSET; | 455 | dso->needs_swap = DSO_SWAP__UNSET; |
432 | INIT_LIST_HEAD(&dso->node); | 456 | INIT_LIST_HEAD(&dso->node); |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index b793053335d6..9ac666abbe7e 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include "types.h" |
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | ||
9 | 10 | ||
10 | enum dso_binary_type { | 11 | enum dso_binary_type { |
11 | DSO_BINARY_TYPE__KALLSYMS = 0, | 12 | DSO_BINARY_TYPE__KALLSYMS = 0, |
@@ -23,6 +24,7 @@ enum dso_binary_type { | |||
23 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 24 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
24 | DSO_BINARY_TYPE__KCORE, | 25 | DSO_BINARY_TYPE__KCORE, |
25 | DSO_BINARY_TYPE__GUEST_KCORE, | 26 | DSO_BINARY_TYPE__GUEST_KCORE, |
27 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | ||
26 | DSO_BINARY_TYPE__NOT_FOUND, | 28 | DSO_BINARY_TYPE__NOT_FOUND, |
27 | }; | 29 | }; |
28 | 30 | ||
@@ -81,6 +83,7 @@ struct dso { | |||
81 | enum dso_binary_type data_type; | 83 | enum dso_binary_type data_type; |
82 | u8 adjust_symbols:1; | 84 | u8 adjust_symbols:1; |
83 | u8 has_build_id:1; | 85 | u8 has_build_id:1; |
86 | u8 has_srcline:1; | ||
84 | u8 hit:1; | 87 | u8 hit:1; |
85 | u8 annotate_warned:1; | 88 | u8 annotate_warned:1; |
86 | u8 sname_alloc:1; | 89 | u8 sname_alloc:1; |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9b393e7dca6f..bb788c109fe6 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -170,7 +170,8 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
170 | union perf_event *event, | 170 | union perf_event *event, |
171 | pid_t pid, pid_t tgid, | 171 | pid_t pid, pid_t tgid, |
172 | perf_event__handler_t process, | 172 | perf_event__handler_t process, |
173 | struct machine *machine) | 173 | struct machine *machine, |
174 | bool mmap_data) | ||
174 | { | 175 | { |
175 | char filename[PATH_MAX]; | 176 | char filename[PATH_MAX]; |
176 | FILE *fp; | 177 | FILE *fp; |
@@ -187,18 +188,13 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
187 | return -1; | 188 | return -1; |
188 | } | 189 | } |
189 | 190 | ||
190 | event->header.type = PERF_RECORD_MMAP2; | 191 | event->header.type = PERF_RECORD_MMAP; |
191 | /* | ||
192 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | ||
193 | */ | ||
194 | event->header.misc = PERF_RECORD_MISC_USER; | ||
195 | 192 | ||
196 | while (1) { | 193 | while (1) { |
197 | char bf[BUFSIZ]; | 194 | char bf[BUFSIZ]; |
198 | char prot[5]; | 195 | char prot[5]; |
199 | char execname[PATH_MAX]; | 196 | char execname[PATH_MAX]; |
200 | char anonstr[] = "//anon"; | 197 | char anonstr[] = "//anon"; |
201 | unsigned int ino; | ||
202 | size_t size; | 198 | size_t size; |
203 | ssize_t n; | 199 | ssize_t n; |
204 | 200 | ||
@@ -209,33 +205,40 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
209 | strcpy(execname, ""); | 205 | strcpy(execname, ""); |
210 | 206 | ||
211 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 207 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
212 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", | 208 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", |
213 | &event->mmap2.start, &event->mmap2.len, prot, | 209 | &event->mmap.start, &event->mmap.len, prot, |
214 | &event->mmap2.pgoff, &event->mmap2.maj, | 210 | &event->mmap.pgoff, |
215 | &event->mmap2.min, | 211 | execname); |
216 | &ino, execname); | 212 | /* |
217 | 213 | * Anon maps don't have the execname. | |
218 | event->mmap2.ino = (u64)ino; | 214 | */ |
219 | 215 | if (n < 4) | |
220 | if (n != 8) | ||
221 | continue; | 216 | continue; |
217 | /* | ||
218 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | ||
219 | */ | ||
220 | event->header.misc = PERF_RECORD_MISC_USER; | ||
222 | 221 | ||
223 | if (prot[2] != 'x') | 222 | if (prot[2] != 'x') { |
224 | continue; | 223 | if (!mmap_data || prot[0] != 'r') |
224 | continue; | ||
225 | |||
226 | event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; | ||
227 | } | ||
225 | 228 | ||
226 | if (!strcmp(execname, "")) | 229 | if (!strcmp(execname, "")) |
227 | strcpy(execname, anonstr); | 230 | strcpy(execname, anonstr); |
228 | 231 | ||
229 | size = strlen(execname) + 1; | 232 | size = strlen(execname) + 1; |
230 | memcpy(event->mmap2.filename, execname, size); | 233 | memcpy(event->mmap.filename, execname, size); |
231 | size = PERF_ALIGN(size, sizeof(u64)); | 234 | size = PERF_ALIGN(size, sizeof(u64)); |
232 | event->mmap2.len -= event->mmap.start; | 235 | event->mmap.len -= event->mmap.start; |
233 | event->mmap2.header.size = (sizeof(event->mmap2) - | 236 | event->mmap.header.size = (sizeof(event->mmap) - |
234 | (sizeof(event->mmap2.filename) - size)); | 237 | (sizeof(event->mmap.filename) - size)); |
235 | memset(event->mmap2.filename + size, 0, machine->id_hdr_size); | 238 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); |
236 | event->mmap2.header.size += machine->id_hdr_size; | 239 | event->mmap.header.size += machine->id_hdr_size; |
237 | event->mmap2.pid = tgid; | 240 | event->mmap.pid = tgid; |
238 | event->mmap2.tid = pid; | 241 | event->mmap.tid = pid; |
239 | 242 | ||
240 | if (process(tool, event, &synth_sample, machine) != 0) { | 243 | if (process(tool, event, &synth_sample, machine) != 0) { |
241 | rc = -1; | 244 | rc = -1; |
@@ -308,20 +311,21 @@ static int __event__synthesize_thread(union perf_event *comm_event, | |||
308 | pid_t pid, int full, | 311 | pid_t pid, int full, |
309 | perf_event__handler_t process, | 312 | perf_event__handler_t process, |
310 | struct perf_tool *tool, | 313 | struct perf_tool *tool, |
311 | struct machine *machine) | 314 | struct machine *machine, bool mmap_data) |
312 | { | 315 | { |
313 | pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, | 316 | pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, |
314 | process, machine); | 317 | process, machine); |
315 | if (tgid == -1) | 318 | if (tgid == -1) |
316 | return -1; | 319 | return -1; |
317 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, | 320 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, |
318 | process, machine); | 321 | process, machine, mmap_data); |
319 | } | 322 | } |
320 | 323 | ||
321 | int perf_event__synthesize_thread_map(struct perf_tool *tool, | 324 | int perf_event__synthesize_thread_map(struct perf_tool *tool, |
322 | struct thread_map *threads, | 325 | struct thread_map *threads, |
323 | perf_event__handler_t process, | 326 | perf_event__handler_t process, |
324 | struct machine *machine) | 327 | struct machine *machine, |
328 | bool mmap_data) | ||
325 | { | 329 | { |
326 | union perf_event *comm_event, *mmap_event; | 330 | union perf_event *comm_event, *mmap_event; |
327 | int err = -1, thread, j; | 331 | int err = -1, thread, j; |
@@ -338,7 +342,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
338 | for (thread = 0; thread < threads->nr; ++thread) { | 342 | for (thread = 0; thread < threads->nr; ++thread) { |
339 | if (__event__synthesize_thread(comm_event, mmap_event, | 343 | if (__event__synthesize_thread(comm_event, mmap_event, |
340 | threads->map[thread], 0, | 344 | threads->map[thread], 0, |
341 | process, tool, machine)) { | 345 | process, tool, machine, |
346 | mmap_data)) { | ||
342 | err = -1; | 347 | err = -1; |
343 | break; | 348 | break; |
344 | } | 349 | } |
@@ -360,10 +365,10 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
360 | 365 | ||
361 | /* if not, generate events for it */ | 366 | /* if not, generate events for it */ |
362 | if (need_leader && | 367 | if (need_leader && |
363 | __event__synthesize_thread(comm_event, | 368 | __event__synthesize_thread(comm_event, mmap_event, |
364 | mmap_event, | 369 | comm_event->comm.pid, 0, |
365 | comm_event->comm.pid, 0, | 370 | process, tool, machine, |
366 | process, tool, machine)) { | 371 | mmap_data)) { |
367 | err = -1; | 372 | err = -1; |
368 | break; | 373 | break; |
369 | } | 374 | } |
@@ -378,7 +383,7 @@ out: | |||
378 | 383 | ||
379 | int perf_event__synthesize_threads(struct perf_tool *tool, | 384 | int perf_event__synthesize_threads(struct perf_tool *tool, |
380 | perf_event__handler_t process, | 385 | perf_event__handler_t process, |
381 | struct machine *machine) | 386 | struct machine *machine, bool mmap_data) |
382 | { | 387 | { |
383 | DIR *proc; | 388 | DIR *proc; |
384 | struct dirent dirent, *next; | 389 | struct dirent dirent, *next; |
@@ -408,7 +413,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
408 | * one thread couldn't be synthesized. | 413 | * one thread couldn't be synthesized. |
409 | */ | 414 | */ |
410 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, | 415 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, |
411 | process, tool, machine); | 416 | process, tool, machine, mmap_data); |
412 | } | 417 | } |
413 | 418 | ||
414 | err = 0; | 419 | err = 0; |
@@ -516,52 +521,55 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) | |||
516 | 521 | ||
517 | int perf_event__process_comm(struct perf_tool *tool __maybe_unused, | 522 | int perf_event__process_comm(struct perf_tool *tool __maybe_unused, |
518 | union perf_event *event, | 523 | union perf_event *event, |
519 | struct perf_sample *sample __maybe_unused, | 524 | struct perf_sample *sample, |
520 | struct machine *machine) | 525 | struct machine *machine) |
521 | { | 526 | { |
522 | return machine__process_comm_event(machine, event); | 527 | return machine__process_comm_event(machine, event, sample); |
523 | } | 528 | } |
524 | 529 | ||
525 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | 530 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
526 | union perf_event *event, | 531 | union perf_event *event, |
527 | struct perf_sample *sample __maybe_unused, | 532 | struct perf_sample *sample, |
528 | struct machine *machine) | 533 | struct machine *machine) |
529 | { | 534 | { |
530 | return machine__process_lost_event(machine, event); | 535 | return machine__process_lost_event(machine, event, sample); |
531 | } | 536 | } |
532 | 537 | ||
533 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | 538 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
534 | { | 539 | { |
535 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", | 540 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n", |
536 | event->mmap.pid, event->mmap.tid, event->mmap.start, | 541 | event->mmap.pid, event->mmap.tid, event->mmap.start, |
537 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); | 542 | event->mmap.len, event->mmap.pgoff, |
543 | (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', | ||
544 | event->mmap.filename); | ||
538 | } | 545 | } |
539 | 546 | ||
540 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) | 547 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) |
541 | { | 548 | { |
542 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 | 549 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 |
543 | " %02x:%02x %"PRIu64" %"PRIu64"]: %s\n", | 550 | " %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n", |
544 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, | 551 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, |
545 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, | 552 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, |
546 | event->mmap2.min, event->mmap2.ino, | 553 | event->mmap2.min, event->mmap2.ino, |
547 | event->mmap2.ino_generation, | 554 | event->mmap2.ino_generation, |
555 | (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', | ||
548 | event->mmap2.filename); | 556 | event->mmap2.filename); |
549 | } | 557 | } |
550 | 558 | ||
551 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, | 559 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, |
552 | union perf_event *event, | 560 | union perf_event *event, |
553 | struct perf_sample *sample __maybe_unused, | 561 | struct perf_sample *sample, |
554 | struct machine *machine) | 562 | struct machine *machine) |
555 | { | 563 | { |
556 | return machine__process_mmap_event(machine, event); | 564 | return machine__process_mmap_event(machine, event, sample); |
557 | } | 565 | } |
558 | 566 | ||
559 | int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, | 567 | int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, |
560 | union perf_event *event, | 568 | union perf_event *event, |
561 | struct perf_sample *sample __maybe_unused, | 569 | struct perf_sample *sample, |
562 | struct machine *machine) | 570 | struct machine *machine) |
563 | { | 571 | { |
564 | return machine__process_mmap2_event(machine, event); | 572 | return machine__process_mmap2_event(machine, event, sample); |
565 | } | 573 | } |
566 | 574 | ||
567 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | 575 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) |
@@ -573,18 +581,18 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | |||
573 | 581 | ||
574 | int perf_event__process_fork(struct perf_tool *tool __maybe_unused, | 582 | int perf_event__process_fork(struct perf_tool *tool __maybe_unused, |
575 | union perf_event *event, | 583 | union perf_event *event, |
576 | struct perf_sample *sample __maybe_unused, | 584 | struct perf_sample *sample, |
577 | struct machine *machine) | 585 | struct machine *machine) |
578 | { | 586 | { |
579 | return machine__process_fork_event(machine, event); | 587 | return machine__process_fork_event(machine, event, sample); |
580 | } | 588 | } |
581 | 589 | ||
582 | int perf_event__process_exit(struct perf_tool *tool __maybe_unused, | 590 | int perf_event__process_exit(struct perf_tool *tool __maybe_unused, |
583 | union perf_event *event, | 591 | union perf_event *event, |
584 | struct perf_sample *sample __maybe_unused, | 592 | struct perf_sample *sample, |
585 | struct machine *machine) | 593 | struct machine *machine) |
586 | { | 594 | { |
587 | return machine__process_exit_event(machine, event); | 595 | return machine__process_exit_event(machine, event, sample); |
588 | } | 596 | } |
589 | 597 | ||
590 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) | 598 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) |
@@ -615,21 +623,21 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
615 | 623 | ||
616 | int perf_event__process(struct perf_tool *tool __maybe_unused, | 624 | int perf_event__process(struct perf_tool *tool __maybe_unused, |
617 | union perf_event *event, | 625 | union perf_event *event, |
618 | struct perf_sample *sample __maybe_unused, | 626 | struct perf_sample *sample, |
619 | struct machine *machine) | 627 | struct machine *machine) |
620 | { | 628 | { |
621 | return machine__process_event(machine, event); | 629 | return machine__process_event(machine, event, sample); |
622 | } | 630 | } |
623 | 631 | ||
624 | void thread__find_addr_map(struct thread *self, | 632 | void thread__find_addr_map(struct thread *thread, |
625 | struct machine *machine, u8 cpumode, | 633 | struct machine *machine, u8 cpumode, |
626 | enum map_type type, u64 addr, | 634 | enum map_type type, u64 addr, |
627 | struct addr_location *al) | 635 | struct addr_location *al) |
628 | { | 636 | { |
629 | struct map_groups *mg = &self->mg; | 637 | struct map_groups *mg = &thread->mg; |
630 | bool load_map = false; | 638 | bool load_map = false; |
631 | 639 | ||
632 | al->thread = self; | 640 | al->thread = thread; |
633 | al->addr = addr; | 641 | al->addr = addr; |
634 | al->cpumode = cpumode; | 642 | al->cpumode = cpumode; |
635 | al->filtered = false; | 643 | al->filtered = false; |
@@ -725,10 +733,10 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
725 | return -1; | 733 | return -1; |
726 | 734 | ||
727 | if (symbol_conf.comm_list && | 735 | if (symbol_conf.comm_list && |
728 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | 736 | !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) |
729 | goto out_filtered; | 737 | goto out_filtered; |
730 | 738 | ||
731 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); | 739 | dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); |
732 | /* | 740 | /* |
733 | * Have we already created the kernel maps for this machine? | 741 | * Have we already created the kernel maps for this machine? |
734 | * | 742 | * |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c67ecc457d29..30fec9901e44 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -61,6 +61,12 @@ struct read_event { | |||
61 | u64 id; | 61 | u64 id; |
62 | }; | 62 | }; |
63 | 63 | ||
64 | struct throttle_event { | ||
65 | struct perf_event_header header; | ||
66 | u64 time; | ||
67 | u64 id; | ||
68 | u64 stream_id; | ||
69 | }; | ||
64 | 70 | ||
65 | #define PERF_SAMPLE_MASK \ | 71 | #define PERF_SAMPLE_MASK \ |
66 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ | 72 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ |
@@ -69,6 +75,9 @@ struct read_event { | |||
69 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \ | 75 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \ |
70 | PERF_SAMPLE_IDENTIFIER) | 76 | PERF_SAMPLE_IDENTIFIER) |
71 | 77 | ||
78 | /* perf sample has 16 bits size limit */ | ||
79 | #define PERF_SAMPLE_MAX_SIZE (1 << 16) | ||
80 | |||
72 | struct sample_event { | 81 | struct sample_event { |
73 | struct perf_event_header header; | 82 | struct perf_event_header header; |
74 | u64 array[]; | 83 | u64 array[]; |
@@ -111,6 +120,7 @@ struct perf_sample { | |||
111 | u64 stream_id; | 120 | u64 stream_id; |
112 | u64 period; | 121 | u64 period; |
113 | u64 weight; | 122 | u64 weight; |
123 | u64 transaction; | ||
114 | u32 cpu; | 124 | u32 cpu; |
115 | u32 raw_size; | 125 | u32 raw_size; |
116 | u64 data_src; | 126 | u64 data_src; |
@@ -177,6 +187,7 @@ union perf_event { | |||
177 | struct fork_event fork; | 187 | struct fork_event fork; |
178 | struct lost_event lost; | 188 | struct lost_event lost; |
179 | struct read_event read; | 189 | struct read_event read; |
190 | struct throttle_event throttle; | ||
180 | struct sample_event sample; | 191 | struct sample_event sample; |
181 | struct attr_event attr; | 192 | struct attr_event attr; |
182 | struct event_type_event event_type; | 193 | struct event_type_event event_type; |
@@ -197,10 +208,10 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool, | |||
197 | int perf_event__synthesize_thread_map(struct perf_tool *tool, | 208 | int perf_event__synthesize_thread_map(struct perf_tool *tool, |
198 | struct thread_map *threads, | 209 | struct thread_map *threads, |
199 | perf_event__handler_t process, | 210 | perf_event__handler_t process, |
200 | struct machine *machine); | 211 | struct machine *machine, bool mmap_data); |
201 | int perf_event__synthesize_threads(struct perf_tool *tool, | 212 | int perf_event__synthesize_threads(struct perf_tool *tool, |
202 | perf_event__handler_t process, | 213 | perf_event__handler_t process, |
203 | struct machine *machine); | 214 | struct machine *machine, bool mmap_data); |
204 | int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | 215 | int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, |
205 | perf_event__handler_t process, | 216 | perf_event__handler_t process, |
206 | struct machine *machine, | 217 | struct machine *machine, |
@@ -240,7 +251,8 @@ int perf_event__process(struct perf_tool *tool, | |||
240 | struct machine *machine); | 251 | struct machine *machine); |
241 | 252 | ||
242 | struct addr_location; | 253 | struct addr_location; |
243 | int perf_event__preprocess_sample(const union perf_event *self, | 254 | |
255 | int perf_event__preprocess_sample(const union perf_event *event, | ||
244 | struct machine *machine, | 256 | struct machine *machine, |
245 | struct addr_location *al, | 257 | struct addr_location *al, |
246 | struct perf_sample *sample); | 258 | struct perf_sample *sample); |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f9f77bee0b1b..bbc746aa5716 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <unistd.h> | 18 | #include <unistd.h> |
19 | 19 | ||
20 | #include "parse-events.h" | 20 | #include "parse-events.h" |
21 | #include "parse-options.h" | ||
21 | 22 | ||
22 | #include <sys/mman.h> | 23 | #include <sys/mman.h> |
23 | 24 | ||
@@ -49,6 +50,18 @@ struct perf_evlist *perf_evlist__new(void) | |||
49 | return evlist; | 50 | return evlist; |
50 | } | 51 | } |
51 | 52 | ||
53 | struct perf_evlist *perf_evlist__new_default(void) | ||
54 | { | ||
55 | struct perf_evlist *evlist = perf_evlist__new(); | ||
56 | |||
57 | if (evlist && perf_evlist__add_default(evlist)) { | ||
58 | perf_evlist__delete(evlist); | ||
59 | evlist = NULL; | ||
60 | } | ||
61 | |||
62 | return evlist; | ||
63 | } | ||
64 | |||
52 | /** | 65 | /** |
53 | * perf_evlist__set_id_pos - set the positions of event ids. | 66 | * perf_evlist__set_id_pos - set the positions of event ids. |
54 | * @evlist: selected event list | 67 | * @evlist: selected event list |
@@ -104,6 +117,8 @@ void perf_evlist__delete(struct perf_evlist *evlist) | |||
104 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | 117 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
105 | { | 118 | { |
106 | list_add_tail(&entry->node, &evlist->entries); | 119 | list_add_tail(&entry->node, &evlist->entries); |
120 | entry->idx = evlist->nr_entries; | ||
121 | |||
107 | if (!evlist->nr_entries++) | 122 | if (!evlist->nr_entries++) |
108 | perf_evlist__set_id_pos(evlist); | 123 | perf_evlist__set_id_pos(evlist); |
109 | } | 124 | } |
@@ -152,7 +167,7 @@ int perf_evlist__add_default(struct perf_evlist *evlist) | |||
152 | 167 | ||
153 | event_attr_init(&attr); | 168 | event_attr_init(&attr); |
154 | 169 | ||
155 | evsel = perf_evsel__new(&attr, 0); | 170 | evsel = perf_evsel__new(&attr); |
156 | if (evsel == NULL) | 171 | if (evsel == NULL) |
157 | goto error; | 172 | goto error; |
158 | 173 | ||
@@ -177,7 +192,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist, | |||
177 | size_t i; | 192 | size_t i; |
178 | 193 | ||
179 | for (i = 0; i < nr_attrs; i++) { | 194 | for (i = 0; i < nr_attrs; i++) { |
180 | evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i); | 195 | evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i); |
181 | if (evsel == NULL) | 196 | if (evsel == NULL) |
182 | goto out_delete_partial_list; | 197 | goto out_delete_partial_list; |
183 | list_add_tail(&evsel->node, &head); | 198 | list_add_tail(&evsel->node, &head); |
@@ -236,13 +251,12 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | |||
236 | int perf_evlist__add_newtp(struct perf_evlist *evlist, | 251 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
237 | const char *sys, const char *name, void *handler) | 252 | const char *sys, const char *name, void *handler) |
238 | { | 253 | { |
239 | struct perf_evsel *evsel; | 254 | struct perf_evsel *evsel = perf_evsel__newtp(sys, name); |
240 | 255 | ||
241 | evsel = perf_evsel__newtp(sys, name, evlist->nr_entries); | ||
242 | if (evsel == NULL) | 256 | if (evsel == NULL) |
243 | return -1; | 257 | return -1; |
244 | 258 | ||
245 | evsel->handler.func = handler; | 259 | evsel->handler = handler; |
246 | perf_evlist__add(evlist, evsel); | 260 | perf_evlist__add(evlist, evsel); |
247 | return 0; | 261 | return 0; |
248 | } | 262 | } |
@@ -527,7 +541,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
527 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | 541 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
528 | unsigned int offset = old; | 542 | unsigned int offset = old; |
529 | unsigned int len = min(sizeof(*event), size), cpy; | 543 | unsigned int len = min(sizeof(*event), size), cpy; |
530 | void *dst = &md->event_copy; | 544 | void *dst = md->event_copy; |
531 | 545 | ||
532 | do { | 546 | do { |
533 | cpy = min(md->mask + 1 - (offset & md->mask), len); | 547 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
@@ -537,7 +551,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
537 | len -= cpy; | 551 | len -= cpy; |
538 | } while (len); | 552 | } while (len); |
539 | 553 | ||
540 | event = &md->event_copy; | 554 | event = (union perf_event *) md->event_copy; |
541 | } | 555 | } |
542 | 556 | ||
543 | old += size; | 557 | old += size; |
@@ -545,12 +559,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
545 | 559 | ||
546 | md->prev = old; | 560 | md->prev = old; |
547 | 561 | ||
548 | if (!evlist->overwrite) | ||
549 | perf_mmap__write_tail(md, old); | ||
550 | |||
551 | return event; | 562 | return event; |
552 | } | 563 | } |
553 | 564 | ||
565 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | ||
566 | { | ||
567 | if (!evlist->overwrite) { | ||
568 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
569 | unsigned int old = md->prev; | ||
570 | |||
571 | perf_mmap__write_tail(md, old); | ||
572 | } | ||
573 | } | ||
574 | |||
554 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | 575 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
555 | { | 576 | { |
556 | if (evlist->mmap[idx].base != NULL) { | 577 | if (evlist->mmap[idx].base != NULL) { |
@@ -587,6 +608,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, | |||
587 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, | 608 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, |
588 | MAP_SHARED, fd, 0); | 609 | MAP_SHARED, fd, 0); |
589 | if (evlist->mmap[idx].base == MAP_FAILED) { | 610 | if (evlist->mmap[idx].base == MAP_FAILED) { |
611 | pr_debug2("failed to mmap perf event ring buffer, error %d\n", | ||
612 | errno); | ||
590 | evlist->mmap[idx].base = NULL; | 613 | evlist->mmap[idx].base = NULL; |
591 | return -1; | 614 | return -1; |
592 | } | 615 | } |
@@ -595,9 +618,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, | |||
595 | return 0; | 618 | return 0; |
596 | } | 619 | } |
597 | 620 | ||
598 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) | 621 | static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, |
622 | int prot, int mask, int cpu, int thread, | ||
623 | int *output) | ||
599 | { | 624 | { |
600 | struct perf_evsel *evsel; | 625 | struct perf_evsel *evsel; |
626 | |||
627 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
628 | int fd = FD(evsel, cpu, thread); | ||
629 | |||
630 | if (*output == -1) { | ||
631 | *output = fd; | ||
632 | if (__perf_evlist__mmap(evlist, idx, prot, mask, | ||
633 | *output) < 0) | ||
634 | return -1; | ||
635 | } else { | ||
636 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) | ||
637 | return -1; | ||
638 | } | ||
639 | |||
640 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
641 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | ||
642 | return -1; | ||
643 | } | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, | ||
649 | int mask) | ||
650 | { | ||
601 | int cpu, thread; | 651 | int cpu, thread; |
602 | int nr_cpus = cpu_map__nr(evlist->cpus); | 652 | int nr_cpus = cpu_map__nr(evlist->cpus); |
603 | int nr_threads = thread_map__nr(evlist->threads); | 653 | int nr_threads = thread_map__nr(evlist->threads); |
@@ -607,23 +657,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
607 | int output = -1; | 657 | int output = -1; |
608 | 658 | ||
609 | for (thread = 0; thread < nr_threads; thread++) { | 659 | for (thread = 0; thread < nr_threads; thread++) { |
610 | list_for_each_entry(evsel, &evlist->entries, node) { | 660 | if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, |
611 | int fd = FD(evsel, cpu, thread); | 661 | cpu, thread, &output)) |
612 | 662 | goto out_unmap; | |
613 | if (output == -1) { | ||
614 | output = fd; | ||
615 | if (__perf_evlist__mmap(evlist, cpu, | ||
616 | prot, mask, output) < 0) | ||
617 | goto out_unmap; | ||
618 | } else { | ||
619 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) | ||
620 | goto out_unmap; | ||
621 | } | ||
622 | |||
623 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
624 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | ||
625 | goto out_unmap; | ||
626 | } | ||
627 | } | 663 | } |
628 | } | 664 | } |
629 | 665 | ||
@@ -635,9 +671,9 @@ out_unmap: | |||
635 | return -1; | 671 | return -1; |
636 | } | 672 | } |
637 | 673 | ||
638 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) | 674 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, |
675 | int mask) | ||
639 | { | 676 | { |
640 | struct perf_evsel *evsel; | ||
641 | int thread; | 677 | int thread; |
642 | int nr_threads = thread_map__nr(evlist->threads); | 678 | int nr_threads = thread_map__nr(evlist->threads); |
643 | 679 | ||
@@ -645,23 +681,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
645 | for (thread = 0; thread < nr_threads; thread++) { | 681 | for (thread = 0; thread < nr_threads; thread++) { |
646 | int output = -1; | 682 | int output = -1; |
647 | 683 | ||
648 | list_for_each_entry(evsel, &evlist->entries, node) { | 684 | if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, |
649 | int fd = FD(evsel, 0, thread); | 685 | thread, &output)) |
650 | 686 | goto out_unmap; | |
651 | if (output == -1) { | ||
652 | output = fd; | ||
653 | if (__perf_evlist__mmap(evlist, thread, | ||
654 | prot, mask, output) < 0) | ||
655 | goto out_unmap; | ||
656 | } else { | ||
657 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) | ||
658 | goto out_unmap; | ||
659 | } | ||
660 | |||
661 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
662 | perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) | ||
663 | goto out_unmap; | ||
664 | } | ||
665 | } | 687 | } |
666 | 688 | ||
667 | return 0; | 689 | return 0; |
@@ -672,20 +694,90 @@ out_unmap: | |||
672 | return -1; | 694 | return -1; |
673 | } | 695 | } |
674 | 696 | ||
675 | /** perf_evlist__mmap - Create per cpu maps to receive events | 697 | static size_t perf_evlist__mmap_size(unsigned long pages) |
676 | * | 698 | { |
677 | * @evlist - list of events | 699 | /* 512 kiB: default amount of unprivileged mlocked memory */ |
678 | * @pages - map length in pages | 700 | if (pages == UINT_MAX) |
679 | * @overwrite - overwrite older events? | 701 | pages = (512 * 1024) / page_size; |
680 | * | 702 | else if (!is_power_of_2(pages)) |
681 | * If overwrite is false the user needs to signal event consuption using: | 703 | return 0; |
682 | * | 704 | |
683 | * struct perf_mmap *m = &evlist->mmap[cpu]; | 705 | return (pages + 1) * page_size; |
684 | * unsigned int head = perf_mmap__read_head(m); | 706 | } |
707 | |||
708 | static long parse_pages_arg(const char *str, unsigned long min, | ||
709 | unsigned long max) | ||
710 | { | ||
711 | unsigned long pages, val; | ||
712 | static struct parse_tag tags[] = { | ||
713 | { .tag = 'B', .mult = 1 }, | ||
714 | { .tag = 'K', .mult = 1 << 10 }, | ||
715 | { .tag = 'M', .mult = 1 << 20 }, | ||
716 | { .tag = 'G', .mult = 1 << 30 }, | ||
717 | { .tag = 0 }, | ||
718 | }; | ||
719 | |||
720 | if (str == NULL) | ||
721 | return -EINVAL; | ||
722 | |||
723 | val = parse_tag_value(str, tags); | ||
724 | if (val != (unsigned long) -1) { | ||
725 | /* we got file size value */ | ||
726 | pages = PERF_ALIGN(val, page_size) / page_size; | ||
727 | } else { | ||
728 | /* we got pages count value */ | ||
729 | char *eptr; | ||
730 | pages = strtoul(str, &eptr, 10); | ||
731 | if (*eptr != '\0') | ||
732 | return -EINVAL; | ||
733 | } | ||
734 | |||
735 | if ((pages == 0) && (min == 0)) { | ||
736 | /* leave number of pages at 0 */ | ||
737 | } else if (pages < (1UL << 31) && !is_power_of_2(pages)) { | ||
738 | /* round pages up to next power of 2 */ | ||
739 | pages = next_pow2(pages); | ||
740 | pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", | ||
741 | pages * page_size, pages); | ||
742 | } | ||
743 | |||
744 | if (pages > max) | ||
745 | return -EINVAL; | ||
746 | |||
747 | return pages; | ||
748 | } | ||
749 | |||
750 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | ||
751 | int unset __maybe_unused) | ||
752 | { | ||
753 | unsigned int *mmap_pages = opt->value; | ||
754 | unsigned long max = UINT_MAX; | ||
755 | long pages; | ||
756 | |||
757 | if (max < SIZE_MAX / page_size) | ||
758 | max = SIZE_MAX / page_size; | ||
759 | |||
760 | pages = parse_pages_arg(str, 1, max); | ||
761 | if (pages < 0) { | ||
762 | pr_err("Invalid argument for --mmap_pages/-m\n"); | ||
763 | return -1; | ||
764 | } | ||
765 | |||
766 | *mmap_pages = pages; | ||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | /** | ||
771 | * perf_evlist__mmap - Create mmaps to receive events. | ||
772 | * @evlist: list of events | ||
773 | * @pages: map length in pages | ||
774 | * @overwrite: overwrite older events? | ||
685 | * | 775 | * |
686 | * perf_mmap__write_tail(m, head) | 776 | * If @overwrite is %false the user needs to signal event consumption using |
777 | * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this | ||
778 | * automatically. | ||
687 | * | 779 | * |
688 | * Using perf_evlist__read_on_cpu does this automatically. | 780 | * Return: %0 on success, negative error code otherwise. |
689 | */ | 781 | */ |
690 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 782 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
691 | bool overwrite) | 783 | bool overwrite) |
@@ -695,14 +787,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
695 | const struct thread_map *threads = evlist->threads; | 787 | const struct thread_map *threads = evlist->threads; |
696 | int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; | 788 | int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; |
697 | 789 | ||
698 | /* 512 kiB: default amount of unprivileged mlocked memory */ | ||
699 | if (pages == UINT_MAX) | ||
700 | pages = (512 * 1024) / page_size; | ||
701 | else if (!is_power_of_2(pages)) | ||
702 | return -EINVAL; | ||
703 | |||
704 | mask = pages * page_size - 1; | ||
705 | |||
706 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 790 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
707 | return -ENOMEM; | 791 | return -ENOMEM; |
708 | 792 | ||
@@ -710,7 +794,9 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
710 | return -ENOMEM; | 794 | return -ENOMEM; |
711 | 795 | ||
712 | evlist->overwrite = overwrite; | 796 | evlist->overwrite = overwrite; |
713 | evlist->mmap_len = (pages + 1) * page_size; | 797 | evlist->mmap_len = perf_evlist__mmap_size(pages); |
798 | pr_debug("mmap size %zuB\n", evlist->mmap_len); | ||
799 | mask = evlist->mmap_len - page_size - 1; | ||
714 | 800 | ||
715 | list_for_each_entry(evsel, &evlist->entries, node) { | 801 | list_for_each_entry(evsel, &evlist->entries, node) { |
716 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 802 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
@@ -725,8 +811,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
725 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 811 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
726 | } | 812 | } |
727 | 813 | ||
728 | int perf_evlist__create_maps(struct perf_evlist *evlist, | 814 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) |
729 | struct perf_target *target) | ||
730 | { | 815 | { |
731 | evlist->threads = thread_map__new_str(target->pid, target->tid, | 816 | evlist->threads = thread_map__new_str(target->pid, target->tid, |
732 | target->uid); | 817 | target->uid); |
@@ -734,9 +819,11 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, | |||
734 | if (evlist->threads == NULL) | 819 | if (evlist->threads == NULL) |
735 | return -1; | 820 | return -1; |
736 | 821 | ||
737 | if (perf_target__has_task(target)) | 822 | if (target->force_per_cpu) |
823 | evlist->cpus = cpu_map__new(target->cpu_list); | ||
824 | else if (target__has_task(target)) | ||
738 | evlist->cpus = cpu_map__dummy_new(); | 825 | evlist->cpus = cpu_map__dummy_new(); |
739 | else if (!perf_target__has_cpu(target) && !target->uses_mmap) | 826 | else if (!target__has_cpu(target) && !target->uses_mmap) |
740 | evlist->cpus = cpu_map__dummy_new(); | 827 | evlist->cpus = cpu_map__dummy_new(); |
741 | else | 828 | else |
742 | evlist->cpus = cpu_map__new(target->cpu_list); | 829 | evlist->cpus = cpu_map__new(target->cpu_list); |
@@ -945,8 +1032,7 @@ out_err: | |||
945 | return err; | 1032 | return err; |
946 | } | 1033 | } |
947 | 1034 | ||
948 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 1035 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, |
949 | struct perf_target *target, | ||
950 | const char *argv[], bool pipe_output, | 1036 | const char *argv[], bool pipe_output, |
951 | bool want_signal) | 1037 | bool want_signal) |
952 | { | 1038 | { |
@@ -998,7 +1084,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
998 | exit(-1); | 1084 | exit(-1); |
999 | } | 1085 | } |
1000 | 1086 | ||
1001 | if (perf_target__none(target)) | 1087 | if (target__none(target)) |
1002 | evlist->threads->map[0] = evlist->workload.pid; | 1088 | evlist->threads->map[0] = evlist->workload.pid; |
1003 | 1089 | ||
1004 | close(child_ready_pipe[1]); | 1090 | close(child_ready_pipe[1]); |
@@ -1064,5 +1150,68 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | |||
1064 | perf_evsel__name(evsel)); | 1150 | perf_evsel__name(evsel)); |
1065 | } | 1151 | } |
1066 | 1152 | ||
1067 | return printed + fprintf(fp, "\n");; | 1153 | return printed + fprintf(fp, "\n"); |
1154 | } | ||
1155 | |||
1156 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, | ||
1157 | int err, char *buf, size_t size) | ||
1158 | { | ||
1159 | char sbuf[128]; | ||
1160 | |||
1161 | switch (err) { | ||
1162 | case ENOENT: | ||
1163 | scnprintf(buf, size, "%s", | ||
1164 | "Error:\tUnable to find debugfs\n" | ||
1165 | "Hint:\tWas your kernel was compiled with debugfs support?\n" | ||
1166 | "Hint:\tIs the debugfs filesystem mounted?\n" | ||
1167 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); | ||
1168 | break; | ||
1169 | case EACCES: | ||
1170 | scnprintf(buf, size, | ||
1171 | "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" | ||
1172 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", | ||
1173 | debugfs_mountpoint, debugfs_mountpoint); | ||
1174 | break; | ||
1175 | default: | ||
1176 | scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); | ||
1177 | break; | ||
1178 | } | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | ||
1184 | int err, char *buf, size_t size) | ||
1185 | { | ||
1186 | int printed, value; | ||
1187 | char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | ||
1188 | |||
1189 | switch (err) { | ||
1190 | case EACCES: | ||
1191 | case EPERM: | ||
1192 | printed = scnprintf(buf, size, | ||
1193 | "Error:\t%s.\n" | ||
1194 | "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); | ||
1195 | |||
1196 | if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value)) | ||
1197 | break; | ||
1198 | |||
1199 | printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); | ||
1200 | |||
1201 | if (value >= 2) { | ||
1202 | printed += scnprintf(buf + printed, size - printed, | ||
1203 | "For your workloads it needs to be <= 1\nHint:\t"); | ||
1204 | } | ||
1205 | printed += scnprintf(buf + printed, size - printed, | ||
1206 | "For system wide tracing it needs to be set to -1"); | ||
1207 | |||
1208 | printed += scnprintf(buf + printed, size - printed, | ||
1209 | ".\nHint:\tThe current value is %d.", value); | ||
1210 | break; | ||
1211 | default: | ||
1212 | scnprintf(buf, size, "%s", emsg); | ||
1213 | break; | ||
1214 | } | ||
1215 | |||
1216 | return 0; | ||
1068 | } | 1217 | } |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 880d7139d2fb..649d6ea98a84 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -21,7 +21,7 @@ struct perf_mmap { | |||
21 | void *base; | 21 | void *base; |
22 | int mask; | 22 | int mask; |
23 | unsigned int prev; | 23 | unsigned int prev; |
24 | union perf_event event_copy; | 24 | char event_copy[PERF_SAMPLE_MAX_SIZE]; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | struct perf_evlist { | 27 | struct perf_evlist { |
@@ -31,7 +31,7 @@ struct perf_evlist { | |||
31 | int nr_groups; | 31 | int nr_groups; |
32 | int nr_fds; | 32 | int nr_fds; |
33 | int nr_mmaps; | 33 | int nr_mmaps; |
34 | int mmap_len; | 34 | size_t mmap_len; |
35 | int id_pos; | 35 | int id_pos; |
36 | int is_pos; | 36 | int is_pos; |
37 | u64 combined_sample_type; | 37 | u64 combined_sample_type; |
@@ -53,6 +53,7 @@ struct perf_evsel_str_handler { | |||
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct perf_evlist *perf_evlist__new(void); | 55 | struct perf_evlist *perf_evlist__new(void); |
56 | struct perf_evlist *perf_evlist__new_default(void); | ||
56 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 57 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
57 | struct thread_map *threads); | 58 | struct thread_map *threads); |
58 | void perf_evlist__exit(struct perf_evlist *evlist); | 59 | void perf_evlist__exit(struct perf_evlist *evlist); |
@@ -87,7 +88,9 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
87 | 88 | ||
88 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); | 89 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); |
89 | 90 | ||
90 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 91 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); |
92 | |||
93 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx); | ||
91 | 94 | ||
92 | int perf_evlist__open(struct perf_evlist *evlist); | 95 | int perf_evlist__open(struct perf_evlist *evlist); |
93 | void perf_evlist__close(struct perf_evlist *evlist); | 96 | void perf_evlist__close(struct perf_evlist *evlist); |
@@ -96,13 +99,18 @@ void perf_evlist__set_id_pos(struct perf_evlist *evlist); | |||
96 | bool perf_can_sample_identifier(void); | 99 | bool perf_can_sample_identifier(void); |
97 | void perf_evlist__config(struct perf_evlist *evlist, | 100 | void perf_evlist__config(struct perf_evlist *evlist, |
98 | struct perf_record_opts *opts); | 101 | struct perf_record_opts *opts); |
102 | int perf_record_opts__config(struct perf_record_opts *opts); | ||
99 | 103 | ||
100 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 104 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
101 | struct perf_target *target, | 105 | struct target *target, |
102 | const char *argv[], bool pipe_output, | 106 | const char *argv[], bool pipe_output, |
103 | bool want_signal); | 107 | bool want_signal); |
104 | int perf_evlist__start_workload(struct perf_evlist *evlist); | 108 | int perf_evlist__start_workload(struct perf_evlist *evlist); |
105 | 109 | ||
110 | int perf_evlist__parse_mmap_pages(const struct option *opt, | ||
111 | const char *str, | ||
112 | int unset); | ||
113 | |||
106 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 114 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
107 | bool overwrite); | 115 | bool overwrite); |
108 | void perf_evlist__munmap(struct perf_evlist *evlist); | 116 | void perf_evlist__munmap(struct perf_evlist *evlist); |
@@ -126,8 +134,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
126 | evlist->threads = threads; | 134 | evlist->threads = threads; |
127 | } | 135 | } |
128 | 136 | ||
129 | int perf_evlist__create_maps(struct perf_evlist *evlist, | 137 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target); |
130 | struct perf_target *target); | ||
131 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 138 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
132 | int perf_evlist__apply_filters(struct perf_evlist *evlist); | 139 | int perf_evlist__apply_filters(struct perf_evlist *evlist); |
133 | 140 | ||
@@ -163,10 +170,13 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | |||
163 | 170 | ||
164 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | 171 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); |
165 | 172 | ||
173 | int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); | ||
174 | int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); | ||
175 | |||
166 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | 176 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) |
167 | { | 177 | { |
168 | struct perf_event_mmap_page *pc = mm->base; | 178 | struct perf_event_mmap_page *pc = mm->base; |
169 | int head = pc->data_head; | 179 | int head = ACCESS_ONCE(pc->data_head); |
170 | rmb(); | 180 | rmb(); |
171 | return head; | 181 | return head; |
172 | } | 182 | } |
@@ -179,7 +189,7 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, | |||
179 | /* | 189 | /* |
180 | * ensure all reads are done before we write the tail out. | 190 | * ensure all reads are done before we write the tail out. |
181 | */ | 191 | */ |
182 | /* mb(); */ | 192 | mb(); |
183 | pc->data_tail = tail; | 193 | pc->data_tail = tail; |
184 | } | 194 | } |
185 | 195 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0ce9febf1ba0..46dd4c2a41ce 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -168,7 +168,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
168 | perf_evsel__calc_id_pos(evsel); | 168 | perf_evsel__calc_id_pos(evsel); |
169 | } | 169 | } |
170 | 170 | ||
171 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 171 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) |
172 | { | 172 | { |
173 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 173 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
174 | 174 | ||
@@ -219,7 +219,7 @@ out: | |||
219 | return format; | 219 | return format; |
220 | } | 220 | } |
221 | 221 | ||
222 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) | 222 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) |
223 | { | 223 | { |
224 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 224 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
225 | 225 | ||
@@ -645,7 +645,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
645 | } | 645 | } |
646 | } | 646 | } |
647 | 647 | ||
648 | if (perf_target__has_cpu(&opts->target)) | 648 | if (target__has_cpu(&opts->target) || opts->target.force_per_cpu) |
649 | perf_evsel__set_sample_bit(evsel, CPU); | 649 | perf_evsel__set_sample_bit(evsel, CPU); |
650 | 650 | ||
651 | if (opts->period) | 651 | if (opts->period) |
@@ -653,7 +653,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
653 | 653 | ||
654 | if (!perf_missing_features.sample_id_all && | 654 | if (!perf_missing_features.sample_id_all && |
655 | (opts->sample_time || !opts->no_inherit || | 655 | (opts->sample_time || !opts->no_inherit || |
656 | perf_target__has_cpu(&opts->target))) | 656 | target__has_cpu(&opts->target) || opts->target.force_per_cpu)) |
657 | perf_evsel__set_sample_bit(evsel, TIME); | 657 | perf_evsel__set_sample_bit(evsel, TIME); |
658 | 658 | ||
659 | if (opts->raw_samples) { | 659 | if (opts->raw_samples) { |
@@ -663,7 +663,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
663 | } | 663 | } |
664 | 664 | ||
665 | if (opts->sample_address) | 665 | if (opts->sample_address) |
666 | attr->sample_type |= PERF_SAMPLE_DATA_SRC; | 666 | perf_evsel__set_sample_bit(evsel, DATA_SRC); |
667 | 667 | ||
668 | if (opts->no_delay) { | 668 | if (opts->no_delay) { |
669 | attr->watermark = 0; | 669 | attr->watermark = 0; |
@@ -675,12 +675,14 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
675 | } | 675 | } |
676 | 676 | ||
677 | if (opts->sample_weight) | 677 | if (opts->sample_weight) |
678 | attr->sample_type |= PERF_SAMPLE_WEIGHT; | 678 | perf_evsel__set_sample_bit(evsel, WEIGHT); |
679 | 679 | ||
680 | attr->mmap = track; | 680 | attr->mmap = track; |
681 | attr->mmap2 = track && !perf_missing_features.mmap2; | ||
682 | attr->comm = track; | 681 | attr->comm = track; |
683 | 682 | ||
683 | if (opts->sample_transaction) | ||
684 | perf_evsel__set_sample_bit(evsel, TRANSACTION); | ||
685 | |||
684 | /* | 686 | /* |
685 | * XXX see the function comment above | 687 | * XXX see the function comment above |
686 | * | 688 | * |
@@ -694,7 +696,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
694 | * Setting enable_on_exec for independent events and | 696 | * Setting enable_on_exec for independent events and |
695 | * group leaders for traced executed by perf. | 697 | * group leaders for traced executed by perf. |
696 | */ | 698 | */ |
697 | if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) | 699 | if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) |
698 | attr->enable_on_exec = 1; | 700 | attr->enable_on_exec = 1; |
699 | } | 701 | } |
700 | 702 | ||
@@ -983,6 +985,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
983 | ret += PRINT_ATTR2(exclude_host, exclude_guest); | 985 | ret += PRINT_ATTR2(exclude_host, exclude_guest); |
984 | ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, | 986 | ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, |
985 | "excl.callchain_user", exclude_callchain_user); | 987 | "excl.callchain_user", exclude_callchain_user); |
988 | ret += PRINT_ATTR_U32(mmap2); | ||
986 | 989 | ||
987 | ret += PRINT_ATTR_U32(wakeup_events); | 990 | ret += PRINT_ATTR_U32(wakeup_events); |
988 | ret += PRINT_ATTR_U32(wakeup_watermark); | 991 | ret += PRINT_ATTR_U32(wakeup_watermark); |
@@ -1048,6 +1051,8 @@ retry_open: | |||
1048 | group_fd, flags); | 1051 | group_fd, flags); |
1049 | if (FD(evsel, cpu, thread) < 0) { | 1052 | if (FD(evsel, cpu, thread) < 0) { |
1050 | err = -errno; | 1053 | err = -errno; |
1054 | pr_debug2("perf_event_open failed, error %d\n", | ||
1055 | err); | ||
1051 | goto try_fallback; | 1056 | goto try_fallback; |
1052 | } | 1057 | } |
1053 | set_rlimit = NO_CHANGE; | 1058 | set_rlimit = NO_CHANGE; |
@@ -1214,6 +1219,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, | |||
1214 | 1219 | ||
1215 | sample->pid = u.val32[0]; | 1220 | sample->pid = u.val32[0]; |
1216 | sample->tid = u.val32[1]; | 1221 | sample->tid = u.val32[1]; |
1222 | array--; | ||
1217 | } | 1223 | } |
1218 | 1224 | ||
1219 | return 0; | 1225 | return 0; |
@@ -1453,6 +1459,9 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1453 | array = (void *)array + sz; | 1459 | array = (void *)array + sz; |
1454 | OVERFLOW_CHECK_u64(array); | 1460 | OVERFLOW_CHECK_u64(array); |
1455 | data->user_stack.size = *array++; | 1461 | data->user_stack.size = *array++; |
1462 | if (WARN_ONCE(data->user_stack.size > sz, | ||
1463 | "user stack dump failure\n")) | ||
1464 | return -EFAULT; | ||
1456 | } | 1465 | } |
1457 | } | 1466 | } |
1458 | 1467 | ||
@@ -1470,6 +1479,13 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1470 | array++; | 1479 | array++; |
1471 | } | 1480 | } |
1472 | 1481 | ||
1482 | data->transaction = 0; | ||
1483 | if (type & PERF_SAMPLE_TRANSACTION) { | ||
1484 | OVERFLOW_CHECK_u64(array); | ||
1485 | data->transaction = *array; | ||
1486 | array++; | ||
1487 | } | ||
1488 | |||
1473 | return 0; | 1489 | return 0; |
1474 | } | 1490 | } |
1475 | 1491 | ||
@@ -1562,6 +1578,9 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | |||
1562 | if (type & PERF_SAMPLE_DATA_SRC) | 1578 | if (type & PERF_SAMPLE_DATA_SRC) |
1563 | result += sizeof(u64); | 1579 | result += sizeof(u64); |
1564 | 1580 | ||
1581 | if (type & PERF_SAMPLE_TRANSACTION) | ||
1582 | result += sizeof(u64); | ||
1583 | |||
1565 | return result; | 1584 | return result; |
1566 | } | 1585 | } |
1567 | 1586 | ||
@@ -1735,6 +1754,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
1735 | array++; | 1754 | array++; |
1736 | } | 1755 | } |
1737 | 1756 | ||
1757 | if (type & PERF_SAMPLE_TRANSACTION) { | ||
1758 | *array = sample->transaction; | ||
1759 | array++; | ||
1760 | } | ||
1761 | |||
1738 | return 0; | 1762 | return 0; |
1739 | } | 1763 | } |
1740 | 1764 | ||
@@ -1982,8 +2006,7 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
1982 | return false; | 2006 | return false; |
1983 | } | 2007 | } |
1984 | 2008 | ||
1985 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | 2009 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
1986 | struct perf_target *target, | ||
1987 | int err, char *msg, size_t size) | 2010 | int err, char *msg, size_t size) |
1988 | { | 2011 | { |
1989 | switch (err) { | 2012 | switch (err) { |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4a7bdc713bab..1ea7c92e6e33 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -74,10 +74,7 @@ struct perf_evsel { | |||
74 | off_t id_offset; | 74 | off_t id_offset; |
75 | }; | 75 | }; |
76 | struct cgroup_sel *cgrp; | 76 | struct cgroup_sel *cgrp; |
77 | struct { | 77 | void *handler; |
78 | void *func; | ||
79 | void *data; | ||
80 | } handler; | ||
81 | struct cpu_map *cpus; | 78 | struct cpu_map *cpus; |
82 | unsigned int sample_size; | 79 | unsigned int sample_size; |
83 | int id_pos; | 80 | int id_pos; |
@@ -99,8 +96,19 @@ struct thread_map; | |||
99 | struct perf_evlist; | 96 | struct perf_evlist; |
100 | struct perf_record_opts; | 97 | struct perf_record_opts; |
101 | 98 | ||
102 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 99 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); |
103 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); | 100 | |
101 | static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) | ||
102 | { | ||
103 | return perf_evsel__new_idx(attr, 0); | ||
104 | } | ||
105 | |||
106 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx); | ||
107 | |||
108 | static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name) | ||
109 | { | ||
110 | return perf_evsel__newtp_idx(sys, name, 0); | ||
111 | } | ||
104 | 112 | ||
105 | struct event_format *event_format__new(const char *sys, const char *name); | 113 | struct event_format *event_format__new(const char *sys, const char *name); |
106 | 114 | ||
@@ -197,6 +205,12 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1, | |||
197 | (e1->attr.config == e2->attr.config); | 205 | (e1->attr.config == e2->attr.config); |
198 | } | 206 | } |
199 | 207 | ||
208 | #define perf_evsel__cmp(a, b) \ | ||
209 | ((a) && \ | ||
210 | (b) && \ | ||
211 | (a)->attr.type == (b)->attr.type && \ | ||
212 | (a)->attr.config == (b)->attr.config) | ||
213 | |||
200 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 214 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
201 | int cpu, int thread, bool scale); | 215 | int cpu, int thread, bool scale); |
202 | 216 | ||
@@ -265,6 +279,11 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | |||
265 | return list_entry(evsel->node.next, struct perf_evsel, node); | 279 | return list_entry(evsel->node.next, struct perf_evsel, node); |
266 | } | 280 | } |
267 | 281 | ||
282 | static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel) | ||
283 | { | ||
284 | return list_entry(evsel->node.prev, struct perf_evsel, node); | ||
285 | } | ||
286 | |||
268 | /** | 287 | /** |
269 | * perf_evsel__is_group_leader - Return whether given evsel is a leader event | 288 | * perf_evsel__is_group_leader - Return whether given evsel is a leader event |
270 | * | 289 | * |
@@ -304,8 +323,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
304 | 323 | ||
305 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | 324 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, |
306 | char *msg, size_t msgsize); | 325 | char *msg, size_t msgsize); |
307 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | 326 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
308 | struct perf_target *target, | ||
309 | int err, char *msg, size_t size); | 327 | int err, char *msg, size_t size); |
310 | 328 | ||
311 | static inline int perf_evsel__group_idx(struct perf_evsel *evsel) | 329 | static inline int perf_evsel__group_idx(struct perf_evsel *evsel) |
diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c new file mode 100644 index 000000000000..f5be1f26e724 --- /dev/null +++ b/tools/perf/util/fs.c | |||
@@ -0,0 +1,119 @@ | |||
1 | |||
2 | /* TODO merge/factor into tools/lib/lk/debugfs.c */ | ||
3 | |||
4 | #include "util.h" | ||
5 | #include "util/fs.h" | ||
6 | |||
7 | static const char * const sysfs__fs_known_mountpoints[] = { | ||
8 | "/sys", | ||
9 | 0, | ||
10 | }; | ||
11 | |||
12 | static const char * const procfs__known_mountpoints[] = { | ||
13 | "/proc", | ||
14 | 0, | ||
15 | }; | ||
16 | |||
17 | struct fs { | ||
18 | const char *name; | ||
19 | const char * const *mounts; | ||
20 | char path[PATH_MAX + 1]; | ||
21 | bool found; | ||
22 | long magic; | ||
23 | }; | ||
24 | |||
25 | enum { | ||
26 | FS__SYSFS = 0, | ||
27 | FS__PROCFS = 1, | ||
28 | }; | ||
29 | |||
30 | static struct fs fs__entries[] = { | ||
31 | [FS__SYSFS] = { | ||
32 | .name = "sysfs", | ||
33 | .mounts = sysfs__fs_known_mountpoints, | ||
34 | .magic = SYSFS_MAGIC, | ||
35 | }, | ||
36 | [FS__PROCFS] = { | ||
37 | .name = "proc", | ||
38 | .mounts = procfs__known_mountpoints, | ||
39 | .magic = PROC_SUPER_MAGIC, | ||
40 | }, | ||
41 | }; | ||
42 | |||
43 | static bool fs__read_mounts(struct fs *fs) | ||
44 | { | ||
45 | bool found = false; | ||
46 | char type[100]; | ||
47 | FILE *fp; | ||
48 | |||
49 | fp = fopen("/proc/mounts", "r"); | ||
50 | if (fp == NULL) | ||
51 | return NULL; | ||
52 | |||
53 | while (!found && | ||
54 | fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", | ||
55 | fs->path, type) == 2) { | ||
56 | |||
57 | if (strcmp(type, fs->name) == 0) | ||
58 | found = true; | ||
59 | } | ||
60 | |||
61 | fclose(fp); | ||
62 | return fs->found = found; | ||
63 | } | ||
64 | |||
65 | static int fs__valid_mount(const char *fs, long magic) | ||
66 | { | ||
67 | struct statfs st_fs; | ||
68 | |||
69 | if (statfs(fs, &st_fs) < 0) | ||
70 | return -ENOENT; | ||
71 | else if (st_fs.f_type != magic) | ||
72 | return -ENOENT; | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static bool fs__check_mounts(struct fs *fs) | ||
78 | { | ||
79 | const char * const *ptr; | ||
80 | |||
81 | ptr = fs->mounts; | ||
82 | while (*ptr) { | ||
83 | if (fs__valid_mount(*ptr, fs->magic) == 0) { | ||
84 | fs->found = true; | ||
85 | strcpy(fs->path, *ptr); | ||
86 | return true; | ||
87 | } | ||
88 | ptr++; | ||
89 | } | ||
90 | |||
91 | return false; | ||
92 | } | ||
93 | |||
94 | static const char *fs__get_mountpoint(struct fs *fs) | ||
95 | { | ||
96 | if (fs__check_mounts(fs)) | ||
97 | return fs->path; | ||
98 | |||
99 | return fs__read_mounts(fs) ? fs->path : NULL; | ||
100 | } | ||
101 | |||
102 | static const char *fs__mountpoint(int idx) | ||
103 | { | ||
104 | struct fs *fs = &fs__entries[idx]; | ||
105 | |||
106 | if (fs->found) | ||
107 | return (const char *)fs->path; | ||
108 | |||
109 | return fs__get_mountpoint(fs); | ||
110 | } | ||
111 | |||
112 | #define FS__MOUNTPOINT(name, idx) \ | ||
113 | const char *name##__mountpoint(void) \ | ||
114 | { \ | ||
115 | return fs__mountpoint(idx); \ | ||
116 | } | ||
117 | |||
118 | FS__MOUNTPOINT(sysfs, FS__SYSFS); | ||
119 | FS__MOUNTPOINT(procfs, FS__PROCFS); | ||
diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h new file mode 100644 index 000000000000..5e09ce1bab0e --- /dev/null +++ b/tools/perf/util/fs.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef __PERF_FS | ||
2 | #define __PERF_FS | ||
3 | |||
4 | const char *sysfs__mountpoint(void); | ||
5 | const char *procfs__mountpoint(void); | ||
6 | |||
7 | #endif /* __PERF_FS */ | ||
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 3ac38031d534..36a885d2cd22 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh | |||
@@ -22,7 +22,7 @@ do | |||
22 | }' "Documentation/perf-$cmd.txt" | 22 | }' "Documentation/perf-$cmd.txt" |
23 | done | 23 | done |
24 | 24 | ||
25 | echo "#ifdef LIBELF_SUPPORT" | 25 | echo "#ifdef HAVE_LIBELF_SUPPORT" |
26 | sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt | | 26 | sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt | |
27 | sort | | 27 | sort | |
28 | while read cmd | 28 | while read cmd |
@@ -35,5 +35,5 @@ do | |||
35 | p | 35 | p |
36 | }' "Documentation/perf-$cmd.txt" | 36 | }' "Documentation/perf-$cmd.txt" |
37 | done | 37 | done |
38 | echo "#endif /* LIBELF_SUPPORT */" | 38 | echo "#endif /* HAVE_LIBELF_SUPPORT */" |
39 | echo "};" | 39 | echo "};" |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c3e5a3b817ab..369c03648f88 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "vdso.h" | 22 | #include "vdso.h" |
23 | #include "strbuf.h" | 23 | #include "strbuf.h" |
24 | #include "build-id.h" | 24 | #include "build-id.h" |
25 | #include "data.h" | ||
25 | 26 | ||
26 | static bool no_buildid_cache = false; | 27 | static bool no_buildid_cache = false; |
27 | 28 | ||
@@ -2189,7 +2190,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | |||
2189 | { | 2190 | { |
2190 | struct header_print_data hd; | 2191 | struct header_print_data hd; |
2191 | struct perf_header *header = &session->header; | 2192 | struct perf_header *header = &session->header; |
2192 | int fd = session->fd; | 2193 | int fd = perf_data_file__fd(session->file); |
2193 | hd.fp = fp; | 2194 | hd.fp = fp; |
2194 | hd.full = full; | 2195 | hd.full = full; |
2195 | 2196 | ||
@@ -2650,7 +2651,8 @@ static int perf_header__read_pipe(struct perf_session *session) | |||
2650 | struct perf_header *header = &session->header; | 2651 | struct perf_header *header = &session->header; |
2651 | struct perf_pipe_file_header f_header; | 2652 | struct perf_pipe_file_header f_header; |
2652 | 2653 | ||
2653 | if (perf_file_header__read_pipe(&f_header, header, session->fd, | 2654 | if (perf_file_header__read_pipe(&f_header, header, |
2655 | perf_data_file__fd(session->file), | ||
2654 | session->repipe) < 0) { | 2656 | session->repipe) < 0) { |
2655 | pr_debug("incompatible file format\n"); | 2657 | pr_debug("incompatible file format\n"); |
2656 | return -EINVAL; | 2658 | return -EINVAL; |
@@ -2751,18 +2753,19 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, | |||
2751 | 2753 | ||
2752 | int perf_session__read_header(struct perf_session *session) | 2754 | int perf_session__read_header(struct perf_session *session) |
2753 | { | 2755 | { |
2756 | struct perf_data_file *file = session->file; | ||
2754 | struct perf_header *header = &session->header; | 2757 | struct perf_header *header = &session->header; |
2755 | struct perf_file_header f_header; | 2758 | struct perf_file_header f_header; |
2756 | struct perf_file_attr f_attr; | 2759 | struct perf_file_attr f_attr; |
2757 | u64 f_id; | 2760 | u64 f_id; |
2758 | int nr_attrs, nr_ids, i, j; | 2761 | int nr_attrs, nr_ids, i, j; |
2759 | int fd = session->fd; | 2762 | int fd = perf_data_file__fd(file); |
2760 | 2763 | ||
2761 | session->evlist = perf_evlist__new(); | 2764 | session->evlist = perf_evlist__new(); |
2762 | if (session->evlist == NULL) | 2765 | if (session->evlist == NULL) |
2763 | return -ENOMEM; | 2766 | return -ENOMEM; |
2764 | 2767 | ||
2765 | if (session->fd_pipe) | 2768 | if (perf_data_file__is_pipe(file)) |
2766 | return perf_header__read_pipe(session); | 2769 | return perf_header__read_pipe(session); |
2767 | 2770 | ||
2768 | if (perf_file_header__read(&f_header, header, fd) < 0) | 2771 | if (perf_file_header__read(&f_header, header, fd) < 0) |
@@ -2777,7 +2780,7 @@ int perf_session__read_header(struct perf_session *session) | |||
2777 | if (f_header.data.size == 0) { | 2780 | if (f_header.data.size == 0) { |
2778 | pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" | 2781 | pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" |
2779 | "Was the 'perf record' command properly terminated?\n", | 2782 | "Was the 'perf record' command properly terminated?\n", |
2780 | session->filename); | 2783 | file->path); |
2781 | } | 2784 | } |
2782 | 2785 | ||
2783 | nr_attrs = f_header.attrs.size / f_header.attr_size; | 2786 | nr_attrs = f_header.attrs.size / f_header.attr_size; |
@@ -2794,7 +2797,7 @@ int perf_session__read_header(struct perf_session *session) | |||
2794 | perf_event__attr_swap(&f_attr.attr); | 2797 | perf_event__attr_swap(&f_attr.attr); |
2795 | 2798 | ||
2796 | tmp = lseek(fd, 0, SEEK_CUR); | 2799 | tmp = lseek(fd, 0, SEEK_CUR); |
2797 | evsel = perf_evsel__new(&f_attr.attr, i); | 2800 | evsel = perf_evsel__new(&f_attr.attr); |
2798 | 2801 | ||
2799 | if (evsel == NULL) | 2802 | if (evsel == NULL) |
2800 | goto out_delete_evlist; | 2803 | goto out_delete_evlist; |
@@ -2913,7 +2916,7 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, | |||
2913 | return -ENOMEM; | 2916 | return -ENOMEM; |
2914 | } | 2917 | } |
2915 | 2918 | ||
2916 | evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries); | 2919 | evsel = perf_evsel__new(&event->attr.attr); |
2917 | if (evsel == NULL) | 2920 | if (evsel == NULL) |
2918 | return -ENOMEM; | 2921 | return -ENOMEM; |
2919 | 2922 | ||
@@ -2990,18 +2993,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, | |||
2990 | struct perf_session *session) | 2993 | struct perf_session *session) |
2991 | { | 2994 | { |
2992 | ssize_t size_read, padding, size = event->tracing_data.size; | 2995 | ssize_t size_read, padding, size = event->tracing_data.size; |
2993 | off_t offset = lseek(session->fd, 0, SEEK_CUR); | 2996 | int fd = perf_data_file__fd(session->file); |
2997 | off_t offset = lseek(fd, 0, SEEK_CUR); | ||
2994 | char buf[BUFSIZ]; | 2998 | char buf[BUFSIZ]; |
2995 | 2999 | ||
2996 | /* setup for reading amidst mmap */ | 3000 | /* setup for reading amidst mmap */ |
2997 | lseek(session->fd, offset + sizeof(struct tracing_data_event), | 3001 | lseek(fd, offset + sizeof(struct tracing_data_event), |
2998 | SEEK_SET); | 3002 | SEEK_SET); |
2999 | 3003 | ||
3000 | size_read = trace_report(session->fd, &session->pevent, | 3004 | size_read = trace_report(fd, &session->pevent, |
3001 | session->repipe); | 3005 | session->repipe); |
3002 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; | 3006 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; |
3003 | 3007 | ||
3004 | if (readn(session->fd, buf, padding) < 0) { | 3008 | if (readn(fd, buf, padding) < 0) { |
3005 | pr_err("%s: reading input file", __func__); | 3009 | pr_err("%s: reading input file", __func__); |
3006 | return -1; | 3010 | return -1; |
3007 | } | 3011 | } |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9ff6cf3e9a99..822903eaa201 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -160,6 +160,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
160 | hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); | 160 | hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); |
161 | hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); | 161 | hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); |
162 | hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); | 162 | hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); |
163 | |||
164 | if (h->transaction) | ||
165 | hists__new_col_len(hists, HISTC_TRANSACTION, | ||
166 | hist_entry__transaction_len()); | ||
163 | } | 167 | } |
164 | 168 | ||
165 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 169 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
@@ -346,7 +350,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
346 | struct rb_node **p; | 350 | struct rb_node **p; |
347 | struct rb_node *parent = NULL; | 351 | struct rb_node *parent = NULL; |
348 | struct hist_entry *he; | 352 | struct hist_entry *he; |
349 | int cmp; | 353 | int64_t cmp; |
350 | 354 | ||
351 | p = &hists->entries_in->rb_node; | 355 | p = &hists->entries_in->rb_node; |
352 | 356 | ||
@@ -395,6 +399,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
395 | if (!he) | 399 | if (!he) |
396 | return NULL; | 400 | return NULL; |
397 | 401 | ||
402 | hists->nr_entries++; | ||
398 | rb_link_node(&he->rb_node_in, parent, p); | 403 | rb_link_node(&he->rb_node_in, parent, p); |
399 | rb_insert_color(&he->rb_node_in, hists->entries_in); | 404 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
400 | out: | 405 | out: |
@@ -402,74 +407,16 @@ out: | |||
402 | return he; | 407 | return he; |
403 | } | 408 | } |
404 | 409 | ||
405 | struct hist_entry *__hists__add_mem_entry(struct hists *self, | 410 | struct hist_entry *__hists__add_entry(struct hists *hists, |
406 | struct addr_location *al, | ||
407 | struct symbol *sym_parent, | ||
408 | struct mem_info *mi, | ||
409 | u64 period, | ||
410 | u64 weight) | ||
411 | { | ||
412 | struct hist_entry entry = { | ||
413 | .thread = al->thread, | ||
414 | .ms = { | ||
415 | .map = al->map, | ||
416 | .sym = al->sym, | ||
417 | }, | ||
418 | .stat = { | ||
419 | .period = period, | ||
420 | .weight = weight, | ||
421 | .nr_events = 1, | ||
422 | }, | ||
423 | .cpu = al->cpu, | ||
424 | .ip = al->addr, | ||
425 | .level = al->level, | ||
426 | .parent = sym_parent, | ||
427 | .filtered = symbol__parent_filter(sym_parent), | ||
428 | .hists = self, | ||
429 | .mem_info = mi, | ||
430 | .branch_info = NULL, | ||
431 | }; | ||
432 | return add_hist_entry(self, &entry, al, period, weight); | ||
433 | } | ||
434 | |||
435 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | ||
436 | struct addr_location *al, | ||
437 | struct symbol *sym_parent, | ||
438 | struct branch_info *bi, | ||
439 | u64 period, | ||
440 | u64 weight) | ||
441 | { | ||
442 | struct hist_entry entry = { | ||
443 | .thread = al->thread, | ||
444 | .ms = { | ||
445 | .map = bi->to.map, | ||
446 | .sym = bi->to.sym, | ||
447 | }, | ||
448 | .cpu = al->cpu, | ||
449 | .ip = bi->to.addr, | ||
450 | .level = al->level, | ||
451 | .stat = { | ||
452 | .period = period, | ||
453 | .nr_events = 1, | ||
454 | .weight = weight, | ||
455 | }, | ||
456 | .parent = sym_parent, | ||
457 | .filtered = symbol__parent_filter(sym_parent), | ||
458 | .branch_info = bi, | ||
459 | .hists = self, | ||
460 | .mem_info = NULL, | ||
461 | }; | ||
462 | |||
463 | return add_hist_entry(self, &entry, al, period, weight); | ||
464 | } | ||
465 | |||
466 | struct hist_entry *__hists__add_entry(struct hists *self, | ||
467 | struct addr_location *al, | 411 | struct addr_location *al, |
468 | struct symbol *sym_parent, u64 period, | 412 | struct symbol *sym_parent, |
469 | u64 weight) | 413 | struct branch_info *bi, |
414 | struct mem_info *mi, | ||
415 | u64 period, u64 weight, u64 transaction) | ||
470 | { | 416 | { |
471 | struct hist_entry entry = { | 417 | struct hist_entry entry = { |
472 | .thread = al->thread, | 418 | .thread = al->thread, |
419 | .comm = thread__comm(al->thread), | ||
473 | .ms = { | 420 | .ms = { |
474 | .map = al->map, | 421 | .map = al->map, |
475 | .sym = al->sym, | 422 | .sym = al->sym, |
@@ -478,18 +425,19 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
478 | .ip = al->addr, | 425 | .ip = al->addr, |
479 | .level = al->level, | 426 | .level = al->level, |
480 | .stat = { | 427 | .stat = { |
481 | .period = period, | ||
482 | .nr_events = 1, | 428 | .nr_events = 1, |
429 | .period = period, | ||
483 | .weight = weight, | 430 | .weight = weight, |
484 | }, | 431 | }, |
485 | .parent = sym_parent, | 432 | .parent = sym_parent, |
486 | .filtered = symbol__parent_filter(sym_parent), | 433 | .filtered = symbol__parent_filter(sym_parent), |
487 | .hists = self, | 434 | .hists = hists, |
488 | .branch_info = NULL, | 435 | .branch_info = bi, |
489 | .mem_info = NULL, | 436 | .mem_info = mi, |
437 | .transaction = transaction, | ||
490 | }; | 438 | }; |
491 | 439 | ||
492 | return add_hist_entry(self, &entry, al, period, weight); | 440 | return add_hist_entry(hists, &entry, al, period, weight); |
493 | } | 441 | } |
494 | 442 | ||
495 | int64_t | 443 | int64_t |
@@ -530,6 +478,7 @@ void hist_entry__free(struct hist_entry *he) | |||
530 | { | 478 | { |
531 | free(he->branch_info); | 479 | free(he->branch_info); |
532 | free(he->mem_info); | 480 | free(he->mem_info); |
481 | free_srcline(he->srcline); | ||
533 | free(he); | 482 | free(he); |
534 | } | 483 | } |
535 | 484 | ||
@@ -598,7 +547,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
598 | hists__filter_entry_by_symbol(hists, he); | 547 | hists__filter_entry_by_symbol(hists, he); |
599 | } | 548 | } |
600 | 549 | ||
601 | void hists__collapse_resort(struct hists *hists) | 550 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) |
602 | { | 551 | { |
603 | struct rb_root *root; | 552 | struct rb_root *root; |
604 | struct rb_node *next; | 553 | struct rb_node *next; |
@@ -625,6 +574,8 @@ void hists__collapse_resort(struct hists *hists) | |||
625 | */ | 574 | */ |
626 | hists__apply_filters(hists, n); | 575 | hists__apply_filters(hists, n); |
627 | } | 576 | } |
577 | if (prog) | ||
578 | ui_progress__update(prog, 1); | ||
628 | } | 579 | } |
629 | } | 580 | } |
630 | 581 | ||
@@ -884,7 +835,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | |||
884 | struct rb_node **p; | 835 | struct rb_node **p; |
885 | struct rb_node *parent = NULL; | 836 | struct rb_node *parent = NULL; |
886 | struct hist_entry *he; | 837 | struct hist_entry *he; |
887 | int cmp; | 838 | int64_t cmp; |
888 | 839 | ||
889 | if (sort__need_collapse) | 840 | if (sort__need_collapse) |
890 | root = &hists->entries_collapsed; | 841 | root = &hists->entries_collapsed; |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1329b6b6ffe6..b621347a1585 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <pthread.h> | 5 | #include <pthread.h> |
6 | #include "callchain.h" | 6 | #include "callchain.h" |
7 | #include "header.h" | 7 | #include "header.h" |
8 | #include "color.h" | ||
9 | #include "ui/progress.h" | ||
8 | 10 | ||
9 | extern struct callchain_param callchain_param; | 11 | extern struct callchain_param callchain_param; |
10 | 12 | ||
@@ -45,6 +47,8 @@ enum hist_column { | |||
45 | HISTC_CPU, | 47 | HISTC_CPU, |
46 | HISTC_SRCLINE, | 48 | HISTC_SRCLINE, |
47 | HISTC_MISPREDICT, | 49 | HISTC_MISPREDICT, |
50 | HISTC_IN_TX, | ||
51 | HISTC_ABORT, | ||
48 | HISTC_SYMBOL_FROM, | 52 | HISTC_SYMBOL_FROM, |
49 | HISTC_SYMBOL_TO, | 53 | HISTC_SYMBOL_TO, |
50 | HISTC_DSO_FROM, | 54 | HISTC_DSO_FROM, |
@@ -57,6 +61,7 @@ enum hist_column { | |||
57 | HISTC_MEM_TLB, | 61 | HISTC_MEM_TLB, |
58 | HISTC_MEM_LVL, | 62 | HISTC_MEM_LVL, |
59 | HISTC_MEM_SNOOP, | 63 | HISTC_MEM_SNOOP, |
64 | HISTC_TRANSACTION, | ||
60 | HISTC_NR_COLS, /* Last entry */ | 65 | HISTC_NR_COLS, /* Last entry */ |
61 | }; | 66 | }; |
62 | 67 | ||
@@ -79,54 +84,43 @@ struct hists { | |||
79 | u16 col_len[HISTC_NR_COLS]; | 84 | u16 col_len[HISTC_NR_COLS]; |
80 | }; | 85 | }; |
81 | 86 | ||
82 | struct hist_entry *__hists__add_entry(struct hists *self, | 87 | struct hist_entry *__hists__add_entry(struct hists *hists, |
83 | struct addr_location *al, | 88 | struct addr_location *al, |
84 | struct symbol *parent, u64 period, | 89 | struct symbol *parent, |
85 | u64 weight); | 90 | struct branch_info *bi, |
91 | struct mem_info *mi, u64 period, | ||
92 | u64 weight, u64 transaction); | ||
86 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); | 93 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
87 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | 94 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
88 | int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, | 95 | int hist_entry__transaction_len(void); |
96 | int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, | ||
89 | struct hists *hists); | 97 | struct hists *hists); |
90 | void hist_entry__free(struct hist_entry *); | 98 | void hist_entry__free(struct hist_entry *); |
91 | 99 | ||
92 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | 100 | void hists__output_resort(struct hists *hists); |
93 | struct addr_location *al, | 101 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
94 | struct symbol *sym_parent, | ||
95 | struct branch_info *bi, | ||
96 | u64 period, | ||
97 | u64 weight); | ||
98 | |||
99 | struct hist_entry *__hists__add_mem_entry(struct hists *self, | ||
100 | struct addr_location *al, | ||
101 | struct symbol *sym_parent, | ||
102 | struct mem_info *mi, | ||
103 | u64 period, | ||
104 | u64 weight); | ||
105 | |||
106 | void hists__output_resort(struct hists *self); | ||
107 | void hists__collapse_resort(struct hists *self); | ||
108 | 102 | ||
109 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 103 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
110 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 104 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
111 | 105 | ||
112 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); | 106 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); |
113 | void hists__inc_nr_events(struct hists *self, u32 type); | 107 | void hists__inc_nr_events(struct hists *hists, u32 type); |
114 | void events_stats__inc(struct events_stats *stats, u32 type); | 108 | void events_stats__inc(struct events_stats *stats, u32 type); |
115 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); | 109 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); |
116 | 110 | ||
117 | size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, | 111 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, |
118 | int max_cols, float min_pcnt, FILE *fp); | 112 | int max_cols, float min_pcnt, FILE *fp); |
119 | 113 | ||
120 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 114 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); |
121 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); | 115 | int hist_entry__annotate(struct hist_entry *he, size_t privsize); |
122 | 116 | ||
123 | void hists__filter_by_dso(struct hists *hists); | 117 | void hists__filter_by_dso(struct hists *hists); |
124 | void hists__filter_by_thread(struct hists *hists); | 118 | void hists__filter_by_thread(struct hists *hists); |
125 | void hists__filter_by_symbol(struct hists *hists); | 119 | void hists__filter_by_symbol(struct hists *hists); |
126 | 120 | ||
127 | u16 hists__col_len(struct hists *self, enum hist_column col); | 121 | u16 hists__col_len(struct hists *hists, enum hist_column col); |
128 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 122 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); |
129 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | 123 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); |
130 | void hists__reset_col_len(struct hists *hists); | 124 | void hists__reset_col_len(struct hists *hists); |
131 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | 125 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); |
132 | 126 | ||
@@ -175,6 +169,18 @@ void perf_hpp__init(void); | |||
175 | void perf_hpp__column_register(struct perf_hpp_fmt *format); | 169 | void perf_hpp__column_register(struct perf_hpp_fmt *format); |
176 | void perf_hpp__column_enable(unsigned col); | 170 | void perf_hpp__column_enable(unsigned col); |
177 | 171 | ||
172 | static inline size_t perf_hpp__use_color(void) | ||
173 | { | ||
174 | return !symbol_conf.field_sep; | ||
175 | } | ||
176 | |||
177 | static inline size_t perf_hpp__color_overhead(void) | ||
178 | { | ||
179 | return perf_hpp__use_color() ? | ||
180 | (COLOR_MAXLEN + sizeof(PERF_COLOR_RESET)) * PERF_HPP__MAX_INDEX | ||
181 | : 0; | ||
182 | } | ||
183 | |||
178 | struct perf_evlist; | 184 | struct perf_evlist; |
179 | 185 | ||
180 | struct hist_browser_timer { | 186 | struct hist_browser_timer { |
@@ -183,7 +189,7 @@ struct hist_browser_timer { | |||
183 | int refresh; | 189 | int refresh; |
184 | }; | 190 | }; |
185 | 191 | ||
186 | #ifdef SLANG_SUPPORT | 192 | #ifdef HAVE_SLANG_SUPPORT |
187 | #include "../ui/keysyms.h" | 193 | #include "../ui/keysyms.h" |
188 | int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, | 194 | int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, |
189 | struct hist_browser_timer *hbt); | 195 | struct hist_browser_timer *hbt); |
@@ -204,12 +210,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
204 | return 0; | 210 | return 0; |
205 | } | 211 | } |
206 | 212 | ||
207 | static inline int hist_entry__tui_annotate(struct hist_entry *self | 213 | static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, |
208 | __maybe_unused, | 214 | struct perf_evsel *evsel __maybe_unused, |
209 | struct perf_evsel *evsel | 215 | struct hist_browser_timer *hbt __maybe_unused) |
210 | __maybe_unused, | ||
211 | struct hist_browser_timer *hbt | ||
212 | __maybe_unused) | ||
213 | { | 216 | { |
214 | return 0; | 217 | return 0; |
215 | } | 218 | } |
@@ -224,20 +227,5 @@ static inline int script_browse(const char *script_opt __maybe_unused) | |||
224 | #define K_SWITCH_INPUT_DATA -3000 | 227 | #define K_SWITCH_INPUT_DATA -3000 |
225 | #endif | 228 | #endif |
226 | 229 | ||
227 | #ifdef GTK2_SUPPORT | 230 | unsigned int hists__sort_list_width(struct hists *hists); |
228 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | ||
229 | struct hist_browser_timer *hbt __maybe_unused, | ||
230 | float min_pcnt); | ||
231 | #else | ||
232 | static inline | ||
233 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | ||
234 | const char *help __maybe_unused, | ||
235 | struct hist_browser_timer *hbt __maybe_unused, | ||
236 | float min_pcnt __maybe_unused) | ||
237 | { | ||
238 | return 0; | ||
239 | } | ||
240 | #endif | ||
241 | |||
242 | unsigned int hists__sort_list_width(struct hists *self); | ||
243 | #endif /* __PERF_HIST_H */ | 231 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index cf6727e99c44..8f149655f497 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef _PERF_DWARF_REGS_H_ | 1 | #ifndef _PERF_DWARF_REGS_H_ |
2 | #define _PERF_DWARF_REGS_H_ | 2 | #define _PERF_DWARF_REGS_H_ |
3 | 3 | ||
4 | #ifdef DWARF_SUPPORT | 4 | #ifdef HAVE_DWARF_SUPPORT |
5 | const char *get_arch_regstr(unsigned int n); | 5 | const char *get_arch_regstr(unsigned int n); |
6 | #endif | 6 | #endif |
7 | 7 | ||
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 96b919dae11c..b003ad7200b2 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -2,20 +2,29 @@ | |||
2 | #define _PERF_LINUX_COMPILER_H_ | 2 | #define _PERF_LINUX_COMPILER_H_ |
3 | 3 | ||
4 | #ifndef __always_inline | 4 | #ifndef __always_inline |
5 | #define __always_inline inline | 5 | # define __always_inline inline __attribute__((always_inline)) |
6 | #endif | 6 | #endif |
7 | |||
7 | #define __user | 8 | #define __user |
9 | |||
8 | #ifndef __attribute_const__ | 10 | #ifndef __attribute_const__ |
9 | #define __attribute_const__ | 11 | # define __attribute_const__ |
10 | #endif | 12 | #endif |
11 | 13 | ||
12 | #ifndef __maybe_unused | 14 | #ifndef __maybe_unused |
13 | #define __maybe_unused __attribute__((unused)) | 15 | # define __maybe_unused __attribute__((unused)) |
16 | #endif | ||
17 | |||
18 | #ifndef __packed | ||
19 | # define __packed __attribute__((__packed__)) | ||
14 | #endif | 20 | #endif |
15 | #define __packed __attribute__((__packed__)) | ||
16 | 21 | ||
17 | #ifndef __force | 22 | #ifndef __force |
18 | #define __force | 23 | # define __force |
24 | #endif | ||
25 | |||
26 | #ifndef __weak | ||
27 | # define __weak __attribute__((weak)) | ||
19 | #endif | 28 | #endif |
20 | 29 | ||
21 | #endif | 30 | #endif |
diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h index 58b64ed4da12..07d63cf3e0f6 100644 --- a/tools/perf/util/include/linux/magic.h +++ b/tools/perf/util/include/linux/magic.h | |||
@@ -9,4 +9,8 @@ | |||
9 | #define SYSFS_MAGIC 0x62656572 | 9 | #define SYSFS_MAGIC 0x62656572 |
10 | #endif | 10 | #endif |
11 | 11 | ||
12 | #ifndef PROC_SUPER_MAGIC | ||
13 | #define PROC_SUPER_MAGIC 0x9fa0 | ||
14 | #endif | ||
15 | |||
12 | #endif | 16 | #endif |
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 11a8d86f7fea..89715b64a315 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c | |||
@@ -20,6 +20,7 @@ static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused, | |||
20 | 20 | ||
21 | if (node != NULL) { | 21 | if (node != NULL) { |
22 | node->i = i; | 22 | node->i = i; |
23 | node->priv = NULL; | ||
23 | rc = &node->rb_node; | 24 | rc = &node->rb_node; |
24 | } | 25 | } |
25 | 26 | ||
@@ -57,22 +58,36 @@ void intlist__remove(struct intlist *ilist, struct int_node *node) | |||
57 | rblist__remove_node(&ilist->rblist, &node->rb_node); | 58 | rblist__remove_node(&ilist->rblist, &node->rb_node); |
58 | } | 59 | } |
59 | 60 | ||
60 | struct int_node *intlist__find(struct intlist *ilist, int i) | 61 | static struct int_node *__intlist__findnew(struct intlist *ilist, |
62 | int i, bool create) | ||
61 | { | 63 | { |
62 | struct int_node *node; | 64 | struct int_node *node = NULL; |
63 | struct rb_node *rb_node; | 65 | struct rb_node *rb_node; |
64 | 66 | ||
65 | if (ilist == NULL) | 67 | if (ilist == NULL) |
66 | return NULL; | 68 | return NULL; |
67 | 69 | ||
68 | node = NULL; | 70 | if (create) |
69 | rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); | 71 | rb_node = rblist__findnew(&ilist->rblist, (void *)((long)i)); |
72 | else | ||
73 | rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); | ||
74 | |||
70 | if (rb_node) | 75 | if (rb_node) |
71 | node = container_of(rb_node, struct int_node, rb_node); | 76 | node = container_of(rb_node, struct int_node, rb_node); |
72 | 77 | ||
73 | return node; | 78 | return node; |
74 | } | 79 | } |
75 | 80 | ||
81 | struct int_node *intlist__find(struct intlist *ilist, int i) | ||
82 | { | ||
83 | return __intlist__findnew(ilist, i, false); | ||
84 | } | ||
85 | |||
86 | struct int_node *intlist__findnew(struct intlist *ilist, int i) | ||
87 | { | ||
88 | return __intlist__findnew(ilist, i, true); | ||
89 | } | ||
90 | |||
76 | static int intlist__parse_list(struct intlist *ilist, const char *s) | 91 | static int intlist__parse_list(struct intlist *ilist, const char *s) |
77 | { | 92 | { |
78 | char *sep; | 93 | char *sep; |
diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 62351dad848f..aa6877d36858 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h | |||
@@ -9,6 +9,7 @@ | |||
9 | struct int_node { | 9 | struct int_node { |
10 | struct rb_node rb_node; | 10 | struct rb_node rb_node; |
11 | int i; | 11 | int i; |
12 | void *priv; | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | struct intlist { | 15 | struct intlist { |
@@ -23,6 +24,7 @@ int intlist__add(struct intlist *ilist, int i); | |||
23 | 24 | ||
24 | struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx); | 25 | struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx); |
25 | struct int_node *intlist__find(struct intlist *ilist, int i); | 26 | struct int_node *intlist__find(struct intlist *ilist, int i); |
27 | struct int_node *intlist__findnew(struct intlist *ilist, int i); | ||
26 | 28 | ||
27 | static inline bool intlist__has_entry(struct intlist *ilist, int i) | 29 | static inline bool intlist__has_entry(struct intlist *ilist, int i) |
28 | { | 30 | { |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6188d2876a71..84cdb072ac83 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -40,12 +40,29 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
40 | return -ENOMEM; | 40 | return -ENOMEM; |
41 | 41 | ||
42 | snprintf(comm, sizeof(comm), "[guest/%d]", pid); | 42 | snprintf(comm, sizeof(comm), "[guest/%d]", pid); |
43 | thread__set_comm(thread, comm); | 43 | thread__set_comm(thread, comm, 0); |
44 | } | 44 | } |
45 | 45 | ||
46 | return 0; | 46 | return 0; |
47 | } | 47 | } |
48 | 48 | ||
49 | struct machine *machine__new_host(void) | ||
50 | { | ||
51 | struct machine *machine = malloc(sizeof(*machine)); | ||
52 | |||
53 | if (machine != NULL) { | ||
54 | machine__init(machine, "", HOST_KERNEL_ID); | ||
55 | |||
56 | if (machine__create_kernel_maps(machine) < 0) | ||
57 | goto out_delete; | ||
58 | } | ||
59 | |||
60 | return machine; | ||
61 | out_delete: | ||
62 | free(machine); | ||
63 | return NULL; | ||
64 | } | ||
65 | |||
49 | static void dsos__delete(struct list_head *dsos) | 66 | static void dsos__delete(struct list_head *dsos) |
50 | { | 67 | { |
51 | struct dso *pos, *n; | 68 | struct dso *pos, *n; |
@@ -314,7 +331,8 @@ struct thread *machine__find_thread(struct machine *machine, pid_t tid) | |||
314 | return __machine__findnew_thread(machine, 0, tid, false); | 331 | return __machine__findnew_thread(machine, 0, tid, false); |
315 | } | 332 | } |
316 | 333 | ||
317 | int machine__process_comm_event(struct machine *machine, union perf_event *event) | 334 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
335 | struct perf_sample *sample) | ||
318 | { | 336 | { |
319 | struct thread *thread = machine__findnew_thread(machine, | 337 | struct thread *thread = machine__findnew_thread(machine, |
320 | event->comm.pid, | 338 | event->comm.pid, |
@@ -323,7 +341,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event | |||
323 | if (dump_trace) | 341 | if (dump_trace) |
324 | perf_event__fprintf_comm(event, stdout); | 342 | perf_event__fprintf_comm(event, stdout); |
325 | 343 | ||
326 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { | 344 | if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { |
327 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 345 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
328 | return -1; | 346 | return -1; |
329 | } | 347 | } |
@@ -332,7 +350,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event | |||
332 | } | 350 | } |
333 | 351 | ||
334 | int machine__process_lost_event(struct machine *machine __maybe_unused, | 352 | int machine__process_lost_event(struct machine *machine __maybe_unused, |
335 | union perf_event *event) | 353 | union perf_event *event, struct perf_sample *sample __maybe_unused) |
336 | { | 354 | { |
337 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | 355 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", |
338 | event->lost.id, event->lost.lost); | 356 | event->lost.id, event->lost.lost); |
@@ -776,75 +794,44 @@ static int machine__set_modules_path(struct machine *machine) | |||
776 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | 794 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); |
777 | } | 795 | } |
778 | 796 | ||
779 | static int machine__create_modules(struct machine *machine) | 797 | static int machine__create_module(void *arg, const char *name, u64 start) |
780 | { | 798 | { |
781 | char *line = NULL; | 799 | struct machine *machine = arg; |
782 | size_t n; | ||
783 | FILE *file; | ||
784 | struct map *map; | 800 | struct map *map; |
801 | |||
802 | map = machine__new_module(machine, start, name); | ||
803 | if (map == NULL) | ||
804 | return -1; | ||
805 | |||
806 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static int machine__create_modules(struct machine *machine) | ||
812 | { | ||
785 | const char *modules; | 813 | const char *modules; |
786 | char path[PATH_MAX]; | 814 | char path[PATH_MAX]; |
787 | 815 | ||
788 | if (machine__is_default_guest(machine)) | 816 | if (machine__is_default_guest(machine)) { |
789 | modules = symbol_conf.default_guest_modules; | 817 | modules = symbol_conf.default_guest_modules; |
790 | else { | 818 | } else { |
791 | sprintf(path, "%s/proc/modules", machine->root_dir); | 819 | snprintf(path, PATH_MAX, "%s/proc/modules", machine->root_dir); |
792 | modules = path; | 820 | modules = path; |
793 | } | 821 | } |
794 | 822 | ||
795 | if (symbol__restricted_filename(modules, "/proc/modules")) | 823 | if (symbol__restricted_filename(modules, "/proc/modules")) |
796 | return -1; | 824 | return -1; |
797 | 825 | ||
798 | file = fopen(modules, "r"); | 826 | if (modules__parse(modules, machine, machine__create_module)) |
799 | if (file == NULL) | ||
800 | return -1; | 827 | return -1; |
801 | 828 | ||
802 | while (!feof(file)) { | 829 | if (!machine__set_modules_path(machine)) |
803 | char name[PATH_MAX]; | 830 | return 0; |
804 | u64 start; | ||
805 | char *sep; | ||
806 | int line_len; | ||
807 | |||
808 | line_len = getline(&line, &n, file); | ||
809 | if (line_len < 0) | ||
810 | break; | ||
811 | |||
812 | if (!line) | ||
813 | goto out_failure; | ||
814 | |||
815 | line[--line_len] = '\0'; /* \n */ | ||
816 | |||
817 | sep = strrchr(line, 'x'); | ||
818 | if (sep == NULL) | ||
819 | continue; | ||
820 | |||
821 | hex2u64(sep + 1, &start); | ||
822 | |||
823 | sep = strchr(line, ' '); | ||
824 | if (sep == NULL) | ||
825 | continue; | ||
826 | |||
827 | *sep = '\0'; | ||
828 | |||
829 | snprintf(name, sizeof(name), "[%s]", line); | ||
830 | map = machine__new_module(machine, start, name); | ||
831 | if (map == NULL) | ||
832 | goto out_delete_line; | ||
833 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
834 | } | ||
835 | 831 | ||
836 | free(line); | 832 | pr_debug("Problems setting modules path maps, continuing anyway...\n"); |
837 | fclose(file); | ||
838 | 833 | ||
839 | if (machine__set_modules_path(machine) < 0) { | ||
840 | pr_debug("Problems setting modules path maps, continuing anyway...\n"); | ||
841 | } | ||
842 | return 0; | 834 | return 0; |
843 | |||
844 | out_delete_line: | ||
845 | free(line); | ||
846 | out_failure: | ||
847 | return -1; | ||
848 | } | 835 | } |
849 | 836 | ||
850 | int machine__create_kernel_maps(struct machine *machine) | 837 | int machine__create_kernel_maps(struct machine *machine) |
@@ -998,7 +985,8 @@ out_problem: | |||
998 | } | 985 | } |
999 | 986 | ||
1000 | int machine__process_mmap2_event(struct machine *machine, | 987 | int machine__process_mmap2_event(struct machine *machine, |
1001 | union perf_event *event) | 988 | union perf_event *event, |
989 | struct perf_sample *sample __maybe_unused) | ||
1002 | { | 990 | { |
1003 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 991 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
1004 | struct thread *thread; | 992 | struct thread *thread; |
@@ -1045,7 +1033,8 @@ out_problem: | |||
1045 | return 0; | 1033 | return 0; |
1046 | } | 1034 | } |
1047 | 1035 | ||
1048 | int machine__process_mmap_event(struct machine *machine, union perf_event *event) | 1036 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, |
1037 | struct perf_sample *sample __maybe_unused) | ||
1049 | { | 1038 | { |
1050 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 1039 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
1051 | struct thread *thread; | 1040 | struct thread *thread; |
@@ -1102,7 +1091,8 @@ static void machine__remove_thread(struct machine *machine, struct thread *th) | |||
1102 | list_add_tail(&th->node, &machine->dead_threads); | 1091 | list_add_tail(&th->node, &machine->dead_threads); |
1103 | } | 1092 | } |
1104 | 1093 | ||
1105 | int machine__process_fork_event(struct machine *machine, union perf_event *event) | 1094 | int machine__process_fork_event(struct machine *machine, union perf_event *event, |
1095 | struct perf_sample *sample) | ||
1106 | { | 1096 | { |
1107 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1097 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1108 | struct thread *parent = machine__findnew_thread(machine, | 1098 | struct thread *parent = machine__findnew_thread(machine, |
@@ -1119,7 +1109,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1119 | perf_event__fprintf_task(event, stdout); | 1109 | perf_event__fprintf_task(event, stdout); |
1120 | 1110 | ||
1121 | if (thread == NULL || parent == NULL || | 1111 | if (thread == NULL || parent == NULL || |
1122 | thread__fork(thread, parent) < 0) { | 1112 | thread__fork(thread, parent, sample->time) < 0) { |
1123 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | 1113 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); |
1124 | return -1; | 1114 | return -1; |
1125 | } | 1115 | } |
@@ -1127,8 +1117,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1127 | return 0; | 1117 | return 0; |
1128 | } | 1118 | } |
1129 | 1119 | ||
1130 | int machine__process_exit_event(struct machine *machine __maybe_unused, | 1120 | int machine__process_exit_event(struct machine *machine, union perf_event *event, |
1131 | union perf_event *event) | 1121 | struct perf_sample *sample __maybe_unused) |
1132 | { | 1122 | { |
1133 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1123 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1134 | 1124 | ||
@@ -1141,23 +1131,24 @@ int machine__process_exit_event(struct machine *machine __maybe_unused, | |||
1141 | return 0; | 1131 | return 0; |
1142 | } | 1132 | } |
1143 | 1133 | ||
1144 | int machine__process_event(struct machine *machine, union perf_event *event) | 1134 | int machine__process_event(struct machine *machine, union perf_event *event, |
1135 | struct perf_sample *sample) | ||
1145 | { | 1136 | { |
1146 | int ret; | 1137 | int ret; |
1147 | 1138 | ||
1148 | switch (event->header.type) { | 1139 | switch (event->header.type) { |
1149 | case PERF_RECORD_COMM: | 1140 | case PERF_RECORD_COMM: |
1150 | ret = machine__process_comm_event(machine, event); break; | 1141 | ret = machine__process_comm_event(machine, event, sample); break; |
1151 | case PERF_RECORD_MMAP: | 1142 | case PERF_RECORD_MMAP: |
1152 | ret = machine__process_mmap_event(machine, event); break; | 1143 | ret = machine__process_mmap_event(machine, event, sample); break; |
1153 | case PERF_RECORD_MMAP2: | 1144 | case PERF_RECORD_MMAP2: |
1154 | ret = machine__process_mmap2_event(machine, event); break; | 1145 | ret = machine__process_mmap2_event(machine, event, sample); break; |
1155 | case PERF_RECORD_FORK: | 1146 | case PERF_RECORD_FORK: |
1156 | ret = machine__process_fork_event(machine, event); break; | 1147 | ret = machine__process_fork_event(machine, event, sample); break; |
1157 | case PERF_RECORD_EXIT: | 1148 | case PERF_RECORD_EXIT: |
1158 | ret = machine__process_exit_event(machine, event); break; | 1149 | ret = machine__process_exit_event(machine, event, sample); break; |
1159 | case PERF_RECORD_LOST: | 1150 | case PERF_RECORD_LOST: |
1160 | ret = machine__process_lost_event(machine, event); break; | 1151 | ret = machine__process_lost_event(machine, event, sample); break; |
1161 | default: | 1152 | default: |
1162 | ret = -1; | 1153 | ret = -1; |
1163 | break; | 1154 | break; |
@@ -1267,10 +1258,12 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
1267 | struct thread *thread, | 1258 | struct thread *thread, |
1268 | struct ip_callchain *chain, | 1259 | struct ip_callchain *chain, |
1269 | struct symbol **parent, | 1260 | struct symbol **parent, |
1270 | struct addr_location *root_al) | 1261 | struct addr_location *root_al, |
1262 | int max_stack) | ||
1271 | { | 1263 | { |
1272 | u8 cpumode = PERF_RECORD_MISC_USER; | 1264 | u8 cpumode = PERF_RECORD_MISC_USER; |
1273 | unsigned int i; | 1265 | int chain_nr = min(max_stack, (int)chain->nr); |
1266 | int i; | ||
1274 | int err; | 1267 | int err; |
1275 | 1268 | ||
1276 | callchain_cursor_reset(&callchain_cursor); | 1269 | callchain_cursor_reset(&callchain_cursor); |
@@ -1280,7 +1273,7 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
1280 | return 0; | 1273 | return 0; |
1281 | } | 1274 | } |
1282 | 1275 | ||
1283 | for (i = 0; i < chain->nr; i++) { | 1276 | for (i = 0; i < chain_nr; i++) { |
1284 | u64 ip; | 1277 | u64 ip; |
1285 | struct addr_location al; | 1278 | struct addr_location al; |
1286 | 1279 | ||
@@ -1352,12 +1345,14 @@ int machine__resolve_callchain(struct machine *machine, | |||
1352 | struct thread *thread, | 1345 | struct thread *thread, |
1353 | struct perf_sample *sample, | 1346 | struct perf_sample *sample, |
1354 | struct symbol **parent, | 1347 | struct symbol **parent, |
1355 | struct addr_location *root_al) | 1348 | struct addr_location *root_al, |
1349 | int max_stack) | ||
1356 | { | 1350 | { |
1357 | int ret; | 1351 | int ret; |
1358 | 1352 | ||
1359 | ret = machine__resolve_callchain_sample(machine, thread, | 1353 | ret = machine__resolve_callchain_sample(machine, thread, |
1360 | sample->callchain, parent, root_al); | 1354 | sample->callchain, parent, |
1355 | root_al, max_stack); | ||
1361 | if (ret) | 1356 | if (ret) |
1362 | return ret; | 1357 | return ret; |
1363 | 1358 | ||
@@ -1373,6 +1368,41 @@ int machine__resolve_callchain(struct machine *machine, | |||
1373 | 1368 | ||
1374 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | 1369 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, |
1375 | thread, evsel->attr.sample_regs_user, | 1370 | thread, evsel->attr.sample_regs_user, |
1376 | sample); | 1371 | sample, max_stack); |
1377 | 1372 | ||
1378 | } | 1373 | } |
1374 | |||
1375 | int machine__for_each_thread(struct machine *machine, | ||
1376 | int (*fn)(struct thread *thread, void *p), | ||
1377 | void *priv) | ||
1378 | { | ||
1379 | struct rb_node *nd; | ||
1380 | struct thread *thread; | ||
1381 | int rc = 0; | ||
1382 | |||
1383 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | ||
1384 | thread = rb_entry(nd, struct thread, rb_node); | ||
1385 | rc = fn(thread, priv); | ||
1386 | if (rc != 0) | ||
1387 | return rc; | ||
1388 | } | ||
1389 | |||
1390 | list_for_each_entry(thread, &machine->dead_threads, node) { | ||
1391 | rc = fn(thread, priv); | ||
1392 | if (rc != 0) | ||
1393 | return rc; | ||
1394 | } | ||
1395 | return rc; | ||
1396 | } | ||
1397 | |||
1398 | int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, | ||
1399 | struct target *target, struct thread_map *threads, | ||
1400 | perf_event__handler_t process, bool data_mmap) | ||
1401 | { | ||
1402 | if (target__has_task(target)) | ||
1403 | return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); | ||
1404 | else if (target__has_cpu(target)) | ||
1405 | return perf_event__synthesize_threads(tool, process, machine, data_mmap); | ||
1406 | /* command specified */ | ||
1407 | return 0; | ||
1408 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 58a6be1fc739..477133015440 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include "map.h" | 6 | #include "map.h" |
7 | #include "event.h" | ||
7 | 8 | ||
8 | struct addr_location; | 9 | struct addr_location; |
9 | struct branch_stack; | 10 | struct branch_stack; |
@@ -40,13 +41,20 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) | |||
40 | 41 | ||
41 | struct thread *machine__find_thread(struct machine *machine, pid_t tid); | 42 | struct thread *machine__find_thread(struct machine *machine, pid_t tid); |
42 | 43 | ||
43 | int machine__process_comm_event(struct machine *machine, union perf_event *event); | 44 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
44 | int machine__process_exit_event(struct machine *machine, union perf_event *event); | 45 | struct perf_sample *sample); |
45 | int machine__process_fork_event(struct machine *machine, union perf_event *event); | 46 | int machine__process_exit_event(struct machine *machine, union perf_event *event, |
46 | int machine__process_lost_event(struct machine *machine, union perf_event *event); | 47 | struct perf_sample *sample); |
47 | int machine__process_mmap_event(struct machine *machine, union perf_event *event); | 48 | int machine__process_fork_event(struct machine *machine, union perf_event *event, |
48 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event); | 49 | struct perf_sample *sample); |
49 | int machine__process_event(struct machine *machine, union perf_event *event); | 50 | int machine__process_lost_event(struct machine *machine, union perf_event *event, |
51 | struct perf_sample *sample); | ||
52 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, | ||
53 | struct perf_sample *sample); | ||
54 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event, | ||
55 | struct perf_sample *sample); | ||
56 | int machine__process_event(struct machine *machine, union perf_event *event, | ||
57 | struct perf_sample *sample); | ||
50 | 58 | ||
51 | typedef void (*machine__process_t)(struct machine *machine, void *data); | 59 | typedef void (*machine__process_t)(struct machine *machine, void *data); |
52 | 60 | ||
@@ -74,6 +82,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | |||
74 | void machines__set_symbol_filter(struct machines *machines, | 82 | void machines__set_symbol_filter(struct machines *machines, |
75 | symbol_filter_t symbol_filter); | 83 | symbol_filter_t symbol_filter); |
76 | 84 | ||
85 | struct machine *machine__new_host(void); | ||
77 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 86 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
78 | void machine__exit(struct machine *machine); | 87 | void machine__exit(struct machine *machine); |
79 | void machine__delete_dead_threads(struct machine *machine); | 88 | void machine__delete_dead_threads(struct machine *machine); |
@@ -91,7 +100,8 @@ int machine__resolve_callchain(struct machine *machine, | |||
91 | struct thread *thread, | 100 | struct thread *thread, |
92 | struct perf_sample *sample, | 101 | struct perf_sample *sample, |
93 | struct symbol **parent, | 102 | struct symbol **parent, |
94 | struct addr_location *root_al); | 103 | struct addr_location *root_al, |
104 | int max_stack); | ||
95 | 105 | ||
96 | /* | 106 | /* |
97 | * Default guest kernel is defined by parameter --guestkallsyms | 107 | * Default guest kernel is defined by parameter --guestkallsyms |
@@ -165,4 +175,19 @@ void machines__destroy_kernel_maps(struct machines *machines); | |||
165 | 175 | ||
166 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | 176 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); |
167 | 177 | ||
178 | int machine__for_each_thread(struct machine *machine, | ||
179 | int (*fn)(struct thread *thread, void *p), | ||
180 | void *priv); | ||
181 | |||
182 | int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, | ||
183 | struct target *target, struct thread_map *threads, | ||
184 | perf_event__handler_t process, bool data_mmap); | ||
185 | static inline | ||
186 | int machine__synthesize_threads(struct machine *machine, struct target *target, | ||
187 | struct thread_map *threads, bool data_mmap) | ||
188 | { | ||
189 | return __machine__synthesize_threads(machine, NULL, target, threads, | ||
190 | perf_event__process, data_mmap); | ||
191 | } | ||
192 | |||
168 | #endif /* __PERF_MACHINE_H */ | 193 | #endif /* __PERF_MACHINE_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4f6680d2043b..ef5bc913ca7a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -172,7 +172,7 @@ int map__load(struct map *map, symbol_filter_t filter) | |||
172 | pr_warning(", continuing without symbols\n"); | 172 | pr_warning(", continuing without symbols\n"); |
173 | return -1; | 173 | return -1; |
174 | } else if (nr == 0) { | 174 | } else if (nr == 0) { |
175 | #ifdef LIBELF_SUPPORT | 175 | #ifdef HAVE_LIBELF_SUPPORT |
176 | const size_t len = strlen(name); | 176 | const size_t len = strlen(name); |
177 | const size_t real_len = len - sizeof(DSO__DELETED); | 177 | const size_t real_len = len - sizeof(DSO__DELETED); |
178 | 178 | ||
@@ -252,10 +252,16 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) | |||
252 | return fprintf(fp, "%s", dsoname); | 252 | return fprintf(fp, "%s", dsoname); |
253 | } | 253 | } |
254 | 254 | ||
255 | /* | 255 | /** |
256 | * map__rip_2objdump - convert symbol start address to objdump address. | ||
257 | * @map: memory map | ||
258 | * @rip: symbol start address | ||
259 | * | ||
256 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | 260 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. |
257 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is | 261 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is |
258 | * relative to section start. | 262 | * relative to section start. |
263 | * | ||
264 | * Return: Address suitable for passing to "objdump --start-address=" | ||
259 | */ | 265 | */ |
260 | u64 map__rip_2objdump(struct map *map, u64 rip) | 266 | u64 map__rip_2objdump(struct map *map, u64 rip) |
261 | { | 267 | { |
@@ -268,6 +274,29 @@ u64 map__rip_2objdump(struct map *map, u64 rip) | |||
268 | return map->unmap_ip(map, rip); | 274 | return map->unmap_ip(map, rip); |
269 | } | 275 | } |
270 | 276 | ||
277 | /** | ||
278 | * map__objdump_2mem - convert objdump address to a memory address. | ||
279 | * @map: memory map | ||
280 | * @ip: objdump address | ||
281 | * | ||
282 | * Closely related to map__rip_2objdump(), this function takes an address from | ||
283 | * objdump and converts it to a memory address. Note this assumes that @map | ||
284 | * contains the address. To be sure the result is valid, check it forwards | ||
285 | * e.g. map__rip_2objdump(map->map_ip(map, map__objdump_2mem(map, ip))) == ip | ||
286 | * | ||
287 | * Return: Memory address. | ||
288 | */ | ||
289 | u64 map__objdump_2mem(struct map *map, u64 ip) | ||
290 | { | ||
291 | if (!map->dso->adjust_symbols) | ||
292 | return map->unmap_ip(map, ip); | ||
293 | |||
294 | if (map->dso->rel) | ||
295 | return map->unmap_ip(map, ip + map->pgoff); | ||
296 | |||
297 | return ip; | ||
298 | } | ||
299 | |||
271 | void map_groups__init(struct map_groups *mg) | 300 | void map_groups__init(struct map_groups *mg) |
272 | { | 301 | { |
273 | int i; | 302 | int i; |
@@ -371,6 +400,23 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | |||
371 | return NULL; | 400 | return NULL; |
372 | } | 401 | } |
373 | 402 | ||
403 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) | ||
404 | { | ||
405 | if (ams->addr < ams->map->start || ams->addr > ams->map->end) { | ||
406 | if (ams->map->groups == NULL) | ||
407 | return -1; | ||
408 | ams->map = map_groups__find(ams->map->groups, ams->map->type, | ||
409 | ams->addr); | ||
410 | if (ams->map == NULL) | ||
411 | return -1; | ||
412 | } | ||
413 | |||
414 | ams->al_addr = ams->map->map_ip(ams->map, ams->addr); | ||
415 | ams->sym = map__find_symbol(ams->map, ams->al_addr, filter); | ||
416 | |||
417 | return ams->sym ? 0 : -1; | ||
418 | } | ||
419 | |||
374 | size_t __map_groups__fprintf_maps(struct map_groups *mg, | 420 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
375 | enum map_type type, int verbose, FILE *fp) | 421 | enum map_type type, int verbose, FILE *fp) |
376 | { | 422 | { |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4886ca280536..e4e259c3ba16 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -84,6 +84,9 @@ static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip) | |||
84 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ | 84 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ |
85 | u64 map__rip_2objdump(struct map *map, u64 rip); | 85 | u64 map__rip_2objdump(struct map *map, u64 rip); |
86 | 86 | ||
87 | /* objdump address -> memory address */ | ||
88 | u64 map__objdump_2mem(struct map *map, u64 ip); | ||
89 | |||
87 | struct symbol; | 90 | struct symbol; |
88 | 91 | ||
89 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 92 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
@@ -167,6 +170,10 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | |||
167 | struct map **mapp, | 170 | struct map **mapp, |
168 | symbol_filter_t filter); | 171 | symbol_filter_t filter); |
169 | 172 | ||
173 | struct addr_map_symbol; | ||
174 | |||
175 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); | ||
176 | |||
170 | static inline | 177 | static inline |
171 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, | 178 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
172 | const char *name, struct map **mapp, | 179 | const char *name, struct map **mapp, |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 98125319b158..6de6f89c2a61 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -277,7 +277,7 @@ static int __add_event(struct list_head *list, int *idx, | |||
277 | 277 | ||
278 | event_attr_init(attr); | 278 | event_attr_init(attr); |
279 | 279 | ||
280 | evsel = perf_evsel__new(attr, (*idx)++); | 280 | evsel = perf_evsel__new_idx(attr, (*idx)++); |
281 | if (!evsel) | 281 | if (!evsel) |
282 | return -ENOMEM; | 282 | return -ENOMEM; |
283 | 283 | ||
@@ -378,7 +378,7 @@ static int add_tracepoint(struct list_head *list, int *idx, | |||
378 | { | 378 | { |
379 | struct perf_evsel *evsel; | 379 | struct perf_evsel *evsel; |
380 | 380 | ||
381 | evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); | 381 | evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++); |
382 | if (!evsel) | 382 | if (!evsel) |
383 | return -ENOMEM; | 383 | return -ENOMEM; |
384 | 384 | ||
@@ -998,8 +998,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
998 | char evt_path[MAXPATHLEN]; | 998 | char evt_path[MAXPATHLEN]; |
999 | char dir_path[MAXPATHLEN]; | 999 | char dir_path[MAXPATHLEN]; |
1000 | 1000 | ||
1001 | if (debugfs_valid_mountpoint(tracing_events_path)) | 1001 | if (debugfs_valid_mountpoint(tracing_events_path)) { |
1002 | printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); | ||
1002 | return; | 1003 | return; |
1004 | } | ||
1003 | 1005 | ||
1004 | sys_dir = opendir(tracing_events_path); | 1006 | sys_dir = opendir(tracing_events_path); |
1005 | if (!sys_dir) | 1007 | if (!sys_dir) |
@@ -1095,7 +1097,7 @@ static bool is_event_supported(u8 type, unsigned config) | |||
1095 | .threads = { 0 }, | 1097 | .threads = { 0 }, |
1096 | }; | 1098 | }; |
1097 | 1099 | ||
1098 | evsel = perf_evsel__new(&attr, 0); | 1100 | evsel = perf_evsel__new(&attr); |
1099 | if (evsel) { | 1101 | if (evsel) { |
1100 | ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; | 1102 | ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; |
1101 | perf_evsel__delete(evsel); | 1103 | perf_evsel__delete(evsel); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 91346b753960..343299575b30 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -126,6 +126,37 @@ modifier_bp [rwx]{1,3} | |||
126 | 126 | ||
127 | } | 127 | } |
128 | 128 | ||
129 | <config>{ | ||
130 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } | ||
131 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } | ||
132 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } | ||
133 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } | ||
134 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | ||
135 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | ||
136 | , { return ','; } | ||
137 | "/" { BEGIN(INITIAL); return '/'; } | ||
138 | {name_minus} { return str(yyscanner, PE_NAME); } | ||
139 | } | ||
140 | |||
141 | <mem>{ | ||
142 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } | ||
143 | : { return ':'; } | ||
144 | {num_dec} { return value(yyscanner, 10); } | ||
145 | {num_hex} { return value(yyscanner, 16); } | ||
146 | /* | ||
147 | * We need to separate 'mem:' scanner part, in order to get specific | ||
148 | * modifier bits parsed out. Otherwise we would need to handle PE_NAME | ||
149 | * and we'd need to parse it manually. During the escape from <mem> | ||
150 | * state we need to put the escaping char back, so we dont miss it. | ||
151 | */ | ||
152 | . { unput(*yytext); BEGIN(INITIAL); } | ||
153 | /* | ||
154 | * We destroy the scanner after reaching EOF, | ||
155 | * but anyway just to be sure get back to INIT state. | ||
156 | */ | ||
157 | <<EOF>> { BEGIN(INITIAL); } | ||
158 | } | ||
159 | |||
129 | cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } | 160 | cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } |
130 | stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } | 161 | stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } |
131 | stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } | 162 | stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } |
@@ -162,18 +193,6 @@ speculative-read|speculative-load | | |||
162 | refs|Reference|ops|access | | 193 | refs|Reference|ops|access | |
163 | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } | 194 | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } |
164 | 195 | ||
165 | <config>{ | ||
166 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } | ||
167 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } | ||
168 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } | ||
169 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } | ||
170 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | ||
171 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | ||
172 | , { return ','; } | ||
173 | "/" { BEGIN(INITIAL); return '/'; } | ||
174 | {name_minus} { return str(yyscanner, PE_NAME); } | ||
175 | } | ||
176 | |||
177 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } | 196 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } |
178 | r{num_raw_hex} { return raw(yyscanner); } | 197 | r{num_raw_hex} { return raw(yyscanner); } |
179 | {num_dec} { return value(yyscanner, 10); } | 198 | {num_dec} { return value(yyscanner, 10); } |
@@ -189,25 +208,7 @@ r{num_raw_hex} { return raw(yyscanner); } | |||
189 | "}" { return '}'; } | 208 | "}" { return '}'; } |
190 | = { return '='; } | 209 | = { return '='; } |
191 | \n { } | 210 | \n { } |
192 | 211 | . { } | |
193 | <mem>{ | ||
194 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } | ||
195 | : { return ':'; } | ||
196 | {num_dec} { return value(yyscanner, 10); } | ||
197 | {num_hex} { return value(yyscanner, 16); } | ||
198 | /* | ||
199 | * We need to separate 'mem:' scanner part, in order to get specific | ||
200 | * modifier bits parsed out. Otherwise we would need to handle PE_NAME | ||
201 | * and we'd need to parse it manually. During the escape from <mem> | ||
202 | * state we need to put the escaping char back, so we dont miss it. | ||
203 | */ | ||
204 | . { unput(*yytext); BEGIN(INITIAL); } | ||
205 | /* | ||
206 | * We destroy the scanner after reaching EOF, | ||
207 | * but anyway just to be sure get back to INIT state. | ||
208 | */ | ||
209 | <<EOF>> { BEGIN(INITIAL); } | ||
210 | } | ||
211 | 212 | ||
212 | %% | 213 | %% |
213 | 214 | ||
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 2bc9e70df7e2..31f404a032a9 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -339,10 +339,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
339 | if (arg[1] != '-') { | 339 | if (arg[1] != '-') { |
340 | ctx->opt = arg + 1; | 340 | ctx->opt = arg + 1; |
341 | if (internal_help && *ctx->opt == 'h') | 341 | if (internal_help && *ctx->opt == 'h') |
342 | return parse_options_usage(usagestr, options); | 342 | return usage_with_options_internal(usagestr, options, 0); |
343 | switch (parse_short_opt(ctx, options)) { | 343 | switch (parse_short_opt(ctx, options)) { |
344 | case -1: | 344 | case -1: |
345 | return parse_options_usage(usagestr, options); | 345 | return parse_options_usage(usagestr, options, arg + 1, 1); |
346 | case -2: | 346 | case -2: |
347 | goto unknown; | 347 | goto unknown; |
348 | default: | 348 | default: |
@@ -352,10 +352,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
352 | check_typos(arg + 1, options); | 352 | check_typos(arg + 1, options); |
353 | while (ctx->opt) { | 353 | while (ctx->opt) { |
354 | if (internal_help && *ctx->opt == 'h') | 354 | if (internal_help && *ctx->opt == 'h') |
355 | return parse_options_usage(usagestr, options); | 355 | return usage_with_options_internal(usagestr, options, 0); |
356 | arg = ctx->opt; | ||
356 | switch (parse_short_opt(ctx, options)) { | 357 | switch (parse_short_opt(ctx, options)) { |
357 | case -1: | 358 | case -1: |
358 | return parse_options_usage(usagestr, options); | 359 | return parse_options_usage(usagestr, options, arg, 1); |
359 | case -2: | 360 | case -2: |
360 | /* fake a short option thing to hide the fact that we may have | 361 | /* fake a short option thing to hide the fact that we may have |
361 | * started to parse aggregated stuff | 362 | * started to parse aggregated stuff |
@@ -383,12 +384,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
383 | if (internal_help && !strcmp(arg + 2, "help-all")) | 384 | if (internal_help && !strcmp(arg + 2, "help-all")) |
384 | return usage_with_options_internal(usagestr, options, 1); | 385 | return usage_with_options_internal(usagestr, options, 1); |
385 | if (internal_help && !strcmp(arg + 2, "help")) | 386 | if (internal_help && !strcmp(arg + 2, "help")) |
386 | return parse_options_usage(usagestr, options); | 387 | return usage_with_options_internal(usagestr, options, 0); |
387 | if (!strcmp(arg + 2, "list-opts")) | 388 | if (!strcmp(arg + 2, "list-opts")) |
388 | return PARSE_OPT_LIST; | 389 | return PARSE_OPT_LIST; |
389 | switch (parse_long_opt(ctx, arg + 2, options)) { | 390 | switch (parse_long_opt(ctx, arg + 2, options)) { |
390 | case -1: | 391 | case -1: |
391 | return parse_options_usage(usagestr, options); | 392 | return parse_options_usage(usagestr, options, arg + 2, 0); |
392 | case -2: | 393 | case -2: |
393 | goto unknown; | 394 | goto unknown; |
394 | default: | 395 | default: |
@@ -445,6 +446,89 @@ int parse_options(int argc, const char **argv, const struct option *options, | |||
445 | #define USAGE_OPTS_WIDTH 24 | 446 | #define USAGE_OPTS_WIDTH 24 |
446 | #define USAGE_GAP 2 | 447 | #define USAGE_GAP 2 |
447 | 448 | ||
449 | static void print_option_help(const struct option *opts, int full) | ||
450 | { | ||
451 | size_t pos; | ||
452 | int pad; | ||
453 | |||
454 | if (opts->type == OPTION_GROUP) { | ||
455 | fputc('\n', stderr); | ||
456 | if (*opts->help) | ||
457 | fprintf(stderr, "%s\n", opts->help); | ||
458 | return; | ||
459 | } | ||
460 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) | ||
461 | return; | ||
462 | |||
463 | pos = fprintf(stderr, " "); | ||
464 | if (opts->short_name) | ||
465 | pos += fprintf(stderr, "-%c", opts->short_name); | ||
466 | else | ||
467 | pos += fprintf(stderr, " "); | ||
468 | |||
469 | if (opts->long_name && opts->short_name) | ||
470 | pos += fprintf(stderr, ", "); | ||
471 | if (opts->long_name) | ||
472 | pos += fprintf(stderr, "--%s", opts->long_name); | ||
473 | |||
474 | switch (opts->type) { | ||
475 | case OPTION_ARGUMENT: | ||
476 | break; | ||
477 | case OPTION_LONG: | ||
478 | case OPTION_U64: | ||
479 | case OPTION_INTEGER: | ||
480 | case OPTION_UINTEGER: | ||
481 | if (opts->flags & PARSE_OPT_OPTARG) | ||
482 | if (opts->long_name) | ||
483 | pos += fprintf(stderr, "[=<n>]"); | ||
484 | else | ||
485 | pos += fprintf(stderr, "[<n>]"); | ||
486 | else | ||
487 | pos += fprintf(stderr, " <n>"); | ||
488 | break; | ||
489 | case OPTION_CALLBACK: | ||
490 | if (opts->flags & PARSE_OPT_NOARG) | ||
491 | break; | ||
492 | /* FALLTHROUGH */ | ||
493 | case OPTION_STRING: | ||
494 | if (opts->argh) { | ||
495 | if (opts->flags & PARSE_OPT_OPTARG) | ||
496 | if (opts->long_name) | ||
497 | pos += fprintf(stderr, "[=<%s>]", opts->argh); | ||
498 | else | ||
499 | pos += fprintf(stderr, "[<%s>]", opts->argh); | ||
500 | else | ||
501 | pos += fprintf(stderr, " <%s>", opts->argh); | ||
502 | } else { | ||
503 | if (opts->flags & PARSE_OPT_OPTARG) | ||
504 | if (opts->long_name) | ||
505 | pos += fprintf(stderr, "[=...]"); | ||
506 | else | ||
507 | pos += fprintf(stderr, "[...]"); | ||
508 | else | ||
509 | pos += fprintf(stderr, " ..."); | ||
510 | } | ||
511 | break; | ||
512 | default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ | ||
513 | case OPTION_END: | ||
514 | case OPTION_GROUP: | ||
515 | case OPTION_BIT: | ||
516 | case OPTION_BOOLEAN: | ||
517 | case OPTION_INCR: | ||
518 | case OPTION_SET_UINT: | ||
519 | case OPTION_SET_PTR: | ||
520 | break; | ||
521 | } | ||
522 | |||
523 | if (pos <= USAGE_OPTS_WIDTH) | ||
524 | pad = USAGE_OPTS_WIDTH - pos; | ||
525 | else { | ||
526 | fputc('\n', stderr); | ||
527 | pad = USAGE_OPTS_WIDTH; | ||
528 | } | ||
529 | fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); | ||
530 | } | ||
531 | |||
448 | int usage_with_options_internal(const char * const *usagestr, | 532 | int usage_with_options_internal(const char * const *usagestr, |
449 | const struct option *opts, int full) | 533 | const struct option *opts, int full) |
450 | { | 534 | { |
@@ -464,87 +548,9 @@ int usage_with_options_internal(const char * const *usagestr, | |||
464 | if (opts->type != OPTION_GROUP) | 548 | if (opts->type != OPTION_GROUP) |
465 | fputc('\n', stderr); | 549 | fputc('\n', stderr); |
466 | 550 | ||
467 | for (; opts->type != OPTION_END; opts++) { | 551 | for ( ; opts->type != OPTION_END; opts++) |
468 | size_t pos; | 552 | print_option_help(opts, full); |
469 | int pad; | ||
470 | |||
471 | if (opts->type == OPTION_GROUP) { | ||
472 | fputc('\n', stderr); | ||
473 | if (*opts->help) | ||
474 | fprintf(stderr, "%s\n", opts->help); | ||
475 | continue; | ||
476 | } | ||
477 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) | ||
478 | continue; | ||
479 | |||
480 | pos = fprintf(stderr, " "); | ||
481 | if (opts->short_name) | ||
482 | pos += fprintf(stderr, "-%c", opts->short_name); | ||
483 | else | ||
484 | pos += fprintf(stderr, " "); | ||
485 | |||
486 | if (opts->long_name && opts->short_name) | ||
487 | pos += fprintf(stderr, ", "); | ||
488 | if (opts->long_name) | ||
489 | pos += fprintf(stderr, "--%s", opts->long_name); | ||
490 | |||
491 | switch (opts->type) { | ||
492 | case OPTION_ARGUMENT: | ||
493 | break; | ||
494 | case OPTION_LONG: | ||
495 | case OPTION_U64: | ||
496 | case OPTION_INTEGER: | ||
497 | case OPTION_UINTEGER: | ||
498 | if (opts->flags & PARSE_OPT_OPTARG) | ||
499 | if (opts->long_name) | ||
500 | pos += fprintf(stderr, "[=<n>]"); | ||
501 | else | ||
502 | pos += fprintf(stderr, "[<n>]"); | ||
503 | else | ||
504 | pos += fprintf(stderr, " <n>"); | ||
505 | break; | ||
506 | case OPTION_CALLBACK: | ||
507 | if (opts->flags & PARSE_OPT_NOARG) | ||
508 | break; | ||
509 | /* FALLTHROUGH */ | ||
510 | case OPTION_STRING: | ||
511 | if (opts->argh) { | ||
512 | if (opts->flags & PARSE_OPT_OPTARG) | ||
513 | if (opts->long_name) | ||
514 | pos += fprintf(stderr, "[=<%s>]", opts->argh); | ||
515 | else | ||
516 | pos += fprintf(stderr, "[<%s>]", opts->argh); | ||
517 | else | ||
518 | pos += fprintf(stderr, " <%s>", opts->argh); | ||
519 | } else { | ||
520 | if (opts->flags & PARSE_OPT_OPTARG) | ||
521 | if (opts->long_name) | ||
522 | pos += fprintf(stderr, "[=...]"); | ||
523 | else | ||
524 | pos += fprintf(stderr, "[...]"); | ||
525 | else | ||
526 | pos += fprintf(stderr, " ..."); | ||
527 | } | ||
528 | break; | ||
529 | default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ | ||
530 | case OPTION_END: | ||
531 | case OPTION_GROUP: | ||
532 | case OPTION_BIT: | ||
533 | case OPTION_BOOLEAN: | ||
534 | case OPTION_INCR: | ||
535 | case OPTION_SET_UINT: | ||
536 | case OPTION_SET_PTR: | ||
537 | break; | ||
538 | } | ||
539 | 553 | ||
540 | if (pos <= USAGE_OPTS_WIDTH) | ||
541 | pad = USAGE_OPTS_WIDTH - pos; | ||
542 | else { | ||
543 | fputc('\n', stderr); | ||
544 | pad = USAGE_OPTS_WIDTH; | ||
545 | } | ||
546 | fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); | ||
547 | } | ||
548 | fputc('\n', stderr); | 554 | fputc('\n', stderr); |
549 | 555 | ||
550 | return PARSE_OPT_HELP; | 556 | return PARSE_OPT_HELP; |
@@ -559,9 +565,45 @@ void usage_with_options(const char * const *usagestr, | |||
559 | } | 565 | } |
560 | 566 | ||
561 | int parse_options_usage(const char * const *usagestr, | 567 | int parse_options_usage(const char * const *usagestr, |
562 | const struct option *opts) | 568 | const struct option *opts, |
569 | const char *optstr, bool short_opt) | ||
563 | { | 570 | { |
564 | return usage_with_options_internal(usagestr, opts, 0); | 571 | if (!usagestr) |
572 | goto opt; | ||
573 | |||
574 | fprintf(stderr, "\n usage: %s\n", *usagestr++); | ||
575 | while (*usagestr && **usagestr) | ||
576 | fprintf(stderr, " or: %s\n", *usagestr++); | ||
577 | while (*usagestr) { | ||
578 | fprintf(stderr, "%s%s\n", | ||
579 | **usagestr ? " " : "", | ||
580 | *usagestr); | ||
581 | usagestr++; | ||
582 | } | ||
583 | fputc('\n', stderr); | ||
584 | |||
585 | opt: | ||
586 | for ( ; opts->type != OPTION_END; opts++) { | ||
587 | if (short_opt) { | ||
588 | if (opts->short_name == *optstr) | ||
589 | break; | ||
590 | continue; | ||
591 | } | ||
592 | |||
593 | if (opts->long_name == NULL) | ||
594 | continue; | ||
595 | |||
596 | if (!prefixcmp(optstr, opts->long_name)) | ||
597 | break; | ||
598 | if (!prefixcmp(optstr, "no-") && | ||
599 | !prefixcmp(optstr + 3, opts->long_name)) | ||
600 | break; | ||
601 | } | ||
602 | |||
603 | if (opts->type != OPTION_END) | ||
604 | print_option_help(opts, 0); | ||
605 | |||
606 | return PARSE_OPT_HELP; | ||
565 | } | 607 | } |
566 | 608 | ||
567 | 609 | ||
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 7bb5999940ca..b0241e28eaf7 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -158,7 +158,9 @@ struct parse_opt_ctx_t { | |||
158 | }; | 158 | }; |
159 | 159 | ||
160 | extern int parse_options_usage(const char * const *usagestr, | 160 | extern int parse_options_usage(const char * const *usagestr, |
161 | const struct option *opts); | 161 | const struct option *opts, |
162 | const char *optstr, | ||
163 | bool short_opt); | ||
162 | 164 | ||
163 | extern void parse_options_start(struct parse_opt_ctx_t *ctx, | 165 | extern void parse_options_start(struct parse_opt_ctx_t *ctx, |
164 | int argc, const char **argv, int flags); | 166 | int argc, const char **argv, int flags); |
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index a8c49548ca48..5d13cb45b317 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c | |||
@@ -22,19 +22,23 @@ static const char *get_perf_dir(void) | |||
22 | return "."; | 22 | return "."; |
23 | } | 23 | } |
24 | 24 | ||
25 | #ifndef HAVE_STRLCPY | 25 | /* |
26 | size_t strlcpy(char *dest, const char *src, size_t size) | 26 | * If libc has strlcpy() then that version will override this |
27 | * implementation: | ||
28 | */ | ||
29 | size_t __weak strlcpy(char *dest, const char *src, size_t size) | ||
27 | { | 30 | { |
28 | size_t ret = strlen(src); | 31 | size_t ret = strlen(src); |
29 | 32 | ||
30 | if (size) { | 33 | if (size) { |
31 | size_t len = (ret >= size) ? size - 1 : ret; | 34 | size_t len = (ret >= size) ? size - 1 : ret; |
35 | |||
32 | memcpy(dest, src, len); | 36 | memcpy(dest, src, len); |
33 | dest[len] = '\0'; | 37 | dest[len] = '\0'; |
34 | } | 38 | } |
39 | |||
35 | return ret; | 40 | return ret; |
36 | } | 41 | } |
37 | #endif | ||
38 | 42 | ||
39 | static char *get_pathname(void) | 43 | static char *get_pathname(void) |
40 | { | 44 | { |
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 5a4f2b6f3738..a3d42cd74919 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_REGS_H | 1 | #ifndef __PERF_REGS_H |
2 | #define __PERF_REGS_H | 2 | #define __PERF_REGS_H |
3 | 3 | ||
4 | #ifdef HAVE_PERF_REGS | 4 | #ifdef HAVE_PERF_REGS_SUPPORT |
5 | #include <perf_regs.h> | 5 | #include <perf_regs.h> |
6 | #else | 6 | #else |
7 | #define PERF_REGS_MASK 0 | 7 | #define PERF_REGS_MASK 0 |
@@ -10,5 +10,5 @@ static inline const char *perf_reg_name(int id __maybe_unused) | |||
10 | { | 10 | { |
11 | return NULL; | 11 | return NULL; |
12 | } | 12 | } |
13 | #endif /* HAVE_PERF_REGS */ | 13 | #endif /* HAVE_PERF_REGS_SUPPORT */ |
14 | #endif /* __PERF_REGS_H */ | 14 | #endif /* __PERF_REGS_H */ |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index bc9d8069d376..c232d8dd410b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <unistd.h> | 4 | #include <unistd.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include <dirent.h> | 6 | #include <dirent.h> |
7 | #include "sysfs.h" | 7 | #include "fs.h" |
8 | #include "util.h" | 8 | #include "util.h" |
9 | #include "pmu.h" | 9 | #include "pmu.h" |
10 | #include "parse-events.h" | 10 | #include "parse-events.h" |
@@ -77,9 +77,8 @@ static int pmu_format(const char *name, struct list_head *format) | |||
77 | { | 77 | { |
78 | struct stat st; | 78 | struct stat st; |
79 | char path[PATH_MAX]; | 79 | char path[PATH_MAX]; |
80 | const char *sysfs; | 80 | const char *sysfs = sysfs__mountpoint(); |
81 | 81 | ||
82 | sysfs = sysfs_find_mountpoint(); | ||
83 | if (!sysfs) | 82 | if (!sysfs) |
84 | return -1; | 83 | return -1; |
85 | 84 | ||
@@ -166,9 +165,8 @@ static int pmu_aliases(const char *name, struct list_head *head) | |||
166 | { | 165 | { |
167 | struct stat st; | 166 | struct stat st; |
168 | char path[PATH_MAX]; | 167 | char path[PATH_MAX]; |
169 | const char *sysfs; | 168 | const char *sysfs = sysfs__mountpoint(); |
170 | 169 | ||
171 | sysfs = sysfs_find_mountpoint(); | ||
172 | if (!sysfs) | 170 | if (!sysfs) |
173 | return -1; | 171 | return -1; |
174 | 172 | ||
@@ -212,11 +210,10 @@ static int pmu_type(const char *name, __u32 *type) | |||
212 | { | 210 | { |
213 | struct stat st; | 211 | struct stat st; |
214 | char path[PATH_MAX]; | 212 | char path[PATH_MAX]; |
215 | const char *sysfs; | ||
216 | FILE *file; | 213 | FILE *file; |
217 | int ret = 0; | 214 | int ret = 0; |
215 | const char *sysfs = sysfs__mountpoint(); | ||
218 | 216 | ||
219 | sysfs = sysfs_find_mountpoint(); | ||
220 | if (!sysfs) | 217 | if (!sysfs) |
221 | return -1; | 218 | return -1; |
222 | 219 | ||
@@ -241,11 +238,10 @@ static int pmu_type(const char *name, __u32 *type) | |||
241 | static void pmu_read_sysfs(void) | 238 | static void pmu_read_sysfs(void) |
242 | { | 239 | { |
243 | char path[PATH_MAX]; | 240 | char path[PATH_MAX]; |
244 | const char *sysfs; | ||
245 | DIR *dir; | 241 | DIR *dir; |
246 | struct dirent *dent; | 242 | struct dirent *dent; |
243 | const char *sysfs = sysfs__mountpoint(); | ||
247 | 244 | ||
248 | sysfs = sysfs_find_mountpoint(); | ||
249 | if (!sysfs) | 245 | if (!sysfs) |
250 | return; | 246 | return; |
251 | 247 | ||
@@ -270,11 +266,10 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
270 | { | 266 | { |
271 | struct stat st; | 267 | struct stat st; |
272 | char path[PATH_MAX]; | 268 | char path[PATH_MAX]; |
273 | const char *sysfs; | ||
274 | FILE *file; | 269 | FILE *file; |
275 | struct cpu_map *cpus; | 270 | struct cpu_map *cpus; |
271 | const char *sysfs = sysfs__mountpoint(); | ||
276 | 272 | ||
277 | sysfs = sysfs_find_mountpoint(); | ||
278 | if (!sysfs) | 273 | if (!sysfs) |
279 | return NULL; | 274 | return NULL; |
280 | 275 | ||
@@ -637,3 +632,19 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
637 | printf("\n"); | 632 | printf("\n"); |
638 | free(aliases); | 633 | free(aliases); |
639 | } | 634 | } |
635 | |||
636 | bool pmu_have_event(const char *pname, const char *name) | ||
637 | { | ||
638 | struct perf_pmu *pmu; | ||
639 | struct perf_pmu_alias *alias; | ||
640 | |||
641 | pmu = NULL; | ||
642 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { | ||
643 | if (strcmp(pname, pmu->name)) | ||
644 | continue; | ||
645 | list_for_each_entry(alias, &pmu->aliases, list) | ||
646 | if (!strcmp(alias->name, name)) | ||
647 | return true; | ||
648 | } | ||
649 | return false; | ||
650 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b2cbe2d4cc3..1179b26f244a 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -42,6 +42,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); | |||
42 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | 42 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); |
43 | 43 | ||
44 | void print_pmu_events(const char *event_glob, bool name_only); | 44 | void print_pmu_events(const char *event_glob, bool name_only); |
45 | bool pmu_have_event(const char *pname, const char *name); | ||
45 | 46 | ||
46 | int perf_pmu__test(void); | 47 | int perf_pmu__test(void); |
47 | #endif /* __PMU_H */ | 48 | #endif /* __PMU_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aa04bf9c9ad7..9c6989ca2bea 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -47,7 +47,6 @@ | |||
47 | #include "session.h" | 47 | #include "session.h" |
48 | 48 | ||
49 | #define MAX_CMDLEN 256 | 49 | #define MAX_CMDLEN 256 |
50 | #define MAX_PROBE_ARGS 128 | ||
51 | #define PERFPROBE_GROUP "probe" | 50 | #define PERFPROBE_GROUP "probe" |
52 | 51 | ||
53 | bool probe_event_dry_run; /* Dry run flag */ | 52 | bool probe_event_dry_run; /* Dry run flag */ |
@@ -201,7 +200,7 @@ static int convert_to_perf_probe_point(struct probe_trace_point *tp, | |||
201 | return 0; | 200 | return 0; |
202 | } | 201 | } |
203 | 202 | ||
204 | #ifdef DWARF_SUPPORT | 203 | #ifdef HAVE_DWARF_SUPPORT |
205 | /* Open new debuginfo of given module */ | 204 | /* Open new debuginfo of given module */ |
206 | static struct debuginfo *open_debuginfo(const char *module) | 205 | static struct debuginfo *open_debuginfo(const char *module) |
207 | { | 206 | { |
@@ -630,7 +629,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
630 | return ret; | 629 | return ret; |
631 | } | 630 | } |
632 | 631 | ||
633 | #else /* !DWARF_SUPPORT */ | 632 | #else /* !HAVE_DWARF_SUPPORT */ |
634 | 633 | ||
635 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 634 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
636 | struct perf_probe_point *pp) | 635 | struct perf_probe_point *pp) |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c09e0a9fdf4c..ffb657ffd327 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -115,7 +115,7 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
115 | }; | 115 | }; |
116 | 116 | ||
117 | /* Get a Dwarf from offline image */ | 117 | /* Get a Dwarf from offline image */ |
118 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, | 118 | static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, |
119 | const char *path) | 119 | const char *path) |
120 | { | 120 | { |
121 | int fd; | 121 | int fd; |
@@ -124,25 +124,25 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self, | |||
124 | if (fd < 0) | 124 | if (fd < 0) |
125 | return fd; | 125 | return fd; |
126 | 126 | ||
127 | self->dwfl = dwfl_begin(&offline_callbacks); | 127 | dbg->dwfl = dwfl_begin(&offline_callbacks); |
128 | if (!self->dwfl) | 128 | if (!dbg->dwfl) |
129 | goto error; | 129 | goto error; |
130 | 130 | ||
131 | self->mod = dwfl_report_offline(self->dwfl, "", "", fd); | 131 | dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); |
132 | if (!self->mod) | 132 | if (!dbg->mod) |
133 | goto error; | 133 | goto error; |
134 | 134 | ||
135 | self->dbg = dwfl_module_getdwarf(self->mod, &self->bias); | 135 | dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); |
136 | if (!self->dbg) | 136 | if (!dbg->dbg) |
137 | goto error; | 137 | goto error; |
138 | 138 | ||
139 | return 0; | 139 | return 0; |
140 | error: | 140 | error: |
141 | if (self->dwfl) | 141 | if (dbg->dwfl) |
142 | dwfl_end(self->dwfl); | 142 | dwfl_end(dbg->dwfl); |
143 | else | 143 | else |
144 | close(fd); | 144 | close(fd); |
145 | memset(self, 0, sizeof(*self)); | 145 | memset(dbg, 0, sizeof(*dbg)); |
146 | 146 | ||
147 | return -ENOENT; | 147 | return -ENOENT; |
148 | } | 148 | } |
@@ -180,24 +180,24 @@ static const Dwfl_Callbacks kernel_callbacks = { | |||
180 | }; | 180 | }; |
181 | 181 | ||
182 | /* Get a Dwarf from live kernel image */ | 182 | /* Get a Dwarf from live kernel image */ |
183 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, | 183 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg, |
184 | Dwarf_Addr addr) | 184 | Dwarf_Addr addr) |
185 | { | 185 | { |
186 | self->dwfl = dwfl_begin(&kernel_callbacks); | 186 | dbg->dwfl = dwfl_begin(&kernel_callbacks); |
187 | if (!self->dwfl) | 187 | if (!dbg->dwfl) |
188 | return -EINVAL; | 188 | return -EINVAL; |
189 | 189 | ||
190 | /* Load the kernel dwarves: Don't care the result here */ | 190 | /* Load the kernel dwarves: Don't care the result here */ |
191 | dwfl_linux_kernel_report_kernel(self->dwfl); | 191 | dwfl_linux_kernel_report_kernel(dbg->dwfl); |
192 | dwfl_linux_kernel_report_modules(self->dwfl); | 192 | dwfl_linux_kernel_report_modules(dbg->dwfl); |
193 | 193 | ||
194 | self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); | 194 | dbg->dbg = dwfl_addrdwarf(dbg->dwfl, addr, &dbg->bias); |
195 | /* Here, check whether we could get a real dwarf */ | 195 | /* Here, check whether we could get a real dwarf */ |
196 | if (!self->dbg) { | 196 | if (!dbg->dbg) { |
197 | pr_debug("Failed to find kernel dwarf at %lx\n", | 197 | pr_debug("Failed to find kernel dwarf at %lx\n", |
198 | (unsigned long)addr); | 198 | (unsigned long)addr); |
199 | dwfl_end(self->dwfl); | 199 | dwfl_end(dbg->dwfl); |
200 | memset(self, 0, sizeof(*self)); | 200 | memset(dbg, 0, sizeof(*dbg)); |
201 | return -ENOENT; | 201 | return -ENOENT; |
202 | } | 202 | } |
203 | 203 | ||
@@ -205,7 +205,7 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, | |||
205 | } | 205 | } |
206 | #else | 206 | #else |
207 | /* With older elfutils, this just support kernel module... */ | 207 | /* With older elfutils, this just support kernel module... */ |
208 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, | 208 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg, |
209 | Dwarf_Addr addr __maybe_unused) | 209 | Dwarf_Addr addr __maybe_unused) |
210 | { | 210 | { |
211 | const char *path = kernel_get_module_path("kernel"); | 211 | const char *path = kernel_get_module_path("kernel"); |
@@ -216,44 +216,45 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, | |||
216 | } | 216 | } |
217 | 217 | ||
218 | pr_debug2("Use file %s for debuginfo\n", path); | 218 | pr_debug2("Use file %s for debuginfo\n", path); |
219 | return debuginfo__init_offline_dwarf(self, path); | 219 | return debuginfo__init_offline_dwarf(dbg, path); |
220 | } | 220 | } |
221 | #endif | 221 | #endif |
222 | 222 | ||
223 | struct debuginfo *debuginfo__new(const char *path) | 223 | struct debuginfo *debuginfo__new(const char *path) |
224 | { | 224 | { |
225 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); | 225 | struct debuginfo *dbg = zalloc(sizeof(*dbg)); |
226 | if (!self) | 226 | if (!dbg) |
227 | return NULL; | 227 | return NULL; |
228 | 228 | ||
229 | if (debuginfo__init_offline_dwarf(self, path) < 0) { | 229 | if (debuginfo__init_offline_dwarf(dbg, path) < 0) { |
230 | free(self); | 230 | free(dbg); |
231 | self = NULL; | 231 | dbg = NULL; |
232 | } | 232 | } |
233 | 233 | ||
234 | return self; | 234 | return dbg; |
235 | } | 235 | } |
236 | 236 | ||
237 | struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) | 237 | struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) |
238 | { | 238 | { |
239 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); | 239 | struct debuginfo *dbg = zalloc(sizeof(*dbg)); |
240 | if (!self) | 240 | |
241 | if (!dbg) | ||
241 | return NULL; | 242 | return NULL; |
242 | 243 | ||
243 | if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { | 244 | if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) { |
244 | free(self); | 245 | free(dbg); |
245 | self = NULL; | 246 | dbg = NULL; |
246 | } | 247 | } |
247 | 248 | ||
248 | return self; | 249 | return dbg; |
249 | } | 250 | } |
250 | 251 | ||
251 | void debuginfo__delete(struct debuginfo *self) | 252 | void debuginfo__delete(struct debuginfo *dbg) |
252 | { | 253 | { |
253 | if (self) { | 254 | if (dbg) { |
254 | if (self->dwfl) | 255 | if (dbg->dwfl) |
255 | dwfl_end(self->dwfl); | 256 | dwfl_end(dbg->dwfl); |
256 | free(self); | 257 | free(dbg); |
257 | } | 258 | } |
258 | } | 259 | } |
259 | 260 | ||
@@ -273,12 +274,15 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) | |||
273 | /* | 274 | /* |
274 | * Convert a location into trace_arg. | 275 | * Convert a location into trace_arg. |
275 | * If tvar == NULL, this just checks variable can be converted. | 276 | * If tvar == NULL, this just checks variable can be converted. |
277 | * If fentry == true and vr_die is a parameter, do huristic search | ||
278 | * for the location fuzzed by function entry mcount. | ||
276 | */ | 279 | */ |
277 | static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, | 280 | static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, |
278 | Dwarf_Op *fb_ops, | 281 | Dwarf_Op *fb_ops, Dwarf_Die *sp_die, |
279 | struct probe_trace_arg *tvar) | 282 | struct probe_trace_arg *tvar) |
280 | { | 283 | { |
281 | Dwarf_Attribute attr; | 284 | Dwarf_Attribute attr; |
285 | Dwarf_Addr tmp = 0; | ||
282 | Dwarf_Op *op; | 286 | Dwarf_Op *op; |
283 | size_t nops; | 287 | size_t nops; |
284 | unsigned int regn; | 288 | unsigned int regn; |
@@ -291,12 +295,29 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, | |||
291 | goto static_var; | 295 | goto static_var; |
292 | 296 | ||
293 | /* TODO: handle more than 1 exprs */ | 297 | /* TODO: handle more than 1 exprs */ |
294 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || | 298 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) |
295 | dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 || | 299 | return -EINVAL; /* Broken DIE ? */ |
296 | nops == 0) { | 300 | if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { |
297 | /* TODO: Support const_value */ | 301 | ret = dwarf_entrypc(sp_die, &tmp); |
302 | if (ret || addr != tmp || | ||
303 | dwarf_tag(vr_die) != DW_TAG_formal_parameter || | ||
304 | dwarf_highpc(sp_die, &tmp)) | ||
305 | return -ENOENT; | ||
306 | /* | ||
307 | * This is fuzzed by fentry mcount. We try to find the | ||
308 | * parameter location at the earliest address. | ||
309 | */ | ||
310 | for (addr += 1; addr <= tmp; addr++) { | ||
311 | if (dwarf_getlocation_addr(&attr, addr, &op, | ||
312 | &nops, 1) > 0) | ||
313 | goto found; | ||
314 | } | ||
298 | return -ENOENT; | 315 | return -ENOENT; |
299 | } | 316 | } |
317 | found: | ||
318 | if (nops == 0) | ||
319 | /* TODO: Support const_value */ | ||
320 | return -ENOENT; | ||
300 | 321 | ||
301 | if (op->atom == DW_OP_addr) { | 322 | if (op->atom == DW_OP_addr) { |
302 | static_var: | 323 | static_var: |
@@ -563,7 +584,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
563 | } | 584 | } |
564 | 585 | ||
565 | if (die_find_member(&type, field->name, die_mem) == NULL) { | 586 | if (die_find_member(&type, field->name, die_mem) == NULL) { |
566 | pr_warning("%s(tyep:%s) has no member %s.\n", varname, | 587 | pr_warning("%s(type:%s) has no member %s.\n", varname, |
567 | dwarf_diename(&type), field->name); | 588 | dwarf_diename(&type), field->name); |
568 | return -EINVAL; | 589 | return -EINVAL; |
569 | } | 590 | } |
@@ -600,7 +621,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
600 | dwarf_diename(vr_die)); | 621 | dwarf_diename(vr_die)); |
601 | 622 | ||
602 | ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, | 623 | ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, |
603 | pf->tvar); | 624 | &pf->sp_die, pf->tvar); |
604 | if (ret == -ENOENT) | 625 | if (ret == -ENOENT) |
605 | pr_err("Failed to find the location of %s at this address.\n" | 626 | pr_err("Failed to find the location of %s at this address.\n" |
606 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | 627 | " Perhaps, it has been optimized out.\n", pf->pvar->var); |
@@ -1063,7 +1084,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | |||
1063 | } | 1084 | } |
1064 | 1085 | ||
1065 | /* Find probe points from debuginfo */ | 1086 | /* Find probe points from debuginfo */ |
1066 | static int debuginfo__find_probes(struct debuginfo *self, | 1087 | static int debuginfo__find_probes(struct debuginfo *dbg, |
1067 | struct probe_finder *pf) | 1088 | struct probe_finder *pf) |
1068 | { | 1089 | { |
1069 | struct perf_probe_point *pp = &pf->pev->point; | 1090 | struct perf_probe_point *pp = &pf->pev->point; |
@@ -1074,7 +1095,7 @@ static int debuginfo__find_probes(struct debuginfo *self, | |||
1074 | 1095 | ||
1075 | #if _ELFUTILS_PREREQ(0, 142) | 1096 | #if _ELFUTILS_PREREQ(0, 142) |
1076 | /* Get the call frame information from this dwarf */ | 1097 | /* Get the call frame information from this dwarf */ |
1077 | pf->cfi = dwarf_getcfi(self->dbg); | 1098 | pf->cfi = dwarf_getcfi(dbg->dbg); |
1078 | #endif | 1099 | #endif |
1079 | 1100 | ||
1080 | off = 0; | 1101 | off = 0; |
@@ -1093,7 +1114,7 @@ static int debuginfo__find_probes(struct debuginfo *self, | |||
1093 | .data = pf, | 1114 | .data = pf, |
1094 | }; | 1115 | }; |
1095 | 1116 | ||
1096 | dwarf_getpubnames(self->dbg, pubname_search_cb, | 1117 | dwarf_getpubnames(dbg->dbg, pubname_search_cb, |
1097 | &pubname_param, 0); | 1118 | &pubname_param, 0); |
1098 | if (pubname_param.found) { | 1119 | if (pubname_param.found) { |
1099 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | 1120 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); |
@@ -1103,9 +1124,9 @@ static int debuginfo__find_probes(struct debuginfo *self, | |||
1103 | } | 1124 | } |
1104 | 1125 | ||
1105 | /* Loop on CUs (Compilation Unit) */ | 1126 | /* Loop on CUs (Compilation Unit) */ |
1106 | while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1127 | while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1107 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1128 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1108 | diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); | 1129 | diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); |
1109 | if (!diep) | 1130 | if (!diep) |
1110 | continue; | 1131 | continue; |
1111 | 1132 | ||
@@ -1136,12 +1157,80 @@ found: | |||
1136 | return ret; | 1157 | return ret; |
1137 | } | 1158 | } |
1138 | 1159 | ||
1160 | struct local_vars_finder { | ||
1161 | struct probe_finder *pf; | ||
1162 | struct perf_probe_arg *args; | ||
1163 | int max_args; | ||
1164 | int nargs; | ||
1165 | int ret; | ||
1166 | }; | ||
1167 | |||
1168 | /* Collect available variables in this scope */ | ||
1169 | static int copy_variables_cb(Dwarf_Die *die_mem, void *data) | ||
1170 | { | ||
1171 | struct local_vars_finder *vf = data; | ||
1172 | struct probe_finder *pf = vf->pf; | ||
1173 | int tag; | ||
1174 | |||
1175 | tag = dwarf_tag(die_mem); | ||
1176 | if (tag == DW_TAG_formal_parameter || | ||
1177 | tag == DW_TAG_variable) { | ||
1178 | if (convert_variable_location(die_mem, vf->pf->addr, | ||
1179 | vf->pf->fb_ops, &pf->sp_die, | ||
1180 | NULL) == 0) { | ||
1181 | vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); | ||
1182 | if (vf->args[vf->nargs].var == NULL) { | ||
1183 | vf->ret = -ENOMEM; | ||
1184 | return DIE_FIND_CB_END; | ||
1185 | } | ||
1186 | pr_debug(" %s", vf->args[vf->nargs].var); | ||
1187 | vf->nargs++; | ||
1188 | } | ||
1189 | } | ||
1190 | |||
1191 | if (dwarf_haspc(die_mem, vf->pf->addr)) | ||
1192 | return DIE_FIND_CB_CONTINUE; | ||
1193 | else | ||
1194 | return DIE_FIND_CB_SIBLING; | ||
1195 | } | ||
1196 | |||
1197 | static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, | ||
1198 | struct perf_probe_arg *args) | ||
1199 | { | ||
1200 | Dwarf_Die die_mem; | ||
1201 | int i; | ||
1202 | int n = 0; | ||
1203 | struct local_vars_finder vf = {.pf = pf, .args = args, | ||
1204 | .max_args = MAX_PROBE_ARGS, .ret = 0}; | ||
1205 | |||
1206 | for (i = 0; i < pf->pev->nargs; i++) { | ||
1207 | /* var never be NULL */ | ||
1208 | if (strcmp(pf->pev->args[i].var, "$vars") == 0) { | ||
1209 | pr_debug("Expanding $vars into:"); | ||
1210 | vf.nargs = n; | ||
1211 | /* Special local variables */ | ||
1212 | die_find_child(sc_die, copy_variables_cb, (void *)&vf, | ||
1213 | &die_mem); | ||
1214 | pr_debug(" (%d)\n", vf.nargs - n); | ||
1215 | if (vf.ret < 0) | ||
1216 | return vf.ret; | ||
1217 | n = vf.nargs; | ||
1218 | } else { | ||
1219 | /* Copy normal argument */ | ||
1220 | args[n] = pf->pev->args[i]; | ||
1221 | n++; | ||
1222 | } | ||
1223 | } | ||
1224 | return n; | ||
1225 | } | ||
1226 | |||
1139 | /* Add a found probe point into trace event list */ | 1227 | /* Add a found probe point into trace event list */ |
1140 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) | 1228 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) |
1141 | { | 1229 | { |
1142 | struct trace_event_finder *tf = | 1230 | struct trace_event_finder *tf = |
1143 | container_of(pf, struct trace_event_finder, pf); | 1231 | container_of(pf, struct trace_event_finder, pf); |
1144 | struct probe_trace_event *tev; | 1232 | struct probe_trace_event *tev; |
1233 | struct perf_probe_arg *args; | ||
1145 | int ret, i; | 1234 | int ret, i; |
1146 | 1235 | ||
1147 | /* Check number of tevs */ | 1236 | /* Check number of tevs */ |
@@ -1161,31 +1250,45 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
1161 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, | 1250 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, |
1162 | tev->point.offset); | 1251 | tev->point.offset); |
1163 | 1252 | ||
1164 | /* Find each argument */ | 1253 | /* Expand special probe argument if exist */ |
1165 | tev->nargs = pf->pev->nargs; | 1254 | args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); |
1166 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1255 | if (args == NULL) |
1167 | if (tev->args == NULL) | ||
1168 | return -ENOMEM; | 1256 | return -ENOMEM; |
1169 | for (i = 0; i < pf->pev->nargs; i++) { | 1257 | |
1170 | pf->pvar = &pf->pev->args[i]; | 1258 | ret = expand_probe_args(sc_die, pf, args); |
1259 | if (ret < 0) | ||
1260 | goto end; | ||
1261 | |||
1262 | tev->nargs = ret; | ||
1263 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | ||
1264 | if (tev->args == NULL) { | ||
1265 | ret = -ENOMEM; | ||
1266 | goto end; | ||
1267 | } | ||
1268 | |||
1269 | /* Find each argument */ | ||
1270 | for (i = 0; i < tev->nargs; i++) { | ||
1271 | pf->pvar = &args[i]; | ||
1171 | pf->tvar = &tev->args[i]; | 1272 | pf->tvar = &tev->args[i]; |
1172 | /* Variable should be found from scope DIE */ | 1273 | /* Variable should be found from scope DIE */ |
1173 | ret = find_variable(sc_die, pf); | 1274 | ret = find_variable(sc_die, pf); |
1174 | if (ret != 0) | 1275 | if (ret != 0) |
1175 | return ret; | 1276 | break; |
1176 | } | 1277 | } |
1177 | 1278 | ||
1178 | return 0; | 1279 | end: |
1280 | free(args); | ||
1281 | return ret; | ||
1179 | } | 1282 | } |
1180 | 1283 | ||
1181 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 1284 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
1182 | int debuginfo__find_trace_events(struct debuginfo *self, | 1285 | int debuginfo__find_trace_events(struct debuginfo *dbg, |
1183 | struct perf_probe_event *pev, | 1286 | struct perf_probe_event *pev, |
1184 | struct probe_trace_event **tevs, int max_tevs) | 1287 | struct probe_trace_event **tevs, int max_tevs) |
1185 | { | 1288 | { |
1186 | struct trace_event_finder tf = { | 1289 | struct trace_event_finder tf = { |
1187 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1290 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
1188 | .mod = self->mod, .max_tevs = max_tevs}; | 1291 | .mod = dbg->mod, .max_tevs = max_tevs}; |
1189 | int ret; | 1292 | int ret; |
1190 | 1293 | ||
1191 | /* Allocate result tevs array */ | 1294 | /* Allocate result tevs array */ |
@@ -1196,7 +1299,7 @@ int debuginfo__find_trace_events(struct debuginfo *self, | |||
1196 | tf.tevs = *tevs; | 1299 | tf.tevs = *tevs; |
1197 | tf.ntevs = 0; | 1300 | tf.ntevs = 0; |
1198 | 1301 | ||
1199 | ret = debuginfo__find_probes(self, &tf.pf); | 1302 | ret = debuginfo__find_probes(dbg, &tf.pf); |
1200 | if (ret < 0) { | 1303 | if (ret < 0) { |
1201 | free(*tevs); | 1304 | free(*tevs); |
1202 | *tevs = NULL; | 1305 | *tevs = NULL; |
@@ -1222,7 +1325,8 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | |||
1222 | if (tag == DW_TAG_formal_parameter || | 1325 | if (tag == DW_TAG_formal_parameter || |
1223 | tag == DW_TAG_variable) { | 1326 | tag == DW_TAG_variable) { |
1224 | ret = convert_variable_location(die_mem, af->pf.addr, | 1327 | ret = convert_variable_location(die_mem, af->pf.addr, |
1225 | af->pf.fb_ops, NULL); | 1328 | af->pf.fb_ops, &af->pf.sp_die, |
1329 | NULL); | ||
1226 | if (ret == 0) { | 1330 | if (ret == 0) { |
1227 | ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); | 1331 | ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); |
1228 | pr_debug2("Add new var: %s\n", buf); | 1332 | pr_debug2("Add new var: %s\n", buf); |
@@ -1286,14 +1390,14 @@ out: | |||
1286 | } | 1390 | } |
1287 | 1391 | ||
1288 | /* Find available variables at given probe point */ | 1392 | /* Find available variables at given probe point */ |
1289 | int debuginfo__find_available_vars_at(struct debuginfo *self, | 1393 | int debuginfo__find_available_vars_at(struct debuginfo *dbg, |
1290 | struct perf_probe_event *pev, | 1394 | struct perf_probe_event *pev, |
1291 | struct variable_list **vls, | 1395 | struct variable_list **vls, |
1292 | int max_vls, bool externs) | 1396 | int max_vls, bool externs) |
1293 | { | 1397 | { |
1294 | struct available_var_finder af = { | 1398 | struct available_var_finder af = { |
1295 | .pf = {.pev = pev, .callback = add_available_vars}, | 1399 | .pf = {.pev = pev, .callback = add_available_vars}, |
1296 | .mod = self->mod, | 1400 | .mod = dbg->mod, |
1297 | .max_vls = max_vls, .externs = externs}; | 1401 | .max_vls = max_vls, .externs = externs}; |
1298 | int ret; | 1402 | int ret; |
1299 | 1403 | ||
@@ -1305,7 +1409,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, | |||
1305 | af.vls = *vls; | 1409 | af.vls = *vls; |
1306 | af.nvls = 0; | 1410 | af.nvls = 0; |
1307 | 1411 | ||
1308 | ret = debuginfo__find_probes(self, &af.pf); | 1412 | ret = debuginfo__find_probes(dbg, &af.pf); |
1309 | if (ret < 0) { | 1413 | if (ret < 0) { |
1310 | /* Free vlist for error */ | 1414 | /* Free vlist for error */ |
1311 | while (af.nvls--) { | 1415 | while (af.nvls--) { |
@@ -1323,7 +1427,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, | |||
1323 | } | 1427 | } |
1324 | 1428 | ||
1325 | /* Reverse search */ | 1429 | /* Reverse search */ |
1326 | int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | 1430 | int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, |
1327 | struct perf_probe_point *ppt) | 1431 | struct perf_probe_point *ppt) |
1328 | { | 1432 | { |
1329 | Dwarf_Die cudie, spdie, indie; | 1433 | Dwarf_Die cudie, spdie, indie; |
@@ -1332,10 +1436,10 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
1332 | int baseline = 0, lineno = 0, ret = 0; | 1436 | int baseline = 0, lineno = 0, ret = 0; |
1333 | 1437 | ||
1334 | /* Adjust address with bias */ | 1438 | /* Adjust address with bias */ |
1335 | addr += self->bias; | 1439 | addr += dbg->bias; |
1336 | 1440 | ||
1337 | /* Find cu die */ | 1441 | /* Find cu die */ |
1338 | if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { | 1442 | if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) { |
1339 | pr_warning("Failed to find debug information for address %lx\n", | 1443 | pr_warning("Failed to find debug information for address %lx\n", |
1340 | addr); | 1444 | addr); |
1341 | ret = -EINVAL; | 1445 | ret = -EINVAL; |
@@ -1357,10 +1461,10 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
1357 | goto post; | 1461 | goto post; |
1358 | } | 1462 | } |
1359 | 1463 | ||
1464 | fname = dwarf_decl_file(&spdie); | ||
1360 | if (addr == (unsigned long)baseaddr) { | 1465 | if (addr == (unsigned long)baseaddr) { |
1361 | /* Function entry - Relative line number is 0 */ | 1466 | /* Function entry - Relative line number is 0 */ |
1362 | lineno = baseline; | 1467 | lineno = baseline; |
1363 | fname = dwarf_decl_file(&spdie); | ||
1364 | goto post; | 1468 | goto post; |
1365 | } | 1469 | } |
1366 | 1470 | ||
@@ -1536,7 +1640,7 @@ static int find_line_range_by_func(struct line_finder *lf) | |||
1536 | return param.retval; | 1640 | return param.retval; |
1537 | } | 1641 | } |
1538 | 1642 | ||
1539 | int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) | 1643 | int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) |
1540 | { | 1644 | { |
1541 | struct line_finder lf = {.lr = lr, .found = 0}; | 1645 | struct line_finder lf = {.lr = lr, .found = 0}; |
1542 | int ret = 0; | 1646 | int ret = 0; |
@@ -1553,7 +1657,7 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) | |||
1553 | struct dwarf_callback_param line_range_param = { | 1657 | struct dwarf_callback_param line_range_param = { |
1554 | .data = (void *)&lf, .retval = 0}; | 1658 | .data = (void *)&lf, .retval = 0}; |
1555 | 1659 | ||
1556 | dwarf_getpubnames(self->dbg, pubname_search_cb, | 1660 | dwarf_getpubnames(dbg->dbg, pubname_search_cb, |
1557 | &pubname_param, 0); | 1661 | &pubname_param, 0); |
1558 | if (pubname_param.found) { | 1662 | if (pubname_param.found) { |
1559 | line_range_search_cb(&lf.sp_die, &line_range_param); | 1663 | line_range_search_cb(&lf.sp_die, &line_range_param); |
@@ -1564,12 +1668,12 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) | |||
1564 | 1668 | ||
1565 | /* Loop on CUs (Compilation Unit) */ | 1669 | /* Loop on CUs (Compilation Unit) */ |
1566 | while (!lf.found && ret >= 0) { | 1670 | while (!lf.found && ret >= 0) { |
1567 | if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, | 1671 | if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, |
1568 | NULL, NULL, NULL) != 0) | 1672 | NULL, NULL, NULL) != 0) |
1569 | break; | 1673 | break; |
1570 | 1674 | ||
1571 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1675 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1572 | diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); | 1676 | diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); |
1573 | if (!diep) | 1677 | if (!diep) |
1574 | continue; | 1678 | continue; |
1575 | 1679 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3b7d63018960..ffc33cdd25cc 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #define MAX_PROBE_BUFFER 1024 | 8 | #define MAX_PROBE_BUFFER 1024 |
9 | #define MAX_PROBES 128 | 9 | #define MAX_PROBES 128 |
10 | #define MAX_PROBE_ARGS 128 | ||
10 | 11 | ||
11 | static inline int is_c_varname(const char *name) | 12 | static inline int is_c_varname(const char *name) |
12 | { | 13 | { |
@@ -14,7 +15,7 @@ static inline int is_c_varname(const char *name) | |||
14 | return isalpha(name[0]) || name[0] == '_'; | 15 | return isalpha(name[0]) || name[0] == '_'; |
15 | } | 16 | } |
16 | 17 | ||
17 | #ifdef DWARF_SUPPORT | 18 | #ifdef HAVE_DWARF_SUPPORT |
18 | 19 | ||
19 | #include "dwarf-aux.h" | 20 | #include "dwarf-aux.h" |
20 | 21 | ||
@@ -30,25 +31,25 @@ struct debuginfo { | |||
30 | 31 | ||
31 | extern struct debuginfo *debuginfo__new(const char *path); | 32 | extern struct debuginfo *debuginfo__new(const char *path); |
32 | extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); | 33 | extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); |
33 | extern void debuginfo__delete(struct debuginfo *self); | 34 | extern void debuginfo__delete(struct debuginfo *dbg); |
34 | 35 | ||
35 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 36 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
36 | extern int debuginfo__find_trace_events(struct debuginfo *self, | 37 | extern int debuginfo__find_trace_events(struct debuginfo *dbg, |
37 | struct perf_probe_event *pev, | 38 | struct perf_probe_event *pev, |
38 | struct probe_trace_event **tevs, | 39 | struct probe_trace_event **tevs, |
39 | int max_tevs); | 40 | int max_tevs); |
40 | 41 | ||
41 | /* Find a perf_probe_point from debuginfo */ | 42 | /* Find a perf_probe_point from debuginfo */ |
42 | extern int debuginfo__find_probe_point(struct debuginfo *self, | 43 | extern int debuginfo__find_probe_point(struct debuginfo *dbg, |
43 | unsigned long addr, | 44 | unsigned long addr, |
44 | struct perf_probe_point *ppt); | 45 | struct perf_probe_point *ppt); |
45 | 46 | ||
46 | /* Find a line range */ | 47 | /* Find a line range */ |
47 | extern int debuginfo__find_line_range(struct debuginfo *self, | 48 | extern int debuginfo__find_line_range(struct debuginfo *dbg, |
48 | struct line_range *lr); | 49 | struct line_range *lr); |
49 | 50 | ||
50 | /* Find available variables */ | 51 | /* Find available variables */ |
51 | extern int debuginfo__find_available_vars_at(struct debuginfo *self, | 52 | extern int debuginfo__find_available_vars_at(struct debuginfo *dbg, |
52 | struct perf_probe_event *pev, | 53 | struct perf_probe_event *pev, |
53 | struct variable_list **vls, | 54 | struct variable_list **vls, |
54 | int max_points, bool externs); | 55 | int max_points, bool externs); |
@@ -105,6 +106,6 @@ struct line_finder { | |||
105 | int found; | 106 | int found; |
106 | }; | 107 | }; |
107 | 108 | ||
108 | #endif /* DWARF_SUPPORT */ | 109 | #endif /* HAVE_DWARF_SUPPORT */ |
109 | 110 | ||
110 | #endif /*_PROBE_FINDER_H */ | 111 | #endif /*_PROBE_FINDER_H */ |
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 4cedea59f518..c3cb6584d527 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h | |||
@@ -5,10 +5,10 @@ | |||
5 | 5 | ||
6 | struct pstack; | 6 | struct pstack; |
7 | struct pstack *pstack__new(unsigned short max_nr_entries); | 7 | struct pstack *pstack__new(unsigned short max_nr_entries); |
8 | void pstack__delete(struct pstack *self); | 8 | void pstack__delete(struct pstack *pstack); |
9 | bool pstack__empty(const struct pstack *self); | 9 | bool pstack__empty(const struct pstack *pstack); |
10 | void pstack__remove(struct pstack *self, void *key); | 10 | void pstack__remove(struct pstack *pstack, void *key); |
11 | void pstack__push(struct pstack *self, void *key); | 11 | void pstack__push(struct pstack *pstack, void *key); |
12 | void *pstack__pop(struct pstack *self); | 12 | void *pstack__pop(struct pstack *pstack); |
13 | 13 | ||
14 | #endif /* _PERF_PSTACK_ */ | 14 | #endif /* _PERF_PSTACK_ */ |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index f75ae1b9900c..239036fb2b2c 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -17,5 +17,5 @@ util/xyarray.c | |||
17 | util/cgroup.c | 17 | util/cgroup.c |
18 | util/rblist.c | 18 | util/rblist.c |
19 | util/strlist.c | 19 | util/strlist.c |
20 | util/sysfs.c | 20 | util/fs.c |
21 | ../../lib/rbtree.c | 21 | ../../lib/rbtree.c |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 71b5412bbbb9..4bf8ace7f511 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -33,13 +33,6 @@ int eprintf(int level, const char *fmt, ...) | |||
33 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, | 33 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, |
34 | #endif | 34 | #endif |
35 | 35 | ||
36 | struct throttle_event { | ||
37 | struct perf_event_header header; | ||
38 | u64 time; | ||
39 | u64 id; | ||
40 | u64 stream_id; | ||
41 | }; | ||
42 | |||
43 | PyMODINIT_FUNC initperf(void); | 36 | PyMODINIT_FUNC initperf(void); |
44 | 37 | ||
45 | #define member_def(type, member, ptype, help) \ | 38 | #define member_def(type, member, ptype, help) \ |
@@ -822,6 +815,8 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
822 | PyObject *pyevent = pyrf_event__new(event); | 815 | PyObject *pyevent = pyrf_event__new(event); |
823 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; | 816 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; |
824 | 817 | ||
818 | perf_evlist__mmap_consume(evlist, cpu); | ||
819 | |||
825 | if (pyevent == NULL) | 820 | if (pyevent == NULL) |
826 | return PyErr_NoMemory(); | 821 | return PyErr_NoMemory(); |
827 | 822 | ||
@@ -1036,6 +1031,7 @@ PyMODINIT_FUNC initperf(void) | |||
1036 | pyrf_cpu_map__setup_types() < 0) | 1031 | pyrf_cpu_map__setup_types() < 0) |
1037 | return; | 1032 | return; |
1038 | 1033 | ||
1034 | /* The page_size is placed in util object. */ | ||
1039 | page_size = sysconf(_SC_PAGE_SIZE); | 1035 | page_size = sysconf(_SC_PAGE_SIZE); |
1040 | 1036 | ||
1041 | Py_INCREF(&pyrf_evlist__type); | 1037 | Py_INCREF(&pyrf_evlist__type); |
diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index a16cdd2625ad..0dfe27d99458 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c | |||
@@ -48,10 +48,12 @@ void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) | |||
48 | rblist->node_delete(rblist, rb_node); | 48 | rblist->node_delete(rblist, rb_node); |
49 | } | 49 | } |
50 | 50 | ||
51 | struct rb_node *rblist__find(struct rblist *rblist, const void *entry) | 51 | static struct rb_node *__rblist__findnew(struct rblist *rblist, |
52 | const void *entry, | ||
53 | bool create) | ||
52 | { | 54 | { |
53 | struct rb_node **p = &rblist->entries.rb_node; | 55 | struct rb_node **p = &rblist->entries.rb_node; |
54 | struct rb_node *parent = NULL; | 56 | struct rb_node *parent = NULL, *new_node = NULL; |
55 | 57 | ||
56 | while (*p != NULL) { | 58 | while (*p != NULL) { |
57 | int rc; | 59 | int rc; |
@@ -67,7 +69,26 @@ struct rb_node *rblist__find(struct rblist *rblist, const void *entry) | |||
67 | return parent; | 69 | return parent; |
68 | } | 70 | } |
69 | 71 | ||
70 | return NULL; | 72 | if (create) { |
73 | new_node = rblist->node_new(rblist, entry); | ||
74 | if (new_node) { | ||
75 | rb_link_node(new_node, parent, p); | ||
76 | rb_insert_color(new_node, &rblist->entries); | ||
77 | ++rblist->nr_entries; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | return new_node; | ||
82 | } | ||
83 | |||
84 | struct rb_node *rblist__find(struct rblist *rblist, const void *entry) | ||
85 | { | ||
86 | return __rblist__findnew(rblist, entry, false); | ||
87 | } | ||
88 | |||
89 | struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry) | ||
90 | { | ||
91 | return __rblist__findnew(rblist, entry, true); | ||
71 | } | 92 | } |
72 | 93 | ||
73 | void rblist__init(struct rblist *rblist) | 94 | void rblist__init(struct rblist *rblist) |
diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h index 6d0cae5ae83d..ff9913b994c2 100644 --- a/tools/perf/util/rblist.h +++ b/tools/perf/util/rblist.h | |||
@@ -32,6 +32,7 @@ void rblist__delete(struct rblist *rblist); | |||
32 | int rblist__add_node(struct rblist *rblist, const void *new_entry); | 32 | int rblist__add_node(struct rblist *rblist, const void *new_entry); |
33 | void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node); | 33 | void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node); |
34 | struct rb_node *rblist__find(struct rblist *rblist, const void *entry); | 34 | struct rb_node *rblist__find(struct rblist *rblist, const void *entry); |
35 | struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry); | ||
35 | struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx); | 36 | struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx); |
36 | 37 | ||
37 | static inline bool rblist__empty(const struct rblist *rblist) | 38 | static inline bool rblist__empty(const struct rblist *rblist) |
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 18d73aa2f0f8..c8845b107f60 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c | |||
@@ -2,6 +2,8 @@ | |||
2 | #include "evsel.h" | 2 | #include "evsel.h" |
3 | #include "cpumap.h" | 3 | #include "cpumap.h" |
4 | #include "parse-events.h" | 4 | #include "parse-events.h" |
5 | #include "fs.h" | ||
6 | #include "util.h" | ||
5 | 7 | ||
6 | typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); | 8 | typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); |
7 | 9 | ||
@@ -106,3 +108,72 @@ void perf_evlist__config(struct perf_evlist *evlist, | |||
106 | 108 | ||
107 | perf_evlist__set_id_pos(evlist); | 109 | perf_evlist__set_id_pos(evlist); |
108 | } | 110 | } |
111 | |||
112 | static int get_max_rate(unsigned int *rate) | ||
113 | { | ||
114 | char path[PATH_MAX]; | ||
115 | const char *procfs = procfs__mountpoint(); | ||
116 | |||
117 | if (!procfs) | ||
118 | return -1; | ||
119 | |||
120 | snprintf(path, PATH_MAX, | ||
121 | "%s/sys/kernel/perf_event_max_sample_rate", procfs); | ||
122 | |||
123 | return filename__read_int(path, (int *) rate); | ||
124 | } | ||
125 | |||
126 | static int perf_record_opts__config_freq(struct perf_record_opts *opts) | ||
127 | { | ||
128 | bool user_freq = opts->user_freq != UINT_MAX; | ||
129 | unsigned int max_rate; | ||
130 | |||
131 | if (opts->user_interval != ULLONG_MAX) | ||
132 | opts->default_interval = opts->user_interval; | ||
133 | if (user_freq) | ||
134 | opts->freq = opts->user_freq; | ||
135 | |||
136 | /* | ||
137 | * User specified count overrides default frequency. | ||
138 | */ | ||
139 | if (opts->default_interval) | ||
140 | opts->freq = 0; | ||
141 | else if (opts->freq) { | ||
142 | opts->default_interval = opts->freq; | ||
143 | } else { | ||
144 | pr_err("frequency and count are zero, aborting\n"); | ||
145 | return -1; | ||
146 | } | ||
147 | |||
148 | if (get_max_rate(&max_rate)) | ||
149 | return 0; | ||
150 | |||
151 | /* | ||
152 | * User specified frequency is over current maximum. | ||
153 | */ | ||
154 | if (user_freq && (max_rate < opts->freq)) { | ||
155 | pr_err("Maximum frequency rate (%u) reached.\n" | ||
156 | "Please use -F freq option with lower value or consider\n" | ||
157 | "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", | ||
158 | max_rate); | ||
159 | return -1; | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * Default frequency is over current maximum. | ||
164 | */ | ||
165 | if (max_rate < opts->freq) { | ||
166 | pr_warning("Lowering default frequency rate to %u.\n" | ||
167 | "Please consider tweaking " | ||
168 | "/proc/sys/kernel/perf_event_max_sample_rate.\n", | ||
169 | max_rate); | ||
170 | opts->freq = max_rate; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | int perf_record_opts__config(struct perf_record_opts *opts) | ||
177 | { | ||
178 | return perf_record_opts__config_freq(opts); | ||
179 | } | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index a85e4ae5f3ac..d5e5969f6fea 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -273,7 +273,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
273 | int cpu = sample->cpu; | 273 | int cpu = sample->cpu; |
274 | void *data = sample->raw_data; | 274 | void *data = sample->raw_data; |
275 | unsigned long long nsecs = sample->time; | 275 | unsigned long long nsecs = sample->time; |
276 | char *comm = thread->comm; | 276 | const char *comm = thread__comm_str(thread); |
277 | 277 | ||
278 | dSP; | 278 | dSP; |
279 | 279 | ||
@@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
282 | 282 | ||
283 | event = find_cache_event(evsel); | 283 | event = find_cache_event(evsel); |
284 | if (!event) | 284 | if (!event) |
285 | die("ug! no event found for type %" PRIu64, evsel->attr.config); | 285 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); |
286 | 286 | ||
287 | pid = raw_field_value(event, "common_pid", data); | 287 | pid = raw_field_value(event, "common_pid", data); |
288 | 288 | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cc75a3cef388..53c20e7fd900 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -56,6 +56,17 @@ static void handler_call_die(const char *handler_name) | |||
56 | Py_FatalError("problem in Python trace event handler"); | 56 | Py_FatalError("problem in Python trace event handler"); |
57 | } | 57 | } |
58 | 58 | ||
59 | /* | ||
60 | * Insert val into into the dictionary and decrement the reference counter. | ||
61 | * This is necessary for dictionaries since PyDict_SetItemString() does not | ||
62 | * steal a reference, as opposed to PyTuple_SetItem(). | ||
63 | */ | ||
64 | static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val) | ||
65 | { | ||
66 | PyDict_SetItemString(dict, key, val); | ||
67 | Py_DECREF(val); | ||
68 | } | ||
69 | |||
59 | static void define_value(enum print_arg_type field_type, | 70 | static void define_value(enum print_arg_type field_type, |
60 | const char *ev_name, | 71 | const char *ev_name, |
61 | const char *field_name, | 72 | const char *field_name, |
@@ -239,7 +250,7 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
239 | int cpu = sample->cpu; | 250 | int cpu = sample->cpu; |
240 | void *data = sample->raw_data; | 251 | void *data = sample->raw_data; |
241 | unsigned long long nsecs = sample->time; | 252 | unsigned long long nsecs = sample->time; |
242 | char *comm = thread->comm; | 253 | const char *comm = thread__comm_str(thread); |
243 | 254 | ||
244 | t = PyTuple_New(MAX_FIELDS); | 255 | t = PyTuple_New(MAX_FIELDS); |
245 | if (!t) | 256 | if (!t) |
@@ -279,11 +290,11 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
279 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); | 290 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); |
280 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); | 291 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); |
281 | } else { | 292 | } else { |
282 | PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu)); | 293 | pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu)); |
283 | PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s)); | 294 | pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s)); |
284 | PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns)); | 295 | pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns)); |
285 | PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid)); | 296 | pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid)); |
286 | PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm)); | 297 | pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm)); |
287 | } | 298 | } |
288 | for (field = event->format.fields; field; field = field->next) { | 299 | for (field = event->format.fields; field; field = field->next) { |
289 | if (field->flags & FIELD_IS_STRING) { | 300 | if (field->flags & FIELD_IS_STRING) { |
@@ -313,7 +324,7 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
313 | if (handler) | 324 | if (handler) |
314 | PyTuple_SetItem(t, n++, obj); | 325 | PyTuple_SetItem(t, n++, obj); |
315 | else | 326 | else |
316 | PyDict_SetItemString(dict, field->name, obj); | 327 | pydict_set_item_string_decref(dict, field->name, obj); |
317 | 328 | ||
318 | } | 329 | } |
319 | if (!handler) | 330 | if (!handler) |
@@ -370,21 +381,21 @@ static void python_process_general_event(union perf_event *perf_event | |||
370 | if (!handler || !PyCallable_Check(handler)) | 381 | if (!handler || !PyCallable_Check(handler)) |
371 | goto exit; | 382 | goto exit; |
372 | 383 | ||
373 | PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | 384 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); |
374 | PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize( | 385 | pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize( |
375 | (const char *)&evsel->attr, sizeof(evsel->attr))); | 386 | (const char *)&evsel->attr, sizeof(evsel->attr))); |
376 | PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize( | 387 | pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize( |
377 | (const char *)sample, sizeof(*sample))); | 388 | (const char *)sample, sizeof(*sample))); |
378 | PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize( | 389 | pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize( |
379 | (const char *)sample->raw_data, sample->raw_size)); | 390 | (const char *)sample->raw_data, sample->raw_size)); |
380 | PyDict_SetItemString(dict, "comm", | 391 | pydict_set_item_string_decref(dict, "comm", |
381 | PyString_FromString(thread->comm)); | 392 | PyString_FromString(thread__comm_str(thread))); |
382 | if (al->map) { | 393 | if (al->map) { |
383 | PyDict_SetItemString(dict, "dso", | 394 | pydict_set_item_string_decref(dict, "dso", |
384 | PyString_FromString(al->map->dso->name)); | 395 | PyString_FromString(al->map->dso->name)); |
385 | } | 396 | } |
386 | if (al->sym) { | 397 | if (al->sym) { |
387 | PyDict_SetItemString(dict, "symbol", | 398 | pydict_set_item_string_decref(dict, "symbol", |
388 | PyString_FromString(al->sym->name)); | 399 | PyString_FromString(al->sym->name)); |
389 | } | 400 | } |
390 | 401 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 568b750c01f6..f36d24a02445 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -16,73 +16,34 @@ | |||
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "vdso.h" | 17 | #include "vdso.h" |
18 | 18 | ||
19 | static int perf_session__open(struct perf_session *self, bool force) | 19 | static int perf_session__open(struct perf_session *session) |
20 | { | 20 | { |
21 | struct stat input_stat; | 21 | struct perf_data_file *file = session->file; |
22 | 22 | ||
23 | if (!strcmp(self->filename, "-")) { | 23 | if (perf_session__read_header(session) < 0) { |
24 | self->fd_pipe = true; | ||
25 | self->fd = STDIN_FILENO; | ||
26 | |||
27 | if (perf_session__read_header(self) < 0) | ||
28 | pr_err("incompatible file format (rerun with -v to learn more)"); | ||
29 | |||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | self->fd = open(self->filename, O_RDONLY); | ||
34 | if (self->fd < 0) { | ||
35 | int err = errno; | ||
36 | |||
37 | pr_err("failed to open %s: %s", self->filename, strerror(err)); | ||
38 | if (err == ENOENT && !strcmp(self->filename, "perf.data")) | ||
39 | pr_err(" (try 'perf record' first)"); | ||
40 | pr_err("\n"); | ||
41 | return -errno; | ||
42 | } | ||
43 | |||
44 | if (fstat(self->fd, &input_stat) < 0) | ||
45 | goto out_close; | ||
46 | |||
47 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
48 | pr_err("file %s not owned by current user or root\n", | ||
49 | self->filename); | ||
50 | goto out_close; | ||
51 | } | ||
52 | |||
53 | if (!input_stat.st_size) { | ||
54 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
55 | self->filename); | ||
56 | goto out_close; | ||
57 | } | ||
58 | |||
59 | if (perf_session__read_header(self) < 0) { | ||
60 | pr_err("incompatible file format (rerun with -v to learn more)"); | 24 | pr_err("incompatible file format (rerun with -v to learn more)"); |
61 | goto out_close; | 25 | return -1; |
62 | } | 26 | } |
63 | 27 | ||
64 | if (!perf_evlist__valid_sample_type(self->evlist)) { | 28 | if (perf_data_file__is_pipe(file)) |
29 | return 0; | ||
30 | |||
31 | if (!perf_evlist__valid_sample_type(session->evlist)) { | ||
65 | pr_err("non matching sample_type"); | 32 | pr_err("non matching sample_type"); |
66 | goto out_close; | 33 | return -1; |
67 | } | 34 | } |
68 | 35 | ||
69 | if (!perf_evlist__valid_sample_id_all(self->evlist)) { | 36 | if (!perf_evlist__valid_sample_id_all(session->evlist)) { |
70 | pr_err("non matching sample_id_all"); | 37 | pr_err("non matching sample_id_all"); |
71 | goto out_close; | 38 | return -1; |
72 | } | 39 | } |
73 | 40 | ||
74 | if (!perf_evlist__valid_read_format(self->evlist)) { | 41 | if (!perf_evlist__valid_read_format(session->evlist)) { |
75 | pr_err("non matching read_format"); | 42 | pr_err("non matching read_format"); |
76 | goto out_close; | 43 | return -1; |
77 | } | 44 | } |
78 | 45 | ||
79 | self->size = input_stat.st_size; | ||
80 | return 0; | 46 | return 0; |
81 | |||
82 | out_close: | ||
83 | close(self->fd); | ||
84 | self->fd = -1; | ||
85 | return -1; | ||
86 | } | 47 | } |
87 | 48 | ||
88 | void perf_session__set_id_hdr_size(struct perf_session *session) | 49 | void perf_session__set_id_hdr_size(struct perf_session *session) |
@@ -92,71 +53,70 @@ void perf_session__set_id_hdr_size(struct perf_session *session) | |||
92 | machines__set_id_hdr_size(&session->machines, id_hdr_size); | 53 | machines__set_id_hdr_size(&session->machines, id_hdr_size); |
93 | } | 54 | } |
94 | 55 | ||
95 | int perf_session__create_kernel_maps(struct perf_session *self) | 56 | int perf_session__create_kernel_maps(struct perf_session *session) |
96 | { | 57 | { |
97 | int ret = machine__create_kernel_maps(&self->machines.host); | 58 | int ret = machine__create_kernel_maps(&session->machines.host); |
98 | 59 | ||
99 | if (ret >= 0) | 60 | if (ret >= 0) |
100 | ret = machines__create_guest_kernel_maps(&self->machines); | 61 | ret = machines__create_guest_kernel_maps(&session->machines); |
101 | return ret; | 62 | return ret; |
102 | } | 63 | } |
103 | 64 | ||
104 | static void perf_session__destroy_kernel_maps(struct perf_session *self) | 65 | static void perf_session__destroy_kernel_maps(struct perf_session *session) |
105 | { | 66 | { |
106 | machines__destroy_kernel_maps(&self->machines); | 67 | machines__destroy_kernel_maps(&session->machines); |
107 | } | 68 | } |
108 | 69 | ||
109 | struct perf_session *perf_session__new(const char *filename, int mode, | 70 | struct perf_session *perf_session__new(struct perf_data_file *file, |
110 | bool force, bool repipe, | 71 | bool repipe, struct perf_tool *tool) |
111 | struct perf_tool *tool) | ||
112 | { | 72 | { |
113 | struct perf_session *self; | 73 | struct perf_session *session = zalloc(sizeof(*session)); |
114 | struct stat st; | ||
115 | size_t len; | ||
116 | |||
117 | if (!filename || !strlen(filename)) { | ||
118 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
119 | filename = "-"; | ||
120 | else | ||
121 | filename = "perf.data"; | ||
122 | } | ||
123 | 74 | ||
124 | len = strlen(filename); | 75 | if (!session) |
125 | self = zalloc(sizeof(*self) + len); | ||
126 | |||
127 | if (self == NULL) | ||
128 | goto out; | 76 | goto out; |
129 | 77 | ||
130 | memcpy(self->filename, filename, len); | 78 | session->repipe = repipe; |
131 | self->repipe = repipe; | 79 | INIT_LIST_HEAD(&session->ordered_samples.samples); |
132 | INIT_LIST_HEAD(&self->ordered_samples.samples); | 80 | INIT_LIST_HEAD(&session->ordered_samples.sample_cache); |
133 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 81 | INIT_LIST_HEAD(&session->ordered_samples.to_free); |
134 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 82 | machines__init(&session->machines); |
135 | machines__init(&self->machines); | ||
136 | 83 | ||
137 | if (mode == O_RDONLY) { | 84 | if (file) { |
138 | if (perf_session__open(self, force) < 0) | 85 | if (perf_data_file__open(file)) |
139 | goto out_delete; | 86 | goto out_delete; |
140 | perf_session__set_id_hdr_size(self); | 87 | |
141 | } else if (mode == O_WRONLY) { | 88 | session->file = file; |
89 | |||
90 | if (perf_data_file__is_read(file)) { | ||
91 | if (perf_session__open(session) < 0) | ||
92 | goto out_close; | ||
93 | |||
94 | perf_session__set_id_hdr_size(session); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (!file || perf_data_file__is_write(file)) { | ||
142 | /* | 99 | /* |
143 | * In O_RDONLY mode this will be performed when reading the | 100 | * In O_RDONLY mode this will be performed when reading the |
144 | * kernel MMAP event, in perf_event__process_mmap(). | 101 | * kernel MMAP event, in perf_event__process_mmap(). |
145 | */ | 102 | */ |
146 | if (perf_session__create_kernel_maps(self) < 0) | 103 | if (perf_session__create_kernel_maps(session) < 0) |
147 | goto out_delete; | 104 | goto out_delete; |
148 | } | 105 | } |
149 | 106 | ||
150 | if (tool && tool->ordering_requires_timestamps && | 107 | if (tool && tool->ordering_requires_timestamps && |
151 | tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) { | 108 | tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { |
152 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 109 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
153 | tool->ordered_samples = false; | 110 | tool->ordered_samples = false; |
154 | } | 111 | } |
155 | 112 | ||
156 | out: | 113 | return session; |
157 | return self; | 114 | |
158 | out_delete: | 115 | out_close: |
159 | perf_session__delete(self); | 116 | perf_data_file__close(file); |
117 | out_delete: | ||
118 | perf_session__delete(session); | ||
119 | out: | ||
160 | return NULL; | 120 | return NULL; |
161 | } | 121 | } |
162 | 122 | ||
@@ -186,15 +146,16 @@ static void perf_session_env__delete(struct perf_session_env *env) | |||
186 | free(env->pmu_mappings); | 146 | free(env->pmu_mappings); |
187 | } | 147 | } |
188 | 148 | ||
189 | void perf_session__delete(struct perf_session *self) | 149 | void perf_session__delete(struct perf_session *session) |
190 | { | 150 | { |
191 | perf_session__destroy_kernel_maps(self); | 151 | perf_session__destroy_kernel_maps(session); |
192 | perf_session__delete_dead_threads(self); | 152 | perf_session__delete_dead_threads(session); |
193 | perf_session__delete_threads(self); | 153 | perf_session__delete_threads(session); |
194 | perf_session_env__delete(&self->header.env); | 154 | perf_session_env__delete(&session->header.env); |
195 | machines__exit(&self->machines); | 155 | machines__exit(&session->machines); |
196 | close(self->fd); | 156 | if (session->file) |
197 | free(self); | 157 | perf_data_file__close(session->file); |
158 | free(session); | ||
198 | vdso__exit(); | 159 | vdso__exit(); |
199 | } | 160 | } |
200 | 161 | ||
@@ -397,6 +358,17 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all) | |||
397 | swap_sample_id_all(event, &event->read + 1); | 358 | swap_sample_id_all(event, &event->read + 1); |
398 | } | 359 | } |
399 | 360 | ||
361 | static void perf_event__throttle_swap(union perf_event *event, | ||
362 | bool sample_id_all) | ||
363 | { | ||
364 | event->throttle.time = bswap_64(event->throttle.time); | ||
365 | event->throttle.id = bswap_64(event->throttle.id); | ||
366 | event->throttle.stream_id = bswap_64(event->throttle.stream_id); | ||
367 | |||
368 | if (sample_id_all) | ||
369 | swap_sample_id_all(event, &event->throttle + 1); | ||
370 | } | ||
371 | |||
400 | static u8 revbyte(u8 b) | 372 | static u8 revbyte(u8 b) |
401 | { | 373 | { |
402 | int rev = (b >> 4) | ((b & 0xf) << 4); | 374 | int rev = (b >> 4) | ((b & 0xf) << 4); |
@@ -442,6 +414,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr) | |||
442 | attr->bp_type = bswap_32(attr->bp_type); | 414 | attr->bp_type = bswap_32(attr->bp_type); |
443 | attr->bp_addr = bswap_64(attr->bp_addr); | 415 | attr->bp_addr = bswap_64(attr->bp_addr); |
444 | attr->bp_len = bswap_64(attr->bp_len); | 416 | attr->bp_len = bswap_64(attr->bp_len); |
417 | attr->branch_sample_type = bswap_64(attr->branch_sample_type); | ||
418 | attr->sample_regs_user = bswap_64(attr->sample_regs_user); | ||
419 | attr->sample_stack_user = bswap_32(attr->sample_stack_user); | ||
445 | 420 | ||
446 | swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); | 421 | swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); |
447 | } | 422 | } |
@@ -482,6 +457,8 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
482 | [PERF_RECORD_EXIT] = perf_event__task_swap, | 457 | [PERF_RECORD_EXIT] = perf_event__task_swap, |
483 | [PERF_RECORD_LOST] = perf_event__all64_swap, | 458 | [PERF_RECORD_LOST] = perf_event__all64_swap, |
484 | [PERF_RECORD_READ] = perf_event__read_swap, | 459 | [PERF_RECORD_READ] = perf_event__read_swap, |
460 | [PERF_RECORD_THROTTLE] = perf_event__throttle_swap, | ||
461 | [PERF_RECORD_UNTHROTTLE] = perf_event__throttle_swap, | ||
485 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, | 462 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
486 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, | 463 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, |
487 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 464 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
@@ -525,13 +502,16 @@ static int flush_sample_queue(struct perf_session *s, | |||
525 | struct perf_sample sample; | 502 | struct perf_sample sample; |
526 | u64 limit = os->next_flush; | 503 | u64 limit = os->next_flush; |
527 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 504 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
528 | unsigned idx = 0, progress_next = os->nr_samples / 16; | ||
529 | bool show_progress = limit == ULLONG_MAX; | 505 | bool show_progress = limit == ULLONG_MAX; |
506 | struct ui_progress prog; | ||
530 | int ret; | 507 | int ret; |
531 | 508 | ||
532 | if (!tool->ordered_samples || !limit) | 509 | if (!tool->ordered_samples || !limit) |
533 | return 0; | 510 | return 0; |
534 | 511 | ||
512 | if (show_progress) | ||
513 | ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); | ||
514 | |||
535 | list_for_each_entry_safe(iter, tmp, head, list) { | 515 | list_for_each_entry_safe(iter, tmp, head, list) { |
536 | if (session_done()) | 516 | if (session_done()) |
537 | return 0; | 517 | return 0; |
@@ -552,11 +532,9 @@ static int flush_sample_queue(struct perf_session *s, | |||
552 | os->last_flush = iter->timestamp; | 532 | os->last_flush = iter->timestamp; |
553 | list_del(&iter->list); | 533 | list_del(&iter->list); |
554 | list_add(&iter->list, &os->sample_cache); | 534 | list_add(&iter->list, &os->sample_cache); |
555 | if (show_progress && (++idx >= progress_next)) { | 535 | |
556 | progress_next += os->nr_samples / 16; | 536 | if (show_progress) |
557 | ui_progress__update(idx, os->nr_samples, | 537 | ui_progress__update(&prog, 1); |
558 | "Processing time ordered events..."); | ||
559 | } | ||
560 | } | 538 | } |
561 | 539 | ||
562 | if (list_empty(head)) { | 540 | if (list_empty(head)) { |
@@ -860,6 +838,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
860 | if (sample_type & PERF_SAMPLE_DATA_SRC) | 838 | if (sample_type & PERF_SAMPLE_DATA_SRC) |
861 | printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); | 839 | printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); |
862 | 840 | ||
841 | if (sample_type & PERF_SAMPLE_TRANSACTION) | ||
842 | printf("... transaction: %" PRIx64 "\n", sample->transaction); | ||
843 | |||
863 | if (sample_type & PERF_SAMPLE_READ) | 844 | if (sample_type & PERF_SAMPLE_READ) |
864 | sample_read__printf(sample, evsel->attr.read_format); | 845 | sample_read__printf(sample, evsel->attr.read_format); |
865 | } | 846 | } |
@@ -1031,6 +1012,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1031 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, | 1012 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, |
1032 | struct perf_tool *tool, u64 file_offset) | 1013 | struct perf_tool *tool, u64 file_offset) |
1033 | { | 1014 | { |
1015 | int fd = perf_data_file__fd(session->file); | ||
1034 | int err; | 1016 | int err; |
1035 | 1017 | ||
1036 | dump_event(session, event, file_offset, NULL); | 1018 | dump_event(session, event, file_offset, NULL); |
@@ -1044,7 +1026,7 @@ static int perf_session__process_user_event(struct perf_session *session, union | |||
1044 | return err; | 1026 | return err; |
1045 | case PERF_RECORD_HEADER_TRACING_DATA: | 1027 | case PERF_RECORD_HEADER_TRACING_DATA: |
1046 | /* setup for reading amidst mmap */ | 1028 | /* setup for reading amidst mmap */ |
1047 | lseek(session->fd, file_offset, SEEK_SET); | 1029 | lseek(fd, file_offset, SEEK_SET); |
1048 | return tool->tracing_data(tool, event, session); | 1030 | return tool->tracing_data(tool, event, session); |
1049 | case PERF_RECORD_HEADER_BUILD_ID: | 1031 | case PERF_RECORD_HEADER_BUILD_ID: |
1050 | return tool->build_id(tool, event, session); | 1032 | return tool->build_id(tool, event, session); |
@@ -1101,11 +1083,11 @@ static int perf_session__process_event(struct perf_session *session, | |||
1101 | file_offset); | 1083 | file_offset); |
1102 | } | 1084 | } |
1103 | 1085 | ||
1104 | void perf_event_header__bswap(struct perf_event_header *self) | 1086 | void perf_event_header__bswap(struct perf_event_header *hdr) |
1105 | { | 1087 | { |
1106 | self->type = bswap_32(self->type); | 1088 | hdr->type = bswap_32(hdr->type); |
1107 | self->misc = bswap_16(self->misc); | 1089 | hdr->misc = bswap_16(hdr->misc); |
1108 | self->size = bswap_16(self->size); | 1090 | hdr->size = bswap_16(hdr->size); |
1109 | } | 1091 | } |
1110 | 1092 | ||
1111 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) | 1093 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) |
@@ -1113,11 +1095,11 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) | |||
1113 | return machine__findnew_thread(&session->machines.host, 0, pid); | 1095 | return machine__findnew_thread(&session->machines.host, 0, pid); |
1114 | } | 1096 | } |
1115 | 1097 | ||
1116 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 1098 | static struct thread *perf_session__register_idle_thread(struct perf_session *session) |
1117 | { | 1099 | { |
1118 | struct thread *thread = perf_session__findnew(self, 0); | 1100 | struct thread *thread = perf_session__findnew(session, 0); |
1119 | 1101 | ||
1120 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | 1102 | if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { |
1121 | pr_err("problem inserting idle task.\n"); | 1103 | pr_err("problem inserting idle task.\n"); |
1122 | thread = NULL; | 1104 | thread = NULL; |
1123 | } | 1105 | } |
@@ -1167,9 +1149,10 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
1167 | 1149 | ||
1168 | volatile int session_done; | 1150 | volatile int session_done; |
1169 | 1151 | ||
1170 | static int __perf_session__process_pipe_events(struct perf_session *self, | 1152 | static int __perf_session__process_pipe_events(struct perf_session *session, |
1171 | struct perf_tool *tool) | 1153 | struct perf_tool *tool) |
1172 | { | 1154 | { |
1155 | int fd = perf_data_file__fd(session->file); | ||
1173 | union perf_event *event; | 1156 | union perf_event *event; |
1174 | uint32_t size, cur_size = 0; | 1157 | uint32_t size, cur_size = 0; |
1175 | void *buf = NULL; | 1158 | void *buf = NULL; |
@@ -1188,7 +1171,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, | |||
1188 | return -errno; | 1171 | return -errno; |
1189 | more: | 1172 | more: |
1190 | event = buf; | 1173 | event = buf; |
1191 | err = readn(self->fd, event, sizeof(struct perf_event_header)); | 1174 | err = readn(fd, event, sizeof(struct perf_event_header)); |
1192 | if (err <= 0) { | 1175 | if (err <= 0) { |
1193 | if (err == 0) | 1176 | if (err == 0) |
1194 | goto done; | 1177 | goto done; |
@@ -1197,7 +1180,7 @@ more: | |||
1197 | goto out_err; | 1180 | goto out_err; |
1198 | } | 1181 | } |
1199 | 1182 | ||
1200 | if (self->header.needs_swap) | 1183 | if (session->header.needs_swap) |
1201 | perf_event_header__bswap(&event->header); | 1184 | perf_event_header__bswap(&event->header); |
1202 | 1185 | ||
1203 | size = event->header.size; | 1186 | size = event->header.size; |
@@ -1220,7 +1203,7 @@ more: | |||
1220 | p += sizeof(struct perf_event_header); | 1203 | p += sizeof(struct perf_event_header); |
1221 | 1204 | ||
1222 | if (size - sizeof(struct perf_event_header)) { | 1205 | if (size - sizeof(struct perf_event_header)) { |
1223 | err = readn(self->fd, p, size - sizeof(struct perf_event_header)); | 1206 | err = readn(fd, p, size - sizeof(struct perf_event_header)); |
1224 | if (err <= 0) { | 1207 | if (err <= 0) { |
1225 | if (err == 0) { | 1208 | if (err == 0) { |
1226 | pr_err("unexpected end of event stream\n"); | 1209 | pr_err("unexpected end of event stream\n"); |
@@ -1232,7 +1215,7 @@ more: | |||
1232 | } | 1215 | } |
1233 | } | 1216 | } |
1234 | 1217 | ||
1235 | if ((skip = perf_session__process_event(self, event, tool, head)) < 0) { | 1218 | if ((skip = perf_session__process_event(session, event, tool, head)) < 0) { |
1236 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", | 1219 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", |
1237 | head, event->header.size, event->header.type); | 1220 | head, event->header.size, event->header.type); |
1238 | err = -EINVAL; | 1221 | err = -EINVAL; |
@@ -1247,11 +1230,13 @@ more: | |||
1247 | if (!session_done()) | 1230 | if (!session_done()) |
1248 | goto more; | 1231 | goto more; |
1249 | done: | 1232 | done: |
1250 | err = 0; | 1233 | /* do the final flush for ordered samples */ |
1234 | session->ordered_samples.next_flush = ULLONG_MAX; | ||
1235 | err = flush_sample_queue(session, tool); | ||
1251 | out_err: | 1236 | out_err: |
1252 | free(buf); | 1237 | free(buf); |
1253 | perf_session__warn_about_errors(self, tool); | 1238 | perf_session__warn_about_errors(session, tool); |
1254 | perf_session_free_sample_buffers(self); | 1239 | perf_session_free_sample_buffers(session); |
1255 | return err; | 1240 | return err; |
1256 | } | 1241 | } |
1257 | 1242 | ||
@@ -1299,12 +1284,14 @@ int __perf_session__process_events(struct perf_session *session, | |||
1299 | u64 data_offset, u64 data_size, | 1284 | u64 data_offset, u64 data_size, |
1300 | u64 file_size, struct perf_tool *tool) | 1285 | u64 file_size, struct perf_tool *tool) |
1301 | { | 1286 | { |
1302 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1287 | int fd = perf_data_file__fd(session->file); |
1288 | u64 head, page_offset, file_offset, file_pos; | ||
1303 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1289 | int err, mmap_prot, mmap_flags, map_idx = 0; |
1304 | size_t mmap_size; | 1290 | size_t mmap_size; |
1305 | char *buf, *mmaps[NUM_MMAPS]; | 1291 | char *buf, *mmaps[NUM_MMAPS]; |
1306 | union perf_event *event; | 1292 | union perf_event *event; |
1307 | uint32_t size; | 1293 | uint32_t size; |
1294 | struct ui_progress prog; | ||
1308 | 1295 | ||
1309 | perf_tool__fill_defaults(tool); | 1296 | perf_tool__fill_defaults(tool); |
1310 | 1297 | ||
@@ -1315,7 +1302,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
1315 | if (data_size && (data_offset + data_size < file_size)) | 1302 | if (data_size && (data_offset + data_size < file_size)) |
1316 | file_size = data_offset + data_size; | 1303 | file_size = data_offset + data_size; |
1317 | 1304 | ||
1318 | progress_next = file_size / 16; | 1305 | ui_progress__init(&prog, file_size, "Processing events..."); |
1319 | 1306 | ||
1320 | mmap_size = MMAP_SIZE; | 1307 | mmap_size = MMAP_SIZE; |
1321 | if (mmap_size > file_size) | 1308 | if (mmap_size > file_size) |
@@ -1331,7 +1318,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
1331 | mmap_flags = MAP_PRIVATE; | 1318 | mmap_flags = MAP_PRIVATE; |
1332 | } | 1319 | } |
1333 | remap: | 1320 | remap: |
1334 | buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, | 1321 | buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd, |
1335 | file_offset); | 1322 | file_offset); |
1336 | if (buf == MAP_FAILED) { | 1323 | if (buf == MAP_FAILED) { |
1337 | pr_err("failed to mmap file\n"); | 1324 | pr_err("failed to mmap file\n"); |
@@ -1370,19 +1357,15 @@ more: | |||
1370 | head += size; | 1357 | head += size; |
1371 | file_pos += size; | 1358 | file_pos += size; |
1372 | 1359 | ||
1373 | if (file_pos >= progress_next) { | 1360 | ui_progress__update(&prog, size); |
1374 | progress_next += file_size / 16; | ||
1375 | ui_progress__update(file_pos, file_size, | ||
1376 | "Processing events..."); | ||
1377 | } | ||
1378 | 1361 | ||
1379 | err = 0; | ||
1380 | if (session_done()) | 1362 | if (session_done()) |
1381 | goto out_err; | 1363 | goto out; |
1382 | 1364 | ||
1383 | if (file_pos < file_size) | 1365 | if (file_pos < file_size) |
1384 | goto more; | 1366 | goto more; |
1385 | 1367 | ||
1368 | out: | ||
1386 | /* do the final flush for ordered samples */ | 1369 | /* do the final flush for ordered samples */ |
1387 | session->ordered_samples.next_flush = ULLONG_MAX; | 1370 | session->ordered_samples.next_flush = ULLONG_MAX; |
1388 | err = flush_sample_queue(session, tool); | 1371 | err = flush_sample_queue(session, tool); |
@@ -1393,21 +1376,22 @@ out_err: | |||
1393 | return err; | 1376 | return err; |
1394 | } | 1377 | } |
1395 | 1378 | ||
1396 | int perf_session__process_events(struct perf_session *self, | 1379 | int perf_session__process_events(struct perf_session *session, |
1397 | struct perf_tool *tool) | 1380 | struct perf_tool *tool) |
1398 | { | 1381 | { |
1382 | u64 size = perf_data_file__size(session->file); | ||
1399 | int err; | 1383 | int err; |
1400 | 1384 | ||
1401 | if (perf_session__register_idle_thread(self) == NULL) | 1385 | if (perf_session__register_idle_thread(session) == NULL) |
1402 | return -ENOMEM; | 1386 | return -ENOMEM; |
1403 | 1387 | ||
1404 | if (!self->fd_pipe) | 1388 | if (!perf_data_file__is_pipe(session->file)) |
1405 | err = __perf_session__process_events(self, | 1389 | err = __perf_session__process_events(session, |
1406 | self->header.data_offset, | 1390 | session->header.data_offset, |
1407 | self->header.data_size, | 1391 | session->header.data_size, |
1408 | self->size, tool); | 1392 | size, tool); |
1409 | else | 1393 | else |
1410 | err = __perf_session__process_pipe_events(self, tool); | 1394 | err = __perf_session__process_pipe_events(session, tool); |
1411 | 1395 | ||
1412 | return err; | 1396 | return err; |
1413 | } | 1397 | } |
@@ -1456,15 +1440,15 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, | |||
1456 | return 0; | 1440 | return 0; |
1457 | } | 1441 | } |
1458 | 1442 | ||
1459 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) | 1443 | size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp) |
1460 | { | 1444 | { |
1461 | return machines__fprintf_dsos(&self->machines, fp); | 1445 | return machines__fprintf_dsos(&session->machines, fp); |
1462 | } | 1446 | } |
1463 | 1447 | ||
1464 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | 1448 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, |
1465 | bool (skip)(struct dso *dso, int parm), int parm) | 1449 | bool (skip)(struct dso *dso, int parm), int parm) |
1466 | { | 1450 | { |
1467 | return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); | 1451 | return machines__fprintf_dsos_buildid(&session->machines, fp, skip, parm); |
1468 | } | 1452 | } |
1469 | 1453 | ||
1470 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | 1454 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) |
@@ -1525,7 +1509,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, | |||
1525 | if (symbol_conf.use_callchain && sample->callchain) { | 1509 | if (symbol_conf.use_callchain && sample->callchain) { |
1526 | 1510 | ||
1527 | if (machine__resolve_callchain(machine, evsel, al.thread, | 1511 | if (machine__resolve_callchain(machine, evsel, al.thread, |
1528 | sample, NULL, NULL) != 0) { | 1512 | sample, NULL, NULL, |
1513 | PERF_MAX_STACK_DEPTH) != 0) { | ||
1529 | if (verbose) | 1514 | if (verbose) |
1530 | error("Failed to resolve callchain. Skipping\n"); | 1515 | error("Failed to resolve callchain. Skipping\n"); |
1531 | return; | 1516 | return; |
@@ -1629,13 +1614,14 @@ int perf_session__cpu_bitmap(struct perf_session *session, | |||
1629 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | 1614 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, |
1630 | bool full) | 1615 | bool full) |
1631 | { | 1616 | { |
1617 | int fd = perf_data_file__fd(session->file); | ||
1632 | struct stat st; | 1618 | struct stat st; |
1633 | int ret; | 1619 | int ret; |
1634 | 1620 | ||
1635 | if (session == NULL || fp == NULL) | 1621 | if (session == NULL || fp == NULL) |
1636 | return; | 1622 | return; |
1637 | 1623 | ||
1638 | ret = fstat(session->fd, &st); | 1624 | ret = fstat(fd, &st); |
1639 | if (ret == -1) | 1625 | if (ret == -1) |
1640 | return; | 1626 | return; |
1641 | 1627 | ||
@@ -1664,9 +1650,9 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
1664 | continue; | 1650 | continue; |
1665 | 1651 | ||
1666 | err = -EEXIST; | 1652 | err = -EEXIST; |
1667 | if (evsel->handler.func != NULL) | 1653 | if (evsel->handler != NULL) |
1668 | goto out; | 1654 | goto out; |
1669 | evsel->handler.func = assocs[i].handler; | 1655 | evsel->handler = assocs[i].handler; |
1670 | } | 1656 | } |
1671 | 1657 | ||
1672 | err = 0; | 1658 | err = 0; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 04bf7373a7e5..50f640958f0f 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "machine.h" | 7 | #include "machine.h" |
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "thread.h" | 9 | #include "thread.h" |
10 | #include "data.h" | ||
10 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
11 | #include <linux/perf_event.h> | 12 | #include <linux/perf_event.h> |
12 | 13 | ||
@@ -29,16 +30,13 @@ struct ordered_samples { | |||
29 | 30 | ||
30 | struct perf_session { | 31 | struct perf_session { |
31 | struct perf_header header; | 32 | struct perf_header header; |
32 | unsigned long size; | ||
33 | struct machines machines; | 33 | struct machines machines; |
34 | struct perf_evlist *evlist; | 34 | struct perf_evlist *evlist; |
35 | struct pevent *pevent; | 35 | struct pevent *pevent; |
36 | struct events_stats stats; | 36 | struct events_stats stats; |
37 | int fd; | ||
38 | bool fd_pipe; | ||
39 | bool repipe; | 37 | bool repipe; |
40 | struct ordered_samples ordered_samples; | 38 | struct ordered_samples ordered_samples; |
41 | char filename[1]; | 39 | struct perf_data_file *file; |
42 | }; | 40 | }; |
43 | 41 | ||
44 | #define PRINT_IP_OPT_IP (1<<0) | 42 | #define PRINT_IP_OPT_IP (1<<0) |
@@ -49,17 +47,16 @@ struct perf_session { | |||
49 | 47 | ||
50 | struct perf_tool; | 48 | struct perf_tool; |
51 | 49 | ||
52 | struct perf_session *perf_session__new(const char *filename, int mode, | 50 | struct perf_session *perf_session__new(struct perf_data_file *file, |
53 | bool force, bool repipe, | 51 | bool repipe, struct perf_tool *tool); |
54 | struct perf_tool *tool); | ||
55 | void perf_session__delete(struct perf_session *session); | 52 | void perf_session__delete(struct perf_session *session); |
56 | 53 | ||
57 | void perf_event_header__bswap(struct perf_event_header *self); | 54 | void perf_event_header__bswap(struct perf_event_header *hdr); |
58 | 55 | ||
59 | int __perf_session__process_events(struct perf_session *self, | 56 | int __perf_session__process_events(struct perf_session *session, |
60 | u64 data_offset, u64 data_size, u64 size, | 57 | u64 data_offset, u64 data_size, u64 size, |
61 | struct perf_tool *tool); | 58 | struct perf_tool *tool); |
62 | int perf_session__process_events(struct perf_session *self, | 59 | int perf_session__process_events(struct perf_session *session, |
63 | struct perf_tool *tool); | 60 | struct perf_tool *tool); |
64 | 61 | ||
65 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 62 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
@@ -67,37 +64,38 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event, | |||
67 | 64 | ||
68 | void perf_tool__fill_defaults(struct perf_tool *tool); | 65 | void perf_tool__fill_defaults(struct perf_tool *tool); |
69 | 66 | ||
70 | int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, | 67 | int perf_session__resolve_callchain(struct perf_session *session, |
68 | struct perf_evsel *evsel, | ||
71 | struct thread *thread, | 69 | struct thread *thread, |
72 | struct ip_callchain *chain, | 70 | struct ip_callchain *chain, |
73 | struct symbol **parent); | 71 | struct symbol **parent); |
74 | 72 | ||
75 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 73 | bool perf_session__has_traces(struct perf_session *session, const char *msg); |
76 | 74 | ||
77 | void mem_bswap_64(void *src, int byte_size); | 75 | void mem_bswap_64(void *src, int byte_size); |
78 | void mem_bswap_32(void *src, int byte_size); | 76 | void mem_bswap_32(void *src, int byte_size); |
79 | void perf_event__attr_swap(struct perf_event_attr *attr); | 77 | void perf_event__attr_swap(struct perf_event_attr *attr); |
80 | 78 | ||
81 | int perf_session__create_kernel_maps(struct perf_session *self); | 79 | int perf_session__create_kernel_maps(struct perf_session *session); |
82 | 80 | ||
83 | void perf_session__set_id_hdr_size(struct perf_session *session); | 81 | void perf_session__set_id_hdr_size(struct perf_session *session); |
84 | 82 | ||
85 | static inline | 83 | static inline |
86 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) | 84 | struct machine *perf_session__find_machine(struct perf_session *session, pid_t pid) |
87 | { | 85 | { |
88 | return machines__find(&self->machines, pid); | 86 | return machines__find(&session->machines, pid); |
89 | } | 87 | } |
90 | 88 | ||
91 | static inline | 89 | static inline |
92 | struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) | 90 | struct machine *perf_session__findnew_machine(struct perf_session *session, pid_t pid) |
93 | { | 91 | { |
94 | return machines__findnew(&self->machines, pid); | 92 | return machines__findnew(&session->machines, pid); |
95 | } | 93 | } |
96 | 94 | ||
97 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 95 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid); |
98 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); | 96 | size_t perf_session__fprintf(struct perf_session *session, FILE *fp); |
99 | 97 | ||
100 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); | 98 | size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp); |
101 | 99 | ||
102 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, | 100 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, |
103 | bool (fn)(struct dso *dso, int parm), int parm); | 101 | bool (fn)(struct dso *dso, int parm), int parm); |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5f118a089519..8b0bb1f4494a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "sort.h" | 1 | #include "sort.h" |
2 | #include "hist.h" | 2 | #include "hist.h" |
3 | #include "comm.h" | ||
3 | #include "symbol.h" | 4 | #include "symbol.h" |
4 | 5 | ||
5 | regex_t parent_regex; | 6 | regex_t parent_regex; |
@@ -42,7 +43,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
42 | return n; | 43 | return n; |
43 | } | 44 | } |
44 | 45 | ||
45 | static int64_t cmp_null(void *l, void *r) | 46 | static int64_t cmp_null(const void *l, const void *r) |
46 | { | 47 | { |
47 | if (!l && !r) | 48 | if (!l && !r) |
48 | return 0; | 49 | return 0; |
@@ -60,11 +61,12 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
60 | return right->thread->tid - left->thread->tid; | 61 | return right->thread->tid - left->thread->tid; |
61 | } | 62 | } |
62 | 63 | ||
63 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 64 | static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, |
64 | size_t size, unsigned int width) | 65 | size_t size, unsigned int width) |
65 | { | 66 | { |
67 | const char *comm = thread__comm_str(he->thread); | ||
66 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, | 68 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, |
67 | self->thread->comm ?: "", self->thread->tid); | 69 | comm ?: "", he->thread->tid); |
68 | } | 70 | } |
69 | 71 | ||
70 | struct sort_entry sort_thread = { | 72 | struct sort_entry sort_thread = { |
@@ -79,25 +81,21 @@ struct sort_entry sort_thread = { | |||
79 | static int64_t | 81 | static int64_t |
80 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | 82 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) |
81 | { | 83 | { |
82 | return right->thread->tid - left->thread->tid; | 84 | /* Compare the addr that should be unique among comm */ |
85 | return comm__str(right->comm) - comm__str(left->comm); | ||
83 | } | 86 | } |
84 | 87 | ||
85 | static int64_t | 88 | static int64_t |
86 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | 89 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) |
87 | { | 90 | { |
88 | char *comm_l = left->thread->comm; | 91 | /* Compare the addr that should be unique among comm */ |
89 | char *comm_r = right->thread->comm; | 92 | return comm__str(right->comm) - comm__str(left->comm); |
90 | |||
91 | if (!comm_l || !comm_r) | ||
92 | return cmp_null(comm_l, comm_r); | ||
93 | |||
94 | return strcmp(comm_l, comm_r); | ||
95 | } | 93 | } |
96 | 94 | ||
97 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | 95 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, |
98 | size_t size, unsigned int width) | 96 | size_t size, unsigned int width) |
99 | { | 97 | { |
100 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 98 | return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); |
101 | } | 99 | } |
102 | 100 | ||
103 | struct sort_entry sort_comm = { | 101 | struct sort_entry sort_comm = { |
@@ -148,10 +146,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, | |||
148 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 146 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
149 | } | 147 | } |
150 | 148 | ||
151 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | 149 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, |
152 | size_t size, unsigned int width) | 150 | size_t size, unsigned int width) |
153 | { | 151 | { |
154 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); | 152 | return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); |
155 | } | 153 | } |
156 | 154 | ||
157 | struct sort_entry sort_dso = { | 155 | struct sort_entry sort_dso = { |
@@ -182,9 +180,19 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) | |||
182 | static int64_t | 180 | static int64_t |
183 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 181 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
184 | { | 182 | { |
183 | int64_t ret; | ||
184 | |||
185 | if (!left->ms.sym && !right->ms.sym) | 185 | if (!left->ms.sym && !right->ms.sym) |
186 | return right->level - left->level; | 186 | return right->level - left->level; |
187 | 187 | ||
188 | /* | ||
189 | * comparing symbol address alone is not enough since it's a | ||
190 | * relative address within a dso. | ||
191 | */ | ||
192 | ret = sort__dso_cmp(left, right); | ||
193 | if (ret != 0) | ||
194 | return ret; | ||
195 | |||
188 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); | 196 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); |
189 | } | 197 | } |
190 | 198 | ||
@@ -224,11 +232,11 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
224 | return ret; | 232 | return ret; |
225 | } | 233 | } |
226 | 234 | ||
227 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | 235 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, |
228 | size_t size, unsigned int width) | 236 | size_t size, unsigned int width) |
229 | { | 237 | { |
230 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | 238 | return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, |
231 | self->level, bf, size, width); | 239 | he->level, bf, size, width); |
232 | } | 240 | } |
233 | 241 | ||
234 | struct sort_entry sort_sym = { | 242 | struct sort_entry sort_sym = { |
@@ -243,50 +251,32 @@ struct sort_entry sort_sym = { | |||
243 | static int64_t | 251 | static int64_t |
244 | sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | 252 | sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) |
245 | { | 253 | { |
246 | return (int64_t)(right->ip - left->ip); | 254 | if (!left->srcline) { |
255 | if (!left->ms.map) | ||
256 | left->srcline = SRCLINE_UNKNOWN; | ||
257 | else { | ||
258 | struct map *map = left->ms.map; | ||
259 | left->srcline = get_srcline(map->dso, | ||
260 | map__rip_2objdump(map, left->ip)); | ||
261 | } | ||
262 | } | ||
263 | if (!right->srcline) { | ||
264 | if (!right->ms.map) | ||
265 | right->srcline = SRCLINE_UNKNOWN; | ||
266 | else { | ||
267 | struct map *map = right->ms.map; | ||
268 | right->srcline = get_srcline(map->dso, | ||
269 | map__rip_2objdump(map, right->ip)); | ||
270 | } | ||
271 | } | ||
272 | return strcmp(left->srcline, right->srcline); | ||
247 | } | 273 | } |
248 | 274 | ||
249 | static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | 275 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
250 | size_t size, | 276 | size_t size, |
251 | unsigned int width __maybe_unused) | 277 | unsigned int width __maybe_unused) |
252 | { | 278 | { |
253 | FILE *fp = NULL; | 279 | return repsep_snprintf(bf, size, "%s", he->srcline); |
254 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; | ||
255 | size_t line_len; | ||
256 | |||
257 | if (path != NULL) | ||
258 | goto out_path; | ||
259 | |||
260 | if (!self->ms.map) | ||
261 | goto out_ip; | ||
262 | |||
263 | if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) | ||
264 | goto out_ip; | ||
265 | |||
266 | snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, | ||
267 | self->ms.map->dso->long_name, self->ip); | ||
268 | fp = popen(cmd, "r"); | ||
269 | if (!fp) | ||
270 | goto out_ip; | ||
271 | |||
272 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
273 | goto out_ip; | ||
274 | self->srcline = strdup(path); | ||
275 | if (self->srcline == NULL) | ||
276 | goto out_ip; | ||
277 | |||
278 | nl = strchr(self->srcline, '\n'); | ||
279 | if (nl != NULL) | ||
280 | *nl = '\0'; | ||
281 | path = self->srcline; | ||
282 | out_path: | ||
283 | if (fp) | ||
284 | pclose(fp); | ||
285 | return repsep_snprintf(bf, size, "%s", path); | ||
286 | out_ip: | ||
287 | if (fp) | ||
288 | pclose(fp); | ||
289 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); | ||
290 | } | 280 | } |
291 | 281 | ||
292 | struct sort_entry sort_srcline = { | 282 | struct sort_entry sort_srcline = { |
@@ -310,11 +300,11 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
310 | return strcmp(sym_l->name, sym_r->name); | 300 | return strcmp(sym_l->name, sym_r->name); |
311 | } | 301 | } |
312 | 302 | ||
313 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | 303 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, |
314 | size_t size, unsigned int width) | 304 | size_t size, unsigned int width) |
315 | { | 305 | { |
316 | return repsep_snprintf(bf, size, "%-*s", width, | 306 | return repsep_snprintf(bf, size, "%-*s", width, |
317 | self->parent ? self->parent->name : "[other]"); | 307 | he->parent ? he->parent->name : "[other]"); |
318 | } | 308 | } |
319 | 309 | ||
320 | struct sort_entry sort_parent = { | 310 | struct sort_entry sort_parent = { |
@@ -332,10 +322,10 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
332 | return right->cpu - left->cpu; | 322 | return right->cpu - left->cpu; |
333 | } | 323 | } |
334 | 324 | ||
335 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | 325 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, |
336 | size_t size, unsigned int width) | 326 | size_t size, unsigned int width) |
337 | { | 327 | { |
338 | return repsep_snprintf(bf, size, "%*d", width, self->cpu); | 328 | return repsep_snprintf(bf, size, "%*d", width, he->cpu); |
339 | } | 329 | } |
340 | 330 | ||
341 | struct sort_entry sort_cpu = { | 331 | struct sort_entry sort_cpu = { |
@@ -354,10 +344,10 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
354 | right->branch_info->from.map); | 344 | right->branch_info->from.map); |
355 | } | 345 | } |
356 | 346 | ||
357 | static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, | 347 | static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, |
358 | size_t size, unsigned int width) | 348 | size_t size, unsigned int width) |
359 | { | 349 | { |
360 | return _hist_entry__dso_snprintf(self->branch_info->from.map, | 350 | return _hist_entry__dso_snprintf(he->branch_info->from.map, |
361 | bf, size, width); | 351 | bf, size, width); |
362 | } | 352 | } |
363 | 353 | ||
@@ -368,10 +358,10 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
368 | right->branch_info->to.map); | 358 | right->branch_info->to.map); |
369 | } | 359 | } |
370 | 360 | ||
371 | static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, | 361 | static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, |
372 | size_t size, unsigned int width) | 362 | size_t size, unsigned int width) |
373 | { | 363 | { |
374 | return _hist_entry__dso_snprintf(self->branch_info->to.map, | 364 | return _hist_entry__dso_snprintf(he->branch_info->to.map, |
375 | bf, size, width); | 365 | bf, size, width); |
376 | } | 366 | } |
377 | 367 | ||
@@ -399,21 +389,21 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
399 | return _sort__sym_cmp(to_l->sym, to_r->sym); | 389 | return _sort__sym_cmp(to_l->sym, to_r->sym); |
400 | } | 390 | } |
401 | 391 | ||
402 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | 392 | static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, |
403 | size_t size, unsigned int width) | 393 | size_t size, unsigned int width) |
404 | { | 394 | { |
405 | struct addr_map_symbol *from = &self->branch_info->from; | 395 | struct addr_map_symbol *from = &he->branch_info->from; |
406 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 396 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, |
407 | self->level, bf, size, width); | 397 | he->level, bf, size, width); |
408 | 398 | ||
409 | } | 399 | } |
410 | 400 | ||
411 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | 401 | static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, |
412 | size_t size, unsigned int width) | 402 | size_t size, unsigned int width) |
413 | { | 403 | { |
414 | struct addr_map_symbol *to = &self->branch_info->to; | 404 | struct addr_map_symbol *to = &he->branch_info->to; |
415 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 405 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, |
416 | self->level, bf, size, width); | 406 | he->level, bf, size, width); |
417 | 407 | ||
418 | } | 408 | } |
419 | 409 | ||
@@ -456,13 +446,13 @@ sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) | |||
456 | return mp || p; | 446 | return mp || p; |
457 | } | 447 | } |
458 | 448 | ||
459 | static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, | 449 | static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, |
460 | size_t size, unsigned int width){ | 450 | size_t size, unsigned int width){ |
461 | static const char *out = "N/A"; | 451 | static const char *out = "N/A"; |
462 | 452 | ||
463 | if (self->branch_info->flags.predicted) | 453 | if (he->branch_info->flags.predicted) |
464 | out = "N"; | 454 | out = "N"; |
465 | else if (self->branch_info->flags.mispred) | 455 | else if (he->branch_info->flags.mispred) |
466 | out = "Y"; | 456 | out = "Y"; |
467 | 457 | ||
468 | return repsep_snprintf(bf, size, "%-*s", width, out); | 458 | return repsep_snprintf(bf, size, "%-*s", width, out); |
@@ -482,19 +472,19 @@ sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) | |||
482 | return (int64_t)(r - l); | 472 | return (int64_t)(r - l); |
483 | } | 473 | } |
484 | 474 | ||
485 | static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, | 475 | static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf, |
486 | size_t size, unsigned int width) | 476 | size_t size, unsigned int width) |
487 | { | 477 | { |
488 | uint64_t addr = 0; | 478 | uint64_t addr = 0; |
489 | struct map *map = NULL; | 479 | struct map *map = NULL; |
490 | struct symbol *sym = NULL; | 480 | struct symbol *sym = NULL; |
491 | 481 | ||
492 | if (self->mem_info) { | 482 | if (he->mem_info) { |
493 | addr = self->mem_info->daddr.addr; | 483 | addr = he->mem_info->daddr.addr; |
494 | map = self->mem_info->daddr.map; | 484 | map = he->mem_info->daddr.map; |
495 | sym = self->mem_info->daddr.sym; | 485 | sym = he->mem_info->daddr.sym; |
496 | } | 486 | } |
497 | return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, | 487 | return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size, |
498 | width); | 488 | width); |
499 | } | 489 | } |
500 | 490 | ||
@@ -512,13 +502,13 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) | |||
512 | return _sort__dso_cmp(map_l, map_r); | 502 | return _sort__dso_cmp(map_l, map_r); |
513 | } | 503 | } |
514 | 504 | ||
515 | static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, | 505 | static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf, |
516 | size_t size, unsigned int width) | 506 | size_t size, unsigned int width) |
517 | { | 507 | { |
518 | struct map *map = NULL; | 508 | struct map *map = NULL; |
519 | 509 | ||
520 | if (self->mem_info) | 510 | if (he->mem_info) |
521 | map = self->mem_info->daddr.map; | 511 | map = he->mem_info->daddr.map; |
522 | 512 | ||
523 | return _hist_entry__dso_snprintf(map, bf, size, width); | 513 | return _hist_entry__dso_snprintf(map, bf, size, width); |
524 | } | 514 | } |
@@ -542,14 +532,14 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) | |||
542 | return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); | 532 | return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); |
543 | } | 533 | } |
544 | 534 | ||
545 | static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, | 535 | static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, |
546 | size_t size, unsigned int width) | 536 | size_t size, unsigned int width) |
547 | { | 537 | { |
548 | const char *out; | 538 | const char *out; |
549 | u64 mask = PERF_MEM_LOCK_NA; | 539 | u64 mask = PERF_MEM_LOCK_NA; |
550 | 540 | ||
551 | if (self->mem_info) | 541 | if (he->mem_info) |
552 | mask = self->mem_info->data_src.mem_lock; | 542 | mask = he->mem_info->data_src.mem_lock; |
553 | 543 | ||
554 | if (mask & PERF_MEM_LOCK_NA) | 544 | if (mask & PERF_MEM_LOCK_NA) |
555 | out = "N/A"; | 545 | out = "N/A"; |
@@ -591,7 +581,7 @@ static const char * const tlb_access[] = { | |||
591 | }; | 581 | }; |
592 | #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) | 582 | #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) |
593 | 583 | ||
594 | static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, | 584 | static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, |
595 | size_t size, unsigned int width) | 585 | size_t size, unsigned int width) |
596 | { | 586 | { |
597 | char out[64]; | 587 | char out[64]; |
@@ -602,8 +592,8 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, | |||
602 | 592 | ||
603 | out[0] = '\0'; | 593 | out[0] = '\0'; |
604 | 594 | ||
605 | if (self->mem_info) | 595 | if (he->mem_info) |
606 | m = self->mem_info->data_src.mem_dtlb; | 596 | m = he->mem_info->data_src.mem_dtlb; |
607 | 597 | ||
608 | hit = m & PERF_MEM_TLB_HIT; | 598 | hit = m & PERF_MEM_TLB_HIT; |
609 | miss = m & PERF_MEM_TLB_MISS; | 599 | miss = m & PERF_MEM_TLB_MISS; |
@@ -668,7 +658,7 @@ static const char * const mem_lvl[] = { | |||
668 | }; | 658 | }; |
669 | #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) | 659 | #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) |
670 | 660 | ||
671 | static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, | 661 | static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, |
672 | size_t size, unsigned int width) | 662 | size_t size, unsigned int width) |
673 | { | 663 | { |
674 | char out[64]; | 664 | char out[64]; |
@@ -677,8 +667,8 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, | |||
677 | u64 m = PERF_MEM_LVL_NA; | 667 | u64 m = PERF_MEM_LVL_NA; |
678 | u64 hit, miss; | 668 | u64 hit, miss; |
679 | 669 | ||
680 | if (self->mem_info) | 670 | if (he->mem_info) |
681 | m = self->mem_info->data_src.mem_lvl; | 671 | m = he->mem_info->data_src.mem_lvl; |
682 | 672 | ||
683 | out[0] = '\0'; | 673 | out[0] = '\0'; |
684 | 674 | ||
@@ -736,7 +726,7 @@ static const char * const snoop_access[] = { | |||
736 | }; | 726 | }; |
737 | #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) | 727 | #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) |
738 | 728 | ||
739 | static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, | 729 | static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, |
740 | size_t size, unsigned int width) | 730 | size_t size, unsigned int width) |
741 | { | 731 | { |
742 | char out[64]; | 732 | char out[64]; |
@@ -746,8 +736,8 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, | |||
746 | 736 | ||
747 | out[0] = '\0'; | 737 | out[0] = '\0'; |
748 | 738 | ||
749 | if (self->mem_info) | 739 | if (he->mem_info) |
750 | m = self->mem_info->data_src.mem_snoop; | 740 | m = he->mem_info->data_src.mem_snoop; |
751 | 741 | ||
752 | for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { | 742 | for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { |
753 | if (!(m & 0x1)) | 743 | if (!(m & 0x1)) |
@@ -784,10 +774,10 @@ sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) | |||
784 | return he_weight(left) - he_weight(right); | 774 | return he_weight(left) - he_weight(right); |
785 | } | 775 | } |
786 | 776 | ||
787 | static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, | 777 | static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, |
788 | size_t size, unsigned int width) | 778 | size_t size, unsigned int width) |
789 | { | 779 | { |
790 | return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); | 780 | return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he)); |
791 | } | 781 | } |
792 | 782 | ||
793 | struct sort_entry sort_local_weight = { | 783 | struct sort_entry sort_local_weight = { |
@@ -803,10 +793,10 @@ sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) | |||
803 | return left->stat.weight - right->stat.weight; | 793 | return left->stat.weight - right->stat.weight; |
804 | } | 794 | } |
805 | 795 | ||
806 | static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, | 796 | static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, |
807 | size_t size, unsigned int width) | 797 | size_t size, unsigned int width) |
808 | { | 798 | { |
809 | return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); | 799 | return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight); |
810 | } | 800 | } |
811 | 801 | ||
812 | struct sort_entry sort_global_weight = { | 802 | struct sort_entry sort_global_weight = { |
@@ -858,6 +848,127 @@ struct sort_entry sort_mem_snoop = { | |||
858 | .se_width_idx = HISTC_MEM_SNOOP, | 848 | .se_width_idx = HISTC_MEM_SNOOP, |
859 | }; | 849 | }; |
860 | 850 | ||
851 | static int64_t | ||
852 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) | ||
853 | { | ||
854 | return left->branch_info->flags.abort != | ||
855 | right->branch_info->flags.abort; | ||
856 | } | ||
857 | |||
858 | static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, | ||
859 | size_t size, unsigned int width) | ||
860 | { | ||
861 | static const char *out = "."; | ||
862 | |||
863 | if (he->branch_info->flags.abort) | ||
864 | out = "A"; | ||
865 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
866 | } | ||
867 | |||
868 | struct sort_entry sort_abort = { | ||
869 | .se_header = "Transaction abort", | ||
870 | .se_cmp = sort__abort_cmp, | ||
871 | .se_snprintf = hist_entry__abort_snprintf, | ||
872 | .se_width_idx = HISTC_ABORT, | ||
873 | }; | ||
874 | |||
875 | static int64_t | ||
876 | sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) | ||
877 | { | ||
878 | return left->branch_info->flags.in_tx != | ||
879 | right->branch_info->flags.in_tx; | ||
880 | } | ||
881 | |||
882 | static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, | ||
883 | size_t size, unsigned int width) | ||
884 | { | ||
885 | static const char *out = "."; | ||
886 | |||
887 | if (he->branch_info->flags.in_tx) | ||
888 | out = "T"; | ||
889 | |||
890 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
891 | } | ||
892 | |||
893 | struct sort_entry sort_in_tx = { | ||
894 | .se_header = "Branch in transaction", | ||
895 | .se_cmp = sort__in_tx_cmp, | ||
896 | .se_snprintf = hist_entry__in_tx_snprintf, | ||
897 | .se_width_idx = HISTC_IN_TX, | ||
898 | }; | ||
899 | |||
900 | static int64_t | ||
901 | sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) | ||
902 | { | ||
903 | return left->transaction - right->transaction; | ||
904 | } | ||
905 | |||
906 | static inline char *add_str(char *p, const char *str) | ||
907 | { | ||
908 | strcpy(p, str); | ||
909 | return p + strlen(str); | ||
910 | } | ||
911 | |||
912 | static struct txbit { | ||
913 | unsigned flag; | ||
914 | const char *name; | ||
915 | int skip_for_len; | ||
916 | } txbits[] = { | ||
917 | { PERF_TXN_ELISION, "EL ", 0 }, | ||
918 | { PERF_TXN_TRANSACTION, "TX ", 1 }, | ||
919 | { PERF_TXN_SYNC, "SYNC ", 1 }, | ||
920 | { PERF_TXN_ASYNC, "ASYNC ", 0 }, | ||
921 | { PERF_TXN_RETRY, "RETRY ", 0 }, | ||
922 | { PERF_TXN_CONFLICT, "CON ", 0 }, | ||
923 | { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, | ||
924 | { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 }, | ||
925 | { 0, NULL, 0 } | ||
926 | }; | ||
927 | |||
928 | int hist_entry__transaction_len(void) | ||
929 | { | ||
930 | int i; | ||
931 | int len = 0; | ||
932 | |||
933 | for (i = 0; txbits[i].name; i++) { | ||
934 | if (!txbits[i].skip_for_len) | ||
935 | len += strlen(txbits[i].name); | ||
936 | } | ||
937 | len += 4; /* :XX<space> */ | ||
938 | return len; | ||
939 | } | ||
940 | |||
941 | static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, | ||
942 | size_t size, unsigned int width) | ||
943 | { | ||
944 | u64 t = he->transaction; | ||
945 | char buf[128]; | ||
946 | char *p = buf; | ||
947 | int i; | ||
948 | |||
949 | buf[0] = 0; | ||
950 | for (i = 0; txbits[i].name; i++) | ||
951 | if (txbits[i].flag & t) | ||
952 | p = add_str(p, txbits[i].name); | ||
953 | if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) | ||
954 | p = add_str(p, "NEITHER "); | ||
955 | if (t & PERF_TXN_ABORT_MASK) { | ||
956 | sprintf(p, ":%" PRIx64, | ||
957 | (t & PERF_TXN_ABORT_MASK) >> | ||
958 | PERF_TXN_ABORT_SHIFT); | ||
959 | p += strlen(p); | ||
960 | } | ||
961 | |||
962 | return repsep_snprintf(bf, size, "%-*s", width, buf); | ||
963 | } | ||
964 | |||
965 | struct sort_entry sort_transaction = { | ||
966 | .se_header = "Transaction ", | ||
967 | .se_cmp = sort__transaction_cmp, | ||
968 | .se_snprintf = hist_entry__transaction_snprintf, | ||
969 | .se_width_idx = HISTC_TRANSACTION, | ||
970 | }; | ||
971 | |||
861 | struct sort_dimension { | 972 | struct sort_dimension { |
862 | const char *name; | 973 | const char *name; |
863 | struct sort_entry *entry; | 974 | struct sort_entry *entry; |
@@ -876,6 +987,7 @@ static struct sort_dimension common_sort_dimensions[] = { | |||
876 | DIM(SORT_SRCLINE, "srcline", sort_srcline), | 987 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
877 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), | 988 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), |
878 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | 989 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), |
990 | DIM(SORT_TRANSACTION, "transaction", sort_transaction), | ||
879 | }; | 991 | }; |
880 | 992 | ||
881 | #undef DIM | 993 | #undef DIM |
@@ -888,6 +1000,8 @@ static struct sort_dimension bstack_sort_dimensions[] = { | |||
888 | DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), | 1000 | DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), |
889 | DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), | 1001 | DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), |
890 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | 1002 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), |
1003 | DIM(SORT_IN_TX, "in_tx", sort_in_tx), | ||
1004 | DIM(SORT_ABORT, "abort", sort_abort), | ||
891 | }; | 1005 | }; |
892 | 1006 | ||
893 | #undef DIM | 1007 | #undef DIM |
@@ -1009,7 +1123,7 @@ int setup_sorting(void) | |||
1009 | return ret; | 1123 | return ret; |
1010 | } | 1124 | } |
1011 | 1125 | ||
1012 | static void sort_entry__setup_elide(struct sort_entry *self, | 1126 | static void sort_entry__setup_elide(struct sort_entry *se, |
1013 | struct strlist *list, | 1127 | struct strlist *list, |
1014 | const char *list_name, FILE *fp) | 1128 | const char *list_name, FILE *fp) |
1015 | { | 1129 | { |
@@ -1017,12 +1131,14 @@ static void sort_entry__setup_elide(struct sort_entry *self, | |||
1017 | if (fp != NULL) | 1131 | if (fp != NULL) |
1018 | fprintf(fp, "# %s: %s\n", list_name, | 1132 | fprintf(fp, "# %s: %s\n", list_name, |
1019 | strlist__entry(list, 0)->s); | 1133 | strlist__entry(list, 0)->s); |
1020 | self->elide = true; | 1134 | se->elide = true; |
1021 | } | 1135 | } |
1022 | } | 1136 | } |
1023 | 1137 | ||
1024 | void sort__setup_elide(FILE *output) | 1138 | void sort__setup_elide(FILE *output) |
1025 | { | 1139 | { |
1140 | struct sort_entry *se; | ||
1141 | |||
1026 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | 1142 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, |
1027 | "dso", output); | 1143 | "dso", output); |
1028 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, | 1144 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, |
@@ -1058,4 +1174,15 @@ void sort__setup_elide(FILE *output) | |||
1058 | "snoop", output); | 1174 | "snoop", output); |
1059 | } | 1175 | } |
1060 | 1176 | ||
1177 | /* | ||
1178 | * It makes no sense to elide all of sort entries. | ||
1179 | * Just revert them to show up again. | ||
1180 | */ | ||
1181 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
1182 | if (!se->elide) | ||
1183 | return; | ||
1184 | } | ||
1185 | |||
1186 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
1187 | se->elide = false; | ||
1061 | } | 1188 | } |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 4e80dbd271e7..43e5ff42a609 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -22,7 +22,6 @@ | |||
22 | #include "parse-events.h" | 22 | #include "parse-events.h" |
23 | 23 | ||
24 | #include "thread.h" | 24 | #include "thread.h" |
25 | #include "sort.h" | ||
26 | 25 | ||
27 | extern regex_t parent_regex; | 26 | extern regex_t parent_regex; |
28 | extern const char *sort_order; | 27 | extern const char *sort_order; |
@@ -84,7 +83,9 @@ struct hist_entry { | |||
84 | struct he_stat stat; | 83 | struct he_stat stat; |
85 | struct map_symbol ms; | 84 | struct map_symbol ms; |
86 | struct thread *thread; | 85 | struct thread *thread; |
86 | struct comm *comm; | ||
87 | u64 ip; | 87 | u64 ip; |
88 | u64 transaction; | ||
88 | s32 cpu; | 89 | s32 cpu; |
89 | 90 | ||
90 | struct hist_entry_diff diff; | 91 | struct hist_entry_diff diff; |
@@ -145,6 +146,7 @@ enum sort_type { | |||
145 | SORT_SRCLINE, | 146 | SORT_SRCLINE, |
146 | SORT_LOCAL_WEIGHT, | 147 | SORT_LOCAL_WEIGHT, |
147 | SORT_GLOBAL_WEIGHT, | 148 | SORT_GLOBAL_WEIGHT, |
149 | SORT_TRANSACTION, | ||
148 | 150 | ||
149 | /* branch stack specific sort keys */ | 151 | /* branch stack specific sort keys */ |
150 | __SORT_BRANCH_STACK, | 152 | __SORT_BRANCH_STACK, |
@@ -153,6 +155,8 @@ enum sort_type { | |||
153 | SORT_SYM_FROM, | 155 | SORT_SYM_FROM, |
154 | SORT_SYM_TO, | 156 | SORT_SYM_TO, |
155 | SORT_MISPREDICT, | 157 | SORT_MISPREDICT, |
158 | SORT_ABORT, | ||
159 | SORT_IN_TX, | ||
156 | 160 | ||
157 | /* memory mode specific sort keys */ | 161 | /* memory mode specific sort keys */ |
158 | __SORT_MEMORY_MODE, | 162 | __SORT_MEMORY_MODE, |
@@ -175,7 +179,7 @@ struct sort_entry { | |||
175 | 179 | ||
176 | int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); | 180 | int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); |
177 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); | 181 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); |
178 | int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, | 182 | int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, |
179 | unsigned int width); | 183 | unsigned int width); |
180 | u8 se_width_idx; | 184 | u8 se_width_idx; |
181 | bool elide; | 185 | bool elide; |
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 000000000000..d11aefbc4b8d --- /dev/null +++ b/tools/perf/util/srcline.c | |||
@@ -0,0 +1,265 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | |||
7 | #include "util/dso.h" | ||
8 | #include "util/util.h" | ||
9 | #include "util/debug.h" | ||
10 | |||
11 | #ifdef HAVE_LIBBFD_SUPPORT | ||
12 | |||
13 | /* | ||
14 | * Implement addr2line using libbfd. | ||
15 | */ | ||
16 | #define PACKAGE "perf" | ||
17 | #include <bfd.h> | ||
18 | |||
19 | struct a2l_data { | ||
20 | const char *input; | ||
21 | unsigned long addr; | ||
22 | |||
23 | bool found; | ||
24 | const char *filename; | ||
25 | const char *funcname; | ||
26 | unsigned line; | ||
27 | |||
28 | bfd *abfd; | ||
29 | asymbol **syms; | ||
30 | }; | ||
31 | |||
32 | static int bfd_error(const char *string) | ||
33 | { | ||
34 | const char *errmsg; | ||
35 | |||
36 | errmsg = bfd_errmsg(bfd_get_error()); | ||
37 | fflush(stdout); | ||
38 | |||
39 | if (string) | ||
40 | pr_debug("%s: %s\n", string, errmsg); | ||
41 | else | ||
42 | pr_debug("%s\n", errmsg); | ||
43 | |||
44 | return -1; | ||
45 | } | ||
46 | |||
47 | static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) | ||
48 | { | ||
49 | long storage; | ||
50 | long symcount; | ||
51 | asymbol **syms; | ||
52 | bfd_boolean dynamic = FALSE; | ||
53 | |||
54 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) | ||
55 | return bfd_error(bfd_get_filename(abfd)); | ||
56 | |||
57 | storage = bfd_get_symtab_upper_bound(abfd); | ||
58 | if (storage == 0L) { | ||
59 | storage = bfd_get_dynamic_symtab_upper_bound(abfd); | ||
60 | dynamic = TRUE; | ||
61 | } | ||
62 | if (storage < 0L) | ||
63 | return bfd_error(bfd_get_filename(abfd)); | ||
64 | |||
65 | syms = malloc(storage); | ||
66 | if (dynamic) | ||
67 | symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); | ||
68 | else | ||
69 | symcount = bfd_canonicalize_symtab(abfd, syms); | ||
70 | |||
71 | if (symcount < 0) { | ||
72 | free(syms); | ||
73 | return bfd_error(bfd_get_filename(abfd)); | ||
74 | } | ||
75 | |||
76 | a2l->syms = syms; | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static void find_address_in_section(bfd *abfd, asection *section, void *data) | ||
81 | { | ||
82 | bfd_vma pc, vma; | ||
83 | bfd_size_type size; | ||
84 | struct a2l_data *a2l = data; | ||
85 | |||
86 | if (a2l->found) | ||
87 | return; | ||
88 | |||
89 | if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) | ||
90 | return; | ||
91 | |||
92 | pc = a2l->addr; | ||
93 | vma = bfd_get_section_vma(abfd, section); | ||
94 | size = bfd_get_section_size(section); | ||
95 | |||
96 | if (pc < vma || pc >= vma + size) | ||
97 | return; | ||
98 | |||
99 | a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, | ||
100 | &a2l->filename, &a2l->funcname, | ||
101 | &a2l->line); | ||
102 | } | ||
103 | |||
104 | static struct a2l_data *addr2line_init(const char *path) | ||
105 | { | ||
106 | bfd *abfd; | ||
107 | struct a2l_data *a2l = NULL; | ||
108 | |||
109 | abfd = bfd_openr(path, NULL); | ||
110 | if (abfd == NULL) | ||
111 | return NULL; | ||
112 | |||
113 | if (!bfd_check_format(abfd, bfd_object)) | ||
114 | goto out; | ||
115 | |||
116 | a2l = zalloc(sizeof(*a2l)); | ||
117 | if (a2l == NULL) | ||
118 | goto out; | ||
119 | |||
120 | a2l->abfd = abfd; | ||
121 | a2l->input = strdup(path); | ||
122 | if (a2l->input == NULL) | ||
123 | goto out; | ||
124 | |||
125 | if (slurp_symtab(abfd, a2l)) | ||
126 | goto out; | ||
127 | |||
128 | return a2l; | ||
129 | |||
130 | out: | ||
131 | if (a2l) { | ||
132 | free((void *)a2l->input); | ||
133 | free(a2l); | ||
134 | } | ||
135 | bfd_close(abfd); | ||
136 | return NULL; | ||
137 | } | ||
138 | |||
139 | static void addr2line_cleanup(struct a2l_data *a2l) | ||
140 | { | ||
141 | if (a2l->abfd) | ||
142 | bfd_close(a2l->abfd); | ||
143 | free((void *)a2l->input); | ||
144 | free(a2l->syms); | ||
145 | free(a2l); | ||
146 | } | ||
147 | |||
148 | static int addr2line(const char *dso_name, unsigned long addr, | ||
149 | char **file, unsigned int *line) | ||
150 | { | ||
151 | int ret = 0; | ||
152 | struct a2l_data *a2l; | ||
153 | |||
154 | a2l = addr2line_init(dso_name); | ||
155 | if (a2l == NULL) { | ||
156 | pr_warning("addr2line_init failed for %s\n", dso_name); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | a2l->addr = addr; | ||
161 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); | ||
162 | |||
163 | if (a2l->found && a2l->filename) { | ||
164 | *file = strdup(a2l->filename); | ||
165 | *line = a2l->line; | ||
166 | |||
167 | if (*file) | ||
168 | ret = 1; | ||
169 | } | ||
170 | |||
171 | addr2line_cleanup(a2l); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | #else /* HAVE_LIBBFD_SUPPORT */ | ||
176 | |||
177 | static int addr2line(const char *dso_name, unsigned long addr, | ||
178 | char **file, unsigned int *line_nr) | ||
179 | { | ||
180 | FILE *fp; | ||
181 | char cmd[PATH_MAX]; | ||
182 | char *filename = NULL; | ||
183 | size_t len; | ||
184 | char *sep; | ||
185 | int ret = 0; | ||
186 | |||
187 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, | ||
188 | dso_name, addr); | ||
189 | |||
190 | fp = popen(cmd, "r"); | ||
191 | if (fp == NULL) { | ||
192 | pr_warning("popen failed for %s\n", dso_name); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | if (getline(&filename, &len, fp) < 0 || !len) { | ||
197 | pr_warning("addr2line has no output for %s\n", dso_name); | ||
198 | goto out; | ||
199 | } | ||
200 | |||
201 | sep = strchr(filename, '\n'); | ||
202 | if (sep) | ||
203 | *sep = '\0'; | ||
204 | |||
205 | if (!strcmp(filename, "??:0")) { | ||
206 | pr_debug("no debugging info in %s\n", dso_name); | ||
207 | free(filename); | ||
208 | goto out; | ||
209 | } | ||
210 | |||
211 | sep = strchr(filename, ':'); | ||
212 | if (sep) { | ||
213 | *sep++ = '\0'; | ||
214 | *file = filename; | ||
215 | *line_nr = strtoul(sep, NULL, 0); | ||
216 | ret = 1; | ||
217 | } | ||
218 | out: | ||
219 | pclose(fp); | ||
220 | return ret; | ||
221 | } | ||
222 | #endif /* HAVE_LIBBFD_SUPPORT */ | ||
223 | |||
224 | char *get_srcline(struct dso *dso, unsigned long addr) | ||
225 | { | ||
226 | char *file = NULL; | ||
227 | unsigned line = 0; | ||
228 | char *srcline; | ||
229 | char *dso_name = dso->long_name; | ||
230 | size_t size; | ||
231 | |||
232 | if (!dso->has_srcline) | ||
233 | return SRCLINE_UNKNOWN; | ||
234 | |||
235 | if (dso_name[0] == '[') | ||
236 | goto out; | ||
237 | |||
238 | if (!strncmp(dso_name, "/tmp/perf-", 10)) | ||
239 | goto out; | ||
240 | |||
241 | if (!addr2line(dso_name, addr, &file, &line)) | ||
242 | goto out; | ||
243 | |||
244 | /* just calculate actual length */ | ||
245 | size = snprintf(NULL, 0, "%s:%u", file, line) + 1; | ||
246 | |||
247 | srcline = malloc(size); | ||
248 | if (srcline) | ||
249 | snprintf(srcline, size, "%s:%u", file, line); | ||
250 | else | ||
251 | srcline = SRCLINE_UNKNOWN; | ||
252 | |||
253 | free(file); | ||
254 | return srcline; | ||
255 | |||
256 | out: | ||
257 | dso->has_srcline = 0; | ||
258 | return SRCLINE_UNKNOWN; | ||
259 | } | ||
260 | |||
261 | void free_srcline(char *srcline) | ||
262 | { | ||
263 | if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) | ||
264 | free(srcline); | ||
265 | } | ||
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 834c8ebfe38e..3edd0538161f 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c | |||
@@ -10,22 +10,22 @@ static const char *OP_not = "!"; /* Logical NOT */ | |||
10 | #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') | 10 | #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') |
11 | #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') | 11 | #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') |
12 | 12 | ||
13 | static void strfilter_node__delete(struct strfilter_node *self) | 13 | static void strfilter_node__delete(struct strfilter_node *node) |
14 | { | 14 | { |
15 | if (self) { | 15 | if (node) { |
16 | if (self->p && !is_operator(*self->p)) | 16 | if (node->p && !is_operator(*node->p)) |
17 | free((char *)self->p); | 17 | free((char *)node->p); |
18 | strfilter_node__delete(self->l); | 18 | strfilter_node__delete(node->l); |
19 | strfilter_node__delete(self->r); | 19 | strfilter_node__delete(node->r); |
20 | free(self); | 20 | free(node); |
21 | } | 21 | } |
22 | } | 22 | } |
23 | 23 | ||
24 | void strfilter__delete(struct strfilter *self) | 24 | void strfilter__delete(struct strfilter *filter) |
25 | { | 25 | { |
26 | if (self) { | 26 | if (filter) { |
27 | strfilter_node__delete(self->root); | 27 | strfilter_node__delete(filter->root); |
28 | free(self); | 28 | free(filter); |
29 | } | 29 | } |
30 | } | 30 | } |
31 | 31 | ||
@@ -62,15 +62,15 @@ static struct strfilter_node *strfilter_node__alloc(const char *op, | |||
62 | struct strfilter_node *l, | 62 | struct strfilter_node *l, |
63 | struct strfilter_node *r) | 63 | struct strfilter_node *r) |
64 | { | 64 | { |
65 | struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); | 65 | struct strfilter_node *node = zalloc(sizeof(*node)); |
66 | 66 | ||
67 | if (ret) { | 67 | if (node) { |
68 | ret->p = op; | 68 | node->p = op; |
69 | ret->l = l; | 69 | node->l = l; |
70 | ret->r = r; | 70 | node->r = r; |
71 | } | 71 | } |
72 | 72 | ||
73 | return ret; | 73 | return node; |
74 | } | 74 | } |
75 | 75 | ||
76 | static struct strfilter_node *strfilter_node__new(const char *s, | 76 | static struct strfilter_node *strfilter_node__new(const char *s, |
@@ -154,46 +154,46 @@ error: | |||
154 | */ | 154 | */ |
155 | struct strfilter *strfilter__new(const char *rules, const char **err) | 155 | struct strfilter *strfilter__new(const char *rules, const char **err) |
156 | { | 156 | { |
157 | struct strfilter *ret = zalloc(sizeof(struct strfilter)); | 157 | struct strfilter *filter = zalloc(sizeof(*filter)); |
158 | const char *ep = NULL; | 158 | const char *ep = NULL; |
159 | 159 | ||
160 | if (ret) | 160 | if (filter) |
161 | ret->root = strfilter_node__new(rules, &ep); | 161 | filter->root = strfilter_node__new(rules, &ep); |
162 | 162 | ||
163 | if (!ret || !ret->root || *ep != '\0') { | 163 | if (!filter || !filter->root || *ep != '\0') { |
164 | if (err) | 164 | if (err) |
165 | *err = ep; | 165 | *err = ep; |
166 | strfilter__delete(ret); | 166 | strfilter__delete(filter); |
167 | ret = NULL; | 167 | filter = NULL; |
168 | } | 168 | } |
169 | 169 | ||
170 | return ret; | 170 | return filter; |
171 | } | 171 | } |
172 | 172 | ||
173 | static bool strfilter_node__compare(struct strfilter_node *self, | 173 | static bool strfilter_node__compare(struct strfilter_node *node, |
174 | const char *str) | 174 | const char *str) |
175 | { | 175 | { |
176 | if (!self || !self->p) | 176 | if (!node || !node->p) |
177 | return false; | 177 | return false; |
178 | 178 | ||
179 | switch (*self->p) { | 179 | switch (*node->p) { |
180 | case '|': /* OR */ | 180 | case '|': /* OR */ |
181 | return strfilter_node__compare(self->l, str) || | 181 | return strfilter_node__compare(node->l, str) || |
182 | strfilter_node__compare(self->r, str); | 182 | strfilter_node__compare(node->r, str); |
183 | case '&': /* AND */ | 183 | case '&': /* AND */ |
184 | return strfilter_node__compare(self->l, str) && | 184 | return strfilter_node__compare(node->l, str) && |
185 | strfilter_node__compare(self->r, str); | 185 | strfilter_node__compare(node->r, str); |
186 | case '!': /* NOT */ | 186 | case '!': /* NOT */ |
187 | return !strfilter_node__compare(self->r, str); | 187 | return !strfilter_node__compare(node->r, str); |
188 | default: | 188 | default: |
189 | return strglobmatch(str, self->p); | 189 | return strglobmatch(str, node->p); |
190 | } | 190 | } |
191 | } | 191 | } |
192 | 192 | ||
193 | /* Return true if STR matches the filter rules */ | 193 | /* Return true if STR matches the filter rules */ |
194 | bool strfilter__compare(struct strfilter *self, const char *str) | 194 | bool strfilter__compare(struct strfilter *filter, const char *str) |
195 | { | 195 | { |
196 | if (!self) | 196 | if (!filter) |
197 | return false; | 197 | return false; |
198 | return strfilter_node__compare(self->root, str); | 198 | return strfilter_node__compare(filter->root, str); |
199 | } | 199 | } |
diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h index 00f58a7506de..fe611f3c9e39 100644 --- a/tools/perf/util/strfilter.h +++ b/tools/perf/util/strfilter.h | |||
@@ -30,19 +30,19 @@ struct strfilter *strfilter__new(const char *rules, const char **err); | |||
30 | 30 | ||
31 | /** | 31 | /** |
32 | * strfilter__compare - compare given string and a string filter | 32 | * strfilter__compare - compare given string and a string filter |
33 | * @self: String filter | 33 | * @filter: String filter |
34 | * @str: target string | 34 | * @str: target string |
35 | * | 35 | * |
36 | * Compare @str and @self. Return true if the str match the rule | 36 | * Compare @str and @filter. Return true if the str match the rule |
37 | */ | 37 | */ |
38 | bool strfilter__compare(struct strfilter *self, const char *str); | 38 | bool strfilter__compare(struct strfilter *filter, const char *str); |
39 | 39 | ||
40 | /** | 40 | /** |
41 | * strfilter__delete - delete a string filter | 41 | * strfilter__delete - delete a string filter |
42 | * @self: String filter to delete | 42 | * @filter: String filter to delete |
43 | * | 43 | * |
44 | * Delete @self. | 44 | * Delete @filter. |
45 | */ | 45 | */ |
46 | void strfilter__delete(struct strfilter *self); | 46 | void strfilter__delete(struct strfilter *filter); |
47 | 47 | ||
48 | #endif | 48 | #endif |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a9c829be5216..eed0b96302af 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -8,7 +8,7 @@ | |||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "debug.h" | 9 | #include "debug.h" |
10 | 10 | ||
11 | #ifndef HAVE_ELF_GETPHDRNUM | 11 | #ifndef HAVE_ELF_GETPHDRNUM_SUPPORT |
12 | static int elf_getphdrnum(Elf *elf, size_t *dst) | 12 | static int elf_getphdrnum(Elf *elf, size_t *dst) |
13 | { | 13 | { |
14 | GElf_Ehdr gehdr; | 14 | GElf_Ehdr gehdr; |
@@ -487,27 +487,27 @@ int filename__read_debuglink(const char *filename, char *debuglink, | |||
487 | 487 | ||
488 | ek = elf_kind(elf); | 488 | ek = elf_kind(elf); |
489 | if (ek != ELF_K_ELF) | 489 | if (ek != ELF_K_ELF) |
490 | goto out_close; | 490 | goto out_elf_end; |
491 | 491 | ||
492 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 492 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
493 | pr_err("%s: cannot get elf header.\n", __func__); | 493 | pr_err("%s: cannot get elf header.\n", __func__); |
494 | goto out_close; | 494 | goto out_elf_end; |
495 | } | 495 | } |
496 | 496 | ||
497 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 497 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
498 | ".gnu_debuglink", NULL); | 498 | ".gnu_debuglink", NULL); |
499 | if (sec == NULL) | 499 | if (sec == NULL) |
500 | goto out_close; | 500 | goto out_elf_end; |
501 | 501 | ||
502 | data = elf_getdata(sec, NULL); | 502 | data = elf_getdata(sec, NULL); |
503 | if (data == NULL) | 503 | if (data == NULL) |
504 | goto out_close; | 504 | goto out_elf_end; |
505 | 505 | ||
506 | /* the start of this section is a zero-terminated string */ | 506 | /* the start of this section is a zero-terminated string */ |
507 | strncpy(debuglink, data->d_buf, size); | 507 | strncpy(debuglink, data->d_buf, size); |
508 | 508 | ||
509 | out_elf_end: | ||
509 | elf_end(elf); | 510 | elf_end(elf); |
510 | |||
511 | out_close: | 511 | out_close: |
512 | close(fd); | 512 | close(fd); |
513 | out: | 513 | out: |
@@ -1018,6 +1018,601 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | |||
1018 | return err; | 1018 | return err; |
1019 | } | 1019 | } |
1020 | 1020 | ||
1021 | static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len) | ||
1022 | { | ||
1023 | ssize_t r; | ||
1024 | size_t n; | ||
1025 | int err = -1; | ||
1026 | char *buf = malloc(page_size); | ||
1027 | |||
1028 | if (buf == NULL) | ||
1029 | return -1; | ||
1030 | |||
1031 | if (lseek(to, to_offs, SEEK_SET) != to_offs) | ||
1032 | goto out; | ||
1033 | |||
1034 | if (lseek(from, from_offs, SEEK_SET) != from_offs) | ||
1035 | goto out; | ||
1036 | |||
1037 | while (len) { | ||
1038 | n = page_size; | ||
1039 | if (len < n) | ||
1040 | n = len; | ||
1041 | /* Use read because mmap won't work on proc files */ | ||
1042 | r = read(from, buf, n); | ||
1043 | if (r < 0) | ||
1044 | goto out; | ||
1045 | if (!r) | ||
1046 | break; | ||
1047 | n = r; | ||
1048 | r = write(to, buf, n); | ||
1049 | if (r < 0) | ||
1050 | goto out; | ||
1051 | if ((size_t)r != n) | ||
1052 | goto out; | ||
1053 | len -= n; | ||
1054 | } | ||
1055 | |||
1056 | err = 0; | ||
1057 | out: | ||
1058 | free(buf); | ||
1059 | return err; | ||
1060 | } | ||
1061 | |||
1062 | struct kcore { | ||
1063 | int fd; | ||
1064 | int elfclass; | ||
1065 | Elf *elf; | ||
1066 | GElf_Ehdr ehdr; | ||
1067 | }; | ||
1068 | |||
1069 | static int kcore__open(struct kcore *kcore, const char *filename) | ||
1070 | { | ||
1071 | GElf_Ehdr *ehdr; | ||
1072 | |||
1073 | kcore->fd = open(filename, O_RDONLY); | ||
1074 | if (kcore->fd == -1) | ||
1075 | return -1; | ||
1076 | |||
1077 | kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL); | ||
1078 | if (!kcore->elf) | ||
1079 | goto out_close; | ||
1080 | |||
1081 | kcore->elfclass = gelf_getclass(kcore->elf); | ||
1082 | if (kcore->elfclass == ELFCLASSNONE) | ||
1083 | goto out_end; | ||
1084 | |||
1085 | ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); | ||
1086 | if (!ehdr) | ||
1087 | goto out_end; | ||
1088 | |||
1089 | return 0; | ||
1090 | |||
1091 | out_end: | ||
1092 | elf_end(kcore->elf); | ||
1093 | out_close: | ||
1094 | close(kcore->fd); | ||
1095 | return -1; | ||
1096 | } | ||
1097 | |||
1098 | static int kcore__init(struct kcore *kcore, char *filename, int elfclass, | ||
1099 | bool temp) | ||
1100 | { | ||
1101 | GElf_Ehdr *ehdr; | ||
1102 | |||
1103 | kcore->elfclass = elfclass; | ||
1104 | |||
1105 | if (temp) | ||
1106 | kcore->fd = mkstemp(filename); | ||
1107 | else | ||
1108 | kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); | ||
1109 | if (kcore->fd == -1) | ||
1110 | return -1; | ||
1111 | |||
1112 | kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL); | ||
1113 | if (!kcore->elf) | ||
1114 | goto out_close; | ||
1115 | |||
1116 | if (!gelf_newehdr(kcore->elf, elfclass)) | ||
1117 | goto out_end; | ||
1118 | |||
1119 | ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); | ||
1120 | if (!ehdr) | ||
1121 | goto out_end; | ||
1122 | |||
1123 | return 0; | ||
1124 | |||
1125 | out_end: | ||
1126 | elf_end(kcore->elf); | ||
1127 | out_close: | ||
1128 | close(kcore->fd); | ||
1129 | unlink(filename); | ||
1130 | return -1; | ||
1131 | } | ||
1132 | |||
1133 | static void kcore__close(struct kcore *kcore) | ||
1134 | { | ||
1135 | elf_end(kcore->elf); | ||
1136 | close(kcore->fd); | ||
1137 | } | ||
1138 | |||
1139 | static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) | ||
1140 | { | ||
1141 | GElf_Ehdr *ehdr = &to->ehdr; | ||
1142 | GElf_Ehdr *kehdr = &from->ehdr; | ||
1143 | |||
1144 | memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT); | ||
1145 | ehdr->e_type = kehdr->e_type; | ||
1146 | ehdr->e_machine = kehdr->e_machine; | ||
1147 | ehdr->e_version = kehdr->e_version; | ||
1148 | ehdr->e_entry = 0; | ||
1149 | ehdr->e_shoff = 0; | ||
1150 | ehdr->e_flags = kehdr->e_flags; | ||
1151 | ehdr->e_phnum = count; | ||
1152 | ehdr->e_shentsize = 0; | ||
1153 | ehdr->e_shnum = 0; | ||
1154 | ehdr->e_shstrndx = 0; | ||
1155 | |||
1156 | if (from->elfclass == ELFCLASS32) { | ||
1157 | ehdr->e_phoff = sizeof(Elf32_Ehdr); | ||
1158 | ehdr->e_ehsize = sizeof(Elf32_Ehdr); | ||
1159 | ehdr->e_phentsize = sizeof(Elf32_Phdr); | ||
1160 | } else { | ||
1161 | ehdr->e_phoff = sizeof(Elf64_Ehdr); | ||
1162 | ehdr->e_ehsize = sizeof(Elf64_Ehdr); | ||
1163 | ehdr->e_phentsize = sizeof(Elf64_Phdr); | ||
1164 | } | ||
1165 | |||
1166 | if (!gelf_update_ehdr(to->elf, ehdr)) | ||
1167 | return -1; | ||
1168 | |||
1169 | if (!gelf_newphdr(to->elf, count)) | ||
1170 | return -1; | ||
1171 | |||
1172 | return 0; | ||
1173 | } | ||
1174 | |||
1175 | static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, | ||
1176 | u64 addr, u64 len) | ||
1177 | { | ||
1178 | GElf_Phdr gphdr; | ||
1179 | GElf_Phdr *phdr; | ||
1180 | |||
1181 | phdr = gelf_getphdr(kcore->elf, idx, &gphdr); | ||
1182 | if (!phdr) | ||
1183 | return -1; | ||
1184 | |||
1185 | phdr->p_type = PT_LOAD; | ||
1186 | phdr->p_flags = PF_R | PF_W | PF_X; | ||
1187 | phdr->p_offset = offset; | ||
1188 | phdr->p_vaddr = addr; | ||
1189 | phdr->p_paddr = 0; | ||
1190 | phdr->p_filesz = len; | ||
1191 | phdr->p_memsz = len; | ||
1192 | phdr->p_align = page_size; | ||
1193 | |||
1194 | if (!gelf_update_phdr(kcore->elf, idx, phdr)) | ||
1195 | return -1; | ||
1196 | |||
1197 | return 0; | ||
1198 | } | ||
1199 | |||
1200 | static off_t kcore__write(struct kcore *kcore) | ||
1201 | { | ||
1202 | return elf_update(kcore->elf, ELF_C_WRITE); | ||
1203 | } | ||
1204 | |||
1205 | struct phdr_data { | ||
1206 | off_t offset; | ||
1207 | u64 addr; | ||
1208 | u64 len; | ||
1209 | }; | ||
1210 | |||
1211 | struct kcore_copy_info { | ||
1212 | u64 stext; | ||
1213 | u64 etext; | ||
1214 | u64 first_symbol; | ||
1215 | u64 last_symbol; | ||
1216 | u64 first_module; | ||
1217 | u64 last_module_symbol; | ||
1218 | struct phdr_data kernel_map; | ||
1219 | struct phdr_data modules_map; | ||
1220 | }; | ||
1221 | |||
1222 | static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, | ||
1223 | u64 start) | ||
1224 | { | ||
1225 | struct kcore_copy_info *kci = arg; | ||
1226 | |||
1227 | if (!symbol_type__is_a(type, MAP__FUNCTION)) | ||
1228 | return 0; | ||
1229 | |||
1230 | if (strchr(name, '[')) { | ||
1231 | if (start > kci->last_module_symbol) | ||
1232 | kci->last_module_symbol = start; | ||
1233 | return 0; | ||
1234 | } | ||
1235 | |||
1236 | if (!kci->first_symbol || start < kci->first_symbol) | ||
1237 | kci->first_symbol = start; | ||
1238 | |||
1239 | if (!kci->last_symbol || start > kci->last_symbol) | ||
1240 | kci->last_symbol = start; | ||
1241 | |||
1242 | if (!strcmp(name, "_stext")) { | ||
1243 | kci->stext = start; | ||
1244 | return 0; | ||
1245 | } | ||
1246 | |||
1247 | if (!strcmp(name, "_etext")) { | ||
1248 | kci->etext = start; | ||
1249 | return 0; | ||
1250 | } | ||
1251 | |||
1252 | return 0; | ||
1253 | } | ||
1254 | |||
1255 | static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci, | ||
1256 | const char *dir) | ||
1257 | { | ||
1258 | char kallsyms_filename[PATH_MAX]; | ||
1259 | |||
1260 | scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir); | ||
1261 | |||
1262 | if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms")) | ||
1263 | return -1; | ||
1264 | |||
1265 | if (kallsyms__parse(kallsyms_filename, kci, | ||
1266 | kcore_copy__process_kallsyms) < 0) | ||
1267 | return -1; | ||
1268 | |||
1269 | return 0; | ||
1270 | } | ||
1271 | |||
1272 | static int kcore_copy__process_modules(void *arg, | ||
1273 | const char *name __maybe_unused, | ||
1274 | u64 start) | ||
1275 | { | ||
1276 | struct kcore_copy_info *kci = arg; | ||
1277 | |||
1278 | if (!kci->first_module || start < kci->first_module) | ||
1279 | kci->first_module = start; | ||
1280 | |||
1281 | return 0; | ||
1282 | } | ||
1283 | |||
1284 | static int kcore_copy__parse_modules(struct kcore_copy_info *kci, | ||
1285 | const char *dir) | ||
1286 | { | ||
1287 | char modules_filename[PATH_MAX]; | ||
1288 | |||
1289 | scnprintf(modules_filename, PATH_MAX, "%s/modules", dir); | ||
1290 | |||
1291 | if (symbol__restricted_filename(modules_filename, "/proc/modules")) | ||
1292 | return -1; | ||
1293 | |||
1294 | if (modules__parse(modules_filename, kci, | ||
1295 | kcore_copy__process_modules) < 0) | ||
1296 | return -1; | ||
1297 | |||
1298 | return 0; | ||
1299 | } | ||
1300 | |||
1301 | static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff, | ||
1302 | u64 s, u64 e) | ||
1303 | { | ||
1304 | if (p->addr || s < start || s >= end) | ||
1305 | return; | ||
1306 | |||
1307 | p->addr = s; | ||
1308 | p->offset = (s - start) + pgoff; | ||
1309 | p->len = e < end ? e - s : end - s; | ||
1310 | } | ||
1311 | |||
1312 | static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data) | ||
1313 | { | ||
1314 | struct kcore_copy_info *kci = data; | ||
1315 | u64 end = start + len; | ||
1316 | |||
1317 | kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext, | ||
1318 | kci->etext); | ||
1319 | |||
1320 | kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module, | ||
1321 | kci->last_module_symbol); | ||
1322 | |||
1323 | return 0; | ||
1324 | } | ||
1325 | |||
1326 | static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf) | ||
1327 | { | ||
1328 | if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0) | ||
1329 | return -1; | ||
1330 | |||
1331 | return 0; | ||
1332 | } | ||
1333 | |||
1334 | static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, | ||
1335 | Elf *elf) | ||
1336 | { | ||
1337 | if (kcore_copy__parse_kallsyms(kci, dir)) | ||
1338 | return -1; | ||
1339 | |||
1340 | if (kcore_copy__parse_modules(kci, dir)) | ||
1341 | return -1; | ||
1342 | |||
1343 | if (kci->stext) | ||
1344 | kci->stext = round_down(kci->stext, page_size); | ||
1345 | else | ||
1346 | kci->stext = round_down(kci->first_symbol, page_size); | ||
1347 | |||
1348 | if (kci->etext) { | ||
1349 | kci->etext = round_up(kci->etext, page_size); | ||
1350 | } else if (kci->last_symbol) { | ||
1351 | kci->etext = round_up(kci->last_symbol, page_size); | ||
1352 | kci->etext += page_size; | ||
1353 | } | ||
1354 | |||
1355 | kci->first_module = round_down(kci->first_module, page_size); | ||
1356 | |||
1357 | if (kci->last_module_symbol) { | ||
1358 | kci->last_module_symbol = round_up(kci->last_module_symbol, | ||
1359 | page_size); | ||
1360 | kci->last_module_symbol += page_size; | ||
1361 | } | ||
1362 | |||
1363 | if (!kci->stext || !kci->etext) | ||
1364 | return -1; | ||
1365 | |||
1366 | if (kci->first_module && !kci->last_module_symbol) | ||
1367 | return -1; | ||
1368 | |||
1369 | return kcore_copy__read_maps(kci, elf); | ||
1370 | } | ||
1371 | |||
1372 | static int kcore_copy__copy_file(const char *from_dir, const char *to_dir, | ||
1373 | const char *name) | ||
1374 | { | ||
1375 | char from_filename[PATH_MAX]; | ||
1376 | char to_filename[PATH_MAX]; | ||
1377 | |||
1378 | scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); | ||
1379 | scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); | ||
1380 | |||
1381 | return copyfile_mode(from_filename, to_filename, 0400); | ||
1382 | } | ||
1383 | |||
1384 | static int kcore_copy__unlink(const char *dir, const char *name) | ||
1385 | { | ||
1386 | char filename[PATH_MAX]; | ||
1387 | |||
1388 | scnprintf(filename, PATH_MAX, "%s/%s", dir, name); | ||
1389 | |||
1390 | return unlink(filename); | ||
1391 | } | ||
1392 | |||
1393 | static int kcore_copy__compare_fds(int from, int to) | ||
1394 | { | ||
1395 | char *buf_from; | ||
1396 | char *buf_to; | ||
1397 | ssize_t ret; | ||
1398 | size_t len; | ||
1399 | int err = -1; | ||
1400 | |||
1401 | buf_from = malloc(page_size); | ||
1402 | buf_to = malloc(page_size); | ||
1403 | if (!buf_from || !buf_to) | ||
1404 | goto out; | ||
1405 | |||
1406 | while (1) { | ||
1407 | /* Use read because mmap won't work on proc files */ | ||
1408 | ret = read(from, buf_from, page_size); | ||
1409 | if (ret < 0) | ||
1410 | goto out; | ||
1411 | |||
1412 | if (!ret) | ||
1413 | break; | ||
1414 | |||
1415 | len = ret; | ||
1416 | |||
1417 | if (readn(to, buf_to, len) != (int)len) | ||
1418 | goto out; | ||
1419 | |||
1420 | if (memcmp(buf_from, buf_to, len)) | ||
1421 | goto out; | ||
1422 | } | ||
1423 | |||
1424 | err = 0; | ||
1425 | out: | ||
1426 | free(buf_to); | ||
1427 | free(buf_from); | ||
1428 | return err; | ||
1429 | } | ||
1430 | |||
1431 | static int kcore_copy__compare_files(const char *from_filename, | ||
1432 | const char *to_filename) | ||
1433 | { | ||
1434 | int from, to, err = -1; | ||
1435 | |||
1436 | from = open(from_filename, O_RDONLY); | ||
1437 | if (from < 0) | ||
1438 | return -1; | ||
1439 | |||
1440 | to = open(to_filename, O_RDONLY); | ||
1441 | if (to < 0) | ||
1442 | goto out_close_from; | ||
1443 | |||
1444 | err = kcore_copy__compare_fds(from, to); | ||
1445 | |||
1446 | close(to); | ||
1447 | out_close_from: | ||
1448 | close(from); | ||
1449 | return err; | ||
1450 | } | ||
1451 | |||
1452 | static int kcore_copy__compare_file(const char *from_dir, const char *to_dir, | ||
1453 | const char *name) | ||
1454 | { | ||
1455 | char from_filename[PATH_MAX]; | ||
1456 | char to_filename[PATH_MAX]; | ||
1457 | |||
1458 | scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); | ||
1459 | scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); | ||
1460 | |||
1461 | return kcore_copy__compare_files(from_filename, to_filename); | ||
1462 | } | ||
1463 | |||
1464 | /** | ||
1465 | * kcore_copy - copy kallsyms, modules and kcore from one directory to another. | ||
1466 | * @from_dir: from directory | ||
1467 | * @to_dir: to directory | ||
1468 | * | ||
1469 | * This function copies kallsyms, modules and kcore files from one directory to | ||
1470 | * another. kallsyms and modules are copied entirely. Only code segments are | ||
1471 | * copied from kcore. It is assumed that two segments suffice: one for the | ||
1472 | * kernel proper and one for all the modules. The code segments are determined | ||
1473 | * from kallsyms and modules files. The kernel map starts at _stext or the | ||
1474 | * lowest function symbol, and ends at _etext or the highest function symbol. | ||
1475 | * The module map starts at the lowest module address and ends at the highest | ||
1476 | * module symbol. Start addresses are rounded down to the nearest page. End | ||
1477 | * addresses are rounded up to the nearest page. An extra page is added to the | ||
1478 | * highest kernel symbol and highest module symbol to, hopefully, encompass that | ||
1479 | * symbol too. Because it contains only code sections, the resulting kcore is | ||
1480 | * unusual. One significant peculiarity is that the mapping (start -> pgoff) | ||
1481 | * is not the same for the kernel map and the modules map. That happens because | ||
1482 | * the data is copied adjacently whereas the original kcore has gaps. Finally, | ||
1483 | * kallsyms and modules files are compared with their copies to check that | ||
1484 | * modules have not been loaded or unloaded while the copies were taking place. | ||
1485 | * | ||
1486 | * Return: %0 on success, %-1 on failure. | ||
1487 | */ | ||
1488 | int kcore_copy(const char *from_dir, const char *to_dir) | ||
1489 | { | ||
1490 | struct kcore kcore; | ||
1491 | struct kcore extract; | ||
1492 | size_t count = 2; | ||
1493 | int idx = 0, err = -1; | ||
1494 | off_t offset = page_size, sz, modules_offset = 0; | ||
1495 | struct kcore_copy_info kci = { .stext = 0, }; | ||
1496 | char kcore_filename[PATH_MAX]; | ||
1497 | char extract_filename[PATH_MAX]; | ||
1498 | |||
1499 | if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms")) | ||
1500 | return -1; | ||
1501 | |||
1502 | if (kcore_copy__copy_file(from_dir, to_dir, "modules")) | ||
1503 | goto out_unlink_kallsyms; | ||
1504 | |||
1505 | scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir); | ||
1506 | scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir); | ||
1507 | |||
1508 | if (kcore__open(&kcore, kcore_filename)) | ||
1509 | goto out_unlink_modules; | ||
1510 | |||
1511 | if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf)) | ||
1512 | goto out_kcore_close; | ||
1513 | |||
1514 | if (kcore__init(&extract, extract_filename, kcore.elfclass, false)) | ||
1515 | goto out_kcore_close; | ||
1516 | |||
1517 | if (!kci.modules_map.addr) | ||
1518 | count -= 1; | ||
1519 | |||
1520 | if (kcore__copy_hdr(&kcore, &extract, count)) | ||
1521 | goto out_extract_close; | ||
1522 | |||
1523 | if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr, | ||
1524 | kci.kernel_map.len)) | ||
1525 | goto out_extract_close; | ||
1526 | |||
1527 | if (kci.modules_map.addr) { | ||
1528 | modules_offset = offset + kci.kernel_map.len; | ||
1529 | if (kcore__add_phdr(&extract, idx, modules_offset, | ||
1530 | kci.modules_map.addr, kci.modules_map.len)) | ||
1531 | goto out_extract_close; | ||
1532 | } | ||
1533 | |||
1534 | sz = kcore__write(&extract); | ||
1535 | if (sz < 0 || sz > offset) | ||
1536 | goto out_extract_close; | ||
1537 | |||
1538 | if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset, | ||
1539 | kci.kernel_map.len)) | ||
1540 | goto out_extract_close; | ||
1541 | |||
1542 | if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset, | ||
1543 | extract.fd, modules_offset, | ||
1544 | kci.modules_map.len)) | ||
1545 | goto out_extract_close; | ||
1546 | |||
1547 | if (kcore_copy__compare_file(from_dir, to_dir, "modules")) | ||
1548 | goto out_extract_close; | ||
1549 | |||
1550 | if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms")) | ||
1551 | goto out_extract_close; | ||
1552 | |||
1553 | err = 0; | ||
1554 | |||
1555 | out_extract_close: | ||
1556 | kcore__close(&extract); | ||
1557 | if (err) | ||
1558 | unlink(extract_filename); | ||
1559 | out_kcore_close: | ||
1560 | kcore__close(&kcore); | ||
1561 | out_unlink_modules: | ||
1562 | if (err) | ||
1563 | kcore_copy__unlink(to_dir, "modules"); | ||
1564 | out_unlink_kallsyms: | ||
1565 | if (err) | ||
1566 | kcore_copy__unlink(to_dir, "kallsyms"); | ||
1567 | |||
1568 | return err; | ||
1569 | } | ||
1570 | |||
1571 | int kcore_extract__create(struct kcore_extract *kce) | ||
1572 | { | ||
1573 | struct kcore kcore; | ||
1574 | struct kcore extract; | ||
1575 | size_t count = 1; | ||
1576 | int idx = 0, err = -1; | ||
1577 | off_t offset = page_size, sz; | ||
1578 | |||
1579 | if (kcore__open(&kcore, kce->kcore_filename)) | ||
1580 | return -1; | ||
1581 | |||
1582 | strcpy(kce->extract_filename, PERF_KCORE_EXTRACT); | ||
1583 | if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true)) | ||
1584 | goto out_kcore_close; | ||
1585 | |||
1586 | if (kcore__copy_hdr(&kcore, &extract, count)) | ||
1587 | goto out_extract_close; | ||
1588 | |||
1589 | if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len)) | ||
1590 | goto out_extract_close; | ||
1591 | |||
1592 | sz = kcore__write(&extract); | ||
1593 | if (sz < 0 || sz > offset) | ||
1594 | goto out_extract_close; | ||
1595 | |||
1596 | if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len)) | ||
1597 | goto out_extract_close; | ||
1598 | |||
1599 | err = 0; | ||
1600 | |||
1601 | out_extract_close: | ||
1602 | kcore__close(&extract); | ||
1603 | if (err) | ||
1604 | unlink(kce->extract_filename); | ||
1605 | out_kcore_close: | ||
1606 | kcore__close(&kcore); | ||
1607 | |||
1608 | return err; | ||
1609 | } | ||
1610 | |||
1611 | void kcore_extract__delete(struct kcore_extract *kce) | ||
1612 | { | ||
1613 | unlink(kce->extract_filename); | ||
1614 | } | ||
1615 | |||
1021 | void symbol__elf_init(void) | 1616 | void symbol__elf_init(void) |
1022 | { | 1617 | { |
1023 | elf_version(EV_CURRENT); | 1618 | elf_version(EV_CURRENT); |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 3a802c300fc5..2d2dd0532b5a 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -308,6 +308,21 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused, | |||
308 | return -1; | 308 | return -1; |
309 | } | 309 | } |
310 | 310 | ||
311 | int kcore_extract__create(struct kcore_extract *kce __maybe_unused) | ||
312 | { | ||
313 | return -1; | ||
314 | } | ||
315 | |||
316 | void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) | ||
317 | { | ||
318 | } | ||
319 | |||
320 | int kcore_copy(const char *from_dir __maybe_unused, | ||
321 | const char *to_dir __maybe_unused) | ||
322 | { | ||
323 | return -1; | ||
324 | } | ||
325 | |||
311 | void symbol__elf_init(void) | 326 | void symbol__elf_init(void) |
312 | { | 327 | { |
313 | } | 328 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7eb0362f4ffd..c0c36965fff0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -51,6 +51,7 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
51 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 51 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
52 | DSO_BINARY_TYPE__GUEST_KMODULE, | 52 | DSO_BINARY_TYPE__GUEST_KMODULE, |
53 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 53 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
54 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | ||
54 | DSO_BINARY_TYPE__NOT_FOUND, | 55 | DSO_BINARY_TYPE__NOT_FOUND, |
55 | }; | 56 | }; |
56 | 57 | ||
@@ -159,10 +160,12 @@ again: | |||
159 | 160 | ||
160 | if (choose_best_symbol(curr, next) == SYMBOL_A) { | 161 | if (choose_best_symbol(curr, next) == SYMBOL_A) { |
161 | rb_erase(&next->rb_node, symbols); | 162 | rb_erase(&next->rb_node, symbols); |
163 | symbol__delete(next); | ||
162 | goto again; | 164 | goto again; |
163 | } else { | 165 | } else { |
164 | nd = rb_next(&curr->rb_node); | 166 | nd = rb_next(&curr->rb_node); |
165 | rb_erase(&curr->rb_node, symbols); | 167 | rb_erase(&curr->rb_node, symbols); |
168 | symbol__delete(curr); | ||
166 | } | 169 | } |
167 | } | 170 | } |
168 | } | 171 | } |
@@ -499,6 +502,64 @@ out_failure: | |||
499 | return -1; | 502 | return -1; |
500 | } | 503 | } |
501 | 504 | ||
505 | int modules__parse(const char *filename, void *arg, | ||
506 | int (*process_module)(void *arg, const char *name, | ||
507 | u64 start)) | ||
508 | { | ||
509 | char *line = NULL; | ||
510 | size_t n; | ||
511 | FILE *file; | ||
512 | int err = 0; | ||
513 | |||
514 | file = fopen(filename, "r"); | ||
515 | if (file == NULL) | ||
516 | return -1; | ||
517 | |||
518 | while (1) { | ||
519 | char name[PATH_MAX]; | ||
520 | u64 start; | ||
521 | char *sep; | ||
522 | ssize_t line_len; | ||
523 | |||
524 | line_len = getline(&line, &n, file); | ||
525 | if (line_len < 0) { | ||
526 | if (feof(file)) | ||
527 | break; | ||
528 | err = -1; | ||
529 | goto out; | ||
530 | } | ||
531 | |||
532 | if (!line) { | ||
533 | err = -1; | ||
534 | goto out; | ||
535 | } | ||
536 | |||
537 | line[--line_len] = '\0'; /* \n */ | ||
538 | |||
539 | sep = strrchr(line, 'x'); | ||
540 | if (sep == NULL) | ||
541 | continue; | ||
542 | |||
543 | hex2u64(sep + 1, &start); | ||
544 | |||
545 | sep = strchr(line, ' '); | ||
546 | if (sep == NULL) | ||
547 | continue; | ||
548 | |||
549 | *sep = '\0'; | ||
550 | |||
551 | scnprintf(name, sizeof(name), "[%s]", line); | ||
552 | |||
553 | err = process_module(arg, name, start); | ||
554 | if (err) | ||
555 | break; | ||
556 | } | ||
557 | out: | ||
558 | free(line); | ||
559 | fclose(file); | ||
560 | return err; | ||
561 | } | ||
562 | |||
502 | struct process_kallsyms_args { | 563 | struct process_kallsyms_args { |
503 | struct map *map; | 564 | struct map *map; |
504 | struct dso *dso; | 565 | struct dso *dso; |
@@ -739,51 +800,242 @@ bool symbol__restricted_filename(const char *filename, | |||
739 | return restricted; | 800 | return restricted; |
740 | } | 801 | } |
741 | 802 | ||
742 | struct kcore_mapfn_data { | 803 | struct module_info { |
743 | struct dso *dso; | 804 | struct rb_node rb_node; |
744 | enum map_type type; | 805 | char *name; |
745 | struct list_head maps; | 806 | u64 start; |
746 | }; | 807 | }; |
747 | 808 | ||
748 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | 809 | static void add_module(struct module_info *mi, struct rb_root *modules) |
749 | { | 810 | { |
750 | struct kcore_mapfn_data *md = data; | 811 | struct rb_node **p = &modules->rb_node; |
751 | struct map *map; | 812 | struct rb_node *parent = NULL; |
813 | struct module_info *m; | ||
752 | 814 | ||
753 | map = map__new2(start, md->dso, md->type); | 815 | while (*p != NULL) { |
754 | if (map == NULL) | 816 | parent = *p; |
817 | m = rb_entry(parent, struct module_info, rb_node); | ||
818 | if (strcmp(mi->name, m->name) < 0) | ||
819 | p = &(*p)->rb_left; | ||
820 | else | ||
821 | p = &(*p)->rb_right; | ||
822 | } | ||
823 | rb_link_node(&mi->rb_node, parent, p); | ||
824 | rb_insert_color(&mi->rb_node, modules); | ||
825 | } | ||
826 | |||
827 | static void delete_modules(struct rb_root *modules) | ||
828 | { | ||
829 | struct module_info *mi; | ||
830 | struct rb_node *next = rb_first(modules); | ||
831 | |||
832 | while (next) { | ||
833 | mi = rb_entry(next, struct module_info, rb_node); | ||
834 | next = rb_next(&mi->rb_node); | ||
835 | rb_erase(&mi->rb_node, modules); | ||
836 | free(mi->name); | ||
837 | free(mi); | ||
838 | } | ||
839 | } | ||
840 | |||
841 | static struct module_info *find_module(const char *name, | ||
842 | struct rb_root *modules) | ||
843 | { | ||
844 | struct rb_node *n = modules->rb_node; | ||
845 | |||
846 | while (n) { | ||
847 | struct module_info *m; | ||
848 | int cmp; | ||
849 | |||
850 | m = rb_entry(n, struct module_info, rb_node); | ||
851 | cmp = strcmp(name, m->name); | ||
852 | if (cmp < 0) | ||
853 | n = n->rb_left; | ||
854 | else if (cmp > 0) | ||
855 | n = n->rb_right; | ||
856 | else | ||
857 | return m; | ||
858 | } | ||
859 | |||
860 | return NULL; | ||
861 | } | ||
862 | |||
863 | static int __read_proc_modules(void *arg, const char *name, u64 start) | ||
864 | { | ||
865 | struct rb_root *modules = arg; | ||
866 | struct module_info *mi; | ||
867 | |||
868 | mi = zalloc(sizeof(struct module_info)); | ||
869 | if (!mi) | ||
755 | return -ENOMEM; | 870 | return -ENOMEM; |
756 | 871 | ||
757 | map->end = map->start + len; | 872 | mi->name = strdup(name); |
758 | map->pgoff = pgoff; | 873 | mi->start = start; |
759 | 874 | ||
760 | list_add(&map->node, &md->maps); | 875 | if (!mi->name) { |
876 | free(mi); | ||
877 | return -ENOMEM; | ||
878 | } | ||
879 | |||
880 | add_module(mi, modules); | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | static int read_proc_modules(const char *filename, struct rb_root *modules) | ||
886 | { | ||
887 | if (symbol__restricted_filename(filename, "/proc/modules")) | ||
888 | return -1; | ||
889 | |||
890 | if (modules__parse(filename, modules, __read_proc_modules)) { | ||
891 | delete_modules(modules); | ||
892 | return -1; | ||
893 | } | ||
761 | 894 | ||
762 | return 0; | 895 | return 0; |
763 | } | 896 | } |
764 | 897 | ||
898 | int compare_proc_modules(const char *from, const char *to) | ||
899 | { | ||
900 | struct rb_root from_modules = RB_ROOT; | ||
901 | struct rb_root to_modules = RB_ROOT; | ||
902 | struct rb_node *from_node, *to_node; | ||
903 | struct module_info *from_m, *to_m; | ||
904 | int ret = -1; | ||
905 | |||
906 | if (read_proc_modules(from, &from_modules)) | ||
907 | return -1; | ||
908 | |||
909 | if (read_proc_modules(to, &to_modules)) | ||
910 | goto out_delete_from; | ||
911 | |||
912 | from_node = rb_first(&from_modules); | ||
913 | to_node = rb_first(&to_modules); | ||
914 | while (from_node) { | ||
915 | if (!to_node) | ||
916 | break; | ||
917 | |||
918 | from_m = rb_entry(from_node, struct module_info, rb_node); | ||
919 | to_m = rb_entry(to_node, struct module_info, rb_node); | ||
920 | |||
921 | if (from_m->start != to_m->start || | ||
922 | strcmp(from_m->name, to_m->name)) | ||
923 | break; | ||
924 | |||
925 | from_node = rb_next(from_node); | ||
926 | to_node = rb_next(to_node); | ||
927 | } | ||
928 | |||
929 | if (!from_node && !to_node) | ||
930 | ret = 0; | ||
931 | |||
932 | delete_modules(&to_modules); | ||
933 | out_delete_from: | ||
934 | delete_modules(&from_modules); | ||
935 | |||
936 | return ret; | ||
937 | } | ||
938 | |||
939 | static int do_validate_kcore_modules(const char *filename, struct map *map, | ||
940 | struct map_groups *kmaps) | ||
941 | { | ||
942 | struct rb_root modules = RB_ROOT; | ||
943 | struct map *old_map; | ||
944 | int err; | ||
945 | |||
946 | err = read_proc_modules(filename, &modules); | ||
947 | if (err) | ||
948 | return err; | ||
949 | |||
950 | old_map = map_groups__first(kmaps, map->type); | ||
951 | while (old_map) { | ||
952 | struct map *next = map_groups__next(old_map); | ||
953 | struct module_info *mi; | ||
954 | |||
955 | if (old_map == map || old_map->start == map->start) { | ||
956 | /* The kernel map */ | ||
957 | old_map = next; | ||
958 | continue; | ||
959 | } | ||
960 | |||
961 | /* Module must be in memory at the same address */ | ||
962 | mi = find_module(old_map->dso->short_name, &modules); | ||
963 | if (!mi || mi->start != old_map->start) { | ||
964 | err = -EINVAL; | ||
965 | goto out; | ||
966 | } | ||
967 | |||
968 | old_map = next; | ||
969 | } | ||
970 | out: | ||
971 | delete_modules(&modules); | ||
972 | return err; | ||
973 | } | ||
974 | |||
765 | /* | 975 | /* |
766 | * If kallsyms is referenced by name then we look for kcore in the same | 976 | * If kallsyms is referenced by name then we look for filename in the same |
767 | * directory. | 977 | * directory. |
768 | */ | 978 | */ |
769 | static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, | 979 | static bool filename_from_kallsyms_filename(char *filename, |
770 | const char *kallsyms_filename) | 980 | const char *base_name, |
981 | const char *kallsyms_filename) | ||
771 | { | 982 | { |
772 | char *name; | 983 | char *name; |
773 | 984 | ||
774 | strcpy(kcore_filename, kallsyms_filename); | 985 | strcpy(filename, kallsyms_filename); |
775 | name = strrchr(kcore_filename, '/'); | 986 | name = strrchr(filename, '/'); |
776 | if (!name) | 987 | if (!name) |
777 | return false; | 988 | return false; |
778 | 989 | ||
779 | if (!strcmp(name, "/kallsyms")) { | 990 | name += 1; |
780 | strcpy(name, "/kcore"); | 991 | |
992 | if (!strcmp(name, "kallsyms")) { | ||
993 | strcpy(name, base_name); | ||
781 | return true; | 994 | return true; |
782 | } | 995 | } |
783 | 996 | ||
784 | return false; | 997 | return false; |
785 | } | 998 | } |
786 | 999 | ||
1000 | static int validate_kcore_modules(const char *kallsyms_filename, | ||
1001 | struct map *map) | ||
1002 | { | ||
1003 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
1004 | char modules_filename[PATH_MAX]; | ||
1005 | |||
1006 | if (!filename_from_kallsyms_filename(modules_filename, "modules", | ||
1007 | kallsyms_filename)) | ||
1008 | return -EINVAL; | ||
1009 | |||
1010 | if (do_validate_kcore_modules(modules_filename, map, kmaps)) | ||
1011 | return -EINVAL; | ||
1012 | |||
1013 | return 0; | ||
1014 | } | ||
1015 | |||
1016 | struct kcore_mapfn_data { | ||
1017 | struct dso *dso; | ||
1018 | enum map_type type; | ||
1019 | struct list_head maps; | ||
1020 | }; | ||
1021 | |||
1022 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | ||
1023 | { | ||
1024 | struct kcore_mapfn_data *md = data; | ||
1025 | struct map *map; | ||
1026 | |||
1027 | map = map__new2(start, md->dso, md->type); | ||
1028 | if (map == NULL) | ||
1029 | return -ENOMEM; | ||
1030 | |||
1031 | map->end = map->start + len; | ||
1032 | map->pgoff = pgoff; | ||
1033 | |||
1034 | list_add(&map->node, &md->maps); | ||
1035 | |||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
787 | static int dso__load_kcore(struct dso *dso, struct map *map, | 1039 | static int dso__load_kcore(struct dso *dso, struct map *map, |
788 | const char *kallsyms_filename) | 1040 | const char *kallsyms_filename) |
789 | { | 1041 | { |
@@ -800,8 +1052,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map, | |||
800 | if (map != machine->vmlinux_maps[map->type]) | 1052 | if (map != machine->vmlinux_maps[map->type]) |
801 | return -EINVAL; | 1053 | return -EINVAL; |
802 | 1054 | ||
803 | if (!kcore_filename_from_kallsyms_filename(kcore_filename, | 1055 | if (!filename_from_kallsyms_filename(kcore_filename, "kcore", |
804 | kallsyms_filename)) | 1056 | kallsyms_filename)) |
1057 | return -EINVAL; | ||
1058 | |||
1059 | /* All modules must be present at their original addresses */ | ||
1060 | if (validate_kcore_modules(kallsyms_filename, map)) | ||
805 | return -EINVAL; | 1061 | return -EINVAL; |
806 | 1062 | ||
807 | md.dso = dso; | 1063 | md.dso = dso; |
@@ -1188,6 +1444,105 @@ out: | |||
1188 | return err; | 1444 | return err; |
1189 | } | 1445 | } |
1190 | 1446 | ||
1447 | static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) | ||
1448 | { | ||
1449 | char kallsyms_filename[PATH_MAX]; | ||
1450 | struct dirent *dent; | ||
1451 | int ret = -1; | ||
1452 | DIR *d; | ||
1453 | |||
1454 | d = opendir(dir); | ||
1455 | if (!d) | ||
1456 | return -1; | ||
1457 | |||
1458 | while (1) { | ||
1459 | dent = readdir(d); | ||
1460 | if (!dent) | ||
1461 | break; | ||
1462 | if (dent->d_type != DT_DIR) | ||
1463 | continue; | ||
1464 | scnprintf(kallsyms_filename, sizeof(kallsyms_filename), | ||
1465 | "%s/%s/kallsyms", dir, dent->d_name); | ||
1466 | if (!validate_kcore_modules(kallsyms_filename, map)) { | ||
1467 | strlcpy(dir, kallsyms_filename, dir_sz); | ||
1468 | ret = 0; | ||
1469 | break; | ||
1470 | } | ||
1471 | } | ||
1472 | |||
1473 | closedir(d); | ||
1474 | |||
1475 | return ret; | ||
1476 | } | ||
1477 | |||
1478 | static char *dso__find_kallsyms(struct dso *dso, struct map *map) | ||
1479 | { | ||
1480 | u8 host_build_id[BUILD_ID_SIZE]; | ||
1481 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1482 | bool is_host = false; | ||
1483 | char path[PATH_MAX]; | ||
1484 | |||
1485 | if (!dso->has_build_id) { | ||
1486 | /* | ||
1487 | * Last resort, if we don't have a build-id and couldn't find | ||
1488 | * any vmlinux file, try the running kernel kallsyms table. | ||
1489 | */ | ||
1490 | goto proc_kallsyms; | ||
1491 | } | ||
1492 | |||
1493 | if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, | ||
1494 | sizeof(host_build_id)) == 0) | ||
1495 | is_host = dso__build_id_equal(dso, host_build_id); | ||
1496 | |||
1497 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
1498 | |||
1499 | /* Use /proc/kallsyms if possible */ | ||
1500 | if (is_host) { | ||
1501 | DIR *d; | ||
1502 | int fd; | ||
1503 | |||
1504 | /* If no cached kcore go with /proc/kallsyms */ | ||
1505 | scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", | ||
1506 | buildid_dir, sbuild_id); | ||
1507 | d = opendir(path); | ||
1508 | if (!d) | ||
1509 | goto proc_kallsyms; | ||
1510 | closedir(d); | ||
1511 | |||
1512 | /* | ||
1513 | * Do not check the build-id cache, until we know we cannot use | ||
1514 | * /proc/kcore. | ||
1515 | */ | ||
1516 | fd = open("/proc/kcore", O_RDONLY); | ||
1517 | if (fd != -1) { | ||
1518 | close(fd); | ||
1519 | /* If module maps match go with /proc/kallsyms */ | ||
1520 | if (!validate_kcore_modules("/proc/kallsyms", map)) | ||
1521 | goto proc_kallsyms; | ||
1522 | } | ||
1523 | |||
1524 | /* Find kallsyms in build-id cache with kcore */ | ||
1525 | if (!find_matching_kcore(map, path, sizeof(path))) | ||
1526 | return strdup(path); | ||
1527 | |||
1528 | goto proc_kallsyms; | ||
1529 | } | ||
1530 | |||
1531 | scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", | ||
1532 | buildid_dir, sbuild_id); | ||
1533 | |||
1534 | if (access(path, F_OK)) { | ||
1535 | pr_err("No kallsyms or vmlinux with build-id %s was found\n", | ||
1536 | sbuild_id); | ||
1537 | return NULL; | ||
1538 | } | ||
1539 | |||
1540 | return strdup(path); | ||
1541 | |||
1542 | proc_kallsyms: | ||
1543 | return strdup("/proc/kallsyms"); | ||
1544 | } | ||
1545 | |||
1191 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 1546 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
1192 | symbol_filter_t filter) | 1547 | symbol_filter_t filter) |
1193 | { | 1548 | { |
@@ -1214,7 +1569,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1214 | goto do_kallsyms; | 1569 | goto do_kallsyms; |
1215 | } | 1570 | } |
1216 | 1571 | ||
1217 | if (symbol_conf.vmlinux_name != NULL) { | 1572 | if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { |
1218 | err = dso__load_vmlinux(dso, map, | 1573 | err = dso__load_vmlinux(dso, map, |
1219 | symbol_conf.vmlinux_name, filter); | 1574 | symbol_conf.vmlinux_name, filter); |
1220 | if (err > 0) { | 1575 | if (err > 0) { |
@@ -1226,7 +1581,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1226 | return err; | 1581 | return err; |
1227 | } | 1582 | } |
1228 | 1583 | ||
1229 | if (vmlinux_path != NULL) { | 1584 | if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { |
1230 | err = dso__load_vmlinux_path(dso, map, filter); | 1585 | err = dso__load_vmlinux_path(dso, map, filter); |
1231 | if (err > 0) | 1586 | if (err > 0) |
1232 | return err; | 1587 | return err; |
@@ -1236,51 +1591,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1236 | if (symbol_conf.symfs[0] != 0) | 1591 | if (symbol_conf.symfs[0] != 0) |
1237 | return -1; | 1592 | return -1; |
1238 | 1593 | ||
1239 | /* | 1594 | kallsyms_allocated_filename = dso__find_kallsyms(dso, map); |
1240 | * Say the kernel DSO was created when processing the build-id header table, | 1595 | if (!kallsyms_allocated_filename) |
1241 | * we have a build-id, so check if it is the same as the running kernel, | 1596 | return -1; |
1242 | * using it if it is. | ||
1243 | */ | ||
1244 | if (dso->has_build_id) { | ||
1245 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | ||
1246 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1247 | |||
1248 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
1249 | sizeof(kallsyms_build_id)) == 0) { | ||
1250 | if (dso__build_id_equal(dso, kallsyms_build_id)) { | ||
1251 | kallsyms_filename = "/proc/kallsyms"; | ||
1252 | goto do_kallsyms; | ||
1253 | } | ||
1254 | } | ||
1255 | /* | ||
1256 | * Now look if we have it on the build-id cache in | ||
1257 | * $HOME/.debug/[kernel.kallsyms]. | ||
1258 | */ | ||
1259 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | ||
1260 | sbuild_id); | ||
1261 | |||
1262 | if (asprintf(&kallsyms_allocated_filename, | ||
1263 | "%s/.debug/[kernel.kallsyms]/%s", | ||
1264 | getenv("HOME"), sbuild_id) == -1) { | ||
1265 | pr_err("Not enough memory for kallsyms file lookup\n"); | ||
1266 | return -1; | ||
1267 | } | ||
1268 | |||
1269 | kallsyms_filename = kallsyms_allocated_filename; | ||
1270 | 1597 | ||
1271 | if (access(kallsyms_filename, F_OK)) { | 1598 | kallsyms_filename = kallsyms_allocated_filename; |
1272 | pr_err("No kallsyms or vmlinux with build-id %s " | ||
1273 | "was found\n", sbuild_id); | ||
1274 | free(kallsyms_allocated_filename); | ||
1275 | return -1; | ||
1276 | } | ||
1277 | } else { | ||
1278 | /* | ||
1279 | * Last resort, if we don't have a build-id and couldn't find | ||
1280 | * any vmlinux file, try the running kernel kallsyms table. | ||
1281 | */ | ||
1282 | kallsyms_filename = "/proc/kallsyms"; | ||
1283 | } | ||
1284 | 1599 | ||
1285 | do_kallsyms: | 1600 | do_kallsyms: |
1286 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 1601 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fd5b70ea2981..07de8fea2f48 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -13,7 +13,7 @@ | |||
13 | #include <libgen.h> | 13 | #include <libgen.h> |
14 | #include "build-id.h" | 14 | #include "build-id.h" |
15 | 15 | ||
16 | #ifdef LIBELF_SUPPORT | 16 | #ifdef HAVE_LIBELF_SUPPORT |
17 | #include <libelf.h> | 17 | #include <libelf.h> |
18 | #include <gelf.h> | 18 | #include <gelf.h> |
19 | #endif | 19 | #endif |
@@ -21,7 +21,7 @@ | |||
21 | 21 | ||
22 | #include "dso.h" | 22 | #include "dso.h" |
23 | 23 | ||
24 | #ifdef HAVE_CPLUS_DEMANGLE | 24 | #ifdef HAVE_CPLUS_DEMANGLE_SUPPORT |
25 | extern char *cplus_demangle(const char *, int); | 25 | extern char *cplus_demangle(const char *, int); |
26 | 26 | ||
27 | static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) | 27 | static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) |
@@ -46,7 +46,7 @@ static inline char *bfd_demangle(void __maybe_unused *v, | |||
46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
47 | * for newer versions we can use mmap to reduce memory usage: | 47 | * for newer versions we can use mmap to reduce memory usage: |
48 | */ | 48 | */ |
49 | #ifdef LIBELF_MMAP | 49 | #ifdef HAVE_LIBELF_MMAP_SUPPORT |
50 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP | 50 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP |
51 | #else | 51 | #else |
52 | # define PERF_ELF_C_READ_MMAP ELF_C_READ | 52 | # define PERF_ELF_C_READ_MMAP ELF_C_READ |
@@ -85,6 +85,7 @@ struct symbol_conf { | |||
85 | unsigned short priv_size; | 85 | unsigned short priv_size; |
86 | unsigned short nr_events; | 86 | unsigned short nr_events; |
87 | bool try_vmlinux_path, | 87 | bool try_vmlinux_path, |
88 | ignore_vmlinux, | ||
88 | show_kernel_path, | 89 | show_kernel_path, |
89 | use_modules, | 90 | use_modules, |
90 | sort_by_name, | 91 | sort_by_name, |
@@ -178,7 +179,7 @@ struct symsrc { | |||
178 | int fd; | 179 | int fd; |
179 | enum dso_binary_type type; | 180 | enum dso_binary_type type; |
180 | 181 | ||
181 | #ifdef LIBELF_SUPPORT | 182 | #ifdef HAVE_LIBELF_SUPPORT |
182 | Elf *elf; | 183 | Elf *elf; |
183 | GElf_Ehdr ehdr; | 184 | GElf_Ehdr ehdr; |
184 | 185 | ||
@@ -222,6 +223,9 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size); | |||
222 | int kallsyms__parse(const char *filename, void *arg, | 223 | int kallsyms__parse(const char *filename, void *arg, |
223 | int (*process_symbol)(void *arg, const char *name, | 224 | int (*process_symbol)(void *arg, const char *name, |
224 | char type, u64 start)); | 225 | char type, u64 start)); |
226 | int modules__parse(const char *filename, void *arg, | ||
227 | int (*process_module)(void *arg, const char *name, | ||
228 | u64 start)); | ||
225 | int filename__read_debuglink(const char *filename, char *debuglink, | 229 | int filename__read_debuglink(const char *filename, char *debuglink, |
226 | size_t size); | 230 | size_t size); |
227 | 231 | ||
@@ -252,4 +256,21 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); | |||
252 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | 256 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, |
253 | bool *is_64_bit); | 257 | bool *is_64_bit); |
254 | 258 | ||
259 | #define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" | ||
260 | |||
261 | struct kcore_extract { | ||
262 | char *kcore_filename; | ||
263 | u64 addr; | ||
264 | u64 offs; | ||
265 | u64 len; | ||
266 | char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; | ||
267 | int fd; | ||
268 | }; | ||
269 | |||
270 | int kcore_extract__create(struct kcore_extract *kce); | ||
271 | void kcore_extract__delete(struct kcore_extract *kce); | ||
272 | |||
273 | int kcore_copy(const char *from_dir, const char *to_dir); | ||
274 | int compare_proc_modules(const char *from, const char *to); | ||
275 | |||
255 | #endif /* __PERF_SYMBOL */ | 276 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c deleted file mode 100644 index f71e9eafe15a..000000000000 --- a/tools/perf/util/sysfs.c +++ /dev/null | |||
@@ -1,60 +0,0 @@ | |||
1 | |||
2 | #include "util.h" | ||
3 | #include "sysfs.h" | ||
4 | |||
5 | static const char * const sysfs_known_mountpoints[] = { | ||
6 | "/sys", | ||
7 | 0, | ||
8 | }; | ||
9 | |||
10 | static int sysfs_found; | ||
11 | char sysfs_mountpoint[PATH_MAX + 1]; | ||
12 | |||
13 | static int sysfs_valid_mountpoint(const char *sysfs) | ||
14 | { | ||
15 | struct statfs st_fs; | ||
16 | |||
17 | if (statfs(sysfs, &st_fs) < 0) | ||
18 | return -ENOENT; | ||
19 | else if (st_fs.f_type != (long) SYSFS_MAGIC) | ||
20 | return -ENOENT; | ||
21 | |||
22 | return 0; | ||
23 | } | ||
24 | |||
25 | const char *sysfs_find_mountpoint(void) | ||
26 | { | ||
27 | const char * const *ptr; | ||
28 | char type[100]; | ||
29 | FILE *fp; | ||
30 | |||
31 | if (sysfs_found) | ||
32 | return (const char *) sysfs_mountpoint; | ||
33 | |||
34 | ptr = sysfs_known_mountpoints; | ||
35 | while (*ptr) { | ||
36 | if (sysfs_valid_mountpoint(*ptr) == 0) { | ||
37 | sysfs_found = 1; | ||
38 | strcpy(sysfs_mountpoint, *ptr); | ||
39 | return sysfs_mountpoint; | ||
40 | } | ||
41 | ptr++; | ||
42 | } | ||
43 | |||
44 | /* give up and parse /proc/mounts */ | ||
45 | fp = fopen("/proc/mounts", "r"); | ||
46 | if (fp == NULL) | ||
47 | return NULL; | ||
48 | |||
49 | while (!sysfs_found && | ||
50 | fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", | ||
51 | sysfs_mountpoint, type) == 2) { | ||
52 | |||
53 | if (strcmp(type, "sysfs") == 0) | ||
54 | sysfs_found = 1; | ||
55 | } | ||
56 | |||
57 | fclose(fp); | ||
58 | |||
59 | return sysfs_found ? sysfs_mountpoint : NULL; | ||
60 | } | ||
diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h deleted file mode 100644 index a813b7203938..000000000000 --- a/tools/perf/util/sysfs.h +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | #ifndef __SYSFS_H__ | ||
2 | #define __SYSFS_H__ | ||
3 | |||
4 | const char *sysfs_find_mountpoint(void); | ||
5 | |||
6 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 065528b7563e..3c778a07b7cc 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c | |||
@@ -13,9 +13,9 @@ | |||
13 | #include <string.h> | 13 | #include <string.h> |
14 | 14 | ||
15 | 15 | ||
16 | enum perf_target_errno perf_target__validate(struct perf_target *target) | 16 | enum target_errno target__validate(struct target *target) |
17 | { | 17 | { |
18 | enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; | 18 | enum target_errno ret = TARGET_ERRNO__SUCCESS; |
19 | 19 | ||
20 | if (target->pid) | 20 | if (target->pid) |
21 | target->tid = target->pid; | 21 | target->tid = target->pid; |
@@ -23,42 +23,42 @@ enum perf_target_errno perf_target__validate(struct perf_target *target) | |||
23 | /* CPU and PID are mutually exclusive */ | 23 | /* CPU and PID are mutually exclusive */ |
24 | if (target->tid && target->cpu_list) { | 24 | if (target->tid && target->cpu_list) { |
25 | target->cpu_list = NULL; | 25 | target->cpu_list = NULL; |
26 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 26 | if (ret == TARGET_ERRNO__SUCCESS) |
27 | ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; | 27 | ret = TARGET_ERRNO__PID_OVERRIDE_CPU; |
28 | } | 28 | } |
29 | 29 | ||
30 | /* UID and PID are mutually exclusive */ | 30 | /* UID and PID are mutually exclusive */ |
31 | if (target->tid && target->uid_str) { | 31 | if (target->tid && target->uid_str) { |
32 | target->uid_str = NULL; | 32 | target->uid_str = NULL; |
33 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 33 | if (ret == TARGET_ERRNO__SUCCESS) |
34 | ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; | 34 | ret = TARGET_ERRNO__PID_OVERRIDE_UID; |
35 | } | 35 | } |
36 | 36 | ||
37 | /* UID and CPU are mutually exclusive */ | 37 | /* UID and CPU are mutually exclusive */ |
38 | if (target->uid_str && target->cpu_list) { | 38 | if (target->uid_str && target->cpu_list) { |
39 | target->cpu_list = NULL; | 39 | target->cpu_list = NULL; |
40 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 40 | if (ret == TARGET_ERRNO__SUCCESS) |
41 | ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; | 41 | ret = TARGET_ERRNO__UID_OVERRIDE_CPU; |
42 | } | 42 | } |
43 | 43 | ||
44 | /* PID and SYSTEM are mutually exclusive */ | 44 | /* PID and SYSTEM are mutually exclusive */ |
45 | if (target->tid && target->system_wide) { | 45 | if (target->tid && target->system_wide) { |
46 | target->system_wide = false; | 46 | target->system_wide = false; |
47 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 47 | if (ret == TARGET_ERRNO__SUCCESS) |
48 | ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; | 48 | ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM; |
49 | } | 49 | } |
50 | 50 | ||
51 | /* UID and SYSTEM are mutually exclusive */ | 51 | /* UID and SYSTEM are mutually exclusive */ |
52 | if (target->uid_str && target->system_wide) { | 52 | if (target->uid_str && target->system_wide) { |
53 | target->system_wide = false; | 53 | target->system_wide = false; |
54 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 54 | if (ret == TARGET_ERRNO__SUCCESS) |
55 | ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; | 55 | ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; |
56 | } | 56 | } |
57 | 57 | ||
58 | return ret; | 58 | return ret; |
59 | } | 59 | } |
60 | 60 | ||
61 | enum perf_target_errno perf_target__parse_uid(struct perf_target *target) | 61 | enum target_errno target__parse_uid(struct target *target) |
62 | { | 62 | { |
63 | struct passwd pwd, *result; | 63 | struct passwd pwd, *result; |
64 | char buf[1024]; | 64 | char buf[1024]; |
@@ -66,7 +66,7 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target) | |||
66 | 66 | ||
67 | target->uid = UINT_MAX; | 67 | target->uid = UINT_MAX; |
68 | if (str == NULL) | 68 | if (str == NULL) |
69 | return PERF_ERRNO_TARGET__SUCCESS; | 69 | return TARGET_ERRNO__SUCCESS; |
70 | 70 | ||
71 | /* Try user name first */ | 71 | /* Try user name first */ |
72 | getpwnam_r(str, &pwd, buf, sizeof(buf), &result); | 72 | getpwnam_r(str, &pwd, buf, sizeof(buf), &result); |
@@ -79,22 +79,22 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target) | |||
79 | int uid = strtol(str, &endptr, 10); | 79 | int uid = strtol(str, &endptr, 10); |
80 | 80 | ||
81 | if (*endptr != '\0') | 81 | if (*endptr != '\0') |
82 | return PERF_ERRNO_TARGET__INVALID_UID; | 82 | return TARGET_ERRNO__INVALID_UID; |
83 | 83 | ||
84 | getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); | 84 | getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); |
85 | 85 | ||
86 | if (result == NULL) | 86 | if (result == NULL) |
87 | return PERF_ERRNO_TARGET__USER_NOT_FOUND; | 87 | return TARGET_ERRNO__USER_NOT_FOUND; |
88 | } | 88 | } |
89 | 89 | ||
90 | target->uid = result->pw_uid; | 90 | target->uid = result->pw_uid; |
91 | return PERF_ERRNO_TARGET__SUCCESS; | 91 | return TARGET_ERRNO__SUCCESS; |
92 | } | 92 | } |
93 | 93 | ||
94 | /* | 94 | /* |
95 | * This must have a same ordering as the enum perf_target_errno. | 95 | * This must have a same ordering as the enum target_errno. |
96 | */ | 96 | */ |
97 | static const char *perf_target__error_str[] = { | 97 | static const char *target__error_str[] = { |
98 | "PID/TID switch overriding CPU", | 98 | "PID/TID switch overriding CPU", |
99 | "PID/TID switch overriding UID", | 99 | "PID/TID switch overriding UID", |
100 | "UID switch overriding CPU", | 100 | "UID switch overriding CPU", |
@@ -104,7 +104,7 @@ static const char *perf_target__error_str[] = { | |||
104 | "Problems obtaining information for user %s", | 104 | "Problems obtaining information for user %s", |
105 | }; | 105 | }; |
106 | 106 | ||
107 | int perf_target__strerror(struct perf_target *target, int errnum, | 107 | int target__strerror(struct target *target, int errnum, |
108 | char *buf, size_t buflen) | 108 | char *buf, size_t buflen) |
109 | { | 109 | { |
110 | int idx; | 110 | int idx; |
@@ -124,21 +124,19 @@ int perf_target__strerror(struct perf_target *target, int errnum, | |||
124 | return 0; | 124 | return 0; |
125 | } | 125 | } |
126 | 126 | ||
127 | if (errnum < __PERF_ERRNO_TARGET__START || | 127 | if (errnum < __TARGET_ERRNO__START || errnum >= __TARGET_ERRNO__END) |
128 | errnum >= __PERF_ERRNO_TARGET__END) | ||
129 | return -1; | 128 | return -1; |
130 | 129 | ||
131 | idx = errnum - __PERF_ERRNO_TARGET__START; | 130 | idx = errnum - __TARGET_ERRNO__START; |
132 | msg = perf_target__error_str[idx]; | 131 | msg = target__error_str[idx]; |
133 | 132 | ||
134 | switch (errnum) { | 133 | switch (errnum) { |
135 | case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU | 134 | case TARGET_ERRNO__PID_OVERRIDE_CPU ... TARGET_ERRNO__UID_OVERRIDE_SYSTEM: |
136 | ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: | ||
137 | snprintf(buf, buflen, "%s", msg); | 135 | snprintf(buf, buflen, "%s", msg); |
138 | break; | 136 | break; |
139 | 137 | ||
140 | case PERF_ERRNO_TARGET__INVALID_UID: | 138 | case TARGET_ERRNO__INVALID_UID: |
141 | case PERF_ERRNO_TARGET__USER_NOT_FOUND: | 139 | case TARGET_ERRNO__USER_NOT_FOUND: |
142 | snprintf(buf, buflen, msg, target->uid_str); | 140 | snprintf(buf, buflen, msg, target->uid_str); |
143 | break; | 141 | break; |
144 | 142 | ||
diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index a4be8575fda5..2d0c50690892 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | 6 | ||
7 | struct perf_target { | 7 | struct target { |
8 | const char *pid; | 8 | const char *pid; |
9 | const char *tid; | 9 | const char *tid; |
10 | const char *cpu_list; | 10 | const char *cpu_list; |
@@ -12,10 +12,11 @@ struct perf_target { | |||
12 | uid_t uid; | 12 | uid_t uid; |
13 | bool system_wide; | 13 | bool system_wide; |
14 | bool uses_mmap; | 14 | bool uses_mmap; |
15 | bool force_per_cpu; | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | enum perf_target_errno { | 18 | enum target_errno { |
18 | PERF_ERRNO_TARGET__SUCCESS = 0, | 19 | TARGET_ERRNO__SUCCESS = 0, |
19 | 20 | ||
20 | /* | 21 | /* |
21 | * Choose an arbitrary negative big number not to clash with standard | 22 | * Choose an arbitrary negative big number not to clash with standard |
@@ -24,42 +25,40 @@ enum perf_target_errno { | |||
24 | * | 25 | * |
25 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html | 26 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html |
26 | */ | 27 | */ |
27 | __PERF_ERRNO_TARGET__START = -10000, | 28 | __TARGET_ERRNO__START = -10000, |
28 | 29 | ||
30 | /* for target__validate() */ | ||
31 | TARGET_ERRNO__PID_OVERRIDE_CPU = __TARGET_ERRNO__START, | ||
32 | TARGET_ERRNO__PID_OVERRIDE_UID, | ||
33 | TARGET_ERRNO__UID_OVERRIDE_CPU, | ||
34 | TARGET_ERRNO__PID_OVERRIDE_SYSTEM, | ||
35 | TARGET_ERRNO__UID_OVERRIDE_SYSTEM, | ||
29 | 36 | ||
30 | /* for perf_target__validate() */ | 37 | /* for target__parse_uid() */ |
31 | PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START, | 38 | TARGET_ERRNO__INVALID_UID, |
32 | PERF_ERRNO_TARGET__PID_OVERRIDE_UID, | 39 | TARGET_ERRNO__USER_NOT_FOUND, |
33 | PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, | ||
34 | PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, | ||
35 | PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, | ||
36 | 40 | ||
37 | /* for perf_target__parse_uid() */ | 41 | __TARGET_ERRNO__END, |
38 | PERF_ERRNO_TARGET__INVALID_UID, | ||
39 | PERF_ERRNO_TARGET__USER_NOT_FOUND, | ||
40 | |||
41 | __PERF_ERRNO_TARGET__END, | ||
42 | }; | 42 | }; |
43 | 43 | ||
44 | enum perf_target_errno perf_target__validate(struct perf_target *target); | 44 | enum target_errno target__validate(struct target *target); |
45 | enum perf_target_errno perf_target__parse_uid(struct perf_target *target); | 45 | enum target_errno target__parse_uid(struct target *target); |
46 | 46 | ||
47 | int perf_target__strerror(struct perf_target *target, int errnum, char *buf, | 47 | int target__strerror(struct target *target, int errnum, char *buf, size_t buflen); |
48 | size_t buflen); | ||
49 | 48 | ||
50 | static inline bool perf_target__has_task(struct perf_target *target) | 49 | static inline bool target__has_task(struct target *target) |
51 | { | 50 | { |
52 | return target->tid || target->pid || target->uid_str; | 51 | return target->tid || target->pid || target->uid_str; |
53 | } | 52 | } |
54 | 53 | ||
55 | static inline bool perf_target__has_cpu(struct perf_target *target) | 54 | static inline bool target__has_cpu(struct target *target) |
56 | { | 55 | { |
57 | return target->system_wide || target->cpu_list; | 56 | return target->system_wide || target->cpu_list; |
58 | } | 57 | } |
59 | 58 | ||
60 | static inline bool perf_target__none(struct perf_target *target) | 59 | static inline bool target__none(struct target *target) |
61 | { | 60 | { |
62 | return !perf_target__has_task(target) && !perf_target__has_cpu(target); | 61 | return !target__has_task(target) && !target__has_cpu(target); |
63 | } | 62 | } |
64 | 63 | ||
65 | #endif /* _PERF_TARGET_H */ | 64 | #endif /* _PERF_TARGET_H */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index e3d4a550a703..cd8e2f592719 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -6,86 +6,137 @@ | |||
6 | #include "thread.h" | 6 | #include "thread.h" |
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | #include "comm.h" | ||
9 | 10 | ||
10 | struct thread *thread__new(pid_t pid, pid_t tid) | 11 | struct thread *thread__new(pid_t pid, pid_t tid) |
11 | { | 12 | { |
12 | struct thread *self = zalloc(sizeof(*self)); | 13 | char *comm_str; |
13 | 14 | struct comm *comm; | |
14 | if (self != NULL) { | 15 | struct thread *thread = zalloc(sizeof(*thread)); |
15 | map_groups__init(&self->mg); | 16 | |
16 | self->pid_ = pid; | 17 | if (thread != NULL) { |
17 | self->tid = tid; | 18 | map_groups__init(&thread->mg); |
18 | self->ppid = -1; | 19 | thread->pid_ = pid; |
19 | self->comm = malloc(32); | 20 | thread->tid = tid; |
20 | if (self->comm) | 21 | thread->ppid = -1; |
21 | snprintf(self->comm, 32, ":%d", self->tid); | 22 | INIT_LIST_HEAD(&thread->comm_list); |
23 | |||
24 | comm_str = malloc(32); | ||
25 | if (!comm_str) | ||
26 | goto err_thread; | ||
27 | |||
28 | snprintf(comm_str, 32, ":%d", tid); | ||
29 | comm = comm__new(comm_str, 0); | ||
30 | free(comm_str); | ||
31 | if (!comm) | ||
32 | goto err_thread; | ||
33 | |||
34 | list_add(&comm->list, &thread->comm_list); | ||
35 | } | ||
36 | |||
37 | return thread; | ||
38 | |||
39 | err_thread: | ||
40 | free(thread); | ||
41 | return NULL; | ||
42 | } | ||
43 | |||
44 | void thread__delete(struct thread *thread) | ||
45 | { | ||
46 | struct comm *comm, *tmp; | ||
47 | |||
48 | map_groups__exit(&thread->mg); | ||
49 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { | ||
50 | list_del(&comm->list); | ||
51 | comm__free(comm); | ||
22 | } | 52 | } |
23 | 53 | ||
24 | return self; | 54 | free(thread); |
25 | } | 55 | } |
26 | 56 | ||
27 | void thread__delete(struct thread *self) | 57 | struct comm *thread__comm(const struct thread *thread) |
28 | { | 58 | { |
29 | map_groups__exit(&self->mg); | 59 | if (list_empty(&thread->comm_list)) |
30 | free(self->comm); | 60 | return NULL; |
31 | free(self); | 61 | |
62 | return list_first_entry(&thread->comm_list, struct comm, list); | ||
32 | } | 63 | } |
33 | 64 | ||
34 | int thread__set_comm(struct thread *self, const char *comm) | 65 | /* CHECKME: time should always be 0 if event aren't ordered */ |
66 | int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) | ||
35 | { | 67 | { |
36 | int err; | 68 | struct comm *new, *curr = thread__comm(thread); |
37 | 69 | ||
38 | if (self->comm) | 70 | /* Override latest entry if it had no specific time coverage */ |
39 | free(self->comm); | 71 | if (!curr->start) { |
40 | self->comm = strdup(comm); | 72 | comm__override(curr, str, timestamp); |
41 | err = self->comm == NULL ? -ENOMEM : 0; | 73 | return 0; |
42 | if (!err) { | ||
43 | self->comm_set = true; | ||
44 | } | 74 | } |
45 | return err; | 75 | |
76 | new = comm__new(str, timestamp); | ||
77 | if (!new) | ||
78 | return -ENOMEM; | ||
79 | |||
80 | list_add(&new->list, &thread->comm_list); | ||
81 | thread->comm_set = true; | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | const char *thread__comm_str(const struct thread *thread) | ||
87 | { | ||
88 | const struct comm *comm = thread__comm(thread); | ||
89 | |||
90 | if (!comm) | ||
91 | return NULL; | ||
92 | |||
93 | return comm__str(comm); | ||
46 | } | 94 | } |
47 | 95 | ||
48 | int thread__comm_len(struct thread *self) | 96 | /* CHECKME: it should probably better return the max comm len from its comm list */ |
97 | int thread__comm_len(struct thread *thread) | ||
49 | { | 98 | { |
50 | if (!self->comm_len) { | 99 | if (!thread->comm_len) { |
51 | if (!self->comm) | 100 | const char *comm = thread__comm_str(thread); |
101 | if (!comm) | ||
52 | return 0; | 102 | return 0; |
53 | self->comm_len = strlen(self->comm); | 103 | thread->comm_len = strlen(comm); |
54 | } | 104 | } |
55 | 105 | ||
56 | return self->comm_len; | 106 | return thread->comm_len; |
57 | } | 107 | } |
58 | 108 | ||
59 | size_t thread__fprintf(struct thread *thread, FILE *fp) | 109 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
60 | { | 110 | { |
61 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) + | 111 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + |
62 | map_groups__fprintf(&thread->mg, verbose, fp); | 112 | map_groups__fprintf(&thread->mg, verbose, fp); |
63 | } | 113 | } |
64 | 114 | ||
65 | void thread__insert_map(struct thread *self, struct map *map) | 115 | void thread__insert_map(struct thread *thread, struct map *map) |
66 | { | 116 | { |
67 | map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); | 117 | map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); |
68 | map_groups__insert(&self->mg, map); | 118 | map_groups__insert(&thread->mg, map); |
69 | } | 119 | } |
70 | 120 | ||
71 | int thread__fork(struct thread *self, struct thread *parent) | 121 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) |
72 | { | 122 | { |
73 | int i; | 123 | int i, err; |
74 | 124 | ||
75 | if (parent->comm_set) { | 125 | if (parent->comm_set) { |
76 | if (self->comm) | 126 | const char *comm = thread__comm_str(parent); |
77 | free(self->comm); | 127 | if (!comm) |
78 | self->comm = strdup(parent->comm); | ||
79 | if (!self->comm) | ||
80 | return -ENOMEM; | 128 | return -ENOMEM; |
81 | self->comm_set = true; | 129 | err = thread__set_comm(thread, comm, timestamp); |
130 | if (!err) | ||
131 | return err; | ||
132 | thread->comm_set = true; | ||
82 | } | 133 | } |
83 | 134 | ||
84 | for (i = 0; i < MAP__NR_TYPES; ++i) | 135 | for (i = 0; i < MAP__NR_TYPES; ++i) |
85 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | 136 | if (map_groups__clone(&thread->mg, &parent->mg, i) < 0) |
86 | return -ENOMEM; | 137 | return -ENOMEM; |
87 | 138 | ||
88 | self->ppid = parent->tid; | 139 | thread->ppid = parent->tid; |
89 | 140 | ||
90 | return 0; | 141 | return 0; |
91 | } | 142 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4ebbb40d46d4..897c1b2a750a 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_THREAD_H | 2 | #define __PERF_THREAD_H |
3 | 3 | ||
4 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
5 | #include <linux/list.h> | ||
5 | #include <unistd.h> | 6 | #include <unistd.h> |
6 | #include <sys/types.h> | 7 | #include <sys/types.h> |
7 | #include "symbol.h" | 8 | #include "symbol.h" |
@@ -18,31 +19,34 @@ struct thread { | |||
18 | char shortname[3]; | 19 | char shortname[3]; |
19 | bool comm_set; | 20 | bool comm_set; |
20 | bool dead; /* if set thread has exited */ | 21 | bool dead; /* if set thread has exited */ |
21 | char *comm; | 22 | struct list_head comm_list; |
22 | int comm_len; | 23 | int comm_len; |
23 | 24 | ||
24 | void *priv; | 25 | void *priv; |
25 | }; | 26 | }; |
26 | 27 | ||
27 | struct machine; | 28 | struct machine; |
29 | struct comm; | ||
28 | 30 | ||
29 | struct thread *thread__new(pid_t pid, pid_t tid); | 31 | struct thread *thread__new(pid_t pid, pid_t tid); |
30 | void thread__delete(struct thread *self); | 32 | void thread__delete(struct thread *thread); |
31 | static inline void thread__exited(struct thread *thread) | 33 | static inline void thread__exited(struct thread *thread) |
32 | { | 34 | { |
33 | thread->dead = true; | 35 | thread->dead = true; |
34 | } | 36 | } |
35 | 37 | ||
36 | int thread__set_comm(struct thread *self, const char *comm); | 38 | int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); |
37 | int thread__comm_len(struct thread *self); | 39 | int thread__comm_len(struct thread *thread); |
38 | void thread__insert_map(struct thread *self, struct map *map); | 40 | struct comm *thread__comm(const struct thread *thread); |
39 | int thread__fork(struct thread *self, struct thread *parent); | 41 | const char *thread__comm_str(const struct thread *thread); |
42 | void thread__insert_map(struct thread *thread, struct map *map); | ||
43 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | ||
40 | size_t thread__fprintf(struct thread *thread, FILE *fp); | 44 | size_t thread__fprintf(struct thread *thread, FILE *fp); |
41 | 45 | ||
42 | static inline struct map *thread__find_map(struct thread *self, | 46 | static inline struct map *thread__find_map(struct thread *thread, |
43 | enum map_type type, u64 addr) | 47 | enum map_type type, u64 addr) |
44 | { | 48 | { |
45 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | 49 | return thread ? map_groups__find(&thread->mg, type, addr) : NULL; |
46 | } | 50 | } |
47 | 51 | ||
48 | void thread__find_addr_map(struct thread *thread, struct machine *machine, | 52 | void thread__find_addr_map(struct thread *thread, struct machine *machine, |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index f857b51b6bde..ce793c7dd23c 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -27,7 +27,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
27 | float ksamples_per_sec; | 27 | float ksamples_per_sec; |
28 | float esamples_percent; | 28 | float esamples_percent; |
29 | struct perf_record_opts *opts = &top->record_opts; | 29 | struct perf_record_opts *opts = &top->record_opts; |
30 | struct perf_target *target = &opts->target; | 30 | struct target *target = &opts->target; |
31 | size_t ret = 0; | 31 | size_t ret = 0; |
32 | 32 | ||
33 | if (top->samples) { | 33 | if (top->samples) { |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b554ffc462b6..88cfeaff600b 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -24,6 +24,7 @@ struct perf_top { | |||
24 | u64 exact_samples; | 24 | u64 exact_samples; |
25 | u64 guest_us_samples, guest_kernel_samples; | 25 | u64 guest_us_samples, guest_kernel_samples; |
26 | int print_entries, count_filter, delay_secs; | 26 | int print_entries, count_filter, delay_secs; |
27 | int max_stack; | ||
27 | bool hide_kernel_symbols, hide_user_symbols, zero; | 28 | bool hide_kernel_symbols, hide_user_symbols, zero; |
28 | bool use_tui, use_stdio; | 29 | bool use_tui, use_stdio; |
29 | bool kptr_restrict_warned; | 30 | bool kptr_restrict_warned; |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index e9e1c03f927d..6681f71f2f95 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -120,42 +120,6 @@ raw_field_value(struct event_format *event, const char *name, void *data) | |||
120 | return val; | 120 | return val; |
121 | } | 121 | } |
122 | 122 | ||
123 | void *raw_field_ptr(struct event_format *event, const char *name, void *data) | ||
124 | { | ||
125 | struct format_field *field; | ||
126 | |||
127 | field = pevent_find_any_field(event, name); | ||
128 | if (!field) | ||
129 | return NULL; | ||
130 | |||
131 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
132 | int offset; | ||
133 | |||
134 | offset = *(int *)(data + field->offset); | ||
135 | offset &= 0xffff; | ||
136 | |||
137 | return data + offset; | ||
138 | } | ||
139 | |||
140 | return data + field->offset; | ||
141 | } | ||
142 | |||
143 | int trace_parse_common_type(struct pevent *pevent, void *data) | ||
144 | { | ||
145 | struct pevent_record record; | ||
146 | |||
147 | record.data = data; | ||
148 | return pevent_data_type(pevent, &record); | ||
149 | } | ||
150 | |||
151 | int trace_parse_common_pid(struct pevent *pevent, void *data) | ||
152 | { | ||
153 | struct pevent_record record; | ||
154 | |||
155 | record.data = data; | ||
156 | return pevent_data_pid(pevent, &record); | ||
157 | } | ||
158 | |||
159 | unsigned long long read_size(struct event_format *event, void *ptr, int size) | 123 | unsigned long long read_size(struct event_format *event, void *ptr, int size) |
160 | { | 124 | { |
161 | return pevent_read_number(event->pevent, ptr, size); | 125 | return pevent_read_number(event->pevent, ptr, size); |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index fafe1a40444a..04df63114109 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -11,8 +11,6 @@ union perf_event; | |||
11 | struct perf_tool; | 11 | struct perf_tool; |
12 | struct thread; | 12 | struct thread; |
13 | 13 | ||
14 | extern struct pevent *perf_pevent; | ||
15 | |||
16 | int bigendian(void); | 14 | int bigendian(void); |
17 | 15 | ||
18 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); | 16 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); |
@@ -23,26 +21,19 @@ int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); | |||
23 | int parse_event_file(struct pevent *pevent, | 21 | int parse_event_file(struct pevent *pevent, |
24 | char *buf, unsigned long size, char *sys); | 22 | char *buf, unsigned long size, char *sys); |
25 | 23 | ||
26 | struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu); | ||
27 | |||
28 | unsigned long long | 24 | unsigned long long |
29 | raw_field_value(struct event_format *event, const char *name, void *data); | 25 | raw_field_value(struct event_format *event, const char *name, void *data); |
30 | void *raw_field_ptr(struct event_format *event, const char *name, void *data); | ||
31 | 26 | ||
32 | void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); | 27 | void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); |
33 | void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); | 28 | void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); |
34 | 29 | ||
35 | ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); | 30 | ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); |
36 | 31 | ||
37 | int trace_parse_common_type(struct pevent *pevent, void *data); | ||
38 | int trace_parse_common_pid(struct pevent *pevent, void *data); | ||
39 | |||
40 | struct event_format *trace_find_next_event(struct pevent *pevent, | 32 | struct event_format *trace_find_next_event(struct pevent *pevent, |
41 | struct event_format *event); | 33 | struct event_format *event); |
42 | unsigned long long read_size(struct event_format *event, void *ptr, int size); | 34 | unsigned long long read_size(struct event_format *event, void *ptr, int size); |
43 | unsigned long long eval_flag(const char *flag); | 35 | unsigned long long eval_flag(const char *flag); |
44 | 36 | ||
45 | struct pevent_record *trace_read_data(struct pevent *pevent, int cpu); | ||
46 | int read_tracing_data(int fd, struct list_head *pattrs); | 37 | int read_tracing_data(int fd, struct list_head *pattrs); |
47 | 38 | ||
48 | struct tracing_data { | 39 | struct tracing_data { |
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 2f891f7e70bf..0efd5393de85 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c | |||
@@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, | |||
39 | 39 | ||
40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) | 40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) |
41 | 41 | ||
42 | extern int | ||
43 | UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, | ||
44 | unw_word_t ip, | ||
45 | unw_word_t segbase, | ||
46 | const char *obj_name, unw_word_t start, | ||
47 | unw_word_t end); | ||
48 | |||
49 | #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) | ||
50 | |||
42 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ | 51 | #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 */ | 52 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ |
44 | 53 | ||
@@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, | |||
245 | return 0; | 254 | return 0; |
246 | } | 255 | } |
247 | 256 | ||
248 | static int read_unwind_spec(struct dso *dso, struct machine *machine, | 257 | static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, |
249 | u64 *table_data, u64 *segbase, u64 *fde_count) | 258 | u64 *table_data, u64 *segbase, |
259 | u64 *fde_count) | ||
250 | { | 260 | { |
251 | int ret = -EINVAL, fd; | 261 | int ret = -EINVAL, fd; |
252 | u64 offset; | 262 | u64 offset; |
@@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, | |||
255 | if (fd < 0) | 265 | if (fd < 0) |
256 | return -EINVAL; | 266 | return -EINVAL; |
257 | 267 | ||
268 | /* Check the .eh_frame section for unwinding info */ | ||
258 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | 269 | offset = elf_section_offset(fd, ".eh_frame_hdr"); |
259 | close(fd); | 270 | close(fd); |
260 | 271 | ||
@@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, | |||
263 | table_data, segbase, | 274 | table_data, segbase, |
264 | fde_count); | 275 | fde_count); |
265 | 276 | ||
266 | /* TODO .debug_frame check if eh_frame_hdr fails */ | ||
267 | return ret; | 277 | return ret; |
268 | } | 278 | } |
269 | 279 | ||
280 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
281 | static int read_unwind_spec_debug_frame(struct dso *dso, | ||
282 | struct machine *machine, u64 *offset) | ||
283 | { | ||
284 | int fd = dso__data_fd(dso, machine); | ||
285 | |||
286 | if (fd < 0) | ||
287 | return -EINVAL; | ||
288 | |||
289 | /* Check the .debug_frame section for unwinding info */ | ||
290 | *offset = elf_section_offset(fd, ".debug_frame"); | ||
291 | close(fd); | ||
292 | |||
293 | if (*offset) | ||
294 | return 0; | ||
295 | |||
296 | return -EINVAL; | ||
297 | } | ||
298 | #endif | ||
299 | |||
270 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | 300 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) |
271 | { | 301 | { |
272 | struct addr_location al; | 302 | struct addr_location al; |
@@ -291,20 +321,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | |||
291 | 321 | ||
292 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); | 322 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); |
293 | 323 | ||
294 | if (read_unwind_spec(map->dso, ui->machine, | 324 | /* Check the .eh_frame section for unwinding info */ |
295 | &table_data, &segbase, &fde_count)) | 325 | if (!read_unwind_spec_eh_frame(map->dso, ui->machine, |
296 | return -EINVAL; | 326 | &table_data, &segbase, &fde_count)) { |
327 | memset(&di, 0, sizeof(di)); | ||
328 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
329 | di.start_ip = map->start; | ||
330 | di.end_ip = map->end; | ||
331 | di.u.rti.segbase = map->start + segbase; | ||
332 | di.u.rti.table_data = map->start + table_data; | ||
333 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
334 | / sizeof(unw_word_t); | ||
335 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
336 | need_unwind_info, arg); | ||
337 | } | ||
338 | |||
339 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
340 | /* Check the .debug_frame section for unwinding info */ | ||
341 | if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { | ||
342 | memset(&di, 0, sizeof(di)); | ||
343 | dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, | ||
344 | map->start, map->end); | ||
345 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
346 | need_unwind_info, arg); | ||
347 | } | ||
348 | #endif | ||
297 | 349 | ||
298 | memset(&di, 0, sizeof(di)); | 350 | return -EINVAL; |
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 | } | 351 | } |
309 | 352 | ||
310 | static int access_fpreg(unw_addr_space_t __maybe_unused as, | 353 | static int access_fpreg(unw_addr_space_t __maybe_unused as, |
@@ -516,7 +559,7 @@ static unw_accessors_t accessors = { | |||
516 | }; | 559 | }; |
517 | 560 | ||
518 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | 561 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, |
519 | void *arg) | 562 | void *arg, int max_stack) |
520 | { | 563 | { |
521 | unw_addr_space_t addr_space; | 564 | unw_addr_space_t addr_space; |
522 | unw_cursor_t c; | 565 | unw_cursor_t c; |
@@ -532,7 +575,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
532 | if (ret) | 575 | if (ret) |
533 | display_error(ret); | 576 | display_error(ret); |
534 | 577 | ||
535 | while (!ret && (unw_step(&c) > 0)) { | 578 | while (!ret && (unw_step(&c) > 0) && max_stack--) { |
536 | unw_word_t ip; | 579 | unw_word_t ip; |
537 | 580 | ||
538 | unw_get_reg(&c, UNW_REG_IP, &ip); | 581 | unw_get_reg(&c, UNW_REG_IP, &ip); |
@@ -545,7 +588,8 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
545 | 588 | ||
546 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 589 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
547 | struct machine *machine, struct thread *thread, | 590 | struct machine *machine, struct thread *thread, |
548 | u64 sample_uregs, struct perf_sample *data) | 591 | u64 sample_uregs, struct perf_sample *data, |
592 | int max_stack) | ||
549 | { | 593 | { |
550 | unw_word_t ip; | 594 | unw_word_t ip; |
551 | struct unwind_info ui = { | 595 | struct unwind_info ui = { |
@@ -567,5 +611,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
567 | if (ret) | 611 | if (ret) |
568 | return -ENOMEM; | 612 | return -ENOMEM; |
569 | 613 | ||
570 | return get_entries(&ui, cb, arg); | 614 | return get_entries(&ui, cb, arg, max_stack); |
571 | } | 615 | } |
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index cb6bc503a792..d5966f49e22c 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
@@ -13,12 +13,12 @@ struct unwind_entry { | |||
13 | 13 | ||
14 | typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); | 14 | typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); |
15 | 15 | ||
16 | #ifdef LIBUNWIND_SUPPORT | 16 | #ifdef HAVE_LIBUNWIND_SUPPORT |
17 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 17 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
18 | struct machine *machine, | 18 | struct machine *machine, |
19 | struct thread *thread, | 19 | struct thread *thread, |
20 | u64 sample_uregs, | 20 | u64 sample_uregs, |
21 | struct perf_sample *data); | 21 | struct perf_sample *data, int max_stack); |
22 | int unwind__arch_reg_id(int regnum); | 22 | int unwind__arch_reg_id(int regnum); |
23 | #else | 23 | #else |
24 | static inline int | 24 | static inline int |
@@ -27,9 +27,10 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | |||
27 | struct machine *machine __maybe_unused, | 27 | struct machine *machine __maybe_unused, |
28 | struct thread *thread __maybe_unused, | 28 | struct thread *thread __maybe_unused, |
29 | u64 sample_uregs __maybe_unused, | 29 | u64 sample_uregs __maybe_unused, |
30 | struct perf_sample *data __maybe_unused) | 30 | struct perf_sample *data __maybe_unused, |
31 | int max_stack __maybe_unused) | ||
31 | { | 32 | { |
32 | return 0; | 33 | return 0; |
33 | } | 34 | } |
34 | #endif /* LIBUNWIND_SUPPORT */ | 35 | #endif /* HAVE_LIBUNWIND_SUPPORT */ |
35 | #endif /* __UNWIND_H */ | 36 | #endif /* __UNWIND_H */ |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 6d17b18e915d..28a0a89c1f73 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #include "../perf.h" | 1 | #include "../perf.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include <sys/mman.h> | 3 | #include <sys/mman.h> |
4 | #ifdef BACKTRACE_SUPPORT | 4 | #ifdef HAVE_BACKTRACE_SUPPORT |
5 | #include <execinfo.h> | 5 | #include <execinfo.h> |
6 | #endif | 6 | #endif |
7 | #include <stdio.h> | 7 | #include <stdio.h> |
@@ -55,17 +55,20 @@ int mkdir_p(char *path, mode_t mode) | |||
55 | return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; | 55 | return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; |
56 | } | 56 | } |
57 | 57 | ||
58 | static int slow_copyfile(const char *from, const char *to) | 58 | static int slow_copyfile(const char *from, const char *to, mode_t mode) |
59 | { | 59 | { |
60 | int err = 0; | 60 | int err = -1; |
61 | char *line = NULL; | 61 | char *line = NULL; |
62 | size_t n; | 62 | size_t n; |
63 | FILE *from_fp = fopen(from, "r"), *to_fp; | 63 | FILE *from_fp = fopen(from, "r"), *to_fp; |
64 | mode_t old_umask; | ||
64 | 65 | ||
65 | if (from_fp == NULL) | 66 | if (from_fp == NULL) |
66 | goto out; | 67 | goto out; |
67 | 68 | ||
69 | old_umask = umask(mode ^ 0777); | ||
68 | to_fp = fopen(to, "w"); | 70 | to_fp = fopen(to, "w"); |
71 | umask(old_umask); | ||
69 | if (to_fp == NULL) | 72 | if (to_fp == NULL) |
70 | goto out_fclose_from; | 73 | goto out_fclose_from; |
71 | 74 | ||
@@ -82,7 +85,7 @@ out: | |||
82 | return err; | 85 | return err; |
83 | } | 86 | } |
84 | 87 | ||
85 | int copyfile(const char *from, const char *to) | 88 | int copyfile_mode(const char *from, const char *to, mode_t mode) |
86 | { | 89 | { |
87 | int fromfd, tofd; | 90 | int fromfd, tofd; |
88 | struct stat st; | 91 | struct stat st; |
@@ -93,13 +96,13 @@ int copyfile(const char *from, const char *to) | |||
93 | goto out; | 96 | goto out; |
94 | 97 | ||
95 | if (st.st_size == 0) /* /proc? do it slowly... */ | 98 | if (st.st_size == 0) /* /proc? do it slowly... */ |
96 | return slow_copyfile(from, to); | 99 | return slow_copyfile(from, to, mode); |
97 | 100 | ||
98 | fromfd = open(from, O_RDONLY); | 101 | fromfd = open(from, O_RDONLY); |
99 | if (fromfd < 0) | 102 | if (fromfd < 0) |
100 | goto out; | 103 | goto out; |
101 | 104 | ||
102 | tofd = creat(to, 0755); | 105 | tofd = creat(to, mode); |
103 | if (tofd < 0) | 106 | if (tofd < 0) |
104 | goto out_close_from; | 107 | goto out_close_from; |
105 | 108 | ||
@@ -121,6 +124,11 @@ out: | |||
121 | return err; | 124 | return err; |
122 | } | 125 | } |
123 | 126 | ||
127 | int copyfile(const char *from, const char *to) | ||
128 | { | ||
129 | return copyfile_mode(from, to, 0755); | ||
130 | } | ||
131 | |||
124 | unsigned long convert_unit(unsigned long value, char *unit) | 132 | unsigned long convert_unit(unsigned long value, char *unit) |
125 | { | 133 | { |
126 | *unit = ' '; | 134 | *unit = ' '; |
@@ -204,7 +212,7 @@ int hex2u64(const char *ptr, u64 *long_val) | |||
204 | } | 212 | } |
205 | 213 | ||
206 | /* Obtain a backtrace and print it to stdout. */ | 214 | /* Obtain a backtrace and print it to stdout. */ |
207 | #ifdef BACKTRACE_SUPPORT | 215 | #ifdef HAVE_BACKTRACE_SUPPORT |
208 | void dump_stack(void) | 216 | void dump_stack(void) |
209 | { | 217 | { |
210 | void *array[16]; | 218 | void *array[16]; |
@@ -361,3 +369,47 @@ int parse_nsec_time(const char *str, u64 *ptime) | |||
361 | *ptime = time_sec * NSEC_PER_SEC + time_nsec; | 369 | *ptime = time_sec * NSEC_PER_SEC + time_nsec; |
362 | return 0; | 370 | return 0; |
363 | } | 371 | } |
372 | |||
373 | unsigned long parse_tag_value(const char *str, struct parse_tag *tags) | ||
374 | { | ||
375 | struct parse_tag *i = tags; | ||
376 | |||
377 | while (i->tag) { | ||
378 | char *s; | ||
379 | |||
380 | s = strchr(str, i->tag); | ||
381 | if (s) { | ||
382 | unsigned long int value; | ||
383 | char *endptr; | ||
384 | |||
385 | value = strtoul(str, &endptr, 10); | ||
386 | if (s != endptr) | ||
387 | break; | ||
388 | |||
389 | if (value > ULONG_MAX / i->mult) | ||
390 | break; | ||
391 | value *= i->mult; | ||
392 | return value; | ||
393 | } | ||
394 | i++; | ||
395 | } | ||
396 | |||
397 | return (unsigned long) -1; | ||
398 | } | ||
399 | |||
400 | int filename__read_int(const char *filename, int *value) | ||
401 | { | ||
402 | char line[64]; | ||
403 | int fd = open(filename, O_RDONLY), err = -1; | ||
404 | |||
405 | if (fd < 0) | ||
406 | return -1; | ||
407 | |||
408 | if (read(fd, line, sizeof(line)) > 0) { | ||
409 | *value = atoi(line); | ||
410 | err = 0; | ||
411 | } | ||
412 | |||
413 | close(fd); | ||
414 | return err; | ||
415 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index a53535949043..c8f362daba87 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -128,6 +128,8 @@ void put_tracing_file(char *file); | |||
128 | #endif | 128 | #endif |
129 | #endif | 129 | #endif |
130 | 130 | ||
131 | #define PERF_GTK_DSO "libperf-gtk.so" | ||
132 | |||
131 | /* General helper functions */ | 133 | /* General helper functions */ |
132 | extern void usage(const char *err) NORETURN; | 134 | extern void usage(const char *err) NORETURN; |
133 | extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); | 135 | extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); |
@@ -241,6 +243,7 @@ static inline int sane_case(int x, int high) | |||
241 | 243 | ||
242 | int mkdir_p(char *path, mode_t mode); | 244 | int mkdir_p(char *path, mode_t mode); |
243 | int copyfile(const char *from, const char *to); | 245 | int copyfile(const char *from, const char *to); |
246 | int copyfile_mode(const char *from, const char *to, mode_t mode); | ||
244 | 247 | ||
245 | s64 perf_atoll(const char *str); | 248 | s64 perf_atoll(const char *str); |
246 | char **argv_split(const char *str, int *argcp); | 249 | char **argv_split(const char *str, int *argcp); |
@@ -270,6 +273,13 @@ bool is_power_of_2(unsigned long n) | |||
270 | return (n != 0 && ((n & (n - 1)) == 0)); | 273 | return (n != 0 && ((n & (n - 1)) == 0)); |
271 | } | 274 | } |
272 | 275 | ||
276 | static inline unsigned next_pow2(unsigned x) | ||
277 | { | ||
278 | if (!x) | ||
279 | return 1; | ||
280 | return 1ULL << (32 - __builtin_clz(x - 1)); | ||
281 | } | ||
282 | |||
273 | size_t hex_width(u64 v); | 283 | size_t hex_width(u64 v); |
274 | int hex2u64(const char *ptr, u64 *val); | 284 | int hex2u64(const char *ptr, u64 *val); |
275 | 285 | ||
@@ -281,4 +291,20 @@ void dump_stack(void); | |||
281 | extern unsigned int page_size; | 291 | extern unsigned int page_size; |
282 | 292 | ||
283 | void get_term_dimensions(struct winsize *ws); | 293 | void get_term_dimensions(struct winsize *ws); |
294 | |||
295 | struct parse_tag { | ||
296 | char tag; | ||
297 | int mult; | ||
298 | }; | ||
299 | |||
300 | unsigned long parse_tag_value(const char *str, struct parse_tag *tags); | ||
301 | |||
302 | #define SRCLINE_UNKNOWN ((char *) "??:0") | ||
303 | |||
304 | struct dso; | ||
305 | |||
306 | char *get_srcline(struct dso *dso, unsigned long addr); | ||
307 | void free_srcline(char *srcline); | ||
308 | |||
309 | int filename__read_int(const char *filename, int *value); | ||
284 | #endif /* GIT_COMPAT_UTIL_H */ | 310 | #endif /* GIT_COMPAT_UTIL_H */ |