diff options
Diffstat (limited to 'tools/perf/util')
46 files changed, 1511 insertions, 541 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 011ed2676604..e5a462f1d07c 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -315,7 +315,7 @@ fallback: | |||
315 | "Please use:\n\n" | 315 | "Please use:\n\n" |
316 | " perf buildid-cache -av vmlinux\n\n" | 316 | " perf buildid-cache -av vmlinux\n\n" |
317 | "or:\n\n" | 317 | "or:\n\n" |
318 | " --vmlinux vmlinux", | 318 | " --vmlinux vmlinux\n", |
319 | sym->name, build_id_msg ?: ""); | 319 | sym->name, build_id_msg ?: ""); |
320 | goto out_free_filename; | 320 | goto out_free_filename; |
321 | } | 321 | } |
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c index 5e230acae1e9..0a1adc1111fd 100644 --- a/tools/perf/util/bitmap.c +++ b/tools/perf/util/bitmap.c | |||
@@ -19,3 +19,13 @@ int __bitmap_weight(const unsigned long *bitmap, int bits) | |||
19 | 19 | ||
20 | return w; | 20 | return w; |
21 | } | 21 | } |
22 | |||
23 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | ||
24 | const unsigned long *bitmap2, int bits) | ||
25 | { | ||
26 | int k; | ||
27 | int nr = BITS_TO_LONGS(bits); | ||
28 | |||
29 | for (k = 0; k < nr; k++) | ||
30 | dst[k] = bitmap1[k] | bitmap2[k]; | ||
31 | } | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 6893eec693ab..adc72f09914d 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -166,6 +166,17 @@ out: | |||
166 | return cpus; | 166 | return cpus; |
167 | } | 167 | } |
168 | 168 | ||
169 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) | ||
170 | { | ||
171 | int i; | ||
172 | size_t printed = fprintf(fp, "%d cpu%s: ", | ||
173 | map->nr, map->nr > 1 ? "s" : ""); | ||
174 | for (i = 0; i < map->nr; ++i) | ||
175 | printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]); | ||
176 | |||
177 | return printed + fprintf(fp, "\n"); | ||
178 | } | ||
179 | |||
169 | struct cpu_map *cpu_map__dummy_new(void) | 180 | struct cpu_map *cpu_map__dummy_new(void) |
170 | { | 181 | { |
171 | struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); | 182 | struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); |
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 072c0a374794..c41518573c6a 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef __PERF_CPUMAP_H | 1 | #ifndef __PERF_CPUMAP_H |
2 | #define __PERF_CPUMAP_H | 2 | #define __PERF_CPUMAP_H |
3 | 3 | ||
4 | #include <stdio.h> | ||
5 | |||
4 | struct cpu_map { | 6 | struct cpu_map { |
5 | int nr; | 7 | int nr; |
6 | int map[]; | 8 | int map[]; |
@@ -10,4 +12,6 @@ struct cpu_map *cpu_map__new(const char *cpu_list); | |||
10 | struct cpu_map *cpu_map__dummy_new(void); | 12 | struct cpu_map *cpu_map__dummy_new(void); |
11 | void cpu_map__delete(struct cpu_map *map); | 13 | void cpu_map__delete(struct cpu_map *map); |
12 | 14 | ||
15 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); | ||
16 | |||
13 | #endif /* __PERF_CPUMAP_H */ | 17 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index 35073621e5de..aada3ac5e891 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * No surprises, and works with signed and unsigned chars. | 4 | * No surprises, and works with signed and unsigned chars. |
5 | */ | 5 | */ |
6 | #include "cache.h" | 6 | #include "util.h" |
7 | 7 | ||
8 | enum { | 8 | enum { |
9 | S = GIT_SPACE, | 9 | S = GIT_SPACE, |
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index ffc35e748e89..dd8b19319c03 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c | |||
@@ -15,32 +15,6 @@ static const char *debugfs_known_mountpoints[] = { | |||
15 | 0, | 15 | 0, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | /* use this to force a umount */ | ||
19 | void debugfs_force_cleanup(void) | ||
20 | { | ||
21 | debugfs_find_mountpoint(); | ||
22 | debugfs_premounted = 0; | ||
23 | debugfs_umount(); | ||
24 | } | ||
25 | |||
26 | /* construct a full path to a debugfs element */ | ||
27 | int debugfs_make_path(const char *element, char *buffer, int size) | ||
28 | { | ||
29 | int len; | ||
30 | |||
31 | if (strlen(debugfs_mountpoint) == 0) { | ||
32 | buffer[0] = '\0'; | ||
33 | return -1; | ||
34 | } | ||
35 | |||
36 | len = strlen(debugfs_mountpoint) + strlen(element) + 1; | ||
37 | if (len >= size) | ||
38 | return len+1; | ||
39 | |||
40 | snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element); | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | static int debugfs_found; | 18 | static int debugfs_found; |
45 | 19 | ||
46 | /* find the path to the mounted debugfs */ | 20 | /* find the path to the mounted debugfs */ |
@@ -97,17 +71,6 @@ int debugfs_valid_mountpoint(const char *debugfs) | |||
97 | return 0; | 71 | return 0; |
98 | } | 72 | } |
99 | 73 | ||
100 | |||
101 | int debugfs_valid_entry(const char *path) | ||
102 | { | ||
103 | struct stat st; | ||
104 | |||
105 | if (stat(path, &st)) | ||
106 | return -errno; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static void debugfs_set_tracing_events_path(const char *mountpoint) | 74 | static void debugfs_set_tracing_events_path(const char *mountpoint) |
112 | { | 75 | { |
113 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | 76 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", |
@@ -149,107 +112,3 @@ void debugfs_set_path(const char *mountpoint) | |||
149 | snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); | 112 | snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); |
150 | debugfs_set_tracing_events_path(mountpoint); | 113 | debugfs_set_tracing_events_path(mountpoint); |
151 | } | 114 | } |
152 | |||
153 | /* umount the debugfs */ | ||
154 | |||
155 | int debugfs_umount(void) | ||
156 | { | ||
157 | char umountcmd[128]; | ||
158 | int ret; | ||
159 | |||
160 | /* if it was already mounted, leave it */ | ||
161 | if (debugfs_premounted) | ||
162 | return 0; | ||
163 | |||
164 | /* make sure it's a valid mount point */ | ||
165 | ret = debugfs_valid_mountpoint(debugfs_mountpoint); | ||
166 | if (ret) | ||
167 | return ret; | ||
168 | |||
169 | snprintf(umountcmd, sizeof(umountcmd), | ||
170 | "/bin/umount %s", debugfs_mountpoint); | ||
171 | return system(umountcmd); | ||
172 | } | ||
173 | |||
174 | int debugfs_write(const char *entry, const char *value) | ||
175 | { | ||
176 | char path[PATH_MAX + 1]; | ||
177 | int ret, count; | ||
178 | int fd; | ||
179 | |||
180 | /* construct the path */ | ||
181 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
182 | |||
183 | /* verify that it exists */ | ||
184 | ret = debugfs_valid_entry(path); | ||
185 | if (ret) | ||
186 | return ret; | ||
187 | |||
188 | /* get how many chars we're going to write */ | ||
189 | count = strlen(value); | ||
190 | |||
191 | /* open the debugfs entry */ | ||
192 | fd = open(path, O_RDWR); | ||
193 | if (fd < 0) | ||
194 | return -errno; | ||
195 | |||
196 | while (count > 0) { | ||
197 | /* write it */ | ||
198 | ret = write(fd, value, count); | ||
199 | if (ret <= 0) { | ||
200 | if (ret == EAGAIN) | ||
201 | continue; | ||
202 | close(fd); | ||
203 | return -errno; | ||
204 | } | ||
205 | count -= ret; | ||
206 | } | ||
207 | |||
208 | /* close it */ | ||
209 | close(fd); | ||
210 | |||
211 | /* return success */ | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * read a debugfs entry | ||
217 | * returns the number of chars read or a negative errno | ||
218 | */ | ||
219 | int debugfs_read(const char *entry, char *buffer, size_t size) | ||
220 | { | ||
221 | char path[PATH_MAX + 1]; | ||
222 | int ret; | ||
223 | int fd; | ||
224 | |||
225 | /* construct the path */ | ||
226 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
227 | |||
228 | /* verify that it exists */ | ||
229 | ret = debugfs_valid_entry(path); | ||
230 | if (ret) | ||
231 | return ret; | ||
232 | |||
233 | /* open the debugfs entry */ | ||
234 | fd = open(path, O_RDONLY); | ||
235 | if (fd < 0) | ||
236 | return -errno; | ||
237 | |||
238 | do { | ||
239 | /* read it */ | ||
240 | ret = read(fd, buffer, size); | ||
241 | if (ret == 0) { | ||
242 | close(fd); | ||
243 | return EOF; | ||
244 | } | ||
245 | } while (ret < 0 && errno == EAGAIN); | ||
246 | |||
247 | /* close it */ | ||
248 | close(fd); | ||
249 | |||
250 | /* make *sure* there's a null character at the end */ | ||
251 | buffer[ret] = '\0'; | ||
252 | |||
253 | /* return the number of chars read */ | ||
254 | return ret; | ||
255 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h index 4a878f735eb0..68f3e87ec57f 100644 --- a/tools/perf/util/debugfs.h +++ b/tools/perf/util/debugfs.h | |||
@@ -3,14 +3,8 @@ | |||
3 | 3 | ||
4 | const char *debugfs_find_mountpoint(void); | 4 | const char *debugfs_find_mountpoint(void); |
5 | int debugfs_valid_mountpoint(const char *debugfs); | 5 | int debugfs_valid_mountpoint(const char *debugfs); |
6 | int debugfs_valid_entry(const char *path); | ||
7 | char *debugfs_mount(const char *mountpoint); | 6 | char *debugfs_mount(const char *mountpoint); |
8 | int debugfs_umount(void); | ||
9 | void debugfs_set_path(const char *mountpoint); | 7 | void debugfs_set_path(const char *mountpoint); |
10 | int debugfs_write(const char *entry, const char *value); | ||
11 | int debugfs_read(const char *entry, char *buffer, size_t size); | ||
12 | void debugfs_force_cleanup(void); | ||
13 | int debugfs_make_path(const char *element, char *buffer, int size); | ||
14 | 8 | ||
15 | extern char debugfs_mountpoint[]; | 9 | extern char debugfs_mountpoint[]; |
16 | extern char tracing_events_path[]; | 10 | extern char tracing_events_path[]; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index cbdeaad9c5e5..1b197280c621 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -81,6 +81,7 @@ struct perf_sample { | |||
81 | u32 raw_size; | 81 | u32 raw_size; |
82 | void *raw_data; | 82 | void *raw_data; |
83 | struct ip_callchain *callchain; | 83 | struct ip_callchain *callchain; |
84 | struct branch_stack *branch_stack; | ||
84 | }; | 85 | }; |
85 | 86 | ||
86 | #define BUILD_ID_SIZE 20 | 87 | #define BUILD_ID_SIZE 20 |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ea32a061f1c8..159263d17c2d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -97,9 +97,9 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | |||
97 | ++evlist->nr_entries; | 97 | ++evlist->nr_entries; |
98 | } | 98 | } |
99 | 99 | ||
100 | static void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 100 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
101 | struct list_head *list, | 101 | struct list_head *list, |
102 | int nr_entries) | 102 | int nr_entries) |
103 | { | 103 | { |
104 | list_splice_tail(list, &evlist->entries); | 104 | list_splice_tail(list, &evlist->entries); |
105 | evlist->nr_entries += nr_entries; | 105 | evlist->nr_entries += nr_entries; |
@@ -597,15 +597,15 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
597 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 597 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
598 | } | 598 | } |
599 | 599 | ||
600 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 600 | int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, |
601 | pid_t target_tid, const char *cpu_list) | 601 | const char *target_tid, uid_t uid, const char *cpu_list) |
602 | { | 602 | { |
603 | evlist->threads = thread_map__new(target_pid, target_tid); | 603 | evlist->threads = thread_map__new_str(target_pid, target_tid, uid); |
604 | 604 | ||
605 | if (evlist->threads == NULL) | 605 | if (evlist->threads == NULL) |
606 | return -1; | 606 | return -1; |
607 | 607 | ||
608 | if (cpu_list == NULL && target_tid != -1) | 608 | if (uid != UINT_MAX || (cpu_list == NULL && target_tid)) |
609 | evlist->cpus = cpu_map__dummy_new(); | 609 | evlist->cpus = cpu_map__dummy_new(); |
610 | else | 610 | else |
611 | evlist->cpus = cpu_map__new(cpu_list); | 611 | evlist->cpus = cpu_map__new(cpu_list); |
@@ -765,6 +765,7 @@ out_err: | |||
765 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | 765 | list_for_each_entry_reverse(evsel, &evlist->entries, node) |
766 | perf_evsel__close(evsel, ncpus, nthreads); | 766 | perf_evsel__close(evsel, ncpus, nthreads); |
767 | 767 | ||
768 | errno = -err; | ||
768 | return err; | 769 | return err; |
769 | } | 770 | } |
770 | 771 | ||
@@ -824,7 +825,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
824 | exit(-1); | 825 | exit(-1); |
825 | } | 826 | } |
826 | 827 | ||
827 | if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1) | 828 | if (!opts->system_wide && !opts->target_tid && !opts->target_pid) |
828 | evlist->threads->map[0] = evlist->workload.pid; | 829 | evlist->threads->map[0] = evlist->workload.pid; |
829 | 830 | ||
830 | close(child_ready_pipe[1]); | 831 | close(child_ready_pipe[1]); |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 8922aeed0467..21f1c9e57f13 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
106 | evlist->threads = threads; | 106 | evlist->threads = threads; |
107 | } | 107 | } |
108 | 108 | ||
109 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 109 | int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, |
110 | pid_t target_tid, const char *cpu_list); | 110 | const char *tid, uid_t uid, const char *cpu_list); |
111 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 111 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
112 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 112 | int perf_evlist__set_filters(struct perf_evlist *evlist); |
113 | 113 | ||
@@ -117,4 +117,9 @@ u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist); | |||
117 | 117 | ||
118 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); | 118 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); |
119 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); | 119 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); |
120 | |||
121 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | ||
122 | struct list_head *list, | ||
123 | int nr_entries); | ||
124 | |||
120 | #endif /* __PERF_EVLIST_H */ | 125 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 7132ee834e0e..f421f7cbc0d3 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -68,7 +68,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
68 | struct perf_event_attr *attr = &evsel->attr; | 68 | struct perf_event_attr *attr = &evsel->attr; |
69 | int track = !evsel->idx; /* only the first counter needs these */ | 69 | int track = !evsel->idx; /* only the first counter needs these */ |
70 | 70 | ||
71 | attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; | 71 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; |
72 | attr->inherit = !opts->no_inherit; | 72 | attr->inherit = !opts->no_inherit; |
73 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 73 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
74 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 74 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -111,7 +111,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
111 | if (opts->period) | 111 | if (opts->period) |
112 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 112 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
113 | 113 | ||
114 | if (opts->sample_id_all_avail && | 114 | if (!opts->sample_id_all_missing && |
115 | (opts->sample_time || opts->system_wide || | 115 | (opts->sample_time || opts->system_wide || |
116 | !opts->no_inherit || opts->cpu_list)) | 116 | !opts->no_inherit || opts->cpu_list)) |
117 | attr->sample_type |= PERF_SAMPLE_TIME; | 117 | attr->sample_type |= PERF_SAMPLE_TIME; |
@@ -126,11 +126,15 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
126 | attr->watermark = 0; | 126 | attr->watermark = 0; |
127 | attr->wakeup_events = 1; | 127 | attr->wakeup_events = 1; |
128 | } | 128 | } |
129 | if (opts->branch_stack) { | ||
130 | attr->sample_type |= PERF_SAMPLE_BRANCH_STACK; | ||
131 | attr->branch_sample_type = opts->branch_stack; | ||
132 | } | ||
129 | 133 | ||
130 | attr->mmap = track; | 134 | attr->mmap = track; |
131 | attr->comm = track; | 135 | attr->comm = track; |
132 | 136 | ||
133 | if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) { | 137 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide) { |
134 | attr->disabled = 1; | 138 | attr->disabled = 1; |
135 | attr->enable_on_exec = 1; | 139 | attr->enable_on_exec = 1; |
136 | } | 140 | } |
@@ -536,7 +540,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
536 | } | 540 | } |
537 | 541 | ||
538 | if (type & PERF_SAMPLE_READ) { | 542 | if (type & PERF_SAMPLE_READ) { |
539 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | 543 | fprintf(stderr, "PERF_SAMPLE_READ is unsupported for now\n"); |
540 | return -1; | 544 | return -1; |
541 | } | 545 | } |
542 | 546 | ||
@@ -576,6 +580,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
576 | data->raw_data = (void *) pdata; | 580 | data->raw_data = (void *) pdata; |
577 | } | 581 | } |
578 | 582 | ||
583 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
584 | u64 sz; | ||
585 | |||
586 | data->branch_stack = (struct branch_stack *)array; | ||
587 | array++; /* nr */ | ||
588 | |||
589 | sz = data->branch_stack->nr * sizeof(struct branch_entry); | ||
590 | sz /= sizeof(u64); | ||
591 | array += sz; | ||
592 | } | ||
579 | return 0; | 593 | return 0; |
580 | } | 594 | } |
581 | 595 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 14bb035c5fd9..fcd9cf3ea63e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -63,9 +63,20 @@ char *perf_header__find_event(u64 id) | |||
63 | return NULL; | 63 | return NULL; |
64 | } | 64 | } |
65 | 65 | ||
66 | static const char *__perf_magic = "PERFFILE"; | 66 | /* |
67 | * magic2 = "PERFILE2" | ||
68 | * must be a numerical value to let the endianness | ||
69 | * determine the memory layout. That way we are able | ||
70 | * to detect endianness when reading the perf.data file | ||
71 | * back. | ||
72 | * | ||
73 | * we check for legacy (PERFFILE) format. | ||
74 | */ | ||
75 | static const char *__perf_magic1 = "PERFFILE"; | ||
76 | static const u64 __perf_magic2 = 0x32454c4946524550ULL; | ||
77 | static const u64 __perf_magic2_sw = 0x50455246494c4532ULL; | ||
67 | 78 | ||
68 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 79 | #define PERF_MAGIC __perf_magic2 |
69 | 80 | ||
70 | struct perf_file_attr { | 81 | struct perf_file_attr { |
71 | struct perf_event_attr attr; | 82 | struct perf_event_attr attr; |
@@ -1012,6 +1023,12 @@ write_it: | |||
1012 | return do_write_string(fd, buffer); | 1023 | return do_write_string(fd, buffer); |
1013 | } | 1024 | } |
1014 | 1025 | ||
1026 | static int write_branch_stack(int fd __used, struct perf_header *h __used, | ||
1027 | struct perf_evlist *evlist __used) | ||
1028 | { | ||
1029 | return 0; | ||
1030 | } | ||
1031 | |||
1015 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | 1032 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) |
1016 | { | 1033 | { |
1017 | char *str = do_read_string(fd, ph); | 1034 | char *str = do_read_string(fd, ph); |
@@ -1133,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1133 | uint64_t id; | 1150 | uint64_t id; |
1134 | void *buf = NULL; | 1151 | void *buf = NULL; |
1135 | char *str; | 1152 | char *str; |
1136 | u32 nre, sz, nr, i, j, msz; | 1153 | u32 nre, sz, nr, i, j; |
1137 | int ret; | 1154 | ssize_t ret; |
1155 | size_t msz; | ||
1138 | 1156 | ||
1139 | /* number of events */ | 1157 | /* number of events */ |
1140 | ret = read(fd, &nre, sizeof(nre)); | 1158 | ret = read(fd, &nre, sizeof(nre)); |
@@ -1151,25 +1169,23 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1151 | if (ph->needs_swap) | 1169 | if (ph->needs_swap) |
1152 | sz = bswap_32(sz); | 1170 | sz = bswap_32(sz); |
1153 | 1171 | ||
1154 | /* | ||
1155 | * ensure it is at least to our ABI rev | ||
1156 | */ | ||
1157 | if (sz < (u32)sizeof(attr)) | ||
1158 | goto error; | ||
1159 | |||
1160 | memset(&attr, 0, sizeof(attr)); | 1172 | memset(&attr, 0, sizeof(attr)); |
1161 | 1173 | ||
1162 | /* read entire region to sync up to next field */ | 1174 | /* buffer to hold on file attr struct */ |
1163 | buf = malloc(sz); | 1175 | buf = malloc(sz); |
1164 | if (!buf) | 1176 | if (!buf) |
1165 | goto error; | 1177 | goto error; |
1166 | 1178 | ||
1167 | msz = sizeof(attr); | 1179 | msz = sizeof(attr); |
1168 | if (sz < msz) | 1180 | if (sz < (ssize_t)msz) |
1169 | msz = sz; | 1181 | msz = sz; |
1170 | 1182 | ||
1171 | for (i = 0 ; i < nre; i++) { | 1183 | for (i = 0 ; i < nre; i++) { |
1172 | 1184 | ||
1185 | /* | ||
1186 | * must read entire on-file attr struct to | ||
1187 | * sync up with layout. | ||
1188 | */ | ||
1173 | ret = read(fd, buf, sz); | 1189 | ret = read(fd, buf, sz); |
1174 | if (ret != (ssize_t)sz) | 1190 | if (ret != (ssize_t)sz) |
1175 | goto error; | 1191 | goto error; |
@@ -1305,25 +1321,204 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | |||
1305 | free(str); | 1321 | free(str); |
1306 | } | 1322 | } |
1307 | 1323 | ||
1324 | static void print_branch_stack(struct perf_header *ph __used, int fd __used, | ||
1325 | FILE *fp) | ||
1326 | { | ||
1327 | fprintf(fp, "# contains samples with branch stack\n"); | ||
1328 | } | ||
1329 | |||
1330 | static int __event_process_build_id(struct build_id_event *bev, | ||
1331 | char *filename, | ||
1332 | struct perf_session *session) | ||
1333 | { | ||
1334 | int err = -1; | ||
1335 | struct list_head *head; | ||
1336 | struct machine *machine; | ||
1337 | u16 misc; | ||
1338 | struct dso *dso; | ||
1339 | enum dso_kernel_type dso_type; | ||
1340 | |||
1341 | machine = perf_session__findnew_machine(session, bev->pid); | ||
1342 | if (!machine) | ||
1343 | goto out; | ||
1344 | |||
1345 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1346 | |||
1347 | switch (misc) { | ||
1348 | case PERF_RECORD_MISC_KERNEL: | ||
1349 | dso_type = DSO_TYPE_KERNEL; | ||
1350 | head = &machine->kernel_dsos; | ||
1351 | break; | ||
1352 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
1353 | dso_type = DSO_TYPE_GUEST_KERNEL; | ||
1354 | head = &machine->kernel_dsos; | ||
1355 | break; | ||
1356 | case PERF_RECORD_MISC_USER: | ||
1357 | case PERF_RECORD_MISC_GUEST_USER: | ||
1358 | dso_type = DSO_TYPE_USER; | ||
1359 | head = &machine->user_dsos; | ||
1360 | break; | ||
1361 | default: | ||
1362 | goto out; | ||
1363 | } | ||
1364 | |||
1365 | dso = __dsos__findnew(head, filename); | ||
1366 | if (dso != NULL) { | ||
1367 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1368 | |||
1369 | dso__set_build_id(dso, &bev->build_id); | ||
1370 | |||
1371 | if (filename[0] == '[') | ||
1372 | dso->kernel = dso_type; | ||
1373 | |||
1374 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | ||
1375 | sbuild_id); | ||
1376 | pr_debug("build id event received for %s: %s\n", | ||
1377 | dso->long_name, sbuild_id); | ||
1378 | } | ||
1379 | |||
1380 | err = 0; | ||
1381 | out: | ||
1382 | return err; | ||
1383 | } | ||
1384 | |||
1385 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | ||
1386 | int input, u64 offset, u64 size) | ||
1387 | { | ||
1388 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1389 | struct { | ||
1390 | struct perf_event_header header; | ||
1391 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
1392 | char filename[0]; | ||
1393 | } old_bev; | ||
1394 | struct build_id_event bev; | ||
1395 | char filename[PATH_MAX]; | ||
1396 | u64 limit = offset + size; | ||
1397 | |||
1398 | while (offset < limit) { | ||
1399 | ssize_t len; | ||
1400 | |||
1401 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | ||
1402 | return -1; | ||
1403 | |||
1404 | if (header->needs_swap) | ||
1405 | perf_event_header__bswap(&old_bev.header); | ||
1406 | |||
1407 | len = old_bev.header.size - sizeof(old_bev); | ||
1408 | if (read(input, filename, len) != len) | ||
1409 | return -1; | ||
1410 | |||
1411 | bev.header = old_bev.header; | ||
1412 | |||
1413 | /* | ||
1414 | * As the pid is the missing value, we need to fill | ||
1415 | * it properly. The header.misc value give us nice hint. | ||
1416 | */ | ||
1417 | bev.pid = HOST_KERNEL_ID; | ||
1418 | if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || | ||
1419 | bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) | ||
1420 | bev.pid = DEFAULT_GUEST_KERNEL_ID; | ||
1421 | |||
1422 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | ||
1423 | __event_process_build_id(&bev, filename, session); | ||
1424 | |||
1425 | offset += bev.header.size; | ||
1426 | } | ||
1427 | |||
1428 | return 0; | ||
1429 | } | ||
1430 | |||
1431 | static int perf_header__read_build_ids(struct perf_header *header, | ||
1432 | int input, u64 offset, u64 size) | ||
1433 | { | ||
1434 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1435 | struct build_id_event bev; | ||
1436 | char filename[PATH_MAX]; | ||
1437 | u64 limit = offset + size, orig_offset = offset; | ||
1438 | int err = -1; | ||
1439 | |||
1440 | while (offset < limit) { | ||
1441 | ssize_t len; | ||
1442 | |||
1443 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
1444 | goto out; | ||
1445 | |||
1446 | if (header->needs_swap) | ||
1447 | perf_event_header__bswap(&bev.header); | ||
1448 | |||
1449 | len = bev.header.size - sizeof(bev); | ||
1450 | if (read(input, filename, len) != len) | ||
1451 | goto out; | ||
1452 | /* | ||
1453 | * The a1645ce1 changeset: | ||
1454 | * | ||
1455 | * "perf: 'perf kvm' tool for monitoring guest performance from host" | ||
1456 | * | ||
1457 | * Added a field to struct build_id_event that broke the file | ||
1458 | * format. | ||
1459 | * | ||
1460 | * Since the kernel build-id is the first entry, process the | ||
1461 | * table using the old format if the well known | ||
1462 | * '[kernel.kallsyms]' string for the kernel build-id has the | ||
1463 | * first 4 characters chopped off (where the pid_t sits). | ||
1464 | */ | ||
1465 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { | ||
1466 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) | ||
1467 | return -1; | ||
1468 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); | ||
1469 | } | ||
1470 | |||
1471 | __event_process_build_id(&bev, filename, session); | ||
1472 | |||
1473 | offset += bev.header.size; | ||
1474 | } | ||
1475 | err = 0; | ||
1476 | out: | ||
1477 | return err; | ||
1478 | } | ||
1479 | |||
1480 | static int process_trace_info(struct perf_file_section *section __unused, | ||
1481 | struct perf_header *ph __unused, | ||
1482 | int feat __unused, int fd) | ||
1483 | { | ||
1484 | trace_report(fd, false); | ||
1485 | return 0; | ||
1486 | } | ||
1487 | |||
1488 | static int process_build_id(struct perf_file_section *section, | ||
1489 | struct perf_header *ph, | ||
1490 | int feat __unused, int fd) | ||
1491 | { | ||
1492 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | ||
1493 | pr_debug("Failed to read buildids, continuing...\n"); | ||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1308 | struct feature_ops { | 1497 | struct feature_ops { |
1309 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 1498 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1310 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 1499 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
1500 | int (*process)(struct perf_file_section *section, | ||
1501 | struct perf_header *h, int feat, int fd); | ||
1311 | const char *name; | 1502 | const char *name; |
1312 | bool full_only; | 1503 | bool full_only; |
1313 | }; | 1504 | }; |
1314 | 1505 | ||
1315 | #define FEAT_OPA(n, func) \ | 1506 | #define FEAT_OPA(n, func) \ |
1316 | [n] = { .name = #n, .write = write_##func, .print = print_##func } | 1507 | [n] = { .name = #n, .write = write_##func, .print = print_##func } |
1508 | #define FEAT_OPP(n, func) \ | ||
1509 | [n] = { .name = #n, .write = write_##func, .print = print_##func, \ | ||
1510 | .process = process_##func } | ||
1317 | #define FEAT_OPF(n, func) \ | 1511 | #define FEAT_OPF(n, func) \ |
1318 | [n] = { .name = #n, .write = write_##func, .print = print_##func, .full_only = true } | 1512 | [n] = { .name = #n, .write = write_##func, .print = print_##func, \ |
1513 | .full_only = true } | ||
1319 | 1514 | ||
1320 | /* feature_ops not implemented: */ | 1515 | /* feature_ops not implemented: */ |
1321 | #define print_trace_info NULL | 1516 | #define print_trace_info NULL |
1322 | #define print_build_id NULL | 1517 | #define print_build_id NULL |
1323 | 1518 | ||
1324 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | 1519 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { |
1325 | FEAT_OPA(HEADER_TRACE_INFO, trace_info), | 1520 | FEAT_OPP(HEADER_TRACE_INFO, trace_info), |
1326 | FEAT_OPA(HEADER_BUILD_ID, build_id), | 1521 | FEAT_OPP(HEADER_BUILD_ID, build_id), |
1327 | FEAT_OPA(HEADER_HOSTNAME, hostname), | 1522 | FEAT_OPA(HEADER_HOSTNAME, hostname), |
1328 | FEAT_OPA(HEADER_OSRELEASE, osrelease), | 1523 | FEAT_OPA(HEADER_OSRELEASE, osrelease), |
1329 | FEAT_OPA(HEADER_VERSION, version), | 1524 | FEAT_OPA(HEADER_VERSION, version), |
@@ -1336,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1336 | FEAT_OPA(HEADER_CMDLINE, cmdline), | 1531 | FEAT_OPA(HEADER_CMDLINE, cmdline), |
1337 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), | 1532 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), |
1338 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 1533 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
1534 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | ||
1339 | }; | 1535 | }; |
1340 | 1536 | ||
1341 | struct header_print_data { | 1537 | struct header_print_data { |
@@ -1620,24 +1816,128 @@ out_free: | |||
1620 | return err; | 1816 | return err; |
1621 | } | 1817 | } |
1622 | 1818 | ||
1819 | static const int attr_file_abi_sizes[] = { | ||
1820 | [0] = PERF_ATTR_SIZE_VER0, | ||
1821 | [1] = PERF_ATTR_SIZE_VER1, | ||
1822 | 0, | ||
1823 | }; | ||
1824 | |||
1825 | /* | ||
1826 | * In the legacy file format, the magic number is not used to encode endianness. | ||
1827 | * hdr_sz was used to encode endianness. But given that hdr_sz can vary based | ||
1828 | * on ABI revisions, we need to try all combinations for all endianness to | ||
1829 | * detect the endianness. | ||
1830 | */ | ||
1831 | static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph) | ||
1832 | { | ||
1833 | uint64_t ref_size, attr_size; | ||
1834 | int i; | ||
1835 | |||
1836 | for (i = 0 ; attr_file_abi_sizes[i]; i++) { | ||
1837 | ref_size = attr_file_abi_sizes[i] | ||
1838 | + sizeof(struct perf_file_section); | ||
1839 | if (hdr_sz != ref_size) { | ||
1840 | attr_size = bswap_64(hdr_sz); | ||
1841 | if (attr_size != ref_size) | ||
1842 | continue; | ||
1843 | |||
1844 | ph->needs_swap = true; | ||
1845 | } | ||
1846 | pr_debug("ABI%d perf.data file detected, need_swap=%d\n", | ||
1847 | i, | ||
1848 | ph->needs_swap); | ||
1849 | return 0; | ||
1850 | } | ||
1851 | /* could not determine endianness */ | ||
1852 | return -1; | ||
1853 | } | ||
1854 | |||
1855 | #define PERF_PIPE_HDR_VER0 16 | ||
1856 | |||
1857 | static const size_t attr_pipe_abi_sizes[] = { | ||
1858 | [0] = PERF_PIPE_HDR_VER0, | ||
1859 | 0, | ||
1860 | }; | ||
1861 | |||
1862 | /* | ||
1863 | * In the legacy pipe format, there is an implicit assumption that endiannesss | ||
1864 | * between host recording the samples, and host parsing the samples is the | ||
1865 | * same. This is not always the case given that the pipe output may always be | ||
1866 | * redirected into a file and analyzed on a different machine with possibly a | ||
1867 | * different endianness and perf_event ABI revsions in the perf tool itself. | ||
1868 | */ | ||
1869 | static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph) | ||
1870 | { | ||
1871 | u64 attr_size; | ||
1872 | int i; | ||
1873 | |||
1874 | for (i = 0 ; attr_pipe_abi_sizes[i]; i++) { | ||
1875 | if (hdr_sz != attr_pipe_abi_sizes[i]) { | ||
1876 | attr_size = bswap_64(hdr_sz); | ||
1877 | if (attr_size != hdr_sz) | ||
1878 | continue; | ||
1879 | |||
1880 | ph->needs_swap = true; | ||
1881 | } | ||
1882 | pr_debug("Pipe ABI%d perf.data file detected\n", i); | ||
1883 | return 0; | ||
1884 | } | ||
1885 | return -1; | ||
1886 | } | ||
1887 | |||
1888 | static int check_magic_endian(u64 magic, uint64_t hdr_sz, | ||
1889 | bool is_pipe, struct perf_header *ph) | ||
1890 | { | ||
1891 | int ret; | ||
1892 | |||
1893 | /* check for legacy format */ | ||
1894 | ret = memcmp(&magic, __perf_magic1, sizeof(magic)); | ||
1895 | if (ret == 0) { | ||
1896 | pr_debug("legacy perf.data format\n"); | ||
1897 | if (is_pipe) | ||
1898 | return try_all_pipe_abis(hdr_sz, ph); | ||
1899 | |||
1900 | return try_all_file_abis(hdr_sz, ph); | ||
1901 | } | ||
1902 | /* | ||
1903 | * the new magic number serves two purposes: | ||
1904 | * - unique number to identify actual perf.data files | ||
1905 | * - encode endianness of file | ||
1906 | */ | ||
1907 | |||
1908 | /* check magic number with one endianness */ | ||
1909 | if (magic == __perf_magic2) | ||
1910 | return 0; | ||
1911 | |||
1912 | /* check magic number with opposite endianness */ | ||
1913 | if (magic != __perf_magic2_sw) | ||
1914 | return -1; | ||
1915 | |||
1916 | ph->needs_swap = true; | ||
1917 | |||
1918 | return 0; | ||
1919 | } | ||
1920 | |||
1623 | int perf_file_header__read(struct perf_file_header *header, | 1921 | int perf_file_header__read(struct perf_file_header *header, |
1624 | struct perf_header *ph, int fd) | 1922 | struct perf_header *ph, int fd) |
1625 | { | 1923 | { |
1924 | int ret; | ||
1925 | |||
1626 | lseek(fd, 0, SEEK_SET); | 1926 | lseek(fd, 0, SEEK_SET); |
1627 | 1927 | ||
1628 | if (readn(fd, header, sizeof(*header)) <= 0 || | 1928 | ret = readn(fd, header, sizeof(*header)); |
1629 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) | 1929 | if (ret <= 0) |
1630 | return -1; | 1930 | return -1; |
1631 | 1931 | ||
1632 | if (header->attr_size != sizeof(struct perf_file_attr)) { | 1932 | if (check_magic_endian(header->magic, |
1633 | u64 attr_size = bswap_64(header->attr_size); | 1933 | header->attr_size, false, ph) < 0) { |
1634 | 1934 | pr_debug("magic/endian check failed\n"); | |
1635 | if (attr_size != sizeof(struct perf_file_attr)) | 1935 | return -1; |
1636 | return -1; | 1936 | } |
1637 | 1937 | ||
1938 | if (ph->needs_swap) { | ||
1638 | mem_bswap_64(header, offsetof(struct perf_file_header, | 1939 | mem_bswap_64(header, offsetof(struct perf_file_header, |
1639 | adds_features)); | 1940 | adds_features)); |
1640 | ph->needs_swap = true; | ||
1641 | } | 1941 | } |
1642 | 1942 | ||
1643 | if (header->size != sizeof(*header)) { | 1943 | if (header->size != sizeof(*header)) { |
@@ -1689,156 +1989,6 @@ int perf_file_header__read(struct perf_file_header *header, | |||
1689 | return 0; | 1989 | return 0; |
1690 | } | 1990 | } |
1691 | 1991 | ||
1692 | static int __event_process_build_id(struct build_id_event *bev, | ||
1693 | char *filename, | ||
1694 | struct perf_session *session) | ||
1695 | { | ||
1696 | int err = -1; | ||
1697 | struct list_head *head; | ||
1698 | struct machine *machine; | ||
1699 | u16 misc; | ||
1700 | struct dso *dso; | ||
1701 | enum dso_kernel_type dso_type; | ||
1702 | |||
1703 | machine = perf_session__findnew_machine(session, bev->pid); | ||
1704 | if (!machine) | ||
1705 | goto out; | ||
1706 | |||
1707 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1708 | |||
1709 | switch (misc) { | ||
1710 | case PERF_RECORD_MISC_KERNEL: | ||
1711 | dso_type = DSO_TYPE_KERNEL; | ||
1712 | head = &machine->kernel_dsos; | ||
1713 | break; | ||
1714 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
1715 | dso_type = DSO_TYPE_GUEST_KERNEL; | ||
1716 | head = &machine->kernel_dsos; | ||
1717 | break; | ||
1718 | case PERF_RECORD_MISC_USER: | ||
1719 | case PERF_RECORD_MISC_GUEST_USER: | ||
1720 | dso_type = DSO_TYPE_USER; | ||
1721 | head = &machine->user_dsos; | ||
1722 | break; | ||
1723 | default: | ||
1724 | goto out; | ||
1725 | } | ||
1726 | |||
1727 | dso = __dsos__findnew(head, filename); | ||
1728 | if (dso != NULL) { | ||
1729 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1730 | |||
1731 | dso__set_build_id(dso, &bev->build_id); | ||
1732 | |||
1733 | if (filename[0] == '[') | ||
1734 | dso->kernel = dso_type; | ||
1735 | |||
1736 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | ||
1737 | sbuild_id); | ||
1738 | pr_debug("build id event received for %s: %s\n", | ||
1739 | dso->long_name, sbuild_id); | ||
1740 | } | ||
1741 | |||
1742 | err = 0; | ||
1743 | out: | ||
1744 | return err; | ||
1745 | } | ||
1746 | |||
1747 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | ||
1748 | int input, u64 offset, u64 size) | ||
1749 | { | ||
1750 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1751 | struct { | ||
1752 | struct perf_event_header header; | ||
1753 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
1754 | char filename[0]; | ||
1755 | } old_bev; | ||
1756 | struct build_id_event bev; | ||
1757 | char filename[PATH_MAX]; | ||
1758 | u64 limit = offset + size; | ||
1759 | |||
1760 | while (offset < limit) { | ||
1761 | ssize_t len; | ||
1762 | |||
1763 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | ||
1764 | return -1; | ||
1765 | |||
1766 | if (header->needs_swap) | ||
1767 | perf_event_header__bswap(&old_bev.header); | ||
1768 | |||
1769 | len = old_bev.header.size - sizeof(old_bev); | ||
1770 | if (read(input, filename, len) != len) | ||
1771 | return -1; | ||
1772 | |||
1773 | bev.header = old_bev.header; | ||
1774 | |||
1775 | /* | ||
1776 | * As the pid is the missing value, we need to fill | ||
1777 | * it properly. The header.misc value give us nice hint. | ||
1778 | */ | ||
1779 | bev.pid = HOST_KERNEL_ID; | ||
1780 | if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || | ||
1781 | bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) | ||
1782 | bev.pid = DEFAULT_GUEST_KERNEL_ID; | ||
1783 | |||
1784 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | ||
1785 | __event_process_build_id(&bev, filename, session); | ||
1786 | |||
1787 | offset += bev.header.size; | ||
1788 | } | ||
1789 | |||
1790 | return 0; | ||
1791 | } | ||
1792 | |||
1793 | static int perf_header__read_build_ids(struct perf_header *header, | ||
1794 | int input, u64 offset, u64 size) | ||
1795 | { | ||
1796 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1797 | struct build_id_event bev; | ||
1798 | char filename[PATH_MAX]; | ||
1799 | u64 limit = offset + size, orig_offset = offset; | ||
1800 | int err = -1; | ||
1801 | |||
1802 | while (offset < limit) { | ||
1803 | ssize_t len; | ||
1804 | |||
1805 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
1806 | goto out; | ||
1807 | |||
1808 | if (header->needs_swap) | ||
1809 | perf_event_header__bswap(&bev.header); | ||
1810 | |||
1811 | len = bev.header.size - sizeof(bev); | ||
1812 | if (read(input, filename, len) != len) | ||
1813 | goto out; | ||
1814 | /* | ||
1815 | * The a1645ce1 changeset: | ||
1816 | * | ||
1817 | * "perf: 'perf kvm' tool for monitoring guest performance from host" | ||
1818 | * | ||
1819 | * Added a field to struct build_id_event that broke the file | ||
1820 | * format. | ||
1821 | * | ||
1822 | * Since the kernel build-id is the first entry, process the | ||
1823 | * table using the old format if the well known | ||
1824 | * '[kernel.kallsyms]' string for the kernel build-id has the | ||
1825 | * first 4 characters chopped off (where the pid_t sits). | ||
1826 | */ | ||
1827 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { | ||
1828 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) | ||
1829 | return -1; | ||
1830 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); | ||
1831 | } | ||
1832 | |||
1833 | __event_process_build_id(&bev, filename, session); | ||
1834 | |||
1835 | offset += bev.header.size; | ||
1836 | } | ||
1837 | err = 0; | ||
1838 | out: | ||
1839 | return err; | ||
1840 | } | ||
1841 | |||
1842 | static int perf_file_section__process(struct perf_file_section *section, | 1992 | static int perf_file_section__process(struct perf_file_section *section, |
1843 | struct perf_header *ph, | 1993 | struct perf_header *ph, |
1844 | int feat, int fd, void *data __used) | 1994 | int feat, int fd, void *data __used) |
@@ -1854,40 +2004,32 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
1854 | return 0; | 2004 | return 0; |
1855 | } | 2005 | } |
1856 | 2006 | ||
1857 | switch (feat) { | 2007 | if (!feat_ops[feat].process) |
1858 | case HEADER_TRACE_INFO: | 2008 | return 0; |
1859 | trace_report(fd, false); | ||
1860 | break; | ||
1861 | case HEADER_BUILD_ID: | ||
1862 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | ||
1863 | pr_debug("Failed to read buildids, continuing...\n"); | ||
1864 | break; | ||
1865 | default: | ||
1866 | break; | ||
1867 | } | ||
1868 | 2009 | ||
1869 | return 0; | 2010 | return feat_ops[feat].process(section, ph, feat, fd); |
1870 | } | 2011 | } |
1871 | 2012 | ||
1872 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, | 2013 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, |
1873 | struct perf_header *ph, int fd, | 2014 | struct perf_header *ph, int fd, |
1874 | bool repipe) | 2015 | bool repipe) |
1875 | { | 2016 | { |
1876 | if (readn(fd, header, sizeof(*header)) <= 0 || | 2017 | int ret; |
1877 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) | ||
1878 | return -1; | ||
1879 | 2018 | ||
1880 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) | 2019 | ret = readn(fd, header, sizeof(*header)); |
2020 | if (ret <= 0) | ||
1881 | return -1; | 2021 | return -1; |
1882 | 2022 | ||
1883 | if (header->size != sizeof(*header)) { | 2023 | if (check_magic_endian(header->magic, header->size, true, ph) < 0) { |
1884 | u64 size = bswap_64(header->size); | 2024 | pr_debug("endian/magic failed\n"); |
2025 | return -1; | ||
2026 | } | ||
1885 | 2027 | ||
1886 | if (size != sizeof(*header)) | 2028 | if (ph->needs_swap) |
1887 | return -1; | 2029 | header->size = bswap_64(header->size); |
1888 | 2030 | ||
1889 | ph->needs_swap = true; | 2031 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) |
1890 | } | 2032 | return -1; |
1891 | 2033 | ||
1892 | return 0; | 2034 | return 0; |
1893 | } | 2035 | } |
@@ -1908,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd) | |||
1908 | return 0; | 2050 | return 0; |
1909 | } | 2051 | } |
1910 | 2052 | ||
2053 | static int read_attr(int fd, struct perf_header *ph, | ||
2054 | struct perf_file_attr *f_attr) | ||
2055 | { | ||
2056 | struct perf_event_attr *attr = &f_attr->attr; | ||
2057 | size_t sz, left; | ||
2058 | size_t our_sz = sizeof(f_attr->attr); | ||
2059 | int ret; | ||
2060 | |||
2061 | memset(f_attr, 0, sizeof(*f_attr)); | ||
2062 | |||
2063 | /* read minimal guaranteed structure */ | ||
2064 | ret = readn(fd, attr, PERF_ATTR_SIZE_VER0); | ||
2065 | if (ret <= 0) { | ||
2066 | pr_debug("cannot read %d bytes of header attr\n", | ||
2067 | PERF_ATTR_SIZE_VER0); | ||
2068 | return -1; | ||
2069 | } | ||
2070 | |||
2071 | /* on file perf_event_attr size */ | ||
2072 | sz = attr->size; | ||
2073 | |||
2074 | if (ph->needs_swap) | ||
2075 | sz = bswap_32(sz); | ||
2076 | |||
2077 | if (sz == 0) { | ||
2078 | /* assume ABI0 */ | ||
2079 | sz = PERF_ATTR_SIZE_VER0; | ||
2080 | } else if (sz > our_sz) { | ||
2081 | pr_debug("file uses a more recent and unsupported ABI" | ||
2082 | " (%zu bytes extra)\n", sz - our_sz); | ||
2083 | return -1; | ||
2084 | } | ||
2085 | /* what we have not yet read and that we know about */ | ||
2086 | left = sz - PERF_ATTR_SIZE_VER0; | ||
2087 | if (left) { | ||
2088 | void *ptr = attr; | ||
2089 | ptr += PERF_ATTR_SIZE_VER0; | ||
2090 | |||
2091 | ret = readn(fd, ptr, left); | ||
2092 | } | ||
2093 | /* read perf_file_section, ids are read in caller */ | ||
2094 | ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids)); | ||
2095 | |||
2096 | return ret <= 0 ? -1 : 0; | ||
2097 | } | ||
2098 | |||
1911 | int perf_session__read_header(struct perf_session *session, int fd) | 2099 | int perf_session__read_header(struct perf_session *session, int fd) |
1912 | { | 2100 | { |
1913 | struct perf_header *header = &session->header; | 2101 | struct perf_header *header = &session->header; |
@@ -1923,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
1923 | if (session->fd_pipe) | 2111 | if (session->fd_pipe) |
1924 | return perf_header__read_pipe(session, fd); | 2112 | return perf_header__read_pipe(session, fd); |
1925 | 2113 | ||
1926 | if (perf_file_header__read(&f_header, header, fd) < 0) { | 2114 | if (perf_file_header__read(&f_header, header, fd) < 0) |
1927 | pr_debug("incompatible file format\n"); | ||
1928 | return -EINVAL; | 2115 | return -EINVAL; |
1929 | } | ||
1930 | 2116 | ||
1931 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 2117 | nr_attrs = f_header.attrs.size / f_header.attr_size; |
1932 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 2118 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
1933 | 2119 | ||
1934 | for (i = 0; i < nr_attrs; i++) { | 2120 | for (i = 0; i < nr_attrs; i++) { |
1935 | struct perf_evsel *evsel; | 2121 | struct perf_evsel *evsel; |
1936 | off_t tmp; | 2122 | off_t tmp; |
1937 | 2123 | ||
1938 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) | 2124 | if (read_attr(fd, header, &f_attr) < 0) |
1939 | goto out_errno; | 2125 | goto out_errno; |
1940 | 2126 | ||
1941 | if (header->needs_swap) | 2127 | if (header->needs_swap) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index ac4ec956024e..21a6be09c129 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | enum { | 12 | enum { |
13 | HEADER_RESERVED = 0, /* always cleared */ | 13 | HEADER_RESERVED = 0, /* always cleared */ |
14 | HEADER_FIRST_FEATURE = 1, | ||
14 | HEADER_TRACE_INFO = 1, | 15 | HEADER_TRACE_INFO = 1, |
15 | HEADER_BUILD_ID, | 16 | HEADER_BUILD_ID, |
16 | 17 | ||
@@ -26,7 +27,7 @@ enum { | |||
26 | HEADER_EVENT_DESC, | 27 | HEADER_EVENT_DESC, |
27 | HEADER_CPU_TOPOLOGY, | 28 | HEADER_CPU_TOPOLOGY, |
28 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
29 | 30 | HEADER_BRANCH_STACK, | |
30 | HEADER_LAST_FEATURE, | 31 | HEADER_LAST_FEATURE, |
31 | HEADER_FEAT_BITS = 256, | 32 | HEADER_FEAT_BITS = 256, |
32 | }; | 33 | }; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e11e482bd185..3dc99a9b71f5 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists) | |||
50 | hists__set_col_len(hists, col, 0); | 50 | hists__set_col_len(hists, col, 0); |
51 | } | 51 | } |
52 | 52 | ||
53 | static void hists__set_unres_dso_col_len(struct hists *hists, int dso) | ||
54 | { | ||
55 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
56 | |||
57 | if (hists__col_len(hists, dso) < unresolved_col_width && | ||
58 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
59 | !symbol_conf.dso_list) | ||
60 | hists__set_col_len(hists, dso, unresolved_col_width); | ||
61 | } | ||
62 | |||
53 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | 63 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
54 | { | 64 | { |
65 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
55 | u16 len; | 66 | u16 len; |
56 | 67 | ||
57 | if (h->ms.sym) | 68 | if (h->ms.sym) |
58 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); | 69 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); |
59 | else { | 70 | else |
60 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 71 | hists__set_unres_dso_col_len(hists, HISTC_DSO); |
61 | |||
62 | if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && | ||
63 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
64 | !symbol_conf.dso_list) | ||
65 | hists__set_col_len(hists, HISTC_DSO, | ||
66 | unresolved_col_width); | ||
67 | } | ||
68 | 72 | ||
69 | len = thread__comm_len(h->thread); | 73 | len = thread__comm_len(h->thread); |
70 | if (hists__new_col_len(hists, HISTC_COMM, len)) | 74 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
@@ -74,6 +78,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
74 | len = dso__name_len(h->ms.map->dso); | 78 | len = dso__name_len(h->ms.map->dso); |
75 | hists__new_col_len(hists, HISTC_DSO, len); | 79 | hists__new_col_len(hists, HISTC_DSO, len); |
76 | } | 80 | } |
81 | |||
82 | if (h->branch_info) { | ||
83 | int symlen; | ||
84 | /* | ||
85 | * +4 accounts for '[x] ' priv level info | ||
86 | * +2 account of 0x prefix on raw addresses | ||
87 | */ | ||
88 | if (h->branch_info->from.sym) { | ||
89 | symlen = (int)h->branch_info->from.sym->namelen + 4; | ||
90 | hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); | ||
91 | |||
92 | symlen = dso__name_len(h->branch_info->from.map->dso); | ||
93 | hists__new_col_len(hists, HISTC_DSO_FROM, symlen); | ||
94 | } else { | ||
95 | symlen = unresolved_col_width + 4 + 2; | ||
96 | hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); | ||
97 | hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM); | ||
98 | } | ||
99 | |||
100 | if (h->branch_info->to.sym) { | ||
101 | symlen = (int)h->branch_info->to.sym->namelen + 4; | ||
102 | hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); | ||
103 | |||
104 | symlen = dso__name_len(h->branch_info->to.map->dso); | ||
105 | hists__new_col_len(hists, HISTC_DSO_TO, symlen); | ||
106 | } else { | ||
107 | symlen = unresolved_col_width + 4 + 2; | ||
108 | hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); | ||
109 | hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); | ||
110 | } | ||
111 | } | ||
77 | } | 112 | } |
78 | 113 | ||
79 | static void hist_entry__add_cpumode_period(struct hist_entry *he, | 114 | static void hist_entry__add_cpumode_period(struct hist_entry *he, |
@@ -195,26 +230,14 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
195 | return 0; | 230 | return 0; |
196 | } | 231 | } |
197 | 232 | ||
198 | struct hist_entry *__hists__add_entry(struct hists *hists, | 233 | static struct hist_entry *add_hist_entry(struct hists *hists, |
234 | struct hist_entry *entry, | ||
199 | struct addr_location *al, | 235 | struct addr_location *al, |
200 | struct symbol *sym_parent, u64 period) | 236 | u64 period) |
201 | { | 237 | { |
202 | struct rb_node **p; | 238 | struct rb_node **p; |
203 | struct rb_node *parent = NULL; | 239 | struct rb_node *parent = NULL; |
204 | struct hist_entry *he; | 240 | struct hist_entry *he; |
205 | struct hist_entry entry = { | ||
206 | .thread = al->thread, | ||
207 | .ms = { | ||
208 | .map = al->map, | ||
209 | .sym = al->sym, | ||
210 | }, | ||
211 | .cpu = al->cpu, | ||
212 | .ip = al->addr, | ||
213 | .level = al->level, | ||
214 | .period = period, | ||
215 | .parent = sym_parent, | ||
216 | .filtered = symbol__parent_filter(sym_parent), | ||
217 | }; | ||
218 | int cmp; | 241 | int cmp; |
219 | 242 | ||
220 | pthread_mutex_lock(&hists->lock); | 243 | pthread_mutex_lock(&hists->lock); |
@@ -225,7 +248,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
225 | parent = *p; | 248 | parent = *p; |
226 | he = rb_entry(parent, struct hist_entry, rb_node_in); | 249 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
227 | 250 | ||
228 | cmp = hist_entry__cmp(&entry, he); | 251 | cmp = hist_entry__cmp(entry, he); |
229 | 252 | ||
230 | if (!cmp) { | 253 | if (!cmp) { |
231 | he->period += period; | 254 | he->period += period; |
@@ -239,7 +262,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
239 | p = &(*p)->rb_right; | 262 | p = &(*p)->rb_right; |
240 | } | 263 | } |
241 | 264 | ||
242 | he = hist_entry__new(&entry); | 265 | he = hist_entry__new(entry); |
243 | if (!he) | 266 | if (!he) |
244 | goto out_unlock; | 267 | goto out_unlock; |
245 | 268 | ||
@@ -252,6 +275,51 @@ out_unlock: | |||
252 | return he; | 275 | return he; |
253 | } | 276 | } |
254 | 277 | ||
278 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | ||
279 | struct addr_location *al, | ||
280 | struct symbol *sym_parent, | ||
281 | struct branch_info *bi, | ||
282 | u64 period) | ||
283 | { | ||
284 | struct hist_entry entry = { | ||
285 | .thread = al->thread, | ||
286 | .ms = { | ||
287 | .map = bi->to.map, | ||
288 | .sym = bi->to.sym, | ||
289 | }, | ||
290 | .cpu = al->cpu, | ||
291 | .ip = bi->to.addr, | ||
292 | .level = al->level, | ||
293 | .period = period, | ||
294 | .parent = sym_parent, | ||
295 | .filtered = symbol__parent_filter(sym_parent), | ||
296 | .branch_info = bi, | ||
297 | }; | ||
298 | |||
299 | return add_hist_entry(self, &entry, al, period); | ||
300 | } | ||
301 | |||
302 | struct hist_entry *__hists__add_entry(struct hists *self, | ||
303 | struct addr_location *al, | ||
304 | struct symbol *sym_parent, u64 period) | ||
305 | { | ||
306 | struct hist_entry entry = { | ||
307 | .thread = al->thread, | ||
308 | .ms = { | ||
309 | .map = al->map, | ||
310 | .sym = al->sym, | ||
311 | }, | ||
312 | .cpu = al->cpu, | ||
313 | .ip = al->addr, | ||
314 | .level = al->level, | ||
315 | .period = period, | ||
316 | .parent = sym_parent, | ||
317 | .filtered = symbol__parent_filter(sym_parent), | ||
318 | }; | ||
319 | |||
320 | return add_hist_entry(self, &entry, al, period); | ||
321 | } | ||
322 | |||
255 | int64_t | 323 | int64_t |
256 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 324 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
257 | { | 325 | { |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f55f0a8d1f81..9413f3e31fea 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -32,6 +32,7 @@ struct events_stats { | |||
32 | u32 nr_unknown_events; | 32 | u32 nr_unknown_events; |
33 | u32 nr_invalid_chains; | 33 | u32 nr_invalid_chains; |
34 | u32 nr_unknown_id; | 34 | u32 nr_unknown_id; |
35 | u32 nr_unprocessable_samples; | ||
35 | }; | 36 | }; |
36 | 37 | ||
37 | enum hist_column { | 38 | enum hist_column { |
@@ -41,6 +42,11 @@ enum hist_column { | |||
41 | HISTC_COMM, | 42 | HISTC_COMM, |
42 | HISTC_PARENT, | 43 | HISTC_PARENT, |
43 | HISTC_CPU, | 44 | HISTC_CPU, |
45 | HISTC_MISPREDICT, | ||
46 | HISTC_SYMBOL_FROM, | ||
47 | HISTC_SYMBOL_TO, | ||
48 | HISTC_DSO_FROM, | ||
49 | HISTC_DSO_TO, | ||
44 | HISTC_NR_COLS, /* Last entry */ | 50 | HISTC_NR_COLS, /* Last entry */ |
45 | }; | 51 | }; |
46 | 52 | ||
@@ -55,6 +61,7 @@ struct hists { | |||
55 | u64 nr_entries; | 61 | u64 nr_entries; |
56 | const struct thread *thread_filter; | 62 | const struct thread *thread_filter; |
57 | const struct dso *dso_filter; | 63 | const struct dso *dso_filter; |
64 | const char *uid_filter_str; | ||
58 | pthread_mutex_t lock; | 65 | pthread_mutex_t lock; |
59 | struct events_stats stats; | 66 | struct events_stats stats; |
60 | u64 event_stream; | 67 | u64 event_stream; |
@@ -72,6 +79,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | |||
72 | struct hists *hists); | 79 | struct hists *hists); |
73 | void hist_entry__free(struct hist_entry *); | 80 | void hist_entry__free(struct hist_entry *); |
74 | 81 | ||
82 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | ||
83 | struct addr_location *al, | ||
84 | struct symbol *sym_parent, | ||
85 | struct branch_info *bi, | ||
86 | u64 period); | ||
87 | |||
75 | void hists__output_resort(struct hists *self); | 88 | void hists__output_resort(struct hists *self); |
76 | void hists__output_resort_threaded(struct hists *hists); | 89 | void hists__output_resort_threaded(struct hists *hists); |
77 | void hists__collapse_resort(struct hists *self); | 90 | void hists__collapse_resort(struct hists *self); |
diff --git a/tools/perf/util/include/asm/dwarf2.h b/tools/perf/util/include/asm/dwarf2.h index bb4198e7837a..afe38199e922 100644 --- a/tools/perf/util/include/asm/dwarf2.h +++ b/tools/perf/util/include/asm/dwarf2.h | |||
@@ -2,10 +2,12 @@ | |||
2 | #ifndef PERF_DWARF2_H | 2 | #ifndef PERF_DWARF2_H |
3 | #define PERF_DWARF2_H | 3 | #define PERF_DWARF2_H |
4 | 4 | ||
5 | /* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */ | 5 | /* dwarf2.h ... dummy header file for including arch/x86/lib/mem{cpy,set}_64.S */ |
6 | 6 | ||
7 | #define CFI_STARTPROC | 7 | #define CFI_STARTPROC |
8 | #define CFI_ENDPROC | 8 | #define CFI_ENDPROC |
9 | #define CFI_REMEMBER_STATE | ||
10 | #define CFI_RESTORE_STATE | ||
9 | 11 | ||
10 | #endif /* PERF_DWARF2_H */ | 12 | #endif /* PERF_DWARF2_H */ |
11 | 13 | ||
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index eda4416efa0a..bb162e40c76c 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/bitops.h> | 5 | #include <linux/bitops.h> |
6 | 6 | ||
7 | int __bitmap_weight(const unsigned long *bitmap, int bits); | 7 | int __bitmap_weight(const unsigned long *bitmap, int bits); |
8 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | ||
9 | const unsigned long *bitmap2, int bits); | ||
8 | 10 | ||
9 | #define BITMAP_LAST_WORD_MASK(nbits) \ | 11 | #define BITMAP_LAST_WORD_MASK(nbits) \ |
10 | ( \ | 12 | ( \ |
@@ -32,4 +34,13 @@ static inline int bitmap_weight(const unsigned long *src, int nbits) | |||
32 | return __bitmap_weight(src, nbits); | 34 | return __bitmap_weight(src, nbits); |
33 | } | 35 | } |
34 | 36 | ||
37 | static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, | ||
38 | const unsigned long *src2, int nbits) | ||
39 | { | ||
40 | if (small_const_nbits(nbits)) | ||
41 | *dst = *src1 | *src2; | ||
42 | else | ||
43 | __bitmap_or(dst, src1, src2, nbits); | ||
44 | } | ||
45 | |||
35 | #endif /* _PERF_BITOPS_H */ | 46 | #endif /* _PERF_BITOPS_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 316aa0ab7122..dea6d1c1a954 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -212,6 +212,21 @@ size_t map__fprintf(struct map *self, FILE *fp) | |||
212 | self->start, self->end, self->pgoff, self->dso->name); | 212 | self->start, self->end, self->pgoff, self->dso->name); |
213 | } | 213 | } |
214 | 214 | ||
215 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) | ||
216 | { | ||
217 | const char *dsoname; | ||
218 | |||
219 | if (map && map->dso && (map->dso->name || map->dso->long_name)) { | ||
220 | if (symbol_conf.show_kernel_path && map->dso->long_name) | ||
221 | dsoname = map->dso->long_name; | ||
222 | else if (map->dso->name) | ||
223 | dsoname = map->dso->name; | ||
224 | } else | ||
225 | dsoname = "[unknown]"; | ||
226 | |||
227 | return fprintf(fp, "%s", dsoname); | ||
228 | } | ||
229 | |||
215 | /* | 230 | /* |
216 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | 231 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. |
217 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. | 232 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2b8017f8a930..b100c20b7f94 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -118,6 +118,7 @@ void map__delete(struct map *self); | |||
118 | struct map *map__clone(struct map *self); | 118 | struct map *map__clone(struct map *self); |
119 | int map__overlap(struct map *l, struct map *r); | 119 | int map__overlap(struct map *l, struct map *r); |
120 | size_t map__fprintf(struct map *self, FILE *fp); | 120 | size_t map__fprintf(struct map *self, FILE *fp); |
121 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); | ||
121 | 122 | ||
122 | int map__load(struct map *self, symbol_filter_t filter); | 123 | int map__load(struct map *self, symbol_filter_t filter); |
123 | struct symbol *map__find_symbol(struct map *self, | 124 | struct symbol *map__find_symbol(struct map *self, |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e33554a562b3..8a8ee64e72d1 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -34,7 +34,6 @@ | |||
34 | 34 | ||
35 | #include "util.h" | 35 | #include "util.h" |
36 | #include "event.h" | 36 | #include "event.h" |
37 | #include "string.h" | ||
38 | #include "strlist.h" | 37 | #include "strlist.h" |
39 | #include "debug.h" | 38 | #include "debug.h" |
40 | #include "cache.h" | 39 | #include "cache.h" |
@@ -273,10 +272,10 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | |||
273 | /* Try to find perf_probe_event with debuginfo */ | 272 | /* Try to find perf_probe_event with debuginfo */ |
274 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 273 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
275 | struct probe_trace_event **tevs, | 274 | struct probe_trace_event **tevs, |
276 | int max_tevs, const char *module) | 275 | int max_tevs, const char *target) |
277 | { | 276 | { |
278 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 277 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
279 | struct debuginfo *dinfo = open_debuginfo(module); | 278 | struct debuginfo *dinfo = open_debuginfo(target); |
280 | int ntevs, ret = 0; | 279 | int ntevs, ret = 0; |
281 | 280 | ||
282 | if (!dinfo) { | 281 | if (!dinfo) { |
@@ -295,9 +294,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
295 | 294 | ||
296 | if (ntevs > 0) { /* Succeeded to find trace events */ | 295 | if (ntevs > 0) { /* Succeeded to find trace events */ |
297 | pr_debug("find %d probe_trace_events.\n", ntevs); | 296 | pr_debug("find %d probe_trace_events.\n", ntevs); |
298 | if (module) | 297 | if (target) |
299 | ret = add_module_to_probe_trace_events(*tevs, ntevs, | 298 | ret = add_module_to_probe_trace_events(*tevs, ntevs, |
300 | module); | 299 | target); |
301 | return ret < 0 ? ret : ntevs; | 300 | return ret < 0 ? ret : ntevs; |
302 | } | 301 | } |
303 | 302 | ||
@@ -1729,7 +1728,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1729 | } | 1728 | } |
1730 | 1729 | ||
1731 | ret = 0; | 1730 | ret = 0; |
1732 | printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); | 1731 | printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); |
1733 | for (i = 0; i < ntevs; i++) { | 1732 | for (i = 0; i < ntevs; i++) { |
1734 | tev = &tevs[i]; | 1733 | tev = &tevs[i]; |
1735 | if (pev->event) | 1734 | if (pev->event) |
@@ -1784,7 +1783,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1784 | 1783 | ||
1785 | if (ret >= 0) { | 1784 | if (ret >= 0) { |
1786 | /* Show how to use the event. */ | 1785 | /* Show how to use the event. */ |
1787 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | 1786 | printf("\nYou can now use it in all perf tools, such as:\n\n"); |
1788 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, | 1787 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, |
1789 | tev->event); | 1788 | tev->event); |
1790 | } | 1789 | } |
@@ -1796,14 +1795,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1796 | 1795 | ||
1797 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 1796 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
1798 | struct probe_trace_event **tevs, | 1797 | struct probe_trace_event **tevs, |
1799 | int max_tevs, const char *module) | 1798 | int max_tevs, const char *target) |
1800 | { | 1799 | { |
1801 | struct symbol *sym; | 1800 | struct symbol *sym; |
1802 | int ret = 0, i; | 1801 | int ret = 0, i; |
1803 | struct probe_trace_event *tev; | 1802 | struct probe_trace_event *tev; |
1804 | 1803 | ||
1805 | /* Convert perf_probe_event with debuginfo */ | 1804 | /* Convert perf_probe_event with debuginfo */ |
1806 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); | 1805 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); |
1807 | if (ret != 0) | 1806 | if (ret != 0) |
1808 | return ret; /* Found in debuginfo or got an error */ | 1807 | return ret; /* Found in debuginfo or got an error */ |
1809 | 1808 | ||
@@ -1819,8 +1818,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1819 | goto error; | 1818 | goto error; |
1820 | } | 1819 | } |
1821 | 1820 | ||
1822 | if (module) { | 1821 | if (target) { |
1823 | tev->point.module = strdup(module); | 1822 | tev->point.module = strdup(target); |
1824 | if (tev->point.module == NULL) { | 1823 | if (tev->point.module == NULL) { |
1825 | ret = -ENOMEM; | 1824 | ret = -ENOMEM; |
1826 | goto error; | 1825 | goto error; |
@@ -1890,7 +1889,7 @@ struct __event_package { | |||
1890 | }; | 1889 | }; |
1891 | 1890 | ||
1892 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 1891 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
1893 | int max_tevs, const char *module, bool force_add) | 1892 | int max_tevs, const char *target, bool force_add) |
1894 | { | 1893 | { |
1895 | int i, j, ret; | 1894 | int i, j, ret; |
1896 | struct __event_package *pkgs; | 1895 | struct __event_package *pkgs; |
@@ -1913,7 +1912,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1913 | ret = convert_to_probe_trace_events(pkgs[i].pev, | 1912 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1914 | &pkgs[i].tevs, | 1913 | &pkgs[i].tevs, |
1915 | max_tevs, | 1914 | max_tevs, |
1916 | module); | 1915 | target); |
1917 | if (ret < 0) | 1916 | if (ret < 0) |
1918 | goto end; | 1917 | goto end; |
1919 | pkgs[i].ntevs = ret; | 1918 | pkgs[i].ntevs = ret; |
@@ -1965,7 +1964,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
1965 | goto error; | 1964 | goto error; |
1966 | } | 1965 | } |
1967 | 1966 | ||
1968 | printf("Remove event: %s\n", ent->s); | 1967 | printf("Removed event: %s\n", ent->s); |
1969 | return 0; | 1968 | return 0; |
1970 | error: | 1969 | error: |
1971 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); | 1970 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); |
@@ -2069,7 +2068,7 @@ static int filter_available_functions(struct map *map __unused, | |||
2069 | return 1; | 2068 | return 1; |
2070 | } | 2069 | } |
2071 | 2070 | ||
2072 | int show_available_funcs(const char *module, struct strfilter *_filter) | 2071 | int show_available_funcs(const char *target, struct strfilter *_filter) |
2073 | { | 2072 | { |
2074 | struct map *map; | 2073 | struct map *map; |
2075 | int ret; | 2074 | int ret; |
@@ -2080,9 +2079,9 @@ int show_available_funcs(const char *module, struct strfilter *_filter) | |||
2080 | if (ret < 0) | 2079 | if (ret < 0) |
2081 | return ret; | 2080 | return ret; |
2082 | 2081 | ||
2083 | map = kernel_get_module_map(module); | 2082 | map = kernel_get_module_map(target); |
2084 | if (!map) { | 2083 | if (!map) { |
2085 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | 2084 | pr_err("Failed to find %s map.\n", (target) ? : "kernel"); |
2086 | return -EINVAL; | 2085 | return -EINVAL; |
2087 | } | 2086 | } |
2088 | available_func_filter = _filter; | 2087 | available_func_filter = _filter; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 74bd2e63c4b4..2cc162d3b78c 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <ctype.h> | ||
34 | #include <dwarf-regs.h> | 33 | #include <dwarf-regs.h> |
35 | 34 | ||
36 | #include <linux/bitops.h> | 35 | #include <linux/bitops.h> |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources new file mode 100644 index 000000000000..2884e67ee625 --- /dev/null +++ b/tools/perf/util/python-ext-sources | |||
@@ -0,0 +1,19 @@ | |||
1 | # | ||
2 | # List of files needed by perf python extention | ||
3 | # | ||
4 | # Each source file must be placed on its own line so that it can be | ||
5 | # processed by Makefile and util/setup.py accordingly. | ||
6 | # | ||
7 | |||
8 | util/python.c | ||
9 | util/ctype.c | ||
10 | util/evlist.c | ||
11 | util/evsel.c | ||
12 | util/cpumap.c | ||
13 | util/thread_map.c | ||
14 | util/util.c | ||
15 | util/xyarray.c | ||
16 | util/cgroup.c | ||
17 | util/debugfs.c | ||
18 | util/strlist.c | ||
19 | ../../lib/rbtree.c | ||
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 9dd47a4f2596..e03b58a48424 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -425,14 +425,14 @@ struct pyrf_thread_map { | |||
425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | 425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, |
426 | PyObject *args, PyObject *kwargs) | 426 | PyObject *args, PyObject *kwargs) |
427 | { | 427 | { |
428 | static char *kwlist[] = { "pid", "tid", NULL }; | 428 | static char *kwlist[] = { "pid", "tid", "uid", NULL }; |
429 | int pid = -1, tid = -1; | 429 | int pid = -1, tid = -1, uid = UINT_MAX; |
430 | 430 | ||
431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | 431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", |
432 | kwlist, &pid, &tid)) | 432 | kwlist, &pid, &tid, &uid)) |
433 | return -1; | 433 | return -1; |
434 | 434 | ||
435 | pthreads->threads = thread_map__new(pid, tid); | 435 | pthreads->threads = thread_map__new(pid, tid, uid); |
436 | if (pthreads->threads == NULL) | 436 | if (pthreads->threads == NULL) |
437 | return -1; | 437 | return -1; |
438 | return 0; | 438 | return 0; |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 0b2a48783172..c2623c6f9b51 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <stdio.h> | 24 | #include <stdio.h> |
25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
26 | #include <string.h> | 26 | #include <string.h> |
27 | #include <ctype.h> | ||
28 | #include <errno.h> | 27 | #include <errno.h> |
29 | 28 | ||
30 | #include "../../perf.h" | 29 | #include "../../perf.h" |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b5ca2558c7bb..002ebbf59f48 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
24 | self->fd = STDIN_FILENO; | 24 | self->fd = STDIN_FILENO; |
25 | 25 | ||
26 | if (perf_session__read_header(self, self->fd) < 0) | 26 | if (perf_session__read_header(self, self->fd) < 0) |
27 | pr_err("incompatible file format"); | 27 | pr_err("incompatible file format (rerun with -v to learn more)"); |
28 | 28 | ||
29 | return 0; | 29 | return 0; |
30 | } | 30 | } |
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
56 | } | 56 | } |
57 | 57 | ||
58 | if (perf_session__read_header(self, self->fd) < 0) { | 58 | if (perf_session__read_header(self, self->fd) < 0) { |
59 | pr_err("incompatible file format"); | 59 | pr_err("incompatible file format (rerun with -v to learn more)"); |
60 | goto out_close; | 60 | goto out_close; |
61 | } | 61 | } |
62 | 62 | ||
@@ -229,6 +229,64 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
229 | return 0; | 229 | return 0; |
230 | } | 230 | } |
231 | 231 | ||
232 | static const u8 cpumodes[] = { | ||
233 | PERF_RECORD_MISC_USER, | ||
234 | PERF_RECORD_MISC_KERNEL, | ||
235 | PERF_RECORD_MISC_GUEST_USER, | ||
236 | PERF_RECORD_MISC_GUEST_KERNEL | ||
237 | }; | ||
238 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
239 | |||
240 | static void ip__resolve_ams(struct machine *self, struct thread *thread, | ||
241 | struct addr_map_symbol *ams, | ||
242 | u64 ip) | ||
243 | { | ||
244 | struct addr_location al; | ||
245 | size_t i; | ||
246 | u8 m; | ||
247 | |||
248 | memset(&al, 0, sizeof(al)); | ||
249 | |||
250 | for (i = 0; i < NCPUMODES; i++) { | ||
251 | m = cpumodes[i]; | ||
252 | /* | ||
253 | * We cannot use the header.misc hint to determine whether a | ||
254 | * branch stack address is user, kernel, guest, hypervisor. | ||
255 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
256 | * Thus, we have to try consecutively until we find a match | ||
257 | * or else, the symbol is unknown | ||
258 | */ | ||
259 | thread__find_addr_location(thread, self, m, MAP__FUNCTION, | ||
260 | ip, &al, NULL); | ||
261 | if (al.sym) | ||
262 | goto found; | ||
263 | } | ||
264 | found: | ||
265 | ams->addr = ip; | ||
266 | ams->al_addr = al.addr; | ||
267 | ams->sym = al.sym; | ||
268 | ams->map = al.map; | ||
269 | } | ||
270 | |||
271 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
272 | struct thread *thr, | ||
273 | struct branch_stack *bs) | ||
274 | { | ||
275 | struct branch_info *bi; | ||
276 | unsigned int i; | ||
277 | |||
278 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
279 | if (!bi) | ||
280 | return NULL; | ||
281 | |||
282 | for (i = 0; i < bs->nr; i++) { | ||
283 | ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to); | ||
284 | ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from); | ||
285 | bi[i].flags = bs->entries[i].flags; | ||
286 | } | ||
287 | return bi; | ||
288 | } | ||
289 | |||
232 | int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, | 290 | int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, |
233 | struct thread *thread, | 291 | struct thread *thread, |
234 | struct ip_callchain *chain, | 292 | struct ip_callchain *chain, |
@@ -697,6 +755,18 @@ static void callchain__printf(struct perf_sample *sample) | |||
697 | i, sample->callchain->ips[i]); | 755 | i, sample->callchain->ips[i]); |
698 | } | 756 | } |
699 | 757 | ||
758 | static void branch_stack__printf(struct perf_sample *sample) | ||
759 | { | ||
760 | uint64_t i; | ||
761 | |||
762 | printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr); | ||
763 | |||
764 | for (i = 0; i < sample->branch_stack->nr; i++) | ||
765 | printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n", | ||
766 | i, sample->branch_stack->entries[i].from, | ||
767 | sample->branch_stack->entries[i].to); | ||
768 | } | ||
769 | |||
700 | static void perf_session__print_tstamp(struct perf_session *session, | 770 | static void perf_session__print_tstamp(struct perf_session *session, |
701 | union perf_event *event, | 771 | union perf_event *event, |
702 | struct perf_sample *sample) | 772 | struct perf_sample *sample) |
@@ -744,6 +814,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event, | |||
744 | 814 | ||
745 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 815 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
746 | callchain__printf(sample); | 816 | callchain__printf(sample); |
817 | |||
818 | if (session->sample_type & PERF_SAMPLE_BRANCH_STACK) | ||
819 | branch_stack__printf(sample); | ||
747 | } | 820 | } |
748 | 821 | ||
749 | static struct machine * | 822 | static struct machine * |
@@ -796,6 +869,10 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
796 | ++session->hists.stats.nr_unknown_id; | 869 | ++session->hists.stats.nr_unknown_id; |
797 | return -1; | 870 | return -1; |
798 | } | 871 | } |
872 | if (machine == NULL) { | ||
873 | ++session->hists.stats.nr_unprocessable_samples; | ||
874 | return -1; | ||
875 | } | ||
799 | return tool->sample(tool, event, sample, evsel, machine); | 876 | return tool->sample(tool, event, sample, evsel, machine); |
800 | case PERF_RECORD_MMAP: | 877 | case PERF_RECORD_MMAP: |
801 | return tool->mmap(tool, event, sample, machine); | 878 | return tool->mmap(tool, event, sample, machine); |
@@ -964,6 +1041,12 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
964 | session->hists.stats.nr_invalid_chains, | 1041 | session->hists.stats.nr_invalid_chains, |
965 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | 1042 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); |
966 | } | 1043 | } |
1044 | |||
1045 | if (session->hists.stats.nr_unprocessable_samples != 0) { | ||
1046 | ui__warning("%u unprocessable samples recorded.\n" | ||
1047 | "Do you have a KVM guest running and not using 'perf kvm'?\n", | ||
1048 | session->hists.stats.nr_unprocessable_samples); | ||
1049 | } | ||
967 | } | 1050 | } |
968 | 1051 | ||
969 | #define session_done() (*(volatile int *)(&session_done)) | 1052 | #define session_done() (*(volatile int *)(&session_done)) |
@@ -1293,10 +1376,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
1293 | 1376 | ||
1294 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | 1377 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, |
1295 | struct machine *machine, struct perf_evsel *evsel, | 1378 | struct machine *machine, struct perf_evsel *evsel, |
1296 | int print_sym, int print_dso) | 1379 | int print_sym, int print_dso, int print_symoffset) |
1297 | { | 1380 | { |
1298 | struct addr_location al; | 1381 | struct addr_location al; |
1299 | const char *symname, *dsoname; | ||
1300 | struct callchain_cursor *cursor = &evsel->hists.callchain_cursor; | 1382 | struct callchain_cursor *cursor = &evsel->hists.callchain_cursor; |
1301 | struct callchain_cursor_node *node; | 1383 | struct callchain_cursor_node *node; |
1302 | 1384 | ||
@@ -1324,20 +1406,13 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | |||
1324 | 1406 | ||
1325 | printf("\t%16" PRIx64, node->ip); | 1407 | printf("\t%16" PRIx64, node->ip); |
1326 | if (print_sym) { | 1408 | if (print_sym) { |
1327 | if (node->sym && node->sym->name) | 1409 | printf(" "); |
1328 | symname = node->sym->name; | 1410 | symbol__fprintf_symname(node->sym, stdout); |
1329 | else | ||
1330 | symname = ""; | ||
1331 | |||
1332 | printf(" %s", symname); | ||
1333 | } | 1411 | } |
1334 | if (print_dso) { | 1412 | if (print_dso) { |
1335 | if (node->map && node->map->dso && node->map->dso->name) | 1413 | printf(" ("); |
1336 | dsoname = node->map->dso->name; | 1414 | map__fprintf_dsoname(al.map, stdout); |
1337 | else | 1415 | printf(")"); |
1338 | dsoname = ""; | ||
1339 | |||
1340 | printf(" (%s)", dsoname); | ||
1341 | } | 1416 | } |
1342 | printf("\n"); | 1417 | printf("\n"); |
1343 | 1418 | ||
@@ -1347,21 +1422,18 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | |||
1347 | } else { | 1422 | } else { |
1348 | printf("%16" PRIx64, sample->ip); | 1423 | printf("%16" PRIx64, sample->ip); |
1349 | if (print_sym) { | 1424 | if (print_sym) { |
1350 | if (al.sym && al.sym->name) | 1425 | printf(" "); |
1351 | symname = al.sym->name; | 1426 | if (print_symoffset) |
1427 | symbol__fprintf_symname_offs(al.sym, &al, | ||
1428 | stdout); | ||
1352 | else | 1429 | else |
1353 | symname = ""; | 1430 | symbol__fprintf_symname(al.sym, stdout); |
1354 | |||
1355 | printf(" %s", symname); | ||
1356 | } | 1431 | } |
1357 | 1432 | ||
1358 | if (print_dso) { | 1433 | if (print_dso) { |
1359 | if (al.map && al.map->dso && al.map->dso->name) | 1434 | printf(" ("); |
1360 | dsoname = al.map->dso->name; | 1435 | map__fprintf_dsoname(al.map, stdout); |
1361 | else | 1436 | printf(")"); |
1362 | dsoname = ""; | ||
1363 | |||
1364 | printf(" (%s)", dsoname); | ||
1365 | } | 1437 | } |
1366 | } | 1438 | } |
1367 | } | 1439 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 37bc38381fb6..7a5434c00565 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel | |||
73 | struct ip_callchain *chain, | 73 | struct ip_callchain *chain, |
74 | struct symbol **parent); | 74 | struct symbol **parent); |
75 | 75 | ||
76 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
77 | struct thread *thread, | ||
78 | struct branch_stack *bs); | ||
79 | |||
76 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 80 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
77 | 81 | ||
78 | void mem_bswap_64(void *src, int byte_size); | 82 | void mem_bswap_64(void *src, int byte_size); |
@@ -147,7 +151,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
147 | 151 | ||
148 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | 152 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, |
149 | struct machine *machine, struct perf_evsel *evsel, | 153 | struct machine *machine, struct perf_evsel *evsel, |
150 | int print_sym, int print_dso); | 154 | int print_sym, int print_dso, int print_symoffset); |
151 | 155 | ||
152 | int perf_session__cpu_bitmap(struct perf_session *session, | 156 | int perf_session__cpu_bitmap(struct perf_session *session, |
153 | const char *cpu_list, unsigned long *cpu_bitmap); | 157 | const char *cpu_list, unsigned long *cpu_bitmap); |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 36d4c5619575..d0f9f29cf181 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -24,11 +24,11 @@ cflags += getenv('CFLAGS', '').split() | |||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | 24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') |
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | 25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') |
26 | 26 | ||
27 | ext_sources = [f.strip() for f in file('util/python-ext-sources') | ||
28 | if len(f.strip()) > 0 and f[0] != '#'] | ||
29 | |||
27 | perf = Extension('perf', | 30 | perf = Extension('perf', |
28 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | 31 | sources = ext_sources, |
29 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | ||
30 | 'util/util.c', 'util/xyarray.c', 'util/cgroup.c', | ||
31 | 'util/debugfs.c'], | ||
32 | include_dirs = ['util/include'], | 32 | include_dirs = ['util/include'], |
33 | extra_compile_args = cflags, | 33 | extra_compile_args = cflags, |
34 | ) | 34 | ) |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 076c9d4e1ea4..a27237430c5f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -8,6 +8,7 @@ const char default_sort_order[] = "comm,dso,symbol"; | |||
8 | const char *sort_order = default_sort_order; | 8 | const char *sort_order = default_sort_order; |
9 | int sort__need_collapse = 0; | 9 | int sort__need_collapse = 0; |
10 | int sort__has_parent = 0; | 10 | int sort__has_parent = 0; |
11 | int sort__branch_mode = -1; /* -1 = means not set */ | ||
11 | 12 | ||
12 | enum sort_type sort__first_dimension; | 13 | enum sort_type sort__first_dimension; |
13 | 14 | ||
@@ -97,6 +98,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | |||
97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 98 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
98 | } | 99 | } |
99 | 100 | ||
101 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | ||
102 | { | ||
103 | struct dso *dso_l = map_l ? map_l->dso : NULL; | ||
104 | struct dso *dso_r = map_r ? map_r->dso : NULL; | ||
105 | const char *dso_name_l, *dso_name_r; | ||
106 | |||
107 | if (!dso_l || !dso_r) | ||
108 | return cmp_null(dso_l, dso_r); | ||
109 | |||
110 | if (verbose) { | ||
111 | dso_name_l = dso_l->long_name; | ||
112 | dso_name_r = dso_r->long_name; | ||
113 | } else { | ||
114 | dso_name_l = dso_l->short_name; | ||
115 | dso_name_r = dso_r->short_name; | ||
116 | } | ||
117 | |||
118 | return strcmp(dso_name_l, dso_name_r); | ||
119 | } | ||
120 | |||
100 | struct sort_entry sort_comm = { | 121 | struct sort_entry sort_comm = { |
101 | .se_header = "Command", | 122 | .se_header = "Command", |
102 | .se_cmp = sort__comm_cmp, | 123 | .se_cmp = sort__comm_cmp, |
@@ -110,36 +131,74 @@ struct sort_entry sort_comm = { | |||
110 | static int64_t | 131 | static int64_t |
111 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
112 | { | 133 | { |
113 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; | 134 | return _sort__dso_cmp(left->ms.map, right->ms.map); |
114 | struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; | 135 | } |
115 | const char *dso_name_l, *dso_name_r; | ||
116 | 136 | ||
117 | if (!dso_l || !dso_r) | ||
118 | return cmp_null(dso_l, dso_r); | ||
119 | 137 | ||
120 | if (verbose) { | 138 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, |
121 | dso_name_l = dso_l->long_name; | 139 | u64 ip_l, u64 ip_r) |
122 | dso_name_r = dso_r->long_name; | 140 | { |
123 | } else { | 141 | if (!sym_l || !sym_r) |
124 | dso_name_l = dso_l->short_name; | 142 | return cmp_null(sym_l, sym_r); |
125 | dso_name_r = dso_r->short_name; | 143 | |
144 | if (sym_l == sym_r) | ||
145 | return 0; | ||
146 | |||
147 | if (sym_l) | ||
148 | ip_l = sym_l->start; | ||
149 | if (sym_r) | ||
150 | ip_r = sym_r->start; | ||
151 | |||
152 | return (int64_t)(ip_r - ip_l); | ||
153 | } | ||
154 | |||
155 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, | ||
156 | size_t size, unsigned int width) | ||
157 | { | ||
158 | if (map && map->dso) { | ||
159 | const char *dso_name = !verbose ? map->dso->short_name : | ||
160 | map->dso->long_name; | ||
161 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | ||
126 | } | 162 | } |
127 | 163 | ||
128 | return strcmp(dso_name_l, dso_name_r); | 164 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
129 | } | 165 | } |
130 | 166 | ||
131 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | 167 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, |
132 | size_t size, unsigned int width) | 168 | size_t size, unsigned int width) |
133 | { | 169 | { |
134 | if (self->ms.map && self->ms.map->dso) { | 170 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); |
135 | const char *dso_name = !verbose ? self->ms.map->dso->short_name : | 171 | } |
136 | self->ms.map->dso->long_name; | 172 | |
137 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | 173 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
174 | u64 ip, char level, char *bf, size_t size, | ||
175 | unsigned int width __used) | ||
176 | { | ||
177 | size_t ret = 0; | ||
178 | |||
179 | if (verbose) { | ||
180 | char o = map ? dso__symtab_origin(map->dso) : '!'; | ||
181 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", | ||
182 | BITS_PER_LONG / 4, ip, o); | ||
138 | } | 183 | } |
139 | 184 | ||
140 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 185 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); |
186 | if (sym) | ||
187 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
188 | width - ret, | ||
189 | sym->name); | ||
190 | else { | ||
191 | size_t len = BITS_PER_LONG / 4; | ||
192 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", | ||
193 | len, ip); | ||
194 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
195 | width - ret, ""); | ||
196 | } | ||
197 | |||
198 | return ret; | ||
141 | } | 199 | } |
142 | 200 | ||
201 | |||
143 | struct sort_entry sort_dso = { | 202 | struct sort_entry sort_dso = { |
144 | .se_header = "Shared Object", | 203 | .se_header = "Shared Object", |
145 | .se_cmp = sort__dso_cmp, | 204 | .se_cmp = sort__dso_cmp, |
@@ -147,8 +206,14 @@ struct sort_entry sort_dso = { | |||
147 | .se_width_idx = HISTC_DSO, | 206 | .se_width_idx = HISTC_DSO, |
148 | }; | 207 | }; |
149 | 208 | ||
150 | /* --sort symbol */ | 209 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
210 | size_t size, unsigned int width __used) | ||
211 | { | ||
212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | ||
213 | self->level, bf, size, width); | ||
214 | } | ||
151 | 215 | ||
216 | /* --sort symbol */ | ||
152 | static int64_t | 217 | static int64_t |
153 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 218 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
154 | { | 219 | { |
@@ -166,31 +231,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
166 | ip_l = left->ms.sym->start; | 231 | ip_l = left->ms.sym->start; |
167 | ip_r = right->ms.sym->start; | 232 | ip_r = right->ms.sym->start; |
168 | 233 | ||
169 | return (int64_t)(ip_r - ip_l); | 234 | return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r); |
170 | } | ||
171 | |||
172 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | ||
173 | size_t size, unsigned int width __used) | ||
174 | { | ||
175 | size_t ret = 0; | ||
176 | |||
177 | if (verbose) { | ||
178 | char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; | ||
179 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", | ||
180 | BITS_PER_LONG / 4, self->ip, o); | ||
181 | } | ||
182 | |||
183 | if (!sort_dso.elide) | ||
184 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | ||
185 | |||
186 | if (self->ms.sym) | ||
187 | ret += repsep_snprintf(bf + ret, size - ret, "%s", | ||
188 | self->ms.sym->name); | ||
189 | else | ||
190 | ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx", | ||
191 | BITS_PER_LONG / 4, self->ip); | ||
192 | |||
193 | return ret; | ||
194 | } | 235 | } |
195 | 236 | ||
196 | struct sort_entry sort_sym = { | 237 | struct sort_entry sort_sym = { |
@@ -249,19 +290,155 @@ struct sort_entry sort_cpu = { | |||
249 | .se_width_idx = HISTC_CPU, | 290 | .se_width_idx = HISTC_CPU, |
250 | }; | 291 | }; |
251 | 292 | ||
293 | static int64_t | ||
294 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | ||
295 | { | ||
296 | return _sort__dso_cmp(left->branch_info->from.map, | ||
297 | right->branch_info->from.map); | ||
298 | } | ||
299 | |||
300 | static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, | ||
301 | size_t size, unsigned int width) | ||
302 | { | ||
303 | return _hist_entry__dso_snprintf(self->branch_info->from.map, | ||
304 | bf, size, width); | ||
305 | } | ||
306 | |||
307 | struct sort_entry sort_dso_from = { | ||
308 | .se_header = "Source Shared Object", | ||
309 | .se_cmp = sort__dso_from_cmp, | ||
310 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
311 | .se_width_idx = HISTC_DSO_FROM, | ||
312 | }; | ||
313 | |||
314 | static int64_t | ||
315 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | ||
316 | { | ||
317 | return _sort__dso_cmp(left->branch_info->to.map, | ||
318 | right->branch_info->to.map); | ||
319 | } | ||
320 | |||
321 | static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, | ||
322 | size_t size, unsigned int width) | ||
323 | { | ||
324 | return _hist_entry__dso_snprintf(self->branch_info->to.map, | ||
325 | bf, size, width); | ||
326 | } | ||
327 | |||
328 | static int64_t | ||
329 | sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | ||
330 | { | ||
331 | struct addr_map_symbol *from_l = &left->branch_info->from; | ||
332 | struct addr_map_symbol *from_r = &right->branch_info->from; | ||
333 | |||
334 | if (!from_l->sym && !from_r->sym) | ||
335 | return right->level - left->level; | ||
336 | |||
337 | return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, | ||
338 | from_r->addr); | ||
339 | } | ||
340 | |||
341 | static int64_t | ||
342 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | ||
343 | { | ||
344 | struct addr_map_symbol *to_l = &left->branch_info->to; | ||
345 | struct addr_map_symbol *to_r = &right->branch_info->to; | ||
346 | |||
347 | if (!to_l->sym && !to_r->sym) | ||
348 | return right->level - left->level; | ||
349 | |||
350 | return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); | ||
351 | } | ||
352 | |||
353 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | ||
354 | size_t size, unsigned int width __used) | ||
355 | { | ||
356 | struct addr_map_symbol *from = &self->branch_info->from; | ||
357 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | ||
358 | self->level, bf, size, width); | ||
359 | |||
360 | } | ||
361 | |||
362 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | ||
363 | size_t size, unsigned int width __used) | ||
364 | { | ||
365 | struct addr_map_symbol *to = &self->branch_info->to; | ||
366 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | ||
367 | self->level, bf, size, width); | ||
368 | |||
369 | } | ||
370 | |||
371 | struct sort_entry sort_dso_to = { | ||
372 | .se_header = "Target Shared Object", | ||
373 | .se_cmp = sort__dso_to_cmp, | ||
374 | .se_snprintf = hist_entry__dso_to_snprintf, | ||
375 | .se_width_idx = HISTC_DSO_TO, | ||
376 | }; | ||
377 | |||
378 | struct sort_entry sort_sym_from = { | ||
379 | .se_header = "Source Symbol", | ||
380 | .se_cmp = sort__sym_from_cmp, | ||
381 | .se_snprintf = hist_entry__sym_from_snprintf, | ||
382 | .se_width_idx = HISTC_SYMBOL_FROM, | ||
383 | }; | ||
384 | |||
385 | struct sort_entry sort_sym_to = { | ||
386 | .se_header = "Target Symbol", | ||
387 | .se_cmp = sort__sym_to_cmp, | ||
388 | .se_snprintf = hist_entry__sym_to_snprintf, | ||
389 | .se_width_idx = HISTC_SYMBOL_TO, | ||
390 | }; | ||
391 | |||
392 | static int64_t | ||
393 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) | ||
394 | { | ||
395 | const unsigned char mp = left->branch_info->flags.mispred != | ||
396 | right->branch_info->flags.mispred; | ||
397 | const unsigned char p = left->branch_info->flags.predicted != | ||
398 | right->branch_info->flags.predicted; | ||
399 | |||
400 | return mp || p; | ||
401 | } | ||
402 | |||
403 | static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, | ||
404 | size_t size, unsigned int width){ | ||
405 | static const char *out = "N/A"; | ||
406 | |||
407 | if (self->branch_info->flags.predicted) | ||
408 | out = "N"; | ||
409 | else if (self->branch_info->flags.mispred) | ||
410 | out = "Y"; | ||
411 | |||
412 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
413 | } | ||
414 | |||
415 | struct sort_entry sort_mispredict = { | ||
416 | .se_header = "Branch Mispredicted", | ||
417 | .se_cmp = sort__mispredict_cmp, | ||
418 | .se_snprintf = hist_entry__mispredict_snprintf, | ||
419 | .se_width_idx = HISTC_MISPREDICT, | ||
420 | }; | ||
421 | |||
252 | struct sort_dimension { | 422 | struct sort_dimension { |
253 | const char *name; | 423 | const char *name; |
254 | struct sort_entry *entry; | 424 | struct sort_entry *entry; |
255 | int taken; | 425 | int taken; |
256 | }; | 426 | }; |
257 | 427 | ||
428 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } | ||
429 | |||
258 | static struct sort_dimension sort_dimensions[] = { | 430 | static struct sort_dimension sort_dimensions[] = { |
259 | { .name = "pid", .entry = &sort_thread, }, | 431 | DIM(SORT_PID, "pid", sort_thread), |
260 | { .name = "comm", .entry = &sort_comm, }, | 432 | DIM(SORT_COMM, "comm", sort_comm), |
261 | { .name = "dso", .entry = &sort_dso, }, | 433 | DIM(SORT_DSO, "dso", sort_dso), |
262 | { .name = "symbol", .entry = &sort_sym, }, | 434 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), |
263 | { .name = "parent", .entry = &sort_parent, }, | 435 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), |
264 | { .name = "cpu", .entry = &sort_cpu, }, | 436 | DIM(SORT_SYM, "symbol", sort_sym), |
437 | DIM(SORT_SYM_TO, "symbol_from", sort_sym_from), | ||
438 | DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to), | ||
439 | DIM(SORT_PARENT, "parent", sort_parent), | ||
440 | DIM(SORT_CPU, "cpu", sort_cpu), | ||
441 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
265 | }; | 442 | }; |
266 | 443 | ||
267 | int sort_dimension__add(const char *tok) | 444 | int sort_dimension__add(const char *tok) |
@@ -273,7 +450,6 @@ int sort_dimension__add(const char *tok) | |||
273 | 450 | ||
274 | if (strncasecmp(tok, sd->name, strlen(tok))) | 451 | if (strncasecmp(tok, sd->name, strlen(tok))) |
275 | continue; | 452 | continue; |
276 | |||
277 | if (sd->entry == &sort_parent) { | 453 | if (sd->entry == &sort_parent) { |
278 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 454 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
279 | if (ret) { | 455 | if (ret) { |
@@ -305,6 +481,16 @@ int sort_dimension__add(const char *tok) | |||
305 | sort__first_dimension = SORT_PARENT; | 481 | sort__first_dimension = SORT_PARENT; |
306 | else if (!strcmp(sd->name, "cpu")) | 482 | else if (!strcmp(sd->name, "cpu")) |
307 | sort__first_dimension = SORT_CPU; | 483 | sort__first_dimension = SORT_CPU; |
484 | else if (!strcmp(sd->name, "symbol_from")) | ||
485 | sort__first_dimension = SORT_SYM_FROM; | ||
486 | else if (!strcmp(sd->name, "symbol_to")) | ||
487 | sort__first_dimension = SORT_SYM_TO; | ||
488 | else if (!strcmp(sd->name, "dso_from")) | ||
489 | sort__first_dimension = SORT_DSO_FROM; | ||
490 | else if (!strcmp(sd->name, "dso_to")) | ||
491 | sort__first_dimension = SORT_DSO_TO; | ||
492 | else if (!strcmp(sd->name, "mispredict")) | ||
493 | sort__first_dimension = SORT_MISPREDICT; | ||
308 | } | 494 | } |
309 | 495 | ||
310 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 496 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
@@ -312,7 +498,6 @@ int sort_dimension__add(const char *tok) | |||
312 | 498 | ||
313 | return 0; | 499 | return 0; |
314 | } | 500 | } |
315 | |||
316 | return -ESRCH; | 501 | return -ESRCH; |
317 | } | 502 | } |
318 | 503 | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 3f67ae395752..472aa5a63a58 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -31,11 +31,16 @@ extern const char *parent_pattern; | |||
31 | extern const char default_sort_order[]; | 31 | extern const char default_sort_order[]; |
32 | extern int sort__need_collapse; | 32 | extern int sort__need_collapse; |
33 | extern int sort__has_parent; | 33 | extern int sort__has_parent; |
34 | extern int sort__branch_mode; | ||
34 | extern char *field_sep; | 35 | extern char *field_sep; |
35 | extern struct sort_entry sort_comm; | 36 | extern struct sort_entry sort_comm; |
36 | extern struct sort_entry sort_dso; | 37 | extern struct sort_entry sort_dso; |
37 | extern struct sort_entry sort_sym; | 38 | extern struct sort_entry sort_sym; |
38 | extern struct sort_entry sort_parent; | 39 | extern struct sort_entry sort_parent; |
40 | extern struct sort_entry sort_dso_from; | ||
41 | extern struct sort_entry sort_dso_to; | ||
42 | extern struct sort_entry sort_sym_from; | ||
43 | extern struct sort_entry sort_sym_to; | ||
39 | extern enum sort_type sort__first_dimension; | 44 | extern enum sort_type sort__first_dimension; |
40 | 45 | ||
41 | /** | 46 | /** |
@@ -72,6 +77,7 @@ struct hist_entry { | |||
72 | struct hist_entry *pair; | 77 | struct hist_entry *pair; |
73 | struct rb_root sorted_chain; | 78 | struct rb_root sorted_chain; |
74 | }; | 79 | }; |
80 | struct branch_info *branch_info; | ||
75 | struct callchain_root callchain[0]; | 81 | struct callchain_root callchain[0]; |
76 | }; | 82 | }; |
77 | 83 | ||
@@ -82,6 +88,11 @@ enum sort_type { | |||
82 | SORT_SYM, | 88 | SORT_SYM, |
83 | SORT_PARENT, | 89 | SORT_PARENT, |
84 | SORT_CPU, | 90 | SORT_CPU, |
91 | SORT_DSO_FROM, | ||
92 | SORT_DSO_TO, | ||
93 | SORT_SYM_FROM, | ||
94 | SORT_SYM_TO, | ||
95 | SORT_MISPREDICT, | ||
85 | }; | 96 | }; |
86 | 97 | ||
87 | /* | 98 | /* |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 0975438c3e72..5dd83c3e2c0c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #include <ctype.h> | ||
2 | #include <dirent.h> | 1 | #include <dirent.h> |
3 | #include <errno.h> | 2 | #include <errno.h> |
4 | #include <libgen.h> | 3 | #include <libgen.h> |
@@ -12,6 +11,7 @@ | |||
12 | #include <unistd.h> | 11 | #include <unistd.h> |
13 | #include <inttypes.h> | 12 | #include <inttypes.h> |
14 | #include "build-id.h" | 13 | #include "build-id.h" |
14 | #include "util.h" | ||
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "strlist.h" | 17 | #include "strlist.h" |
@@ -263,6 +263,28 @@ static size_t symbol__fprintf(struct symbol *sym, FILE *fp) | |||
263 | sym->name); | 263 | sym->name); |
264 | } | 264 | } |
265 | 265 | ||
266 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | ||
267 | const struct addr_location *al, FILE *fp) | ||
268 | { | ||
269 | unsigned long offset; | ||
270 | size_t length; | ||
271 | |||
272 | if (sym && sym->name) { | ||
273 | length = fprintf(fp, "%s", sym->name); | ||
274 | if (al) { | ||
275 | offset = al->addr - sym->start; | ||
276 | length += fprintf(fp, "+0x%lx", offset); | ||
277 | } | ||
278 | return length; | ||
279 | } else | ||
280 | return fprintf(fp, "[unknown]"); | ||
281 | } | ||
282 | |||
283 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | ||
284 | { | ||
285 | return symbol__fprintf_symname_offs(sym, NULL, fp); | ||
286 | } | ||
287 | |||
266 | void dso__set_long_name(struct dso *dso, char *name) | 288 | void dso__set_long_name(struct dso *dso, char *name) |
267 | { | 289 | { |
268 | if (name == NULL) | 290 | if (name == NULL) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 123c2e14353e..ac49ef208a5f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stdint.h> | 6 | #include <stdint.h> |
7 | #include "map.h" | 7 | #include "map.h" |
8 | #include "../perf.h" | ||
8 | #include <linux/list.h> | 9 | #include <linux/list.h> |
9 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
10 | #include <stdio.h> | 11 | #include <stdio.h> |
@@ -70,6 +71,7 @@ struct symbol_conf { | |||
70 | unsigned short priv_size; | 71 | unsigned short priv_size; |
71 | unsigned short nr_events; | 72 | unsigned short nr_events; |
72 | bool try_vmlinux_path, | 73 | bool try_vmlinux_path, |
74 | show_kernel_path, | ||
73 | use_modules, | 75 | use_modules, |
74 | sort_by_name, | 76 | sort_by_name, |
75 | show_nr_samples, | 77 | show_nr_samples, |
@@ -95,7 +97,11 @@ struct symbol_conf { | |||
95 | *col_width_list_str; | 97 | *col_width_list_str; |
96 | struct strlist *dso_list, | 98 | struct strlist *dso_list, |
97 | *comm_list, | 99 | *comm_list, |
98 | *sym_list; | 100 | *sym_list, |
101 | *dso_from_list, | ||
102 | *dso_to_list, | ||
103 | *sym_from_list, | ||
104 | *sym_to_list; | ||
99 | const char *symfs; | 105 | const char *symfs; |
100 | }; | 106 | }; |
101 | 107 | ||
@@ -119,6 +125,19 @@ struct map_symbol { | |||
119 | bool has_children; | 125 | bool has_children; |
120 | }; | 126 | }; |
121 | 127 | ||
128 | struct addr_map_symbol { | ||
129 | struct map *map; | ||
130 | struct symbol *sym; | ||
131 | u64 addr; | ||
132 | u64 al_addr; | ||
133 | }; | ||
134 | |||
135 | struct branch_info { | ||
136 | struct addr_map_symbol from; | ||
137 | struct addr_map_symbol to; | ||
138 | struct branch_flags flags; | ||
139 | }; | ||
140 | |||
122 | struct addr_location { | 141 | struct addr_location { |
123 | struct thread *thread; | 142 | struct thread *thread; |
124 | struct map *map; | 143 | struct map *map; |
@@ -241,6 +260,9 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines); | |||
241 | 260 | ||
242 | int symbol__init(void); | 261 | int symbol__init(void); |
243 | void symbol__exit(void); | 262 | void symbol__exit(void); |
263 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | ||
264 | const struct addr_location *al, FILE *fp); | ||
265 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | ||
244 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 266 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
245 | 267 | ||
246 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | 268 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); |
diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c new file mode 100644 index 000000000000..48c6902e749f --- /dev/null +++ b/tools/perf/util/sysfs.c | |||
@@ -0,0 +1,60 @@ | |||
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]; | ||
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 new file mode 100644 index 000000000000..a813b7203938 --- /dev/null +++ b/tools/perf/util/sysfs.h | |||
@@ -0,0 +1,6 @@ | |||
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/thread_map.c b/tools/perf/util/thread_map.c index a5df131b77c3..84d9bd782004 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
@@ -1,6 +1,13 @@ | |||
1 | #include <dirent.h> | 1 | #include <dirent.h> |
2 | #include <limits.h> | ||
3 | #include <stdbool.h> | ||
2 | #include <stdlib.h> | 4 | #include <stdlib.h> |
3 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include <sys/types.h> | ||
7 | #include <sys/stat.h> | ||
8 | #include <unistd.h> | ||
9 | #include "strlist.h" | ||
10 | #include <string.h> | ||
4 | #include "thread_map.h" | 11 | #include "thread_map.h" |
5 | 12 | ||
6 | /* Skip "." and ".." directories */ | 13 | /* Skip "." and ".." directories */ |
@@ -23,7 +30,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid) | |||
23 | sprintf(name, "/proc/%d/task", pid); | 30 | sprintf(name, "/proc/%d/task", pid); |
24 | items = scandir(name, &namelist, filter, NULL); | 31 | items = scandir(name, &namelist, filter, NULL); |
25 | if (items <= 0) | 32 | if (items <= 0) |
26 | return NULL; | 33 | return NULL; |
27 | 34 | ||
28 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | 35 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); |
29 | if (threads != NULL) { | 36 | if (threads != NULL) { |
@@ -51,14 +58,240 @@ struct thread_map *thread_map__new_by_tid(pid_t tid) | |||
51 | return threads; | 58 | return threads; |
52 | } | 59 | } |
53 | 60 | ||
54 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | 61 | struct thread_map *thread_map__new_by_uid(uid_t uid) |
62 | { | ||
63 | DIR *proc; | ||
64 | int max_threads = 32, items, i; | ||
65 | char path[256]; | ||
66 | struct dirent dirent, *next, **namelist = NULL; | ||
67 | struct thread_map *threads = malloc(sizeof(*threads) + | ||
68 | max_threads * sizeof(pid_t)); | ||
69 | if (threads == NULL) | ||
70 | goto out; | ||
71 | |||
72 | proc = opendir("/proc"); | ||
73 | if (proc == NULL) | ||
74 | goto out_free_threads; | ||
75 | |||
76 | threads->nr = 0; | ||
77 | |||
78 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
79 | char *end; | ||
80 | bool grow = false; | ||
81 | struct stat st; | ||
82 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
83 | |||
84 | if (*end) /* only interested in proper numerical dirents */ | ||
85 | continue; | ||
86 | |||
87 | snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); | ||
88 | |||
89 | if (stat(path, &st) != 0) | ||
90 | continue; | ||
91 | |||
92 | if (st.st_uid != uid) | ||
93 | continue; | ||
94 | |||
95 | snprintf(path, sizeof(path), "/proc/%d/task", pid); | ||
96 | items = scandir(path, &namelist, filter, NULL); | ||
97 | if (items <= 0) | ||
98 | goto out_free_closedir; | ||
99 | |||
100 | while (threads->nr + items >= max_threads) { | ||
101 | max_threads *= 2; | ||
102 | grow = true; | ||
103 | } | ||
104 | |||
105 | if (grow) { | ||
106 | struct thread_map *tmp; | ||
107 | |||
108 | tmp = realloc(threads, (sizeof(*threads) + | ||
109 | max_threads * sizeof(pid_t))); | ||
110 | if (tmp == NULL) | ||
111 | goto out_free_namelist; | ||
112 | |||
113 | threads = tmp; | ||
114 | } | ||
115 | |||
116 | for (i = 0; i < items; i++) | ||
117 | threads->map[threads->nr + i] = atoi(namelist[i]->d_name); | ||
118 | |||
119 | for (i = 0; i < items; i++) | ||
120 | free(namelist[i]); | ||
121 | free(namelist); | ||
122 | |||
123 | threads->nr += items; | ||
124 | } | ||
125 | |||
126 | out_closedir: | ||
127 | closedir(proc); | ||
128 | out: | ||
129 | return threads; | ||
130 | |||
131 | out_free_threads: | ||
132 | free(threads); | ||
133 | return NULL; | ||
134 | |||
135 | out_free_namelist: | ||
136 | for (i = 0; i < items; i++) | ||
137 | free(namelist[i]); | ||
138 | free(namelist); | ||
139 | |||
140 | out_free_closedir: | ||
141 | free(threads); | ||
142 | threads = NULL; | ||
143 | goto out_closedir; | ||
144 | } | ||
145 | |||
146 | struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) | ||
55 | { | 147 | { |
56 | if (pid != -1) | 148 | if (pid != -1) |
57 | return thread_map__new_by_pid(pid); | 149 | return thread_map__new_by_pid(pid); |
150 | |||
151 | if (tid == -1 && uid != UINT_MAX) | ||
152 | return thread_map__new_by_uid(uid); | ||
153 | |||
58 | return thread_map__new_by_tid(tid); | 154 | return thread_map__new_by_tid(tid); |
59 | } | 155 | } |
60 | 156 | ||
157 | static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) | ||
158 | { | ||
159 | struct thread_map *threads = NULL, *nt; | ||
160 | char name[256]; | ||
161 | int items, total_tasks = 0; | ||
162 | struct dirent **namelist = NULL; | ||
163 | int i, j = 0; | ||
164 | pid_t pid, prev_pid = INT_MAX; | ||
165 | char *end_ptr; | ||
166 | struct str_node *pos; | ||
167 | struct strlist *slist = strlist__new(false, pid_str); | ||
168 | |||
169 | if (!slist) | ||
170 | return NULL; | ||
171 | |||
172 | strlist__for_each(pos, slist) { | ||
173 | pid = strtol(pos->s, &end_ptr, 10); | ||
174 | |||
175 | if (pid == INT_MIN || pid == INT_MAX || | ||
176 | (*end_ptr != '\0' && *end_ptr != ',')) | ||
177 | goto out_free_threads; | ||
178 | |||
179 | if (pid == prev_pid) | ||
180 | continue; | ||
181 | |||
182 | sprintf(name, "/proc/%d/task", pid); | ||
183 | items = scandir(name, &namelist, filter, NULL); | ||
184 | if (items <= 0) | ||
185 | goto out_free_threads; | ||
186 | |||
187 | total_tasks += items; | ||
188 | nt = realloc(threads, (sizeof(*threads) + | ||
189 | sizeof(pid_t) * total_tasks)); | ||
190 | if (nt == NULL) | ||
191 | goto out_free_threads; | ||
192 | |||
193 | threads = nt; | ||
194 | |||
195 | if (threads) { | ||
196 | for (i = 0; i < items; i++) | ||
197 | threads->map[j++] = atoi(namelist[i]->d_name); | ||
198 | threads->nr = total_tasks; | ||
199 | } | ||
200 | |||
201 | for (i = 0; i < items; i++) | ||
202 | free(namelist[i]); | ||
203 | free(namelist); | ||
204 | |||
205 | if (!threads) | ||
206 | break; | ||
207 | } | ||
208 | |||
209 | out: | ||
210 | strlist__delete(slist); | ||
211 | return threads; | ||
212 | |||
213 | out_free_threads: | ||
214 | free(threads); | ||
215 | threads = NULL; | ||
216 | goto out; | ||
217 | } | ||
218 | |||
219 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | ||
220 | { | ||
221 | struct thread_map *threads = NULL, *nt; | ||
222 | int ntasks = 0; | ||
223 | pid_t tid, prev_tid = INT_MAX; | ||
224 | char *end_ptr; | ||
225 | struct str_node *pos; | ||
226 | struct strlist *slist; | ||
227 | |||
228 | /* perf-stat expects threads to be generated even if tid not given */ | ||
229 | if (!tid_str) { | ||
230 | threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
231 | if (threads != NULL) { | ||
232 | threads->map[0] = -1; | ||
233 | threads->nr = 1; | ||
234 | } | ||
235 | return threads; | ||
236 | } | ||
237 | |||
238 | slist = strlist__new(false, tid_str); | ||
239 | if (!slist) | ||
240 | return NULL; | ||
241 | |||
242 | strlist__for_each(pos, slist) { | ||
243 | tid = strtol(pos->s, &end_ptr, 10); | ||
244 | |||
245 | if (tid == INT_MIN || tid == INT_MAX || | ||
246 | (*end_ptr != '\0' && *end_ptr != ',')) | ||
247 | goto out_free_threads; | ||
248 | |||
249 | if (tid == prev_tid) | ||
250 | continue; | ||
251 | |||
252 | ntasks++; | ||
253 | nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks); | ||
254 | |||
255 | if (nt == NULL) | ||
256 | goto out_free_threads; | ||
257 | |||
258 | threads = nt; | ||
259 | threads->map[ntasks - 1] = tid; | ||
260 | threads->nr = ntasks; | ||
261 | } | ||
262 | out: | ||
263 | return threads; | ||
264 | |||
265 | out_free_threads: | ||
266 | free(threads); | ||
267 | threads = NULL; | ||
268 | goto out; | ||
269 | } | ||
270 | |||
271 | struct thread_map *thread_map__new_str(const char *pid, const char *tid, | ||
272 | uid_t uid) | ||
273 | { | ||
274 | if (pid) | ||
275 | return thread_map__new_by_pid_str(pid); | ||
276 | |||
277 | if (!tid && uid != UINT_MAX) | ||
278 | return thread_map__new_by_uid(uid); | ||
279 | |||
280 | return thread_map__new_by_tid_str(tid); | ||
281 | } | ||
282 | |||
61 | void thread_map__delete(struct thread_map *threads) | 283 | void thread_map__delete(struct thread_map *threads) |
62 | { | 284 | { |
63 | free(threads); | 285 | free(threads); |
64 | } | 286 | } |
287 | |||
288 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) | ||
289 | { | ||
290 | int i; | ||
291 | size_t printed = fprintf(fp, "%d thread%s: ", | ||
292 | threads->nr, threads->nr > 1 ? "s" : ""); | ||
293 | for (i = 0; i < threads->nr; ++i) | ||
294 | printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]); | ||
295 | |||
296 | return printed + fprintf(fp, "\n"); | ||
297 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 3cb907311409..7da80f14418b 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_THREAD_MAP_H | 2 | #define __PERF_THREAD_MAP_H |
3 | 3 | ||
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <stdio.h> | ||
5 | 6 | ||
6 | struct thread_map { | 7 | struct thread_map { |
7 | int nr; | 8 | int nr; |
@@ -10,6 +11,14 @@ struct thread_map { | |||
10 | 11 | ||
11 | struct thread_map *thread_map__new_by_pid(pid_t pid); | 12 | struct thread_map *thread_map__new_by_pid(pid_t pid); |
12 | struct thread_map *thread_map__new_by_tid(pid_t tid); | 13 | struct thread_map *thread_map__new_by_tid(pid_t tid); |
13 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | 14 | struct thread_map *thread_map__new_by_uid(uid_t uid); |
15 | struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); | ||
16 | |||
17 | struct thread_map *thread_map__new_str(const char *pid, | ||
18 | const char *tid, uid_t uid); | ||
19 | |||
14 | void thread_map__delete(struct thread_map *threads); | 20 | void thread_map__delete(struct thread_map *threads); |
21 | |||
22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); | ||
23 | |||
15 | #endif /* __PERF_THREAD_MAP_H */ | 24 | #endif /* __PERF_THREAD_MAP_H */ |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 500471dffa4f..09fe579ccafb 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -69,12 +69,15 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
69 | 69 | ||
70 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 70 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
71 | 71 | ||
72 | if (top->target_pid != -1) | 72 | if (top->target_pid) |
73 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d", | 73 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", |
74 | top->target_pid); | 74 | top->target_pid); |
75 | else if (top->target_tid != -1) | 75 | else if (top->target_tid) |
76 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", | 76 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", |
77 | top->target_tid); | 77 | top->target_tid); |
78 | else if (top->uid_str != NULL) | ||
79 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", | ||
80 | top->uid_str); | ||
78 | else | 81 | else |
79 | ret += SNPRINTF(bf + ret, size - ret, " (all"); | 82 | ret += SNPRINTF(bf + ret, size - ret, " (all"); |
80 | 83 | ||
@@ -82,7 +85,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
82 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | 85 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", |
83 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); | 86 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); |
84 | else { | 87 | else { |
85 | if (top->target_tid != -1) | 88 | if (top->target_tid) |
86 | ret += SNPRINTF(bf + ret, size - ret, ")"); | 89 | ret += SNPRINTF(bf + ret, size - ret, ")"); |
87 | else | 90 | else |
88 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | 91 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index f2eab81435ae..ce61cb2d1acf 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -23,7 +23,8 @@ struct perf_top { | |||
23 | u64 guest_us_samples, guest_kernel_samples; | 23 | u64 guest_us_samples, guest_kernel_samples; |
24 | int print_entries, count_filter, delay_secs; | 24 | int print_entries, count_filter, delay_secs; |
25 | int freq; | 25 | int freq; |
26 | pid_t target_pid, target_tid; | 26 | const char *target_pid, *target_tid; |
27 | uid_t uid; | ||
27 | bool hide_kernel_symbols, hide_user_symbols, zero; | 28 | bool hide_kernel_symbols, hide_user_symbols, zero; |
28 | bool system_wide; | 29 | bool system_wide; |
29 | bool use_tui, use_stdio; | 30 | bool use_tui, use_stdio; |
@@ -33,7 +34,7 @@ struct perf_top { | |||
33 | bool vmlinux_warned; | 34 | bool vmlinux_warned; |
34 | bool inherit; | 35 | bool inherit; |
35 | bool group; | 36 | bool group; |
36 | bool sample_id_all_avail; | 37 | bool sample_id_all_missing; |
37 | bool exclude_guest_missing; | 38 | bool exclude_guest_missing; |
38 | bool dump_symtab; | 39 | bool dump_symtab; |
39 | const char *cpu_list; | 40 | const char *cpu_list; |
@@ -46,6 +47,7 @@ struct perf_top { | |||
46 | int realtime_prio; | 47 | int realtime_prio; |
47 | int sym_pcnt_filter; | 48 | int sym_pcnt_filter; |
48 | const char *sym_filter; | 49 | const char *sym_filter; |
50 | const char *uid_str; | ||
49 | }; | 51 | }; |
50 | 52 | ||
51 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 53 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 1a8d4dc4f386..a4088ced1e64 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <stdio.h> | 25 | #include <stdio.h> |
26 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | #include <string.h> | 27 | #include <string.h> |
28 | #include <ctype.h> | ||
29 | #include <errno.h> | 28 | #include <errno.h> |
30 | 29 | ||
31 | #include "../perf.h" | 30 | #include "../perf.h" |
@@ -1424,6 +1423,11 @@ static long long arg_num_eval(struct print_arg *arg) | |||
1424 | die("unknown op '%s'", arg->op.op); | 1423 | die("unknown op '%s'", arg->op.op); |
1425 | } | 1424 | } |
1426 | break; | 1425 | break; |
1426 | case '+': | ||
1427 | left = arg_num_eval(arg->op.left); | ||
1428 | right = arg_num_eval(arg->op.right); | ||
1429 | val = left + right; | ||
1430 | break; | ||
1427 | default: | 1431 | default: |
1428 | die("unknown op '%s'", arg->op.op); | 1432 | die("unknown op '%s'", arg->op.op); |
1429 | } | 1433 | } |
@@ -1484,6 +1488,13 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) | |||
1484 | 1488 | ||
1485 | free_token(token); | 1489 | free_token(token); |
1486 | type = process_arg(event, arg, &token); | 1490 | type = process_arg(event, arg, &token); |
1491 | |||
1492 | if (type == EVENT_OP) | ||
1493 | type = process_op(event, arg, &token); | ||
1494 | |||
1495 | if (type == EVENT_ERROR) | ||
1496 | goto out_free; | ||
1497 | |||
1487 | if (test_type_token(type, token, EVENT_DELIM, ",")) | 1498 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1488 | goto out_free; | 1499 | goto out_free; |
1489 | 1500 | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f55cc3a765a1..b9592e0de8d7 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -33,7 +33,6 @@ | |||
33 | #include <pthread.h> | 33 | #include <pthread.h> |
34 | #include <fcntl.h> | 34 | #include <fcntl.h> |
35 | #include <unistd.h> | 35 | #include <unistd.h> |
36 | #include <ctype.h> | ||
37 | #include <errno.h> | 36 | #include <errno.h> |
38 | 37 | ||
39 | #include "../perf.h" | 38 | #include "../perf.h" |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index a3fdf55f317b..18ae6c1831d3 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <stdio.h> | 22 | #include <stdio.h> |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <ctype.h> | ||
26 | #include <errno.h> | 25 | #include <errno.h> |
27 | 26 | ||
28 | #include "../perf.h" | 27 | #include "../perf.h" |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 295a9c93f945..57a4c6ef3fd2 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -69,14 +69,17 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
69 | if (!self->navkeypressed) | 69 | if (!self->navkeypressed) |
70 | width += 1; | 70 | width += 1; |
71 | 71 | ||
72 | if (!ab->hide_src_code && ol->offset != -1) | ||
73 | if (!current_entry || (self->use_navkeypressed && | ||
74 | !self->navkeypressed)) | ||
75 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
76 | |||
72 | if (!*ol->line) | 77 | if (!*ol->line) |
73 | slsmg_write_nstring(" ", width - 18); | 78 | slsmg_write_nstring(" ", width - 18); |
74 | else | 79 | else |
75 | slsmg_write_nstring(ol->line, width - 18); | 80 | slsmg_write_nstring(ol->line, width - 18); |
76 | 81 | ||
77 | if (!current_entry) | 82 | if (current_entry) |
78 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
79 | else | ||
80 | ab->selection = ol; | 83 | ab->selection = ol; |
81 | } | 84 | } |
82 | 85 | ||
@@ -230,9 +233,9 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
230 | struct rb_node *nd = NULL; | 233 | struct rb_node *nd = NULL; |
231 | struct map_symbol *ms = self->b.priv; | 234 | struct map_symbol *ms = self->b.priv; |
232 | struct symbol *sym = ms->sym; | 235 | struct symbol *sym = ms->sym; |
233 | const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " | 236 | const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, " |
234 | "H: Hottest, -> Line action, S -> Toggle source " | 237 | "H: Go to hottest line, ->/ENTER: Line action, " |
235 | "code view"; | 238 | "S: Toggle source code view"; |
236 | int key; | 239 | int key; |
237 | 240 | ||
238 | if (ui_browser__show(&self->b, sym->name, help) < 0) | 241 | if (ui_browser__show(&self->b, sym->name, help) < 0) |
@@ -284,9 +287,11 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
284 | nd = self->curr_hot; | 287 | nd = self->curr_hot; |
285 | break; | 288 | break; |
286 | case 'H': | 289 | case 'H': |
290 | case 'h': | ||
287 | nd = self->curr_hot; | 291 | nd = self->curr_hot; |
288 | break; | 292 | break; |
289 | case 'S': | 293 | case 'S': |
294 | case 's': | ||
290 | if (annotate_browser__toggle_source(self)) | 295 | if (annotate_browser__toggle_source(self)) |
291 | ui_helpline__puts(help); | 296 | ui_helpline__puts(help); |
292 | continue; | 297 | continue; |
@@ -338,6 +343,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
338 | pthread_mutex_unlock(¬es->lock); | 343 | pthread_mutex_unlock(¬es->lock); |
339 | symbol__tui_annotate(target, ms->map, evidx, | 344 | symbol__tui_annotate(target, ms->map, evidx, |
340 | timer, arg, delay_secs); | 345 | timer, arg, delay_secs); |
346 | ui_browser__show_title(&self->b, sym->name); | ||
341 | } | 347 | } |
342 | continue; | 348 | continue; |
343 | case K_LEFT: | 349 | case K_LEFT: |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index bb9197c9c4a4..fa530fcc764a 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -805,8 +805,11 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
805 | self->hists = hists; | 805 | self->hists = hists; |
806 | self->b.refresh = hist_browser__refresh; | 806 | self->b.refresh = hist_browser__refresh; |
807 | self->b.seek = ui_browser__hists_seek; | 807 | self->b.seek = ui_browser__hists_seek; |
808 | self->b.use_navkeypressed = true, | 808 | self->b.use_navkeypressed = true; |
809 | self->has_symbols = sort_sym.list.next != NULL; | 809 | if (sort__branch_mode == 1) |
810 | self->has_symbols = sort_sym_from.list.next != NULL; | ||
811 | else | ||
812 | self->has_symbols = sort_sym.list.next != NULL; | ||
810 | } | 813 | } |
811 | 814 | ||
812 | return self; | 815 | return self; |
@@ -839,6 +842,9 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
839 | nr_events = convert_unit(nr_events, &unit); | 842 | nr_events = convert_unit(nr_events, &unit); |
840 | printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); | 843 | printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); |
841 | 844 | ||
845 | if (self->uid_filter_str) | ||
846 | printed += snprintf(bf + printed, size - printed, | ||
847 | ", UID: %s", self->uid_filter_str); | ||
842 | if (thread) | 848 | if (thread) |
843 | printed += scnprintf(bf + printed, size - printed, | 849 | printed += scnprintf(bf + printed, size - printed, |
844 | ", Thread: %s(%d)", | 850 | ", Thread: %s(%d)", |
@@ -850,6 +856,16 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
850 | return printed; | 856 | return printed; |
851 | } | 857 | } |
852 | 858 | ||
859 | static inline void free_popup_options(char **options, int n) | ||
860 | { | ||
861 | int i; | ||
862 | |||
863 | for (i = 0; i < n; ++i) { | ||
864 | free(options[i]); | ||
865 | options[i] = NULL; | ||
866 | } | ||
867 | } | ||
868 | |||
853 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 869 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
854 | const char *helpline, const char *ev_name, | 870 | const char *helpline, const char *ev_name, |
855 | bool left_exits, | 871 | bool left_exits, |
@@ -858,7 +874,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
858 | { | 874 | { |
859 | struct hists *self = &evsel->hists; | 875 | struct hists *self = &evsel->hists; |
860 | struct hist_browser *browser = hist_browser__new(self); | 876 | struct hist_browser *browser = hist_browser__new(self); |
877 | struct branch_info *bi; | ||
861 | struct pstack *fstack; | 878 | struct pstack *fstack; |
879 | char *options[16]; | ||
880 | int nr_options = 0; | ||
862 | int key = -1; | 881 | int key = -1; |
863 | 882 | ||
864 | if (browser == NULL) | 883 | if (browser == NULL) |
@@ -870,13 +889,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
870 | 889 | ||
871 | ui_helpline__push(helpline); | 890 | ui_helpline__push(helpline); |
872 | 891 | ||
892 | memset(options, 0, sizeof(options)); | ||
893 | |||
873 | while (1) { | 894 | while (1) { |
874 | const struct thread *thread = NULL; | 895 | const struct thread *thread = NULL; |
875 | const struct dso *dso = NULL; | 896 | const struct dso *dso = NULL; |
876 | char *options[16]; | 897 | int choice = 0, |
877 | int nr_options = 0, choice = 0, i, | ||
878 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 898 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
879 | browse_map = -2; | 899 | annotate_f = -2, annotate_t = -2, browse_map = -2; |
900 | |||
901 | nr_options = 0; | ||
880 | 902 | ||
881 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); | 903 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
882 | 904 | ||
@@ -884,7 +906,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
884 | thread = hist_browser__selected_thread(browser); | 906 | thread = hist_browser__selected_thread(browser); |
885 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 907 | dso = browser->selection->map ? browser->selection->map->dso : NULL; |
886 | } | 908 | } |
887 | |||
888 | switch (key) { | 909 | switch (key) { |
889 | case K_TAB: | 910 | case K_TAB: |
890 | case K_UNTAB: | 911 | case K_UNTAB: |
@@ -899,7 +920,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
899 | if (!browser->has_symbols) { | 920 | if (!browser->has_symbols) { |
900 | ui_browser__warning(&browser->b, delay_secs * 2, | 921 | ui_browser__warning(&browser->b, delay_secs * 2, |
901 | "Annotation is only available for symbolic views, " | 922 | "Annotation is only available for symbolic views, " |
902 | "include \"sym\" in --sort to use it."); | 923 | "include \"sym*\" in --sort to use it."); |
903 | continue; | 924 | continue; |
904 | } | 925 | } |
905 | 926 | ||
@@ -969,12 +990,34 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
969 | if (!browser->has_symbols) | 990 | if (!browser->has_symbols) |
970 | goto add_exit_option; | 991 | goto add_exit_option; |
971 | 992 | ||
972 | if (browser->selection != NULL && | 993 | if (sort__branch_mode == 1) { |
973 | browser->selection->sym != NULL && | 994 | bi = browser->he_selection->branch_info; |
974 | !browser->selection->map->dso->annotate_warned && | 995 | if (browser->selection != NULL && |
975 | asprintf(&options[nr_options], "Annotate %s", | 996 | bi && |
976 | browser->selection->sym->name) > 0) | 997 | bi->from.sym != NULL && |
977 | annotate = nr_options++; | 998 | !bi->from.map->dso->annotate_warned && |
999 | asprintf(&options[nr_options], "Annotate %s", | ||
1000 | bi->from.sym->name) > 0) | ||
1001 | annotate_f = nr_options++; | ||
1002 | |||
1003 | if (browser->selection != NULL && | ||
1004 | bi && | ||
1005 | bi->to.sym != NULL && | ||
1006 | !bi->to.map->dso->annotate_warned && | ||
1007 | (bi->to.sym != bi->from.sym || | ||
1008 | bi->to.map->dso != bi->from.map->dso) && | ||
1009 | asprintf(&options[nr_options], "Annotate %s", | ||
1010 | bi->to.sym->name) > 0) | ||
1011 | annotate_t = nr_options++; | ||
1012 | } else { | ||
1013 | |||
1014 | if (browser->selection != NULL && | ||
1015 | browser->selection->sym != NULL && | ||
1016 | !browser->selection->map->dso->annotate_warned && | ||
1017 | asprintf(&options[nr_options], "Annotate %s", | ||
1018 | browser->selection->sym->name) > 0) | ||
1019 | annotate = nr_options++; | ||
1020 | } | ||
978 | 1021 | ||
979 | if (thread != NULL && | 1022 | if (thread != NULL && |
980 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 1023 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
@@ -995,25 +1038,39 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
995 | browse_map = nr_options++; | 1038 | browse_map = nr_options++; |
996 | add_exit_option: | 1039 | add_exit_option: |
997 | options[nr_options++] = (char *)"Exit"; | 1040 | options[nr_options++] = (char *)"Exit"; |
998 | 1041 | retry_popup_menu: | |
999 | choice = ui__popup_menu(nr_options, options); | 1042 | choice = ui__popup_menu(nr_options, options); |
1000 | 1043 | ||
1001 | for (i = 0; i < nr_options - 1; ++i) | ||
1002 | free(options[i]); | ||
1003 | |||
1004 | if (choice == nr_options - 1) | 1044 | if (choice == nr_options - 1) |
1005 | break; | 1045 | break; |
1006 | 1046 | ||
1007 | if (choice == -1) | 1047 | if (choice == -1) { |
1048 | free_popup_options(options, nr_options - 1); | ||
1008 | continue; | 1049 | continue; |
1050 | } | ||
1009 | 1051 | ||
1010 | if (choice == annotate) { | 1052 | if (choice == annotate || choice == annotate_t || choice == annotate_f) { |
1011 | struct hist_entry *he; | 1053 | struct hist_entry *he; |
1012 | int err; | 1054 | int err; |
1013 | do_annotate: | 1055 | do_annotate: |
1014 | he = hist_browser__selected_entry(browser); | 1056 | he = hist_browser__selected_entry(browser); |
1015 | if (he == NULL) | 1057 | if (he == NULL) |
1016 | continue; | 1058 | continue; |
1059 | |||
1060 | /* | ||
1061 | * we stash the branch_info symbol + map into the | ||
1062 | * the ms so we don't have to rewrite all the annotation | ||
1063 | * code to use branch_info. | ||
1064 | * in branch mode, the ms struct is not used | ||
1065 | */ | ||
1066 | if (choice == annotate_f) { | ||
1067 | he->ms.sym = he->branch_info->from.sym; | ||
1068 | he->ms.map = he->branch_info->from.map; | ||
1069 | } else if (choice == annotate_t) { | ||
1070 | he->ms.sym = he->branch_info->to.sym; | ||
1071 | he->ms.map = he->branch_info->to.map; | ||
1072 | } | ||
1073 | |||
1017 | /* | 1074 | /* |
1018 | * Don't let this be freed, say, by hists__decay_entry. | 1075 | * Don't let this be freed, say, by hists__decay_entry. |
1019 | */ | 1076 | */ |
@@ -1021,9 +1078,18 @@ do_annotate: | |||
1021 | err = hist_entry__tui_annotate(he, evsel->idx, | 1078 | err = hist_entry__tui_annotate(he, evsel->idx, |
1022 | timer, arg, delay_secs); | 1079 | timer, arg, delay_secs); |
1023 | he->used = false; | 1080 | he->used = false; |
1081 | /* | ||
1082 | * offer option to annotate the other branch source or target | ||
1083 | * (if they exists) when returning from annotate | ||
1084 | */ | ||
1085 | if ((err == 'q' || err == CTRL('c')) | ||
1086 | && annotate_t != -2 && annotate_f != -2) | ||
1087 | goto retry_popup_menu; | ||
1088 | |||
1024 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | 1089 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); |
1025 | if (err) | 1090 | if (err) |
1026 | ui_browser__handle_resize(&browser->b); | 1091 | ui_browser__handle_resize(&browser->b); |
1092 | |||
1027 | } else if (choice == browse_map) | 1093 | } else if (choice == browse_map) |
1028 | map__browse(browser->selection->map); | 1094 | map__browse(browser->selection->map); |
1029 | else if (choice == zoom_dso) { | 1095 | else if (choice == zoom_dso) { |
@@ -1069,6 +1135,7 @@ out_free_stack: | |||
1069 | pstack__delete(fstack); | 1135 | pstack__delete(fstack); |
1070 | out: | 1136 | out: |
1071 | hist_browser__delete(browser); | 1137 | hist_browser__delete(browser); |
1138 | free_popup_options(options, nr_options - 1); | ||
1072 | return key; | 1139 | return key; |
1073 | } | 1140 | } |
1074 | 1141 | ||
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 6905bcc8be2d..eca6575abfd0 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -3,9 +3,9 @@ | |||
3 | #include <newt.h> | 3 | #include <newt.h> |
4 | #include <inttypes.h> | 4 | #include <inttypes.h> |
5 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
6 | #include <ctype.h> | ||
7 | #include <string.h> | 6 | #include <string.h> |
8 | #include <linux/bitops.h> | 7 | #include <linux/bitops.h> |
8 | #include "../../util.h" | ||
9 | #include "../../debug.h" | 9 | #include "../../debug.h" |
10 | #include "../../symbol.h" | 10 | #include "../../symbol.h" |
11 | #include "../browser.h" | 11 | #include "../browser.h" |
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index d76d1c0ff98f..52bb07c6442a 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * Copyright (C) Linus Torvalds, 2005 | 7 | * Copyright (C) Linus Torvalds, 2005 |
8 | */ | 8 | */ |
9 | #include "util.h" | 9 | #include "util.h" |
10 | #include "debug.h" | ||
10 | 11 | ||
11 | static void report(const char *prefix, const char *err, va_list params) | 12 | static void report(const char *prefix, const char *err, va_list params) |
12 | { | 13 | { |
@@ -81,3 +82,41 @@ void warning(const char *warn, ...) | |||
81 | warn_routine(warn, params); | 82 | warn_routine(warn, params); |
82 | va_end(params); | 83 | va_end(params); |
83 | } | 84 | } |
85 | |||
86 | uid_t parse_target_uid(const char *str, const char *tid, const char *pid) | ||
87 | { | ||
88 | struct passwd pwd, *result; | ||
89 | char buf[1024]; | ||
90 | |||
91 | if (str == NULL) | ||
92 | return UINT_MAX; | ||
93 | |||
94 | /* UID and PID are mutually exclusive */ | ||
95 | if (tid || pid) { | ||
96 | ui__warning("PID/TID switch overriding UID\n"); | ||
97 | sleep(1); | ||
98 | return UINT_MAX; | ||
99 | } | ||
100 | |||
101 | getpwnam_r(str, &pwd, buf, sizeof(buf), &result); | ||
102 | |||
103 | if (result == NULL) { | ||
104 | char *endptr; | ||
105 | int uid = strtol(str, &endptr, 10); | ||
106 | |||
107 | if (*endptr != '\0') { | ||
108 | ui__error("Invalid user %s\n", str); | ||
109 | return UINT_MAX - 1; | ||
110 | } | ||
111 | |||
112 | getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); | ||
113 | |||
114 | if (result == NULL) { | ||
115 | ui__error("Problems obtaining information for user %s\n", | ||
116 | str); | ||
117 | return UINT_MAX - 1; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | return result->pw_uid; | ||
122 | } | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index fb25d1329218..8109a907841e 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -14,6 +14,8 @@ void event_attr_init(struct perf_event_attr *attr) | |||
14 | attr->exclude_host = 1; | 14 | attr->exclude_host = 1; |
15 | if (!perf_guest) | 15 | if (!perf_guest) |
16 | attr->exclude_guest = 1; | 16 | attr->exclude_guest = 1; |
17 | /* to capture ABI version */ | ||
18 | attr->size = sizeof(*attr); | ||
17 | } | 19 | } |
18 | 20 | ||
19 | int mkdir_p(char *path, mode_t mode) | 21 | int mkdir_p(char *path, mode_t mode) |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index ecf9898169c8..0f99f394d8e0 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -199,6 +199,8 @@ static inline int has_extension(const char *filename, const char *ext) | |||
199 | #undef isalpha | 199 | #undef isalpha |
200 | #undef isprint | 200 | #undef isprint |
201 | #undef isalnum | 201 | #undef isalnum |
202 | #undef islower | ||
203 | #undef isupper | ||
202 | #undef tolower | 204 | #undef tolower |
203 | #undef toupper | 205 | #undef toupper |
204 | 206 | ||
@@ -219,6 +221,8 @@ extern unsigned char sane_ctype[256]; | |||
219 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 221 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
220 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 222 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
221 | #define isprint(x) sane_istest(x,GIT_PRINT) | 223 | #define isprint(x) sane_istest(x,GIT_PRINT) |
224 | #define islower(x) (sane_istest(x,GIT_ALPHA) && sane_istest(x,0x20)) | ||
225 | #define isupper(x) (sane_istest(x,GIT_ALPHA) && !sane_istest(x,0x20)) | ||
222 | #define tolower(x) sane_case((unsigned char)(x), 0x20) | 226 | #define tolower(x) sane_case((unsigned char)(x), 0x20) |
223 | #define toupper(x) sane_case((unsigned char)(x), 0) | 227 | #define toupper(x) sane_case((unsigned char)(x), 0) |
224 | 228 | ||
@@ -245,6 +249,8 @@ struct perf_event_attr; | |||
245 | 249 | ||
246 | void event_attr_init(struct perf_event_attr *attr); | 250 | void event_attr_init(struct perf_event_attr *attr); |
247 | 251 | ||
252 | uid_t parse_target_uid(const char *str, const char *tid, const char *pid); | ||
253 | |||
248 | #define _STR(x) #x | 254 | #define _STR(x) #x |
249 | #define STR(x) _STR(x) | 255 | #define STR(x) _STR(x) |
250 | 256 | ||