diff options
Diffstat (limited to 'tools/perf/util')
39 files changed, 3321 insertions, 1354 deletions
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c new file mode 100644 index 000000000000..04904b35ba81 --- /dev/null +++ b/tools/perf/util/build-id.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * build-id.c | ||
3 | * | ||
4 | * build-id support | ||
5 | * | ||
6 | * Copyright (C) 2009, 2010 Red Hat Inc. | ||
7 | * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | ||
9 | #include "build-id.h" | ||
10 | #include "event.h" | ||
11 | #include "symbol.h" | ||
12 | #include <linux/kernel.h> | ||
13 | |||
14 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | ||
15 | { | ||
16 | struct addr_location al; | ||
17 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
18 | struct thread *thread = perf_session__findnew(session, event->ip.pid); | ||
19 | |||
20 | if (thread == NULL) { | ||
21 | pr_err("problem processing %d event, skipping it.\n", | ||
22 | event->header.type); | ||
23 | return -1; | ||
24 | } | ||
25 | |||
26 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | ||
27 | event->ip.ip, &al); | ||
28 | |||
29 | if (al.map != NULL) | ||
30 | al.map->dso->hit = 1; | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | struct perf_event_ops build_id__mark_dso_hit_ops = { | ||
36 | .sample = build_id__mark_dso_hit, | ||
37 | .mmap = event__process_mmap, | ||
38 | .fork = event__process_task, | ||
39 | }; | ||
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h new file mode 100644 index 000000000000..1d981d63cf9a --- /dev/null +++ b/tools/perf/util/build-id.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef PERF_BUILD_ID_H_ | ||
2 | #define PERF_BUILD_ID_H_ 1 | ||
3 | |||
4 | #include "session.h" | ||
5 | |||
6 | extern struct perf_event_ops build_id__mark_dso_hit_ops; | ||
7 | |||
8 | #endif | ||
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c deleted file mode 100644 index b557b836de3d..000000000000 --- a/tools/perf/util/data_map.c +++ /dev/null | |||
@@ -1,252 +0,0 @@ | |||
1 | #include "symbol.h" | ||
2 | #include "util.h" | ||
3 | #include "debug.h" | ||
4 | #include "thread.h" | ||
5 | #include "session.h" | ||
6 | |||
7 | static int process_event_stub(event_t *event __used, | ||
8 | struct perf_session *session __used) | ||
9 | { | ||
10 | dump_printf(": unhandled!\n"); | ||
11 | return 0; | ||
12 | } | ||
13 | |||
14 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
15 | { | ||
16 | if (!handler->process_sample_event) | ||
17 | handler->process_sample_event = process_event_stub; | ||
18 | if (!handler->process_mmap_event) | ||
19 | handler->process_mmap_event = process_event_stub; | ||
20 | if (!handler->process_comm_event) | ||
21 | handler->process_comm_event = process_event_stub; | ||
22 | if (!handler->process_fork_event) | ||
23 | handler->process_fork_event = process_event_stub; | ||
24 | if (!handler->process_exit_event) | ||
25 | handler->process_exit_event = process_event_stub; | ||
26 | if (!handler->process_lost_event) | ||
27 | handler->process_lost_event = process_event_stub; | ||
28 | if (!handler->process_read_event) | ||
29 | handler->process_read_event = process_event_stub; | ||
30 | if (!handler->process_throttle_event) | ||
31 | handler->process_throttle_event = process_event_stub; | ||
32 | if (!handler->process_unthrottle_event) | ||
33 | handler->process_unthrottle_event = process_event_stub; | ||
34 | } | ||
35 | |||
36 | static const char *event__name[] = { | ||
37 | [0] = "TOTAL", | ||
38 | [PERF_RECORD_MMAP] = "MMAP", | ||
39 | [PERF_RECORD_LOST] = "LOST", | ||
40 | [PERF_RECORD_COMM] = "COMM", | ||
41 | [PERF_RECORD_EXIT] = "EXIT", | ||
42 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
43 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
44 | [PERF_RECORD_FORK] = "FORK", | ||
45 | [PERF_RECORD_READ] = "READ", | ||
46 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
47 | }; | ||
48 | |||
49 | unsigned long event__total[PERF_RECORD_MAX]; | ||
50 | |||
51 | void event__print_totals(void) | ||
52 | { | ||
53 | int i; | ||
54 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
55 | pr_info("%10s events: %10ld\n", | ||
56 | event__name[i], event__total[i]); | ||
57 | } | ||
58 | |||
59 | static int process_event(event_t *event, struct perf_session *session, | ||
60 | struct perf_event_ops *ops, | ||
61 | unsigned long offset, unsigned long head) | ||
62 | { | ||
63 | trace_event(event); | ||
64 | |||
65 | if (event->header.type < PERF_RECORD_MAX) { | ||
66 | dump_printf("%p [%p]: PERF_RECORD_%s", | ||
67 | (void *)(offset + head), | ||
68 | (void *)(long)(event->header.size), | ||
69 | event__name[event->header.type]); | ||
70 | ++event__total[0]; | ||
71 | ++event__total[event->header.type]; | ||
72 | } | ||
73 | |||
74 | switch (event->header.type) { | ||
75 | case PERF_RECORD_SAMPLE: | ||
76 | return ops->process_sample_event(event, session); | ||
77 | case PERF_RECORD_MMAP: | ||
78 | return ops->process_mmap_event(event, session); | ||
79 | case PERF_RECORD_COMM: | ||
80 | return ops->process_comm_event(event, session); | ||
81 | case PERF_RECORD_FORK: | ||
82 | return ops->process_fork_event(event, session); | ||
83 | case PERF_RECORD_EXIT: | ||
84 | return ops->process_exit_event(event, session); | ||
85 | case PERF_RECORD_LOST: | ||
86 | return ops->process_lost_event(event, session); | ||
87 | case PERF_RECORD_READ: | ||
88 | return ops->process_read_event(event, session); | ||
89 | case PERF_RECORD_THROTTLE: | ||
90 | return ops->process_throttle_event(event, session); | ||
91 | case PERF_RECORD_UNTHROTTLE: | ||
92 | return ops->process_unthrottle_event(event, session); | ||
93 | default: | ||
94 | ops->total_unknown++; | ||
95 | return -1; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | int perf_header__read_build_ids(int input, u64 offset, u64 size) | ||
100 | { | ||
101 | struct build_id_event bev; | ||
102 | char filename[PATH_MAX]; | ||
103 | u64 limit = offset + size; | ||
104 | int err = -1; | ||
105 | |||
106 | while (offset < limit) { | ||
107 | struct dso *dso; | ||
108 | ssize_t len; | ||
109 | |||
110 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
111 | goto out; | ||
112 | |||
113 | len = bev.header.size - sizeof(bev); | ||
114 | if (read(input, filename, len) != len) | ||
115 | goto out; | ||
116 | |||
117 | dso = dsos__findnew(filename); | ||
118 | if (dso != NULL) | ||
119 | dso__set_build_id(dso, &bev.build_id); | ||
120 | |||
121 | offset += bev.header.size; | ||
122 | } | ||
123 | err = 0; | ||
124 | out: | ||
125 | return err; | ||
126 | } | ||
127 | |||
128 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
129 | { | ||
130 | struct thread *thread = perf_session__findnew(self, 0); | ||
131 | |||
132 | if (!thread || thread__set_comm(thread, "swapper")) { | ||
133 | pr_err("problem inserting idle task.\n"); | ||
134 | thread = NULL; | ||
135 | } | ||
136 | |||
137 | return thread; | ||
138 | } | ||
139 | |||
140 | int perf_session__process_events(struct perf_session *self, | ||
141 | struct perf_event_ops *ops) | ||
142 | { | ||
143 | int err; | ||
144 | unsigned long head, shift; | ||
145 | unsigned long offset = 0; | ||
146 | size_t page_size; | ||
147 | event_t *event; | ||
148 | uint32_t size; | ||
149 | char *buf; | ||
150 | |||
151 | if (perf_session__register_idle_thread(self) == NULL) | ||
152 | return -ENOMEM; | ||
153 | |||
154 | perf_event_ops__fill_defaults(ops); | ||
155 | |||
156 | page_size = getpagesize(); | ||
157 | |||
158 | head = self->header.data_offset; | ||
159 | self->sample_type = perf_header__sample_type(&self->header); | ||
160 | |||
161 | err = -EINVAL; | ||
162 | if (ops->sample_type_check && ops->sample_type_check(self) < 0) | ||
163 | goto out_err; | ||
164 | |||
165 | if (!ops->full_paths) { | ||
166 | char bf[PATH_MAX]; | ||
167 | |||
168 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
169 | err = -errno; | ||
170 | out_getcwd_err: | ||
171 | pr_err("failed to get the current directory\n"); | ||
172 | goto out_err; | ||
173 | } | ||
174 | self->cwd = strdup(bf); | ||
175 | if (self->cwd == NULL) { | ||
176 | err = -ENOMEM; | ||
177 | goto out_getcwd_err; | ||
178 | } | ||
179 | self->cwdlen = strlen(self->cwd); | ||
180 | } | ||
181 | |||
182 | shift = page_size * (head / page_size); | ||
183 | offset += shift; | ||
184 | head -= shift; | ||
185 | |||
186 | remap: | ||
187 | buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, | ||
188 | MAP_SHARED, self->fd, offset); | ||
189 | if (buf == MAP_FAILED) { | ||
190 | pr_err("failed to mmap file\n"); | ||
191 | err = -errno; | ||
192 | goto out_err; | ||
193 | } | ||
194 | |||
195 | more: | ||
196 | event = (event_t *)(buf + head); | ||
197 | |||
198 | size = event->header.size; | ||
199 | if (!size) | ||
200 | size = 8; | ||
201 | |||
202 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
203 | int munmap_ret; | ||
204 | |||
205 | shift = page_size * (head / page_size); | ||
206 | |||
207 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
208 | assert(munmap_ret == 0); | ||
209 | |||
210 | offset += shift; | ||
211 | head -= shift; | ||
212 | goto remap; | ||
213 | } | ||
214 | |||
215 | size = event->header.size; | ||
216 | |||
217 | dump_printf("\n%p [%p]: event: %d\n", | ||
218 | (void *)(offset + head), | ||
219 | (void *)(long)event->header.size, | ||
220 | event->header.type); | ||
221 | |||
222 | if (!size || process_event(event, self, ops, offset, head) < 0) { | ||
223 | |||
224 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | ||
225 | (void *)(offset + head), | ||
226 | (void *)(long)(event->header.size), | ||
227 | event->header.type); | ||
228 | |||
229 | /* | ||
230 | * assume we lost track of the stream, check alignment, and | ||
231 | * increment a single u64 in the hope to catch on again 'soon'. | ||
232 | */ | ||
233 | |||
234 | if (unlikely(head & 7)) | ||
235 | head &= ~7ULL; | ||
236 | |||
237 | size = 8; | ||
238 | } | ||
239 | |||
240 | head += size; | ||
241 | |||
242 | if (offset + head >= self->header.data_offset + self->header.data_size) | ||
243 | goto done; | ||
244 | |||
245 | if (offset + head < self->size) | ||
246 | goto more; | ||
247 | |||
248 | done: | ||
249 | err = 0; | ||
250 | out_err: | ||
251 | return err; | ||
252 | } | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 28d520d5a1fb..0905600c3851 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "color.h" | 9 | #include "color.h" |
10 | #include "event.h" | 10 | #include "event.h" |
11 | #include "debug.h" | 11 | #include "debug.h" |
12 | #include "util.h" | ||
12 | 13 | ||
13 | int verbose = 0; | 14 | int verbose = 0; |
14 | int dump_trace = 0; | 15 | int dump_trace = 0; |
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index 06b73ee02c49..a88fefc0cc0a 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c | |||
@@ -106,16 +106,14 @@ int debugfs_valid_entry(const char *path) | |||
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | /* mount the debugfs somewhere */ | 109 | /* mount the debugfs somewhere if it's not mounted */ |
110 | 110 | ||
111 | int debugfs_mount(const char *mountpoint) | 111 | char *debugfs_mount(const char *mountpoint) |
112 | { | 112 | { |
113 | char mountcmd[128]; | ||
114 | |||
115 | /* see if it's already mounted */ | 113 | /* see if it's already mounted */ |
116 | if (debugfs_find_mountpoint()) { | 114 | if (debugfs_find_mountpoint()) { |
117 | debugfs_premounted = 1; | 115 | debugfs_premounted = 1; |
118 | return 0; | 116 | return debugfs_mountpoint; |
119 | } | 117 | } |
120 | 118 | ||
121 | /* if not mounted and no argument */ | 119 | /* if not mounted and no argument */ |
@@ -127,13 +125,14 @@ int debugfs_mount(const char *mountpoint) | |||
127 | mountpoint = "/sys/kernel/debug"; | 125 | mountpoint = "/sys/kernel/debug"; |
128 | } | 126 | } |
129 | 127 | ||
128 | if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) | ||
129 | return NULL; | ||
130 | |||
130 | /* save the mountpoint */ | 131 | /* save the mountpoint */ |
131 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | 132 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); |
133 | debugfs_found = 1; | ||
132 | 134 | ||
133 | /* mount it */ | 135 | return debugfs_mountpoint; |
134 | snprintf(mountcmd, sizeof(mountcmd), | ||
135 | "/bin/mount -t debugfs debugfs %s", mountpoint); | ||
136 | return system(mountcmd); | ||
137 | } | 136 | } |
138 | 137 | ||
139 | /* umount the debugfs */ | 138 | /* umount the debugfs */ |
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h index 3cd14f9ae784..83a02879745f 100644 --- a/tools/perf/util/debugfs.h +++ b/tools/perf/util/debugfs.h | |||
@@ -15,7 +15,7 @@ | |||
15 | extern const char *debugfs_find_mountpoint(void); | 15 | extern const char *debugfs_find_mountpoint(void); |
16 | extern int debugfs_valid_mountpoint(const char *debugfs); | 16 | extern int debugfs_valid_mountpoint(const char *debugfs); |
17 | extern int debugfs_valid_entry(const char *path); | 17 | extern int debugfs_valid_entry(const char *path); |
18 | extern int debugfs_mount(const char *mountpoint); | 18 | extern char *debugfs_mount(const char *mountpoint); |
19 | extern int debugfs_umount(void); | 19 | extern int debugfs_umount(void); |
20 | extern int debugfs_write(const char *entry, const char *value); | 20 | extern int debugfs_write(const char *entry, const char *value); |
21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); | 21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bb0fd6da2d56..705ec63548b4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -8,8 +8,7 @@ | |||
8 | #include "thread.h" | 8 | #include "thread.h" |
9 | 9 | ||
10 | static pid_t event__synthesize_comm(pid_t pid, int full, | 10 | static pid_t event__synthesize_comm(pid_t pid, int full, |
11 | int (*process)(event_t *event, | 11 | event__handler_t process, |
12 | struct perf_session *session), | ||
13 | struct perf_session *session) | 12 | struct perf_session *session) |
14 | { | 13 | { |
15 | event_t ev; | 14 | event_t ev; |
@@ -91,8 +90,7 @@ out_failure: | |||
91 | } | 90 | } |
92 | 91 | ||
93 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | 92 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, |
94 | int (*process)(event_t *event, | 93 | event__handler_t process, |
95 | struct perf_session *session), | ||
96 | struct perf_session *session) | 94 | struct perf_session *session) |
97 | { | 95 | { |
98 | char filename[PATH_MAX]; | 96 | char filename[PATH_MAX]; |
@@ -112,7 +110,10 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
112 | while (1) { | 110 | while (1) { |
113 | char bf[BUFSIZ], *pbf = bf; | 111 | char bf[BUFSIZ], *pbf = bf; |
114 | event_t ev = { | 112 | event_t ev = { |
115 | .header = { .type = PERF_RECORD_MMAP }, | 113 | .header = { |
114 | .type = PERF_RECORD_MMAP, | ||
115 | .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ | ||
116 | }, | ||
116 | }; | 117 | }; |
117 | int n; | 118 | int n; |
118 | size_t size; | 119 | size_t size; |
@@ -156,9 +157,38 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
156 | return 0; | 157 | return 0; |
157 | } | 158 | } |
158 | 159 | ||
159 | int event__synthesize_thread(pid_t pid, | 160 | int event__synthesize_modules(event__handler_t process, |
160 | int (*process)(event_t *event, | 161 | struct perf_session *session) |
161 | struct perf_session *session), | 162 | { |
163 | struct rb_node *nd; | ||
164 | |||
165 | for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); | ||
166 | nd; nd = rb_next(nd)) { | ||
167 | event_t ev; | ||
168 | size_t size; | ||
169 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
170 | |||
171 | if (pos->dso->kernel) | ||
172 | continue; | ||
173 | |||
174 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | ||
175 | memset(&ev, 0, sizeof(ev)); | ||
176 | ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
177 | ev.mmap.header.type = PERF_RECORD_MMAP; | ||
178 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
179 | (sizeof(ev.mmap.filename) - size)); | ||
180 | ev.mmap.start = pos->start; | ||
181 | ev.mmap.len = pos->end - pos->start; | ||
182 | |||
183 | memcpy(ev.mmap.filename, pos->dso->long_name, | ||
184 | pos->dso->long_name_len + 1); | ||
185 | process(&ev, session); | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
162 | struct perf_session *session) | 192 | struct perf_session *session) |
163 | { | 193 | { |
164 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | 194 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); |
@@ -167,8 +197,7 @@ int event__synthesize_thread(pid_t pid, | |||
167 | return event__synthesize_mmap_events(pid, tgid, process, session); | 197 | return event__synthesize_mmap_events(pid, tgid, process, session); |
168 | } | 198 | } |
169 | 199 | ||
170 | void event__synthesize_threads(int (*process)(event_t *event, | 200 | void event__synthesize_threads(event__handler_t process, |
171 | struct perf_session *session), | ||
172 | struct perf_session *session) | 201 | struct perf_session *session) |
173 | { | 202 | { |
174 | DIR *proc; | 203 | DIR *proc; |
@@ -189,6 +218,59 @@ void event__synthesize_threads(int (*process)(event_t *event, | |||
189 | closedir(proc); | 218 | closedir(proc); |
190 | } | 219 | } |
191 | 220 | ||
221 | struct process_symbol_args { | ||
222 | const char *name; | ||
223 | u64 start; | ||
224 | }; | ||
225 | |||
226 | static int find_symbol_cb(void *arg, const char *name, char type, u64 start) | ||
227 | { | ||
228 | struct process_symbol_args *args = arg; | ||
229 | |||
230 | /* | ||
231 | * Must be a function or at least an alias, as in PARISC64, where "_text" is | ||
232 | * an 'A' to the same address as "_stext". | ||
233 | */ | ||
234 | if (!(symbol_type__is_a(type, MAP__FUNCTION) || | ||
235 | type == 'A') || strcmp(name, args->name)) | ||
236 | return 0; | ||
237 | |||
238 | args->start = start; | ||
239 | return 1; | ||
240 | } | ||
241 | |||
242 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
243 | struct perf_session *session, | ||
244 | const char *symbol_name) | ||
245 | { | ||
246 | size_t size; | ||
247 | event_t ev = { | ||
248 | .header = { | ||
249 | .type = PERF_RECORD_MMAP, | ||
250 | .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
251 | }, | ||
252 | }; | ||
253 | /* | ||
254 | * We should get this from /sys/kernel/sections/.text, but till that is | ||
255 | * available use this, and after it is use this as a fallback for older | ||
256 | * kernels. | ||
257 | */ | ||
258 | struct process_symbol_args args = { .name = symbol_name, }; | ||
259 | |||
260 | if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) | ||
261 | return -ENOENT; | ||
262 | |||
263 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | ||
264 | "[kernel.kallsyms.%s]", symbol_name) + 1; | ||
265 | size = ALIGN(size, sizeof(u64)); | ||
266 | ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); | ||
267 | ev.mmap.pgoff = args.start; | ||
268 | ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; | ||
269 | ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; | ||
270 | |||
271 | return process(&ev, session); | ||
272 | } | ||
273 | |||
192 | static void thread__comm_adjust(struct thread *self) | 274 | static void thread__comm_adjust(struct thread *self) |
193 | { | 275 | { |
194 | char *comm = self->comm; | 276 | char *comm = self->comm; |
@@ -240,22 +322,88 @@ int event__process_lost(event_t *self, struct perf_session *session) | |||
240 | 322 | ||
241 | int event__process_mmap(event_t *self, struct perf_session *session) | 323 | int event__process_mmap(event_t *self, struct perf_session *session) |
242 | { | 324 | { |
243 | struct thread *thread = perf_session__findnew(session, self->mmap.pid); | 325 | struct thread *thread; |
244 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, | 326 | struct map *map; |
245 | session->cwd, session->cwdlen); | 327 | |
328 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", | ||
329 | self->mmap.pid, self->mmap.tid, self->mmap.start, | ||
330 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | ||
331 | |||
332 | if (self->mmap.pid == 0) { | ||
333 | static const char kmmap_prefix[] = "[kernel.kallsyms."; | ||
334 | |||
335 | if (self->mmap.filename[0] == '/') { | ||
336 | char short_module_name[1024]; | ||
337 | char *name = strrchr(self->mmap.filename, '/'), *dot; | ||
338 | |||
339 | if (name == NULL) | ||
340 | goto out_problem; | ||
341 | |||
342 | ++name; /* skip / */ | ||
343 | dot = strrchr(name, '.'); | ||
344 | if (dot == NULL) | ||
345 | goto out_problem; | ||
346 | |||
347 | snprintf(short_module_name, sizeof(short_module_name), | ||
348 | "[%.*s]", (int)(dot - name), name); | ||
349 | strxfrchar(short_module_name, '-', '_'); | ||
350 | |||
351 | map = perf_session__new_module_map(session, | ||
352 | self->mmap.start, | ||
353 | self->mmap.filename); | ||
354 | if (map == NULL) | ||
355 | goto out_problem; | ||
356 | |||
357 | name = strdup(short_module_name); | ||
358 | if (name == NULL) | ||
359 | goto out_problem; | ||
360 | |||
361 | map->dso->short_name = name; | ||
362 | map->end = map->start + self->mmap.len; | ||
363 | } else if (memcmp(self->mmap.filename, kmmap_prefix, | ||
364 | sizeof(kmmap_prefix) - 1) == 0) { | ||
365 | const char *symbol_name = (self->mmap.filename + | ||
366 | sizeof(kmmap_prefix) - 1); | ||
367 | /* | ||
368 | * Should be there already, from the build-id table in | ||
369 | * the header. | ||
370 | */ | ||
371 | struct dso *kernel = __dsos__findnew(&dsos__kernel, | ||
372 | "[kernel.kallsyms]"); | ||
373 | if (kernel == NULL) | ||
374 | goto out_problem; | ||
375 | |||
376 | kernel->kernel = 1; | ||
377 | if (__perf_session__create_kernel_maps(session, kernel) < 0) | ||
378 | goto out_problem; | ||
379 | |||
380 | session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; | ||
381 | session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | ||
382 | /* | ||
383 | * Be a bit paranoid here, some perf.data file came with | ||
384 | * a zero sized synthesized MMAP event for the kernel. | ||
385 | */ | ||
386 | if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) | ||
387 | session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; | ||
388 | |||
389 | perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, | ||
390 | self->mmap.pgoff); | ||
391 | } | ||
392 | return 0; | ||
393 | } | ||
246 | 394 | ||
247 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", | 395 | thread = perf_session__findnew(session, self->mmap.pid); |
248 | self->mmap.pid, self->mmap.tid, | 396 | map = map__new(&self->mmap, MAP__FUNCTION, |
249 | (void *)(long)self->mmap.start, | 397 | session->cwd, session->cwdlen); |
250 | (void *)(long)self->mmap.len, | ||
251 | (void *)(long)self->mmap.pgoff, | ||
252 | self->mmap.filename); | ||
253 | 398 | ||
254 | if (thread == NULL || map == NULL) | 399 | if (thread == NULL || map == NULL) |
255 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | 400 | goto out_problem; |
256 | else | ||
257 | thread__insert_map(thread, map); | ||
258 | 401 | ||
402 | thread__insert_map(thread, map); | ||
403 | return 0; | ||
404 | |||
405 | out_problem: | ||
406 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
259 | return 0; | 407 | return 0; |
260 | } | 408 | } |
261 | 409 | ||
@@ -284,26 +432,24 @@ int event__process_task(event_t *self, struct perf_session *session) | |||
284 | return 0; | 432 | return 0; |
285 | } | 433 | } |
286 | 434 | ||
287 | void thread__find_addr_location(struct thread *self, | 435 | void thread__find_addr_map(struct thread *self, |
288 | struct perf_session *session, u8 cpumode, | 436 | struct perf_session *session, u8 cpumode, |
289 | enum map_type type, u64 addr, | 437 | enum map_type type, u64 addr, |
290 | struct addr_location *al, | 438 | struct addr_location *al) |
291 | symbol_filter_t filter) | ||
292 | { | 439 | { |
293 | struct map_groups *mg = &self->mg; | 440 | struct map_groups *mg = &self->mg; |
294 | 441 | ||
295 | al->thread = self; | 442 | al->thread = self; |
296 | al->addr = addr; | 443 | al->addr = addr; |
297 | 444 | ||
298 | if (cpumode & PERF_RECORD_MISC_KERNEL) { | 445 | if (cpumode == PERF_RECORD_MISC_KERNEL) { |
299 | al->level = 'k'; | 446 | al->level = 'k'; |
300 | mg = &session->kmaps; | 447 | mg = &session->kmaps; |
301 | } else if (cpumode & PERF_RECORD_MISC_USER) | 448 | } else if (cpumode == PERF_RECORD_MISC_USER) |
302 | al->level = '.'; | 449 | al->level = '.'; |
303 | else { | 450 | else { |
304 | al->level = 'H'; | 451 | al->level = 'H'; |
305 | al->map = NULL; | 452 | al->map = NULL; |
306 | al->sym = NULL; | ||
307 | return; | 453 | return; |
308 | } | 454 | } |
309 | try_again: | 455 | try_again: |
@@ -322,11 +468,21 @@ try_again: | |||
322 | mg = &session->kmaps; | 468 | mg = &session->kmaps; |
323 | goto try_again; | 469 | goto try_again; |
324 | } | 470 | } |
325 | al->sym = NULL; | 471 | } else |
326 | } else { | ||
327 | al->addr = al->map->map_ip(al->map, al->addr); | 472 | al->addr = al->map->map_ip(al->map, al->addr); |
328 | al->sym = map__find_symbol(al->map, session, al->addr, filter); | 473 | } |
329 | } | 474 | |
475 | void thread__find_addr_location(struct thread *self, | ||
476 | struct perf_session *session, u8 cpumode, | ||
477 | enum map_type type, u64 addr, | ||
478 | struct addr_location *al, | ||
479 | symbol_filter_t filter) | ||
480 | { | ||
481 | thread__find_addr_map(self, session, cpumode, type, addr, al); | ||
482 | if (al->map != NULL) | ||
483 | al->sym = map__find_symbol(al->map, al->addr, filter); | ||
484 | else | ||
485 | al->sym = NULL; | ||
330 | } | 486 | } |
331 | 487 | ||
332 | static void dso__calc_col_width(struct dso *self) | 488 | static void dso__calc_col_width(struct dso *self) |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 690a96d0467c..50a7132887f5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -1,10 +1,10 @@ | |||
1 | #ifndef __PERF_RECORD_H | 1 | #ifndef __PERF_RECORD_H |
2 | #define __PERF_RECORD_H | 2 | #define __PERF_RECORD_H |
3 | 3 | ||
4 | #include <limits.h> | ||
5 | |||
4 | #include "../perf.h" | 6 | #include "../perf.h" |
5 | #include "util.h" | 7 | #include "map.h" |
6 | #include <linux/list.h> | ||
7 | #include <linux/rbtree.h> | ||
8 | 8 | ||
9 | /* | 9 | /* |
10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
@@ -101,74 +101,19 @@ struct events_stats { | |||
101 | 101 | ||
102 | void event__print_totals(void); | 102 | void event__print_totals(void); |
103 | 103 | ||
104 | enum map_type { | ||
105 | MAP__FUNCTION = 0, | ||
106 | MAP__VARIABLE, | ||
107 | }; | ||
108 | |||
109 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
110 | |||
111 | struct map { | ||
112 | union { | ||
113 | struct rb_node rb_node; | ||
114 | struct list_head node; | ||
115 | }; | ||
116 | u64 start; | ||
117 | u64 end; | ||
118 | enum map_type type; | ||
119 | u64 pgoff; | ||
120 | u64 (*map_ip)(struct map *, u64); | ||
121 | u64 (*unmap_ip)(struct map *, u64); | ||
122 | struct dso *dso; | ||
123 | }; | ||
124 | |||
125 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
126 | { | ||
127 | return ip - map->start + map->pgoff; | ||
128 | } | ||
129 | |||
130 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | ||
131 | { | ||
132 | return ip + map->start - map->pgoff; | ||
133 | } | ||
134 | |||
135 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
136 | { | ||
137 | return ip; | ||
138 | } | ||
139 | |||
140 | struct symbol; | ||
141 | |||
142 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
143 | |||
144 | void map__init(struct map *self, enum map_type type, | ||
145 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
146 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
147 | char *cwd, int cwdlen); | ||
148 | void map__delete(struct map *self); | ||
149 | struct map *map__clone(struct map *self); | ||
150 | int map__overlap(struct map *l, struct map *r); | ||
151 | size_t map__fprintf(struct map *self, FILE *fp); | ||
152 | |||
153 | struct perf_session; | 104 | struct perf_session; |
154 | 105 | ||
155 | int map__load(struct map *self, struct perf_session *session, | 106 | typedef int (*event__handler_t)(event_t *event, struct perf_session *session); |
156 | symbol_filter_t filter); | 107 | |
157 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | 108 | int event__synthesize_thread(pid_t pid, event__handler_t process, |
158 | u64 addr, symbol_filter_t filter); | ||
159 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
160 | struct perf_session *session, | ||
161 | symbol_filter_t filter); | ||
162 | void map__fixup_start(struct map *self); | ||
163 | void map__fixup_end(struct map *self); | ||
164 | |||
165 | int event__synthesize_thread(pid_t pid, | ||
166 | int (*process)(event_t *event, | ||
167 | struct perf_session *session), | ||
168 | struct perf_session *session); | 109 | struct perf_session *session); |
169 | void event__synthesize_threads(int (*process)(event_t *event, | 110 | void event__synthesize_threads(event__handler_t process, |
170 | struct perf_session *session), | ||
171 | struct perf_session *session); | 111 | struct perf_session *session); |
112 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
113 | struct perf_session *session, | ||
114 | const char *symbol_name); | ||
115 | int event__synthesize_modules(event__handler_t process, | ||
116 | struct perf_session *session); | ||
172 | 117 | ||
173 | int event__process_comm(event_t *self, struct perf_session *session); | 118 | int event__process_comm(event_t *self, struct perf_session *session); |
174 | int event__process_lost(event_t *self, struct perf_session *session); | 119 | int event__process_lost(event_t *self, struct perf_session *session); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 8a0bca55106f..6c9aa16ee51f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,8 +1,12 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
1 | #include <sys/types.h> | 3 | #include <sys/types.h> |
4 | #include <byteswap.h> | ||
2 | #include <unistd.h> | 5 | #include <unistd.h> |
3 | #include <stdio.h> | 6 | #include <stdio.h> |
4 | #include <stdlib.h> | 7 | #include <stdlib.h> |
5 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | ||
6 | 10 | ||
7 | #include "util.h" | 11 | #include "util.h" |
8 | #include "header.h" | 12 | #include "header.h" |
@@ -105,24 +109,28 @@ struct perf_trace_event_type { | |||
105 | static int event_count; | 109 | static int event_count; |
106 | static struct perf_trace_event_type *events; | 110 | static struct perf_trace_event_type *events; |
107 | 111 | ||
108 | void perf_header__push_event(u64 id, const char *name) | 112 | int perf_header__push_event(u64 id, const char *name) |
109 | { | 113 | { |
110 | if (strlen(name) > MAX_EVENT_NAME) | 114 | if (strlen(name) > MAX_EVENT_NAME) |
111 | pr_warning("Event %s will be truncated\n", name); | 115 | pr_warning("Event %s will be truncated\n", name); |
112 | 116 | ||
113 | if (!events) { | 117 | if (!events) { |
114 | events = malloc(sizeof(struct perf_trace_event_type)); | 118 | events = malloc(sizeof(struct perf_trace_event_type)); |
115 | if (!events) | 119 | if (events == NULL) |
116 | die("nomem"); | 120 | return -ENOMEM; |
117 | } else { | 121 | } else { |
118 | events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type)); | 122 | struct perf_trace_event_type *nevents; |
119 | if (!events) | 123 | |
120 | die("nomem"); | 124 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); |
125 | if (nevents == NULL) | ||
126 | return -ENOMEM; | ||
127 | events = nevents; | ||
121 | } | 128 | } |
122 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 129 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); |
123 | events[event_count].event_id = id; | 130 | events[event_count].event_id = id; |
124 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 131 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); |
125 | event_count++; | 132 | event_count++; |
133 | return 0; | ||
126 | } | 134 | } |
127 | 135 | ||
128 | char *perf_header__find_event(u64 id) | 136 | char *perf_header__find_event(u64 id) |
@@ -169,31 +177,48 @@ static int do_write(int fd, const void *buf, size_t size) | |||
169 | return 0; | 177 | return 0; |
170 | } | 178 | } |
171 | 179 | ||
172 | static int __dsos__write_buildid_table(struct list_head *head, int fd) | 180 | #define NAME_ALIGN 64 |
181 | |||
182 | static int write_padded(int fd, const void *bf, size_t count, | ||
183 | size_t count_aligned) | ||
173 | { | 184 | { |
174 | #define NAME_ALIGN 64 | ||
175 | struct dso *pos; | ||
176 | static const char zero_buf[NAME_ALIGN]; | 185 | static const char zero_buf[NAME_ALIGN]; |
186 | int err = do_write(fd, bf, count); | ||
187 | |||
188 | if (!err) | ||
189 | err = do_write(fd, zero_buf, count_aligned - count); | ||
190 | |||
191 | return err; | ||
192 | } | ||
177 | 193 | ||
178 | list_for_each_entry(pos, head, node) { | 194 | #define dsos__for_each_with_build_id(pos, head) \ |
195 | list_for_each_entry(pos, head, node) \ | ||
196 | if (!pos->has_build_id) \ | ||
197 | continue; \ | ||
198 | else | ||
199 | |||
200 | static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | ||
201 | { | ||
202 | struct dso *pos; | ||
203 | |||
204 | dsos__for_each_with_build_id(pos, head) { | ||
179 | int err; | 205 | int err; |
180 | struct build_id_event b; | 206 | struct build_id_event b; |
181 | size_t len; | 207 | size_t len; |
182 | 208 | ||
183 | if (!pos->has_build_id) | 209 | if (!pos->hit) |
184 | continue; | 210 | continue; |
185 | len = pos->long_name_len + 1; | 211 | len = pos->long_name_len + 1; |
186 | len = ALIGN(len, NAME_ALIGN); | 212 | len = ALIGN(len, NAME_ALIGN); |
187 | memset(&b, 0, sizeof(b)); | 213 | memset(&b, 0, sizeof(b)); |
188 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 214 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); |
215 | b.header.misc = misc; | ||
189 | b.header.size = sizeof(b) + len; | 216 | b.header.size = sizeof(b) + len; |
190 | err = do_write(fd, &b, sizeof(b)); | 217 | err = do_write(fd, &b, sizeof(b)); |
191 | if (err < 0) | 218 | if (err < 0) |
192 | return err; | 219 | return err; |
193 | err = do_write(fd, pos->long_name, pos->long_name_len + 1); | 220 | err = write_padded(fd, pos->long_name, |
194 | if (err < 0) | 221 | pos->long_name_len + 1, len); |
195 | return err; | ||
196 | err = do_write(fd, zero_buf, len - pos->long_name_len - 1); | ||
197 | if (err < 0) | 222 | if (err < 0) |
198 | return err; | 223 | return err; |
199 | } | 224 | } |
@@ -203,12 +228,143 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd) | |||
203 | 228 | ||
204 | static int dsos__write_buildid_table(int fd) | 229 | static int dsos__write_buildid_table(int fd) |
205 | { | 230 | { |
206 | int err = __dsos__write_buildid_table(&dsos__kernel, fd); | 231 | int err = __dsos__write_buildid_table(&dsos__kernel, |
232 | PERF_RECORD_MISC_KERNEL, fd); | ||
207 | if (err == 0) | 233 | if (err == 0) |
208 | err = __dsos__write_buildid_table(&dsos__user, fd); | 234 | err = __dsos__write_buildid_table(&dsos__user, |
235 | PERF_RECORD_MISC_USER, fd); | ||
209 | return err; | 236 | return err; |
210 | } | 237 | } |
211 | 238 | ||
239 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
240 | const char *name, bool is_kallsyms) | ||
241 | { | ||
242 | const size_t size = PATH_MAX; | ||
243 | char *filename = malloc(size), | ||
244 | *linkname = malloc(size), *targetname; | ||
245 | int len, err = -1; | ||
246 | |||
247 | if (filename == NULL || linkname == NULL) | ||
248 | goto out_free; | ||
249 | |||
250 | len = snprintf(filename, size, "%s%s%s", | ||
251 | debugdir, is_kallsyms ? "/" : "", name); | ||
252 | if (mkdir_p(filename, 0755)) | ||
253 | goto out_free; | ||
254 | |||
255 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); | ||
256 | |||
257 | if (access(filename, F_OK)) { | ||
258 | if (is_kallsyms) { | ||
259 | if (copyfile("/proc/kallsyms", filename)) | ||
260 | goto out_free; | ||
261 | } else if (link(name, filename) && copyfile(name, filename)) | ||
262 | goto out_free; | ||
263 | } | ||
264 | |||
265 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | ||
266 | debugdir, sbuild_id); | ||
267 | |||
268 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
269 | goto out_free; | ||
270 | |||
271 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
272 | targetname = filename + strlen(debugdir) - 5; | ||
273 | memcpy(targetname, "../..", 5); | ||
274 | |||
275 | if (symlink(targetname, linkname) == 0) | ||
276 | err = 0; | ||
277 | out_free: | ||
278 | free(filename); | ||
279 | free(linkname); | ||
280 | return err; | ||
281 | } | ||
282 | |||
283 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
284 | const char *name, const char *debugdir, | ||
285 | bool is_kallsyms) | ||
286 | { | ||
287 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
288 | |||
289 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
290 | |||
291 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | ||
292 | } | ||
293 | |||
294 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
295 | { | ||
296 | const size_t size = PATH_MAX; | ||
297 | char *filename = malloc(size), | ||
298 | *linkname = malloc(size); | ||
299 | int err = -1; | ||
300 | |||
301 | if (filename == NULL || linkname == NULL) | ||
302 | goto out_free; | ||
303 | |||
304 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
305 | debugdir, sbuild_id, sbuild_id + 2); | ||
306 | |||
307 | if (access(linkname, F_OK)) | ||
308 | goto out_free; | ||
309 | |||
310 | if (readlink(linkname, filename, size) < 0) | ||
311 | goto out_free; | ||
312 | |||
313 | if (unlink(linkname)) | ||
314 | goto out_free; | ||
315 | |||
316 | /* | ||
317 | * Since the link is relative, we must make it absolute: | ||
318 | */ | ||
319 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
320 | debugdir, sbuild_id, filename); | ||
321 | |||
322 | if (unlink(linkname)) | ||
323 | goto out_free; | ||
324 | |||
325 | err = 0; | ||
326 | out_free: | ||
327 | free(filename); | ||
328 | free(linkname); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||
333 | { | ||
334 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | ||
335 | |||
336 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | ||
337 | self->long_name, debugdir, is_kallsyms); | ||
338 | } | ||
339 | |||
340 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | ||
341 | { | ||
342 | struct dso *pos; | ||
343 | int err = 0; | ||
344 | |||
345 | dsos__for_each_with_build_id(pos, head) | ||
346 | if (dso__cache_build_id(pos, debugdir)) | ||
347 | err = -1; | ||
348 | |||
349 | return err; | ||
350 | } | ||
351 | |||
352 | static int dsos__cache_build_ids(void) | ||
353 | { | ||
354 | int err_kernel, err_user; | ||
355 | char debugdir[PATH_MAX]; | ||
356 | |||
357 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
358 | DEBUG_CACHE_DIR); | ||
359 | |||
360 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
361 | return -1; | ||
362 | |||
363 | err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); | ||
364 | err_user = __dsos__cache_build_ids(&dsos__user, debugdir); | ||
365 | return err_kernel || err_user ? -1 : 0; | ||
366 | } | ||
367 | |||
212 | static int perf_header__adds_write(struct perf_header *self, int fd) | 368 | static int perf_header__adds_write(struct perf_header *self, int fd) |
213 | { | 369 | { |
214 | int nr_sections; | 370 | int nr_sections; |
@@ -217,7 +373,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
217 | u64 sec_start; | 373 | u64 sec_start; |
218 | int idx = 0, err; | 374 | int idx = 0, err; |
219 | 375 | ||
220 | if (dsos__read_build_ids()) | 376 | if (dsos__read_build_ids(true)) |
221 | perf_header__set_feat(self, HEADER_BUILD_ID); | 377 | perf_header__set_feat(self, HEADER_BUILD_ID); |
222 | 378 | ||
223 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 379 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); |
@@ -257,7 +413,9 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
257 | pr_debug("failed to write buildid table\n"); | 413 | pr_debug("failed to write buildid table\n"); |
258 | goto out_free; | 414 | goto out_free; |
259 | } | 415 | } |
260 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; | 416 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - |
417 | buildid_sec->offset; | ||
418 | dsos__cache_build_ids(); | ||
261 | } | 419 | } |
262 | 420 | ||
263 | lseek(fd, sec_start, SEEK_SET); | 421 | lseek(fd, sec_start, SEEK_SET); |
@@ -360,30 +518,43 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
360 | return 0; | 518 | return 0; |
361 | } | 519 | } |
362 | 520 | ||
363 | static void do_read(int fd, void *buf, size_t size) | 521 | static int do_read(int fd, void *buf, size_t size) |
364 | { | 522 | { |
365 | while (size) { | 523 | while (size) { |
366 | int ret = read(fd, buf, size); | 524 | int ret = read(fd, buf, size); |
367 | 525 | ||
368 | if (ret < 0) | 526 | if (ret <= 0) |
369 | die("failed to read"); | 527 | return -1; |
370 | if (ret == 0) | ||
371 | die("failed to read: missing data"); | ||
372 | 528 | ||
373 | size -= ret; | 529 | size -= ret; |
374 | buf += ret; | 530 | buf += ret; |
375 | } | 531 | } |
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int perf_header__getbuffer64(struct perf_header *self, | ||
537 | int fd, void *buf, size_t size) | ||
538 | { | ||
539 | if (do_read(fd, buf, size)) | ||
540 | return -1; | ||
541 | |||
542 | if (self->needs_swap) | ||
543 | mem_bswap_64(buf, size); | ||
544 | |||
545 | return 0; | ||
376 | } | 546 | } |
377 | 547 | ||
378 | int perf_header__process_sections(struct perf_header *self, int fd, | 548 | int perf_header__process_sections(struct perf_header *self, int fd, |
379 | int (*process)(struct perf_file_section *self, | 549 | int (*process)(struct perf_file_section *self, |
550 | struct perf_header *ph, | ||
380 | int feat, int fd)) | 551 | int feat, int fd)) |
381 | { | 552 | { |
382 | struct perf_file_section *feat_sec; | 553 | struct perf_file_section *feat_sec; |
383 | int nr_sections; | 554 | int nr_sections; |
384 | int sec_size; | 555 | int sec_size; |
385 | int idx = 0; | 556 | int idx = 0; |
386 | int err = 0, feat = 1; | 557 | int err = -1, feat = 1; |
387 | 558 | ||
388 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 559 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); |
389 | if (!nr_sections) | 560 | if (!nr_sections) |
@@ -397,33 +568,45 @@ int perf_header__process_sections(struct perf_header *self, int fd, | |||
397 | 568 | ||
398 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 569 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
399 | 570 | ||
400 | do_read(fd, feat_sec, sec_size); | 571 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) |
572 | goto out_free; | ||
401 | 573 | ||
574 | err = 0; | ||
402 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | 575 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { |
403 | if (perf_header__has_feat(self, feat)) { | 576 | if (perf_header__has_feat(self, feat)) { |
404 | struct perf_file_section *sec = &feat_sec[idx++]; | 577 | struct perf_file_section *sec = &feat_sec[idx++]; |
405 | 578 | ||
406 | err = process(sec, feat, fd); | 579 | err = process(sec, self, feat, fd); |
407 | if (err < 0) | 580 | if (err < 0) |
408 | break; | 581 | break; |
409 | } | 582 | } |
410 | ++feat; | 583 | ++feat; |
411 | } | 584 | } |
412 | 585 | out_free: | |
413 | free(feat_sec); | 586 | free(feat_sec); |
414 | return err; | 587 | return err; |
415 | }; | 588 | } |
416 | 589 | ||
417 | int perf_file_header__read(struct perf_file_header *self, | 590 | int perf_file_header__read(struct perf_file_header *self, |
418 | struct perf_header *ph, int fd) | 591 | struct perf_header *ph, int fd) |
419 | { | 592 | { |
420 | lseek(fd, 0, SEEK_SET); | 593 | lseek(fd, 0, SEEK_SET); |
421 | do_read(fd, self, sizeof(*self)); | ||
422 | 594 | ||
423 | if (self->magic != PERF_MAGIC || | 595 | if (do_read(fd, self, sizeof(*self)) || |
424 | self->attr_size != sizeof(struct perf_file_attr)) | 596 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
425 | return -1; | 597 | return -1; |
426 | 598 | ||
599 | if (self->attr_size != sizeof(struct perf_file_attr)) { | ||
600 | u64 attr_size = bswap_64(self->attr_size); | ||
601 | |||
602 | if (attr_size != sizeof(struct perf_file_attr)) | ||
603 | return -1; | ||
604 | |||
605 | mem_bswap_64(self, offsetof(struct perf_file_header, | ||
606 | adds_features)); | ||
607 | ph->needs_swap = true; | ||
608 | } | ||
609 | |||
427 | if (self->size != sizeof(*self)) { | 610 | if (self->size != sizeof(*self)) { |
428 | /* Support the previous format */ | 611 | /* Support the previous format */ |
429 | if (self->size == offsetof(typeof(*self), adds_features)) | 612 | if (self->size == offsetof(typeof(*self), adds_features)) |
@@ -433,19 +616,31 @@ int perf_file_header__read(struct perf_file_header *self, | |||
433 | } | 616 | } |
434 | 617 | ||
435 | memcpy(&ph->adds_features, &self->adds_features, | 618 | memcpy(&ph->adds_features, &self->adds_features, |
436 | sizeof(self->adds_features)); | 619 | sizeof(ph->adds_features)); |
620 | /* | ||
621 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
622 | * may be coming from an arch with a different word-size, ergo different | ||
623 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
624 | * safe to assume that we have a build-id section. Trace files probably | ||
625 | * have several other issues in this realm anyway... | ||
626 | */ | ||
627 | if (ph->needs_swap) { | ||
628 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
629 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
630 | } | ||
437 | 631 | ||
438 | ph->event_offset = self->event_types.offset; | 632 | ph->event_offset = self->event_types.offset; |
439 | ph->event_size = self->event_types.size; | 633 | ph->event_size = self->event_types.size; |
440 | ph->data_offset = self->data.offset; | 634 | ph->data_offset = self->data.offset; |
441 | ph->data_size = self->data.size; | 635 | ph->data_size = self->data.size; |
442 | return 0; | 636 | return 0; |
443 | } | 637 | } |
444 | 638 | ||
445 | static int perf_file_section__process(struct perf_file_section *self, | 639 | static int perf_file_section__process(struct perf_file_section *self, |
640 | struct perf_header *ph, | ||
446 | int feat, int fd) | 641 | int feat, int fd) |
447 | { | 642 | { |
448 | if (lseek(fd, self->offset, SEEK_SET) < 0) { | 643 | if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { |
449 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | 644 | pr_debug("Failed to lseek to %Ld offset for feature %d, " |
450 | "continuing...\n", self->offset, feat); | 645 | "continuing...\n", self->offset, feat); |
451 | return 0; | 646 | return 0; |
@@ -457,7 +652,7 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
457 | break; | 652 | break; |
458 | 653 | ||
459 | case HEADER_BUILD_ID: | 654 | case HEADER_BUILD_ID: |
460 | if (perf_header__read_build_ids(fd, self->offset, self->size)) | 655 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) |
461 | pr_debug("Failed to read buildids, continuing...\n"); | 656 | pr_debug("Failed to read buildids, continuing...\n"); |
462 | break; | 657 | break; |
463 | default: | 658 | default: |
@@ -469,7 +664,7 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
469 | 664 | ||
470 | int perf_header__read(struct perf_header *self, int fd) | 665 | int perf_header__read(struct perf_header *self, int fd) |
471 | { | 666 | { |
472 | struct perf_file_header f_header; | 667 | struct perf_file_header f_header; |
473 | struct perf_file_attr f_attr; | 668 | struct perf_file_attr f_attr; |
474 | u64 f_id; | 669 | u64 f_id; |
475 | int nr_attrs, nr_ids, i, j; | 670 | int nr_attrs, nr_ids, i, j; |
@@ -486,7 +681,9 @@ int perf_header__read(struct perf_header *self, int fd) | |||
486 | struct perf_header_attr *attr; | 681 | struct perf_header_attr *attr; |
487 | off_t tmp; | 682 | off_t tmp; |
488 | 683 | ||
489 | do_read(fd, &f_attr, sizeof(f_attr)); | 684 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) |
685 | goto out_errno; | ||
686 | |||
490 | tmp = lseek(fd, 0, SEEK_CUR); | 687 | tmp = lseek(fd, 0, SEEK_CUR); |
491 | 688 | ||
492 | attr = perf_header_attr__new(&f_attr.attr); | 689 | attr = perf_header_attr__new(&f_attr.attr); |
@@ -497,7 +694,8 @@ int perf_header__read(struct perf_header *self, int fd) | |||
497 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 694 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
498 | 695 | ||
499 | for (j = 0; j < nr_ids; j++) { | 696 | for (j = 0; j < nr_ids; j++) { |
500 | do_read(fd, &f_id, sizeof(f_id)); | 697 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) |
698 | goto out_errno; | ||
501 | 699 | ||
502 | if (perf_header_attr__add_id(attr, f_id) < 0) { | 700 | if (perf_header_attr__add_id(attr, f_id) < 0) { |
503 | perf_header_attr__delete(attr); | 701 | perf_header_attr__delete(attr); |
@@ -517,7 +715,9 @@ int perf_header__read(struct perf_header *self, int fd) | |||
517 | events = malloc(f_header.event_types.size); | 715 | events = malloc(f_header.event_types.size); |
518 | if (events == NULL) | 716 | if (events == NULL) |
519 | return -ENOMEM; | 717 | return -ENOMEM; |
520 | do_read(fd, events, f_header.event_types.size); | 718 | if (perf_header__getbuffer64(self, fd, events, |
719 | f_header.event_types.size)) | ||
720 | goto out_errno; | ||
521 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 721 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
522 | } | 722 | } |
523 | 723 | ||
@@ -527,6 +727,8 @@ int perf_header__read(struct perf_header *self, int fd) | |||
527 | 727 | ||
528 | self->frozen = 1; | 728 | self->frozen = 1; |
529 | return 0; | 729 | return 0; |
730 | out_errno: | ||
731 | return -errno; | ||
530 | } | 732 | } |
531 | 733 | ||
532 | u64 perf_header__sample_type(struct perf_header *header) | 734 | u64 perf_header__sample_type(struct perf_header *header) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d118d05d3abe..82a6af72d4cc 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include "types.h" |
8 | #include "event.h" | ||
8 | 9 | ||
9 | #include <linux/bitmap.h> | 10 | #include <linux/bitmap.h> |
10 | 11 | ||
@@ -52,6 +53,7 @@ struct perf_header { | |||
52 | u64 data_size; | 53 | u64 data_size; |
53 | u64 event_offset; | 54 | u64 event_offset; |
54 | u64 event_size; | 55 | u64 event_size; |
56 | bool needs_swap; | ||
55 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 57 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
56 | }; | 58 | }; |
57 | 59 | ||
@@ -64,7 +66,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit); | |||
64 | int perf_header__add_attr(struct perf_header *self, | 66 | int perf_header__add_attr(struct perf_header *self, |
65 | struct perf_header_attr *attr); | 67 | struct perf_header_attr *attr); |
66 | 68 | ||
67 | void perf_header__push_event(u64 id, const char *name); | 69 | int perf_header__push_event(u64 id, const char *name); |
68 | char *perf_header__find_event(u64 id); | 70 | char *perf_header__find_event(u64 id); |
69 | 71 | ||
70 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | 72 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); |
@@ -80,6 +82,11 @@ bool perf_header__has_feat(const struct perf_header *self, int feat); | |||
80 | 82 | ||
81 | int perf_header__process_sections(struct perf_header *self, int fd, | 83 | int perf_header__process_sections(struct perf_header *self, int fd, |
82 | int (*process)(struct perf_file_section *self, | 84 | int (*process)(struct perf_file_section *self, |
85 | struct perf_header *ph, | ||
83 | int feat, int fd)); | 86 | int feat, int fd)); |
84 | 87 | ||
88 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
89 | const char *name, bool is_kallsyms); | ||
90 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
91 | |||
85 | #endif /* __PERF_HEADER_H */ | 92 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e8daf5ca6fd2..44408c2621cf 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -321,7 +321,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
321 | new_depth_mask &= ~(1 << (depth - 1)); | 321 | new_depth_mask &= ~(1 << (depth - 1)); |
322 | 322 | ||
323 | /* | 323 | /* |
324 | * But we keep the older depth mask for the line seperator | 324 | * But we keep the older depth mask for the line separator |
325 | * to keep the level link until we reach the last child | 325 | * to keep the level link until we reach the last child |
326 | */ | 326 | */ |
327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | 327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, |
diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h new file mode 100644 index 000000000000..201f57397997 --- /dev/null +++ b/tools/perf/util/include/linux/hash.h | |||
@@ -0,0 +1,5 @@ | |||
1 | #include "../../../../include/linux/hash.h" | ||
2 | |||
3 | #ifndef PERF_HASH_H | ||
4 | #define PERF_HASH_H | ||
5 | #endif | ||
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 21c0274c02fa..f2611655ab51 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
@@ -101,5 +101,6 @@ simple_strtoul(const char *nptr, char **endptr, int base) | |||
101 | eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) | 101 | eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) |
102 | #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) | 102 | #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) |
103 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | 103 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) |
104 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) | ||
104 | 105 | ||
105 | #endif | 106 | #endif |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c4d55a0da2ea..e509cd59c67d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -5,6 +5,11 @@ | |||
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include "debug.h" | 6 | #include "debug.h" |
7 | 7 | ||
8 | const char *map_type__name[MAP__NR_TYPES] = { | ||
9 | [MAP__FUNCTION] = "Functions", | ||
10 | [MAP__VARIABLE] = "Variables", | ||
11 | }; | ||
12 | |||
8 | static inline int is_anon_memory(const char *filename) | 13 | static inline int is_anon_memory(const char *filename) |
9 | { | 14 | { |
10 | return strcmp(filename, "//anon") == 0; | 15 | return strcmp(filename, "//anon") == 0; |
@@ -68,8 +73,13 @@ struct map *map__new(struct mmap_event *event, enum map_type type, | |||
68 | map__init(self, type, event->start, event->start + event->len, | 73 | map__init(self, type, event->start, event->start + event->len, |
69 | event->pgoff, dso); | 74 | event->pgoff, dso); |
70 | 75 | ||
71 | if (self->dso == vdso || anon) | 76 | if (anon) { |
77 | set_identity: | ||
72 | self->map_ip = self->unmap_ip = identity__map_ip; | 78 | self->map_ip = self->unmap_ip = identity__map_ip; |
79 | } else if (strcmp(filename, "[vdso]") == 0) { | ||
80 | dso__set_loaded(dso, self->type); | ||
81 | goto set_identity; | ||
82 | } | ||
73 | } | 83 | } |
74 | return self; | 84 | return self; |
75 | out_delete: | 85 | out_delete: |
@@ -104,8 +114,7 @@ void map__fixup_end(struct map *self) | |||
104 | 114 | ||
105 | #define DSO__DELETED "(deleted)" | 115 | #define DSO__DELETED "(deleted)" |
106 | 116 | ||
107 | int map__load(struct map *self, struct perf_session *session, | 117 | int map__load(struct map *self, symbol_filter_t filter) |
108 | symbol_filter_t filter) | ||
109 | { | 118 | { |
110 | const char *name = self->dso->long_name; | 119 | const char *name = self->dso->long_name; |
111 | int nr; | 120 | int nr; |
@@ -113,7 +122,7 @@ int map__load(struct map *self, struct perf_session *session, | |||
113 | if (dso__loaded(self->dso, self->type)) | 122 | if (dso__loaded(self->dso, self->type)) |
114 | return 0; | 123 | return 0; |
115 | 124 | ||
116 | nr = dso__load(self->dso, self, session, filter); | 125 | nr = dso__load(self->dso, self, filter); |
117 | if (nr < 0) { | 126 | if (nr < 0) { |
118 | if (self->dso->has_build_id) { | 127 | if (self->dso->has_build_id) { |
119 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 128 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
@@ -144,24 +153,29 @@ int map__load(struct map *self, struct perf_session *session, | |||
144 | 153 | ||
145 | return -1; | 154 | return -1; |
146 | } | 155 | } |
156 | /* | ||
157 | * Only applies to the kernel, as its symtabs aren't relative like the | ||
158 | * module ones. | ||
159 | */ | ||
160 | if (self->dso->kernel) | ||
161 | map__reloc_vmlinux(self); | ||
147 | 162 | ||
148 | return 0; | 163 | return 0; |
149 | } | 164 | } |
150 | 165 | ||
151 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | 166 | struct symbol *map__find_symbol(struct map *self, u64 addr, |
152 | u64 addr, symbol_filter_t filter) | 167 | symbol_filter_t filter) |
153 | { | 168 | { |
154 | if (map__load(self, session, filter) < 0) | 169 | if (map__load(self, filter) < 0) |
155 | return NULL; | 170 | return NULL; |
156 | 171 | ||
157 | return dso__find_symbol(self->dso, self->type, addr); | 172 | return dso__find_symbol(self->dso, self->type, addr); |
158 | } | 173 | } |
159 | 174 | ||
160 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 175 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, |
161 | struct perf_session *session, | ||
162 | symbol_filter_t filter) | 176 | symbol_filter_t filter) |
163 | { | 177 | { |
164 | if (map__load(self, session, filter) < 0) | 178 | if (map__load(self, filter) < 0) |
165 | return NULL; | 179 | return NULL; |
166 | 180 | ||
167 | if (!dso__sorted_by_name(self->dso, self->type)) | 181 | if (!dso__sorted_by_name(self->dso, self->type)) |
@@ -201,3 +215,23 @@ size_t map__fprintf(struct map *self, FILE *fp) | |||
201 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | 215 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", |
202 | self->start, self->end, self->pgoff, self->dso->name); | 216 | self->start, self->end, self->pgoff, self->dso->name); |
203 | } | 217 | } |
218 | |||
219 | /* | ||
220 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | ||
221 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. | ||
222 | */ | ||
223 | u64 map__rip_2objdump(struct map *map, u64 rip) | ||
224 | { | ||
225 | u64 addr = map->dso->adjust_symbols ? | ||
226 | map->unmap_ip(map, rip) : /* RIP -> IP */ | ||
227 | rip; | ||
228 | return addr; | ||
229 | } | ||
230 | |||
231 | u64 map__objdump_2ip(struct map *map, u64 addr) | ||
232 | { | ||
233 | u64 ip = map->dso->adjust_symbols ? | ||
234 | addr : | ||
235 | map->unmap_ip(map, addr); /* RIP -> IP */ | ||
236 | return ip; | ||
237 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h new file mode 100644 index 000000000000..b756368076c6 --- /dev/null +++ b/tools/perf/util/map.h | |||
@@ -0,0 +1,94 @@ | |||
1 | #ifndef __PERF_MAP_H | ||
2 | #define __PERF_MAP_H | ||
3 | |||
4 | #include <linux/compiler.h> | ||
5 | #include <linux/list.h> | ||
6 | #include <linux/rbtree.h> | ||
7 | #include <linux/types.h> | ||
8 | |||
9 | enum map_type { | ||
10 | MAP__FUNCTION = 0, | ||
11 | MAP__VARIABLE, | ||
12 | }; | ||
13 | |||
14 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
15 | |||
16 | extern const char *map_type__name[MAP__NR_TYPES]; | ||
17 | |||
18 | struct dso; | ||
19 | struct ref_reloc_sym; | ||
20 | struct map_groups; | ||
21 | |||
22 | struct map { | ||
23 | union { | ||
24 | struct rb_node rb_node; | ||
25 | struct list_head node; | ||
26 | }; | ||
27 | u64 start; | ||
28 | u64 end; | ||
29 | enum map_type type; | ||
30 | u64 pgoff; | ||
31 | |||
32 | /* ip -> dso rip */ | ||
33 | u64 (*map_ip)(struct map *, u64); | ||
34 | /* dso rip -> ip */ | ||
35 | u64 (*unmap_ip)(struct map *, u64); | ||
36 | |||
37 | struct dso *dso; | ||
38 | }; | ||
39 | |||
40 | struct kmap { | ||
41 | struct ref_reloc_sym *ref_reloc_sym; | ||
42 | struct map_groups *kmaps; | ||
43 | }; | ||
44 | |||
45 | static inline struct kmap *map__kmap(struct map *self) | ||
46 | { | ||
47 | return (struct kmap *)(self + 1); | ||
48 | } | ||
49 | |||
50 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
51 | { | ||
52 | return ip - map->start + map->pgoff; | ||
53 | } | ||
54 | |||
55 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | ||
56 | { | ||
57 | return ip + map->start - map->pgoff; | ||
58 | } | ||
59 | |||
60 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
61 | { | ||
62 | return ip; | ||
63 | } | ||
64 | |||
65 | |||
66 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ | ||
67 | u64 map__rip_2objdump(struct map *map, u64 rip); | ||
68 | u64 map__objdump_2ip(struct map *map, u64 addr); | ||
69 | |||
70 | struct symbol; | ||
71 | struct mmap_event; | ||
72 | |||
73 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
74 | |||
75 | void map__init(struct map *self, enum map_type type, | ||
76 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
77 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
78 | char *cwd, int cwdlen); | ||
79 | void map__delete(struct map *self); | ||
80 | struct map *map__clone(struct map *self); | ||
81 | int map__overlap(struct map *l, struct map *r); | ||
82 | size_t map__fprintf(struct map *self, FILE *fp); | ||
83 | |||
84 | int map__load(struct map *self, symbol_filter_t filter); | ||
85 | struct symbol *map__find_symbol(struct map *self, | ||
86 | u64 addr, symbol_filter_t filter); | ||
87 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
88 | symbol_filter_t filter); | ||
89 | void map__fixup_start(struct map *self); | ||
90 | void map__fixup_end(struct map *self); | ||
91 | |||
92 | void map__reloc_vmlinux(struct map *self); | ||
93 | |||
94 | #endif /* __PERF_MAP_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index e5bc0fb016b2..05d0c5c2030c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -450,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
450 | /* sys + ':' + event + ':' + flags*/ | 450 | /* sys + ':' + event + ':' + flags*/ |
451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
452 | static enum event_result | 452 | static enum event_result |
453 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | 453 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, |
454 | char *flags) | ||
454 | { | 455 | { |
455 | char evt_path[MAXPATHLEN]; | 456 | char evt_path[MAXPATHLEN]; |
456 | struct dirent *evt_ent; | 457 | struct dirent *evt_ent; |
@@ -474,6 +475,9 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
474 | || !strcmp(evt_ent->d_name, "filter")) | 475 | || !strcmp(evt_ent->d_name, "filter")) |
475 | continue; | 476 | continue; |
476 | 477 | ||
478 | if (!strglobmatch(evt_ent->d_name, evt_exp)) | ||
479 | continue; | ||
480 | |||
477 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | 481 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, |
478 | evt_ent->d_name, flags ? ":" : "", | 482 | evt_ent->d_name, flags ? ":" : "", |
479 | flags ?: ""); | 483 | flags ?: ""); |
@@ -522,9 +526,10 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
522 | if (evt_length >= MAX_EVENT_LENGTH) | 526 | if (evt_length >= MAX_EVENT_LENGTH) |
523 | return EVT_FAILED; | 527 | return EVT_FAILED; |
524 | 528 | ||
525 | if (!strcmp(evt_name, "*")) { | 529 | if (strpbrk(evt_name, "*?")) { |
526 | *strp = evt_name + evt_length; | 530 | *strp = evt_name + evt_length; |
527 | return parse_subsystem_tracepoint_event(sys_name, flags); | 531 | return parse_multiple_tracepoint_event(sys_name, evt_name, |
532 | flags); | ||
528 | } else | 533 | } else |
529 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
530 | evt_length, flags, | 535 | evt_length, flags, |
@@ -753,11 +758,11 @@ modifier: | |||
753 | return ret; | 758 | return ret; |
754 | } | 759 | } |
755 | 760 | ||
756 | static void store_event_type(const char *orgname) | 761 | static int store_event_type(const char *orgname) |
757 | { | 762 | { |
758 | char filename[PATH_MAX], *c; | 763 | char filename[PATH_MAX], *c; |
759 | FILE *file; | 764 | FILE *file; |
760 | int id; | 765 | int id, n; |
761 | 766 | ||
762 | sprintf(filename, "%s/", debugfs_path); | 767 | sprintf(filename, "%s/", debugfs_path); |
763 | strncat(filename, orgname, strlen(orgname)); | 768 | strncat(filename, orgname, strlen(orgname)); |
@@ -769,11 +774,14 @@ static void store_event_type(const char *orgname) | |||
769 | 774 | ||
770 | file = fopen(filename, "r"); | 775 | file = fopen(filename, "r"); |
771 | if (!file) | 776 | if (!file) |
772 | return; | 777 | return 0; |
773 | if (fscanf(file, "%i", &id) < 1) | 778 | n = fscanf(file, "%i", &id); |
774 | die("cannot store event ID"); | ||
775 | fclose(file); | 779 | fclose(file); |
776 | perf_header__push_event(id, orgname); | 780 | if (n < 1) { |
781 | pr_err("cannot store event ID\n"); | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | return perf_header__push_event(id, orgname); | ||
777 | } | 785 | } |
778 | 786 | ||
779 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 787 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
@@ -782,7 +790,8 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
782 | enum event_result ret; | 790 | enum event_result ret; |
783 | 791 | ||
784 | if (strchr(str, ':')) | 792 | if (strchr(str, ':')) |
785 | store_event_type(str); | 793 | if (store_event_type(str) < 0) |
794 | return -1; | ||
786 | 795 | ||
787 | for (;;) { | 796 | for (;;) { |
788 | if (nr_counters == MAX_COUNTERS) | 797 | if (nr_counters == MAX_COUNTERS) |
@@ -835,11 +844,12 @@ int parse_filter(const struct option *opt __used, const char *str, | |||
835 | } | 844 | } |
836 | 845 | ||
837 | static const char * const event_type_descriptors[] = { | 846 | static const char * const event_type_descriptors[] = { |
838 | "", | ||
839 | "Hardware event", | 847 | "Hardware event", |
840 | "Software event", | 848 | "Software event", |
841 | "Tracepoint event", | 849 | "Tracepoint event", |
842 | "Hardware cache event", | 850 | "Hardware cache event", |
851 | "Raw hardware event descriptor", | ||
852 | "Hardware breakpoint", | ||
843 | }; | 853 | }; |
844 | 854 | ||
845 | /* | 855 | /* |
@@ -872,7 +882,7 @@ static void print_tracepoint_events(void) | |||
872 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 882 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
873 | sys_dirent.d_name, evt_dirent.d_name); | 883 | sys_dirent.d_name, evt_dirent.d_name); |
874 | printf(" %-42s [%s]\n", evt_path, | 884 | printf(" %-42s [%s]\n", evt_path, |
875 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 885 | event_type_descriptors[PERF_TYPE_TRACEPOINT]); |
876 | } | 886 | } |
877 | closedir(evt_dir); | 887 | closedir(evt_dir); |
878 | } | 888 | } |
@@ -892,9 +902,7 @@ void print_events(void) | |||
892 | printf("List of pre-defined events (to be used in -e):\n"); | 902 | printf("List of pre-defined events (to be used in -e):\n"); |
893 | 903 | ||
894 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 904 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
895 | type = syms->type + 1; | 905 | type = syms->type; |
896 | if (type >= ARRAY_SIZE(event_type_descriptors)) | ||
897 | type = 0; | ||
898 | 906 | ||
899 | if (type != prev_type) | 907 | if (type != prev_type) |
900 | printf("\n"); | 908 | printf("\n"); |
@@ -919,17 +927,19 @@ void print_events(void) | |||
919 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 927 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
920 | printf(" %-42s [%s]\n", | 928 | printf(" %-42s [%s]\n", |
921 | event_cache_name(type, op, i), | 929 | event_cache_name(type, op, i), |
922 | event_type_descriptors[4]); | 930 | event_type_descriptors[PERF_TYPE_HW_CACHE]); |
923 | } | 931 | } |
924 | } | 932 | } |
925 | } | 933 | } |
926 | 934 | ||
927 | printf("\n"); | 935 | printf("\n"); |
928 | printf(" %-42s [raw hardware event descriptor]\n", | 936 | printf(" %-42s [%s]\n", |
929 | "rNNN"); | 937 | "rNNN", event_type_descriptors[PERF_TYPE_RAW]); |
930 | printf("\n"); | 938 | printf("\n"); |
931 | 939 | ||
932 | printf(" %-42s [hardware breakpoint]\n", "mem:<addr>[:access]"); | 940 | printf(" %-42s [%s]\n", |
941 | "mem:<addr>[:access]", | ||
942 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | ||
933 | printf("\n"); | 943 | printf("\n"); |
934 | 944 | ||
935 | print_tracepoint_events(); | 945 | print_tracepoint_events(); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 29465d440043..53181dbfe4a8 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -37,6 +37,8 @@ | |||
37 | #include "string.h" | 37 | #include "string.h" |
38 | #include "strlist.h" | 38 | #include "strlist.h" |
39 | #include "debug.h" | 39 | #include "debug.h" |
40 | #include "cache.h" | ||
41 | #include "color.h" | ||
40 | #include "parse-events.h" /* For debugfs_path */ | 42 | #include "parse-events.h" /* For debugfs_path */ |
41 | #include "probe-event.h" | 43 | #include "probe-event.h" |
42 | 44 | ||
@@ -62,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
62 | return ret; | 64 | return ret; |
63 | } | 65 | } |
64 | 66 | ||
67 | void parse_line_range_desc(const char *arg, struct line_range *lr) | ||
68 | { | ||
69 | const char *ptr; | ||
70 | char *tmp; | ||
71 | /* | ||
72 | * <Syntax> | ||
73 | * SRC:SLN[+NUM|-ELN] | ||
74 | * FUNC[:SLN[+NUM|-ELN]] | ||
75 | */ | ||
76 | ptr = strchr(arg, ':'); | ||
77 | if (ptr) { | ||
78 | lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); | ||
79 | if (*tmp == '+') | ||
80 | lr->end = lr->start + (unsigned int)strtoul(tmp + 1, | ||
81 | &tmp, 0); | ||
82 | else if (*tmp == '-') | ||
83 | lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); | ||
84 | else | ||
85 | lr->end = 0; | ||
86 | pr_debug("Line range is %u to %u\n", lr->start, lr->end); | ||
87 | if (lr->end && lr->start > lr->end) | ||
88 | semantic_error("Start line must be smaller" | ||
89 | " than end line."); | ||
90 | if (*tmp != '\0') | ||
91 | semantic_error("Tailing with invalid character '%d'.", | ||
92 | *tmp); | ||
93 | tmp = strndup(arg, (ptr - arg)); | ||
94 | } else | ||
95 | tmp = strdup(arg); | ||
96 | |||
97 | if (strchr(tmp, '.')) | ||
98 | lr->file = tmp; | ||
99 | else | ||
100 | lr->function = tmp; | ||
101 | } | ||
102 | |||
65 | /* Check the name is good for event/group */ | 103 | /* Check the name is good for event/group */ |
66 | static bool check_event_name(const char *name) | 104 | static bool check_event_name(const char *name) |
67 | { | 105 | { |
@@ -81,14 +119,14 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
81 | char c, nc = 0; | 119 | char c, nc = 0; |
82 | /* | 120 | /* |
83 | * <Syntax> | 121 | * <Syntax> |
84 | * perf probe [EVENT=]SRC:LN | 122 | * perf probe [EVENT=]SRC[:LN|;PTN] |
85 | * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] | 123 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] |
86 | * | 124 | * |
87 | * TODO:Group name support | 125 | * TODO:Group name support |
88 | */ | 126 | */ |
89 | 127 | ||
90 | ptr = strchr(arg, '='); | 128 | ptr = strpbrk(arg, ";=@+%"); |
91 | if (ptr) { /* Event name */ | 129 | if (ptr && *ptr == '=') { /* Event name */ |
92 | *ptr = '\0'; | 130 | *ptr = '\0'; |
93 | tmp = ptr + 1; | 131 | tmp = ptr + 1; |
94 | ptr = strchr(arg, ':'); | 132 | ptr = strchr(arg, ':'); |
@@ -101,7 +139,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
101 | arg = tmp; | 139 | arg = tmp; |
102 | } | 140 | } |
103 | 141 | ||
104 | ptr = strpbrk(arg, ":+@%"); | 142 | ptr = strpbrk(arg, ";:+@%"); |
105 | if (ptr) { | 143 | if (ptr) { |
106 | nc = *ptr; | 144 | nc = *ptr; |
107 | *ptr++ = '\0'; | 145 | *ptr++ = '\0'; |
@@ -118,7 +156,11 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
118 | while (ptr) { | 156 | while (ptr) { |
119 | arg = ptr; | 157 | arg = ptr; |
120 | c = nc; | 158 | c = nc; |
121 | ptr = strpbrk(arg, ":+@%"); | 159 | if (c == ';') { /* Lazy pattern must be the last part */ |
160 | pp->lazy_line = strdup(arg); | ||
161 | break; | ||
162 | } | ||
163 | ptr = strpbrk(arg, ";:+@%"); | ||
122 | if (ptr) { | 164 | if (ptr) { |
123 | nc = *ptr; | 165 | nc = *ptr; |
124 | *ptr++ = '\0'; | 166 | *ptr++ = '\0'; |
@@ -127,13 +169,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
127 | case ':': /* Line number */ | 169 | case ':': /* Line number */ |
128 | pp->line = strtoul(arg, &tmp, 0); | 170 | pp->line = strtoul(arg, &tmp, 0); |
129 | if (*tmp != '\0') | 171 | if (*tmp != '\0') |
130 | semantic_error("There is non-digit charactor" | 172 | semantic_error("There is non-digit char" |
131 | " in line number."); | 173 | " in line number."); |
132 | break; | 174 | break; |
133 | case '+': /* Byte offset from a symbol */ | 175 | case '+': /* Byte offset from a symbol */ |
134 | pp->offset = strtoul(arg, &tmp, 0); | 176 | pp->offset = strtoul(arg, &tmp, 0); |
135 | if (*tmp != '\0') | 177 | if (*tmp != '\0') |
136 | semantic_error("There is non-digit charactor" | 178 | semantic_error("There is non-digit character" |
137 | " in offset."); | 179 | " in offset."); |
138 | break; | 180 | break; |
139 | case '@': /* File name */ | 181 | case '@': /* File name */ |
@@ -141,9 +183,6 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
141 | semantic_error("SRC@SRC is not allowed."); | 183 | semantic_error("SRC@SRC is not allowed."); |
142 | pp->file = strdup(arg); | 184 | pp->file = strdup(arg); |
143 | DIE_IF(pp->file == NULL); | 185 | DIE_IF(pp->file == NULL); |
144 | if (ptr) | ||
145 | semantic_error("@SRC must be the last " | ||
146 | "option."); | ||
147 | break; | 186 | break; |
148 | case '%': /* Probe places */ | 187 | case '%': /* Probe places */ |
149 | if (strcmp(arg, "return") == 0) { | 188 | if (strcmp(arg, "return") == 0) { |
@@ -158,11 +197,18 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
158 | } | 197 | } |
159 | 198 | ||
160 | /* Exclusion check */ | 199 | /* Exclusion check */ |
200 | if (pp->lazy_line && pp->line) | ||
201 | semantic_error("Lazy pattern can't be used with line number."); | ||
202 | |||
203 | if (pp->lazy_line && pp->offset) | ||
204 | semantic_error("Lazy pattern can't be used with offset."); | ||
205 | |||
161 | if (pp->line && pp->offset) | 206 | if (pp->line && pp->offset) |
162 | semantic_error("Offset can't be used with line number."); | 207 | semantic_error("Offset can't be used with line number."); |
163 | 208 | ||
164 | if (!pp->line && pp->file && !pp->function) | 209 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) |
165 | semantic_error("File always requires line number."); | 210 | semantic_error("File always requires line number or " |
211 | "lazy pattern."); | ||
166 | 212 | ||
167 | if (pp->offset && !pp->function) | 213 | if (pp->offset && !pp->function) |
168 | semantic_error("Offset requires an entry function."); | 214 | semantic_error("Offset requires an entry function."); |
@@ -170,11 +216,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
170 | if (pp->retprobe && !pp->function) | 216 | if (pp->retprobe && !pp->function) |
171 | semantic_error("Return probe requires an entry function."); | 217 | semantic_error("Return probe requires an entry function."); |
172 | 218 | ||
173 | if ((pp->offset || pp->line) && pp->retprobe) | 219 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) |
174 | semantic_error("Offset/Line can't be used with return probe."); | 220 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
221 | "return probe."); | ||
175 | 222 | ||
176 | pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", | 223 | pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", |
177 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe); | 224 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, |
225 | pp->lazy_line); | ||
178 | } | 226 | } |
179 | 227 | ||
180 | /* Parse perf-probe event definition */ | 228 | /* Parse perf-probe event definition */ |
@@ -272,6 +320,7 @@ int synthesize_perf_probe_point(struct probe_point *pp) | |||
272 | int ret; | 320 | int ret; |
273 | 321 | ||
274 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | 322 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); |
323 | pp->found = 1; | ||
275 | if (!buf) | 324 | if (!buf) |
276 | die("Failed to allocate memory by zalloc."); | 325 | die("Failed to allocate memory by zalloc."); |
277 | if (pp->offset) { | 326 | if (pp->offset) { |
@@ -294,6 +343,7 @@ int synthesize_perf_probe_point(struct probe_point *pp) | |||
294 | error: | 343 | error: |
295 | free(pp->probes[0]); | 344 | free(pp->probes[0]); |
296 | pp->probes[0] = NULL; | 345 | pp->probes[0] = NULL; |
346 | pp->found = 0; | ||
297 | } | 347 | } |
298 | return ret; | 348 | return ret; |
299 | } | 349 | } |
@@ -368,7 +418,7 @@ static int open_kprobe_events(int flags, int mode) | |||
368 | if (ret < 0) { | 418 | if (ret < 0) { |
369 | if (errno == ENOENT) | 419 | if (errno == ENOENT) |
370 | die("kprobe_events file does not exist -" | 420 | die("kprobe_events file does not exist -" |
371 | " please rebuild with CONFIG_KPROBE_TRACER."); | 421 | " please rebuild with CONFIG_KPROBE_EVENT."); |
372 | else | 422 | else |
373 | die("Could not open kprobe_events file: %s", | 423 | die("Could not open kprobe_events file: %s", |
374 | strerror(errno)); | 424 | strerror(errno)); |
@@ -418,6 +468,8 @@ static void clear_probe_point(struct probe_point *pp) | |||
418 | free(pp->function); | 468 | free(pp->function); |
419 | if (pp->file) | 469 | if (pp->file) |
420 | free(pp->file); | 470 | free(pp->file); |
471 | if (pp->lazy_line) | ||
472 | free(pp->lazy_line); | ||
421 | for (i = 0; i < pp->nr_args; i++) | 473 | for (i = 0; i < pp->nr_args; i++) |
422 | free(pp->args[i]); | 474 | free(pp->args[i]); |
423 | if (pp->args) | 475 | if (pp->args) |
@@ -455,6 +507,9 @@ void show_perf_probe_events(void) | |||
455 | struct strlist *rawlist; | 507 | struct strlist *rawlist; |
456 | struct str_node *ent; | 508 | struct str_node *ent; |
457 | 509 | ||
510 | setup_pager(); | ||
511 | memset(&pp, 0, sizeof(pp)); | ||
512 | |||
458 | fd = open_kprobe_events(O_RDONLY, 0); | 513 | fd = open_kprobe_events(O_RDONLY, 0); |
459 | rawlist = get_trace_kprobe_event_rawlist(fd); | 514 | rawlist = get_trace_kprobe_event_rawlist(fd); |
460 | close(fd); | 515 | close(fd); |
@@ -675,3 +730,73 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
675 | close(fd); | 730 | close(fd); |
676 | } | 731 | } |
677 | 732 | ||
733 | #define LINEBUF_SIZE 256 | ||
734 | #define NR_ADDITIONAL_LINES 2 | ||
735 | |||
736 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
737 | { | ||
738 | char buf[LINEBUF_SIZE]; | ||
739 | const char *color = PERF_COLOR_BLUE; | ||
740 | |||
741 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
742 | goto error; | ||
743 | if (!skip) { | ||
744 | if (show_num) | ||
745 | fprintf(stdout, "%7u %s", l, buf); | ||
746 | else | ||
747 | color_fprintf(stdout, color, " %s", buf); | ||
748 | } | ||
749 | |||
750 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
751 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
752 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
753 | goto error; | ||
754 | if (!skip) { | ||
755 | if (show_num) | ||
756 | fprintf(stdout, "%s", buf); | ||
757 | else | ||
758 | color_fprintf(stdout, color, "%s", buf); | ||
759 | } | ||
760 | } | ||
761 | return; | ||
762 | error: | ||
763 | if (feof(fp)) | ||
764 | die("Source file is shorter than expected."); | ||
765 | else | ||
766 | die("File read error: %s", strerror(errno)); | ||
767 | } | ||
768 | |||
769 | void show_line_range(struct line_range *lr) | ||
770 | { | ||
771 | unsigned int l = 1; | ||
772 | struct line_node *ln; | ||
773 | FILE *fp; | ||
774 | |||
775 | setup_pager(); | ||
776 | |||
777 | if (lr->function) | ||
778 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
779 | lr->start - lr->offset); | ||
780 | else | ||
781 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
782 | |||
783 | fp = fopen(lr->path, "r"); | ||
784 | if (fp == NULL) | ||
785 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
786 | /* Skip to starting line number */ | ||
787 | while (l < lr->start) | ||
788 | show_one_line(fp, l++, true, false); | ||
789 | |||
790 | list_for_each_entry(ln, &lr->line_list, list) { | ||
791 | while (ln->line > l) | ||
792 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
793 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
794 | } | ||
795 | |||
796 | if (lr->end == INT_MAX) | ||
797 | lr->end = l + NR_ADDITIONAL_LINES; | ||
798 | while (l < lr->end && !feof(fp)) | ||
799 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
800 | |||
801 | fclose(fp); | ||
802 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 7f1d499118c0..711287d4baea 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "probe-finder.h" | 5 | #include "probe-finder.h" |
6 | #include "strlist.h" | 6 | #include "strlist.h" |
7 | 7 | ||
8 | extern void parse_line_range_desc(const char *arg, struct line_range *lr); | ||
8 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | 9 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, |
9 | bool *need_dwarf); | 10 | bool *need_dwarf); |
10 | extern int synthesize_perf_probe_point(struct probe_point *pp); | 11 | extern int synthesize_perf_probe_point(struct probe_point *pp); |
@@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | |||
15 | bool force_add); | 16 | bool force_add); |
16 | extern void del_trace_kprobe_events(struct strlist *dellist); | 17 | extern void del_trace_kprobe_events(struct strlist *dellist); |
17 | extern void show_perf_probe_events(void); | 18 | extern void show_perf_probe_events(void); |
19 | extern void show_line_range(struct line_range *lr); | ||
18 | 20 | ||
19 | /* Maximum index number of event-name postfix */ | 21 | /* Maximum index number of event-name postfix */ |
20 | #define MAX_EVENT_INDEX 1024 | 22 | #define MAX_EVENT_INDEX 1024 |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 4b852c0d16a5..e77dc886760e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -32,21 +32,13 @@ | |||
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <ctype.h> | 33 | #include <ctype.h> |
34 | 34 | ||
35 | #include "string.h" | ||
35 | #include "event.h" | 36 | #include "event.h" |
36 | #include "debug.h" | 37 | #include "debug.h" |
37 | #include "util.h" | 38 | #include "util.h" |
38 | #include "probe-finder.h" | 39 | #include "probe-finder.h" |
39 | 40 | ||
40 | 41 | ||
41 | /* Dwarf_Die Linkage to parent Die */ | ||
42 | struct die_link { | ||
43 | struct die_link *parent; /* Parent die */ | ||
44 | Dwarf_Die die; /* Current die */ | ||
45 | }; | ||
46 | |||
47 | static Dwarf_Debug __dw_debug; | ||
48 | static Dwarf_Error __dw_error; | ||
49 | |||
50 | /* | 42 | /* |
51 | * Generic dwarf analysis helpers | 43 | * Generic dwarf analysis helpers |
52 | */ | 44 | */ |
@@ -113,256 +105,190 @@ static int strtailcmp(const char *s1, const char *s2) | |||
113 | return 0; | 105 | return 0; |
114 | } | 106 | } |
115 | 107 | ||
116 | /* Find the fileno of the target file. */ | 108 | /* Line number list operations */ |
117 | static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) | ||
118 | { | ||
119 | Dwarf_Signed cnt, i; | ||
120 | Dwarf_Unsigned found = 0; | ||
121 | char **srcs; | ||
122 | int ret; | ||
123 | |||
124 | if (!fname) | ||
125 | return 0; | ||
126 | 109 | ||
127 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | 110 | /* Add a line to line number list */ |
128 | if (ret == DW_DLV_OK) { | 111 | static void line_list__add_line(struct list_head *head, unsigned int line) |
129 | for (i = 0; i < cnt && !found; i++) { | 112 | { |
130 | if (strtailcmp(srcs[i], fname) == 0) | 113 | struct line_node *ln; |
131 | found = i + 1; | 114 | struct list_head *p; |
132 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | 115 | |
133 | } | 116 | /* Reverse search, because new line will be the last one */ |
134 | for (; i < cnt; i++) | 117 | list_for_each_entry_reverse(ln, head, list) { |
135 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | 118 | if (ln->line < line) { |
136 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | 119 | p = &ln->list; |
120 | goto found; | ||
121 | } else if (ln->line == line) /* Already exist */ | ||
122 | return ; | ||
137 | } | 123 | } |
138 | if (found) | 124 | /* List is empty, or the smallest entry */ |
139 | pr_debug("found fno: %d\n", (int)found); | 125 | p = head; |
140 | return found; | 126 | found: |
127 | pr_debug("line list: add a line %u\n", line); | ||
128 | ln = zalloc(sizeof(struct line_node)); | ||
129 | DIE_IF(ln == NULL); | ||
130 | ln->line = line; | ||
131 | INIT_LIST_HEAD(&ln->list); | ||
132 | list_add(&ln->list, p); | ||
141 | } | 133 | } |
142 | 134 | ||
143 | /* Compare diename and tname */ | 135 | /* Check if the line in line number list */ |
144 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) | 136 | static int line_list__has_line(struct list_head *head, unsigned int line) |
145 | { | 137 | { |
146 | char *name; | 138 | struct line_node *ln; |
147 | int ret; | ||
148 | ret = dwarf_diename(dw_die, &name, &__dw_error); | ||
149 | DIE_IF(ret == DW_DLV_ERROR); | ||
150 | if (ret == DW_DLV_OK) { | ||
151 | ret = strcmp(tname, name); | ||
152 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
153 | } else | ||
154 | ret = -1; | ||
155 | return ret; | ||
156 | } | ||
157 | 139 | ||
158 | /* Check the address is in the subprogram(function). */ | 140 | /* Reverse search, because new line will be the last one */ |
159 | static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr, | 141 | list_for_each_entry(ln, head, list) |
160 | Dwarf_Signed *offs) | 142 | if (ln->line == line) |
161 | { | 143 | return 1; |
162 | Dwarf_Addr lopc, hipc; | ||
163 | int ret; | ||
164 | 144 | ||
165 | /* TODO: check ranges */ | 145 | return 0; |
166 | ret = dwarf_lowpc(sp_die, &lopc, &__dw_error); | ||
167 | DIE_IF(ret == DW_DLV_ERROR); | ||
168 | if (ret == DW_DLV_NO_ENTRY) | ||
169 | return 0; | ||
170 | ret = dwarf_highpc(sp_die, &hipc, &__dw_error); | ||
171 | DIE_IF(ret != DW_DLV_OK); | ||
172 | if (lopc <= addr && addr < hipc) { | ||
173 | *offs = addr - lopc; | ||
174 | return 1; | ||
175 | } else | ||
176 | return 0; | ||
177 | } | 146 | } |
178 | 147 | ||
179 | /* Check the die is inlined function */ | 148 | /* Init line number list */ |
180 | static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die) | 149 | static void line_list__init(struct list_head *head) |
181 | { | 150 | { |
182 | /* TODO: check strictly */ | 151 | INIT_LIST_HEAD(head); |
183 | Dwarf_Bool inl; | 152 | } |
184 | int ret; | ||
185 | 153 | ||
186 | ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error); | 154 | /* Free line number list */ |
187 | DIE_IF(ret == DW_DLV_ERROR); | 155 | static void line_list__free(struct list_head *head) |
188 | return inl; | 156 | { |
157 | struct line_node *ln; | ||
158 | while (!list_empty(head)) { | ||
159 | ln = list_first_entry(head, struct line_node, list); | ||
160 | list_del(&ln->list); | ||
161 | free(ln); | ||
162 | } | ||
189 | } | 163 | } |
190 | 164 | ||
191 | /* Get the offset of abstruct_origin */ | 165 | /* Dwarf wrappers */ |
192 | static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die) | 166 | |
167 | /* Find the realpath of the target file. */ | ||
168 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
193 | { | 169 | { |
194 | Dwarf_Attribute attr; | 170 | Dwarf_Files *files; |
195 | Dwarf_Off cu_offs; | 171 | size_t nfiles, i; |
172 | const char *src; | ||
196 | int ret; | 173 | int ret; |
197 | 174 | ||
198 | ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error); | 175 | if (!fname) |
199 | DIE_IF(ret != DW_DLV_OK); | 176 | return NULL; |
200 | ret = dwarf_formref(attr, &cu_offs, &__dw_error); | 177 | |
201 | DIE_IF(ret != DW_DLV_OK); | 178 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); |
202 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | 179 | if (ret != 0) |
203 | return cu_offs; | 180 | return NULL; |
181 | |||
182 | for (i = 0; i < nfiles; i++) { | ||
183 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
184 | if (strtailcmp(src, fname) == 0) | ||
185 | break; | ||
186 | } | ||
187 | return src; | ||
204 | } | 188 | } |
205 | 189 | ||
206 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | 190 | struct __addr_die_search_param { |
207 | static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die) | 191 | Dwarf_Addr addr; |
192 | Dwarf_Die *die_mem; | ||
193 | }; | ||
194 | |||
195 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
208 | { | 196 | { |
209 | Dwarf_Attribute attr; | 197 | struct __addr_die_search_param *ad = data; |
210 | Dwarf_Addr addr; | ||
211 | Dwarf_Off offs; | ||
212 | Dwarf_Ranges *ranges; | ||
213 | Dwarf_Signed cnt; | ||
214 | int ret; | ||
215 | 198 | ||
216 | /* Try to get entry pc */ | 199 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && |
217 | ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error); | 200 | dwarf_haspc(fn_die, ad->addr)) { |
218 | DIE_IF(ret == DW_DLV_ERROR); | 201 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); |
219 | if (ret == DW_DLV_OK) { | 202 | return DWARF_CB_ABORT; |
220 | ret = dwarf_formaddr(attr, &addr, &__dw_error); | ||
221 | DIE_IF(ret != DW_DLV_OK); | ||
222 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
223 | return addr; | ||
224 | } | 203 | } |
204 | return DWARF_CB_OK; | ||
205 | } | ||
225 | 206 | ||
226 | /* Try to get low pc */ | 207 | /* Search a real subprogram including this line, */ |
227 | ret = dwarf_lowpc(dw_die, &addr, &__dw_error); | 208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, |
228 | DIE_IF(ret == DW_DLV_ERROR); | 209 | Dwarf_Die *die_mem) |
229 | if (ret == DW_DLV_OK) | 210 | { |
230 | return addr; | 211 | struct __addr_die_search_param ad; |
231 | 212 | ad.addr = addr; | |
232 | /* Try to get ranges */ | 213 | ad.die_mem = die_mem; |
233 | ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error); | 214 | /* dwarf_getscopes can't find subprogram. */ |
234 | DIE_IF(ret != DW_DLV_OK); | 215 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) |
235 | ret = dwarf_formref(attr, &offs, &__dw_error); | 216 | return NULL; |
236 | DIE_IF(ret != DW_DLV_OK); | 217 | else |
237 | ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL, | 218 | return die_mem; |
238 | &__dw_error); | ||
239 | DIE_IF(ret != DW_DLV_OK); | ||
240 | addr = ranges[0].dwr_addr1; | ||
241 | dwarf_ranges_dealloc(__dw_debug, ranges, cnt); | ||
242 | return addr; | ||
243 | } | 219 | } |
244 | 220 | ||
245 | /* | 221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ |
246 | * Search a Die from Die tree. | 222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
247 | * Note: cur_link->die should be deallocated in this function. | 223 | Dwarf_Die *die_mem) |
248 | */ | ||
249 | static int __search_die_tree(struct die_link *cur_link, | ||
250 | int (*die_cb)(struct die_link *, void *), | ||
251 | void *data) | ||
252 | { | 224 | { |
253 | Dwarf_Die new_die; | 225 | Dwarf_Die child_die; |
254 | struct die_link new_link; | ||
255 | int ret; | 226 | int ret; |
256 | 227 | ||
257 | if (!die_cb) | 228 | ret = dwarf_child(sp_die, die_mem); |
258 | return 0; | 229 | if (ret != 0) |
259 | 230 | return NULL; | |
260 | /* Check current die */ | ||
261 | while (!(ret = die_cb(cur_link, data))) { | ||
262 | /* Check child die */ | ||
263 | ret = dwarf_child(cur_link->die, &new_die, &__dw_error); | ||
264 | DIE_IF(ret == DW_DLV_ERROR); | ||
265 | if (ret == DW_DLV_OK) { | ||
266 | new_link.parent = cur_link; | ||
267 | new_link.die = new_die; | ||
268 | ret = __search_die_tree(&new_link, die_cb, data); | ||
269 | if (ret) | ||
270 | break; | ||
271 | } | ||
272 | 231 | ||
273 | /* Move to next sibling */ | 232 | do { |
274 | ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die, | 233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && |
275 | &__dw_error); | 234 | dwarf_haspc(die_mem, addr)) |
276 | DIE_IF(ret == DW_DLV_ERROR); | 235 | return die_mem; |
277 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
278 | cur_link->die = new_die; | ||
279 | if (ret == DW_DLV_NO_ENTRY) | ||
280 | return 0; | ||
281 | } | ||
282 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
283 | return ret; | ||
284 | } | ||
285 | 236 | ||
286 | /* Search a die in its children's die tree */ | 237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { |
287 | static int search_die_from_children(Dwarf_Die parent_die, | 238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); |
288 | int (*die_cb)(struct die_link *, void *), | 239 | return die_mem; |
289 | void *data) | 240 | } |
290 | { | 241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); |
291 | struct die_link new_link; | ||
292 | int ret; | ||
293 | 242 | ||
294 | new_link.parent = NULL; | 243 | return NULL; |
295 | ret = dwarf_child(parent_die, &new_link.die, &__dw_error); | ||
296 | DIE_IF(ret == DW_DLV_ERROR); | ||
297 | if (ret == DW_DLV_OK) | ||
298 | return __search_die_tree(&new_link, die_cb, data); | ||
299 | else | ||
300 | return 0; | ||
301 | } | 244 | } |
302 | 245 | ||
303 | /* Find a locdesc corresponding to the address */ | 246 | /* Compare diename and tname */ |
304 | static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc, | 247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
305 | Dwarf_Addr addr) | ||
306 | { | 248 | { |
307 | Dwarf_Signed lcnt; | 249 | const char *name; |
308 | Dwarf_Locdesc **llbuf; | 250 | name = dwarf_diename(dw_die); |
309 | int ret, i; | 251 | DIE_IF(name == NULL); |
310 | 252 | return strcmp(tname, name); | |
311 | ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error); | ||
312 | DIE_IF(ret != DW_DLV_OK); | ||
313 | ret = DW_DLV_NO_ENTRY; | ||
314 | for (i = 0; i < lcnt; ++i) { | ||
315 | if (llbuf[i]->ld_lopc <= addr && | ||
316 | llbuf[i]->ld_hipc > addr) { | ||
317 | memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc)); | ||
318 | desc->ld_s = | ||
319 | malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
320 | DIE_IF(desc->ld_s == NULL); | ||
321 | memcpy(desc->ld_s, llbuf[i]->ld_s, | ||
322 | sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
323 | ret = DW_DLV_OK; | ||
324 | break; | ||
325 | } | ||
326 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
327 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
328 | } | ||
329 | /* Releasing loop */ | ||
330 | for (; i < lcnt; ++i) { | ||
331 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
332 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
333 | } | ||
334 | dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST); | ||
335 | return ret; | ||
336 | } | 253 | } |
337 | 254 | ||
338 | /* Get decl_file attribute value (file number) */ | 255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ |
339 | static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die) | 256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) |
340 | { | 257 | { |
341 | Dwarf_Attribute attr; | 258 | Dwarf_Addr epc; |
342 | Dwarf_Unsigned fno; | ||
343 | int ret; | 259 | int ret; |
344 | 260 | ||
345 | ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error); | 261 | ret = dwarf_entrypc(dw_die, &epc); |
346 | DIE_IF(ret != DW_DLV_OK); | 262 | DIE_IF(ret == -1); |
347 | dwarf_formudata(attr, &fno, &__dw_error); | 263 | return epc; |
348 | DIE_IF(ret != DW_DLV_OK); | ||
349 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
350 | return fno; | ||
351 | } | 264 | } |
352 | 265 | ||
353 | /* Get decl_line attribute value (line number) */ | 266 | /* Get a variable die */ |
354 | static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | 267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, |
268 | Dwarf_Die *die_mem) | ||
355 | { | 269 | { |
356 | Dwarf_Attribute attr; | 270 | Dwarf_Die child_die; |
357 | Dwarf_Unsigned lno; | 271 | int tag; |
358 | int ret; | 272 | int ret; |
359 | 273 | ||
360 | ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error); | 274 | ret = dwarf_child(sp_die, die_mem); |
361 | DIE_IF(ret != DW_DLV_OK); | 275 | if (ret != 0) |
362 | dwarf_formudata(attr, &lno, &__dw_error); | 276 | return NULL; |
363 | DIE_IF(ret != DW_DLV_OK); | 277 | |
364 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | 278 | do { |
365 | return lno; | 279 | tag = dwarf_tag(die_mem); |
280 | if ((tag == DW_TAG_formal_parameter || | ||
281 | tag == DW_TAG_variable) && | ||
282 | (die_compare_name(die_mem, name) == 0)) | ||
283 | return die_mem; | ||
284 | |||
285 | if (die_find_variable(die_mem, name, &child_die)) { | ||
286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
287 | return die_mem; | ||
288 | } | ||
289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
290 | |||
291 | return NULL; | ||
366 | } | 292 | } |
367 | 293 | ||
368 | /* | 294 | /* |
@@ -370,47 +296,45 @@ static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | |||
370 | */ | 296 | */ |
371 | 297 | ||
372 | /* Show a location */ | 298 | /* Show a location */ |
373 | static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | 299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) |
374 | { | 300 | { |
375 | Dwarf_Small op; | 301 | unsigned int regn; |
376 | Dwarf_Unsigned regn; | 302 | Dwarf_Word offs = 0; |
377 | Dwarf_Signed offs; | ||
378 | int deref = 0, ret; | 303 | int deref = 0, ret; |
379 | const char *regs; | 304 | const char *regs; |
380 | 305 | ||
381 | op = loc->lr_atom; | 306 | /* TODO: support CFA */ |
382 | |||
383 | /* If this is based on frame buffer, set the offset */ | 307 | /* If this is based on frame buffer, set the offset */ |
384 | if (op == DW_OP_fbreg) { | 308 | if (op->atom == DW_OP_fbreg) { |
309 | if (pf->fb_ops == NULL) | ||
310 | die("The attribute of frame base is not supported.\n"); | ||
385 | deref = 1; | 311 | deref = 1; |
386 | offs = (Dwarf_Signed)loc->lr_number; | 312 | offs = op->number; |
387 | op = pf->fbloc.ld_s[0].lr_atom; | 313 | op = &pf->fb_ops[0]; |
388 | loc = &pf->fbloc.ld_s[0]; | 314 | } |
389 | } else | ||
390 | offs = 0; | ||
391 | 315 | ||
392 | if (op >= DW_OP_breg0 && op <= DW_OP_breg31) { | 316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { |
393 | regn = op - DW_OP_breg0; | 317 | regn = op->atom - DW_OP_breg0; |
394 | offs += (Dwarf_Signed)loc->lr_number; | 318 | offs += op->number; |
395 | deref = 1; | 319 | deref = 1; |
396 | } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) { | 320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { |
397 | regn = op - DW_OP_reg0; | 321 | regn = op->atom - DW_OP_reg0; |
398 | } else if (op == DW_OP_bregx) { | 322 | } else if (op->atom == DW_OP_bregx) { |
399 | regn = loc->lr_number; | 323 | regn = op->number; |
400 | offs += (Dwarf_Signed)loc->lr_number2; | 324 | offs += op->number2; |
401 | deref = 1; | 325 | deref = 1; |
402 | } else if (op == DW_OP_regx) { | 326 | } else if (op->atom == DW_OP_regx) { |
403 | regn = loc->lr_number; | 327 | regn = op->number; |
404 | } else | 328 | } else |
405 | die("Dwarf_OP %d is not supported.\n", op); | 329 | die("DW_OP %d is not supported.", op->atom); |
406 | 330 | ||
407 | regs = get_arch_regstr(regn); | 331 | regs = get_arch_regstr(regn); |
408 | if (!regs) | 332 | if (!regs) |
409 | die("%lld exceeds max register number.\n", regn); | 333 | die("%u exceeds max register number.", regn); |
410 | 334 | ||
411 | if (deref) | 335 | if (deref) |
412 | ret = snprintf(pf->buf, pf->len, | 336 | ret = snprintf(pf->buf, pf->len, " %s=+%ju(%s)", |
413 | " %s=%+lld(%s)", pf->var, offs, regs); | 337 | pf->var, (uintmax_t)offs, regs); |
414 | else | 338 | else |
415 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | 339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); |
416 | DIE_IF(ret < 0); | 340 | DIE_IF(ret < 0); |
@@ -418,52 +342,37 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | |||
418 | } | 342 | } |
419 | 343 | ||
420 | /* Show a variables in kprobe event format */ | 344 | /* Show a variables in kprobe event format */ |
421 | static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) | 345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
422 | { | 346 | { |
423 | Dwarf_Attribute attr; | 347 | Dwarf_Attribute attr; |
424 | Dwarf_Locdesc ld; | 348 | Dwarf_Op *expr; |
349 | size_t nexpr; | ||
425 | int ret; | 350 | int ret; |
426 | 351 | ||
427 | ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error); | 352 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) |
428 | if (ret != DW_DLV_OK) | ||
429 | goto error; | 353 | goto error; |
430 | ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base)); | 354 | /* TODO: handle more than 1 exprs */ |
431 | if (ret != DW_DLV_OK) | 355 | ret = dwarf_getlocation_addr(&attr, (pf->addr - pf->cu_base), |
356 | &expr, &nexpr, 1); | ||
357 | if (ret <= 0 || nexpr == 0) | ||
432 | goto error; | 358 | goto error; |
433 | /* TODO? */ | 359 | |
434 | DIE_IF(ld.ld_cents != 1); | 360 | show_location(expr, pf); |
435 | show_location(&ld.ld_s[0], pf); | 361 | /* *expr will be cached in libdw. Don't free it. */ |
436 | free(ld.ld_s); | ||
437 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
438 | return ; | 362 | return ; |
439 | error: | 363 | error: |
364 | /* TODO: Support const_value */ | ||
440 | die("Failed to find the location of %s at this address.\n" | 365 | die("Failed to find the location of %s at this address.\n" |
441 | " Perhaps, it has been optimized out.\n", pf->var); | 366 | " Perhaps, it has been optimized out.", pf->var); |
442 | } | ||
443 | |||
444 | static int variable_callback(struct die_link *dlink, void *data) | ||
445 | { | ||
446 | struct probe_finder *pf = (struct probe_finder *)data; | ||
447 | Dwarf_Half tag; | ||
448 | int ret; | ||
449 | |||
450 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
451 | DIE_IF(ret == DW_DLV_ERROR); | ||
452 | if ((tag == DW_TAG_formal_parameter || | ||
453 | tag == DW_TAG_variable) && | ||
454 | (die_compare_name(dlink->die, pf->var) == 0)) { | ||
455 | show_variable(dlink->die, pf); | ||
456 | return 1; | ||
457 | } | ||
458 | /* TODO: Support struct members and arrays */ | ||
459 | return 0; | ||
460 | } | 367 | } |
461 | 368 | ||
462 | /* Find a variable in a subprogram die */ | 369 | /* Find a variable in a subprogram die */ |
463 | static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | 370 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
464 | { | 371 | { |
465 | int ret; | 372 | int ret; |
373 | Dwarf_Die vr_die; | ||
466 | 374 | ||
375 | /* TODO: Support struct members and arrays */ | ||
467 | if (!is_c_varname(pf->var)) { | 376 | if (!is_c_varname(pf->var)) { |
468 | /* Output raw parameters */ | 377 | /* Output raw parameters */ |
469 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | 378 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); |
@@ -474,58 +383,51 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | |||
474 | 383 | ||
475 | pr_debug("Searching '%s' variable in context.\n", pf->var); | 384 | pr_debug("Searching '%s' variable in context.\n", pf->var); |
476 | /* Search child die for local variables and parameters. */ | 385 | /* Search child die for local variables and parameters. */ |
477 | ret = search_die_from_children(sp_die, variable_callback, pf); | 386 | if (!die_find_variable(sp_die, pf->var, &vr_die)) |
478 | if (!ret) | 387 | die("Failed to find '%s' in this function.", pf->var); |
479 | die("Failed to find '%s' in this function.\n", pf->var); | ||
480 | } | ||
481 | 388 | ||
482 | /* Get a frame base on the address */ | 389 | show_variable(&vr_die, pf); |
483 | static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf) | ||
484 | { | ||
485 | Dwarf_Attribute attr; | ||
486 | int ret; | ||
487 | |||
488 | ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error); | ||
489 | DIE_IF(ret != DW_DLV_OK); | ||
490 | ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base)); | ||
491 | DIE_IF(ret != DW_DLV_OK); | ||
492 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
493 | } | ||
494 | |||
495 | static void free_current_frame_base(struct probe_finder *pf) | ||
496 | { | ||
497 | free(pf->fbloc.ld_s); | ||
498 | memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc)); | ||
499 | } | 390 | } |
500 | 391 | ||
501 | /* Show a probe point to output buffer */ | 392 | /* Show a probe point to output buffer */ |
502 | static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | 393 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
503 | struct probe_finder *pf) | ||
504 | { | 394 | { |
505 | struct probe_point *pp = pf->pp; | 395 | struct probe_point *pp = pf->pp; |
506 | char *name; | 396 | Dwarf_Addr eaddr; |
397 | Dwarf_Die die_mem; | ||
398 | const char *name; | ||
507 | char tmp[MAX_PROBE_BUFFER]; | 399 | char tmp[MAX_PROBE_BUFFER]; |
508 | int ret, i, len; | 400 | int ret, i, len; |
401 | Dwarf_Attribute fb_attr; | ||
402 | size_t nops; | ||
403 | |||
404 | /* If no real subprogram, find a real one */ | ||
405 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
406 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
407 | pf->addr, &die_mem); | ||
408 | if (!sp_die) | ||
409 | die("Probe point is not found in subprograms."); | ||
410 | } | ||
509 | 411 | ||
510 | /* Output name of probe point */ | 412 | /* Output name of probe point */ |
511 | ret = dwarf_diename(sp_die, &name, &__dw_error); | 413 | name = dwarf_diename(sp_die); |
512 | DIE_IF(ret == DW_DLV_ERROR); | 414 | if (name) { |
513 | if (ret == DW_DLV_OK) { | 415 | dwarf_entrypc(sp_die, &eaddr); |
514 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, | 416 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, |
515 | (unsigned int)offs); | 417 | (unsigned long)(pf->addr - eaddr)); |
516 | /* Copy the function name if possible */ | 418 | /* Copy the function name if possible */ |
517 | if (!pp->function) { | 419 | if (!pp->function) { |
518 | pp->function = strdup(name); | 420 | pp->function = strdup(name); |
519 | pp->offset = offs; | 421 | pp->offset = (size_t)(pf->addr - eaddr); |
520 | } | 422 | } |
521 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
522 | } else { | 423 | } else { |
523 | /* This function has no name. */ | 424 | /* This function has no name. */ |
524 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr); | 425 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", |
426 | (uintmax_t)pf->addr); | ||
525 | if (!pp->function) { | 427 | if (!pp->function) { |
526 | /* TODO: Use _stext */ | 428 | /* TODO: Use _stext */ |
527 | pp->function = strdup(""); | 429 | pp->function = strdup(""); |
528 | pp->offset = (int)pf->addr; | 430 | pp->offset = (size_t)pf->addr; |
529 | } | 431 | } |
530 | } | 432 | } |
531 | DIE_IF(ret < 0); | 433 | DIE_IF(ret < 0); |
@@ -533,8 +435,15 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | |||
533 | len = ret; | 435 | len = ret; |
534 | pr_debug("Probe point found: %s\n", tmp); | 436 | pr_debug("Probe point found: %s\n", tmp); |
535 | 437 | ||
438 | /* Get the frame base attribute/ops */ | ||
439 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | ||
440 | ret = dwarf_getlocation_addr(&fb_attr, (pf->addr - pf->cu_base), | ||
441 | &pf->fb_ops, &nops, 1); | ||
442 | if (ret <= 0 || nops == 0) | ||
443 | pf->fb_ops = NULL; | ||
444 | |||
536 | /* Find each argument */ | 445 | /* Find each argument */ |
537 | get_current_frame_base(sp_die, pf); | 446 | /* TODO: use dwarf_cfi_addrframe */ |
538 | for (i = 0; i < pp->nr_args; i++) { | 447 | for (i = 0; i < pp->nr_args; i++) { |
539 | pf->var = pp->args[i]; | 448 | pf->var = pp->args[i]; |
540 | pf->buf = &tmp[len]; | 449 | pf->buf = &tmp[len]; |
@@ -542,189 +451,383 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | |||
542 | find_variable(sp_die, pf); | 451 | find_variable(sp_die, pf); |
543 | len += strlen(pf->buf); | 452 | len += strlen(pf->buf); |
544 | } | 453 | } |
545 | free_current_frame_base(pf); | 454 | |
455 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | ||
456 | pf->fb_ops = NULL; | ||
546 | 457 | ||
547 | pp->probes[pp->found] = strdup(tmp); | 458 | pp->probes[pp->found] = strdup(tmp); |
548 | pp->found++; | 459 | pp->found++; |
549 | } | 460 | } |
550 | 461 | ||
551 | static int probeaddr_callback(struct die_link *dlink, void *data) | 462 | /* Find probe point from its line number */ |
463 | static void find_probe_point_by_line(struct probe_finder *pf) | ||
552 | { | 464 | { |
553 | struct probe_finder *pf = (struct probe_finder *)data; | 465 | Dwarf_Lines *lines; |
554 | Dwarf_Half tag; | 466 | Dwarf_Line *line; |
555 | Dwarf_Signed offs; | 467 | size_t nlines, i; |
468 | Dwarf_Addr addr; | ||
469 | int lineno; | ||
556 | int ret; | 470 | int ret; |
557 | 471 | ||
558 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 472 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); |
559 | DIE_IF(ret == DW_DLV_ERROR); | 473 | DIE_IF(ret != 0); |
560 | /* Check the address is in this subprogram */ | 474 | |
561 | if (tag == DW_TAG_subprogram && | 475 | for (i = 0; i < nlines; i++) { |
562 | die_within_subprogram(dlink->die, pf->addr, &offs)) { | 476 | line = dwarf_onesrcline(lines, i); |
563 | show_probepoint(dlink->die, offs, pf); | 477 | dwarf_lineno(line, &lineno); |
564 | return 1; | 478 | if (lineno != pf->lno) |
479 | continue; | ||
480 | |||
481 | /* TODO: Get fileno from line, but how? */ | ||
482 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
483 | continue; | ||
484 | |||
485 | ret = dwarf_lineaddr(line, &addr); | ||
486 | DIE_IF(ret != 0); | ||
487 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
488 | (int)i, lineno, (uintmax_t)addr); | ||
489 | pf->addr = addr; | ||
490 | |||
491 | show_probe_point(NULL, pf); | ||
492 | /* Continuing, because target line might be inlined. */ | ||
565 | } | 493 | } |
566 | return 0; | ||
567 | } | 494 | } |
568 | 495 | ||
569 | /* Find probe point from its line number */ | 496 | /* Find lines which match lazy pattern */ |
570 | static void find_by_line(struct probe_finder *pf) | 497 | static int find_lazy_match_lines(struct list_head *head, |
498 | const char *fname, const char *pat) | ||
499 | { | ||
500 | char *fbuf, *p1, *p2; | ||
501 | int fd, line, nlines = 0; | ||
502 | struct stat st; | ||
503 | |||
504 | fd = open(fname, O_RDONLY); | ||
505 | if (fd < 0) | ||
506 | die("failed to open %s", fname); | ||
507 | DIE_IF(fstat(fd, &st) < 0); | ||
508 | fbuf = malloc(st.st_size + 2); | ||
509 | DIE_IF(fbuf == NULL); | ||
510 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | ||
511 | close(fd); | ||
512 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
513 | fbuf[st.st_size + 1] = '\0'; | ||
514 | p1 = fbuf; | ||
515 | line = 1; | ||
516 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
517 | *p2 = '\0'; | ||
518 | if (strlazymatch(p1, pat)) { | ||
519 | line_list__add_line(head, line); | ||
520 | nlines++; | ||
521 | } | ||
522 | line++; | ||
523 | p1 = p2 + 1; | ||
524 | } | ||
525 | free(fbuf); | ||
526 | return nlines; | ||
527 | } | ||
528 | |||
529 | /* Find probe points from lazy pattern */ | ||
530 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
571 | { | 531 | { |
572 | Dwarf_Signed cnt, i, clm; | 532 | Dwarf_Lines *lines; |
573 | Dwarf_Line *lines; | 533 | Dwarf_Line *line; |
574 | Dwarf_Unsigned lineno = 0; | 534 | size_t nlines, i; |
575 | Dwarf_Addr addr; | 535 | Dwarf_Addr addr; |
576 | Dwarf_Unsigned fno; | 536 | Dwarf_Die die_mem; |
537 | int lineno; | ||
577 | int ret; | 538 | int ret; |
578 | 539 | ||
579 | ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error); | 540 | if (list_empty(&pf->lcache)) { |
580 | DIE_IF(ret != DW_DLV_OK); | 541 | /* Matching lazy line pattern */ |
542 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | ||
543 | pf->pp->lazy_line); | ||
544 | if (ret <= 0) | ||
545 | die("No matched lines found in %s.", pf->fname); | ||
546 | } | ||
547 | |||
548 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
549 | DIE_IF(ret != 0); | ||
550 | for (i = 0; i < nlines; i++) { | ||
551 | line = dwarf_onesrcline(lines, i); | ||
581 | 552 | ||
582 | for (i = 0; i < cnt; i++) { | 553 | dwarf_lineno(line, &lineno); |
583 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | 554 | if (!line_list__has_line(&pf->lcache, lineno)) |
584 | DIE_IF(ret != DW_DLV_OK); | ||
585 | if (fno != pf->fno) | ||
586 | continue; | 555 | continue; |
587 | 556 | ||
588 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | 557 | /* TODO: Get fileno from line, but how? */ |
589 | DIE_IF(ret != DW_DLV_OK); | 558 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) |
590 | if (lineno != pf->lno) | ||
591 | continue; | 559 | continue; |
592 | 560 | ||
593 | ret = dwarf_lineoff(lines[i], &clm, &__dw_error); | 561 | ret = dwarf_lineaddr(line, &addr); |
594 | DIE_IF(ret != DW_DLV_OK); | 562 | DIE_IF(ret != 0); |
563 | if (sp_die) { | ||
564 | /* Address filtering 1: does sp_die include addr? */ | ||
565 | if (!dwarf_haspc(sp_die, addr)) | ||
566 | continue; | ||
567 | /* Address filtering 2: No child include addr? */ | ||
568 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
569 | continue; | ||
570 | } | ||
595 | 571 | ||
596 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | 572 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", |
597 | DIE_IF(ret != DW_DLV_OK); | 573 | (int)i, lineno, (unsigned long long)addr); |
598 | pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n", | ||
599 | (int)i, (unsigned)lineno, (int)clm, addr); | ||
600 | pf->addr = addr; | 574 | pf->addr = addr; |
601 | /* Search a real subprogram including this line, */ | 575 | |
602 | ret = search_die_from_children(pf->cu_die, | 576 | show_probe_point(sp_die, pf); |
603 | probeaddr_callback, pf); | ||
604 | if (ret == 0) | ||
605 | die("Probe point is not found in subprograms.\n"); | ||
606 | /* Continuing, because target line might be inlined. */ | 577 | /* Continuing, because target line might be inlined. */ |
607 | } | 578 | } |
608 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 579 | /* TODO: deallocate lines, but how? */ |
580 | } | ||
581 | |||
582 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
583 | { | ||
584 | struct probe_finder *pf = (struct probe_finder *)data; | ||
585 | struct probe_point *pp = pf->pp; | ||
586 | |||
587 | if (pp->lazy_line) | ||
588 | find_probe_point_lazy(in_die, pf); | ||
589 | else { | ||
590 | /* Get probe address */ | ||
591 | pf->addr = die_get_entrypc(in_die); | ||
592 | pf->addr += pp->offset; | ||
593 | pr_debug("found inline addr: 0x%jx\n", | ||
594 | (uintmax_t)pf->addr); | ||
595 | |||
596 | show_probe_point(in_die, pf); | ||
597 | } | ||
598 | |||
599 | return DWARF_CB_OK; | ||
609 | } | 600 | } |
610 | 601 | ||
611 | /* Search function from function name */ | 602 | /* Search function from function name */ |
612 | static int probefunc_callback(struct die_link *dlink, void *data) | 603 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
613 | { | 604 | { |
614 | struct probe_finder *pf = (struct probe_finder *)data; | 605 | struct probe_finder *pf = (struct probe_finder *)data; |
615 | struct probe_point *pp = pf->pp; | 606 | struct probe_point *pp = pf->pp; |
616 | struct die_link *lk; | ||
617 | Dwarf_Signed offs; | ||
618 | Dwarf_Half tag; | ||
619 | int ret; | ||
620 | 607 | ||
621 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 608 | /* Check tag and diename */ |
622 | DIE_IF(ret == DW_DLV_ERROR); | 609 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
623 | if (tag == DW_TAG_subprogram) { | 610 | die_compare_name(sp_die, pp->function) != 0) |
624 | if (die_compare_name(dlink->die, pp->function) == 0) { | 611 | return 0; |
625 | if (pp->line) { /* Function relative line */ | 612 | |
626 | pf->fno = die_get_decl_file(dlink->die); | 613 | pf->fname = dwarf_decl_file(sp_die); |
627 | pf->lno = die_get_decl_line(dlink->die) | 614 | if (pp->line) { /* Function relative line */ |
628 | + pp->line; | 615 | dwarf_decl_line(sp_die, &pf->lno); |
629 | find_by_line(pf); | 616 | pf->lno += pp->line; |
630 | return 1; | 617 | find_probe_point_by_line(pf); |
631 | } | 618 | } else if (!dwarf_func_inline(sp_die)) { |
632 | if (die_inlined_subprogram(dlink->die)) { | 619 | /* Real function */ |
633 | /* Inlined function, save it. */ | 620 | if (pp->lazy_line) |
634 | ret = dwarf_die_CU_offset(dlink->die, | 621 | find_probe_point_lazy(sp_die, pf); |
635 | &pf->inl_offs, | 622 | else { |
636 | &__dw_error); | 623 | pf->addr = die_get_entrypc(sp_die); |
637 | DIE_IF(ret != DW_DLV_OK); | ||
638 | pr_debug("inline definition offset %lld\n", | ||
639 | pf->inl_offs); | ||
640 | return 0; /* Continue to search */ | ||
641 | } | ||
642 | /* Get probe address */ | ||
643 | pf->addr = die_get_entrypc(dlink->die); | ||
644 | pf->addr += pp->offset; | 624 | pf->addr += pp->offset; |
645 | /* TODO: Check the address in this function */ | 625 | /* TODO: Check the address in this function */ |
646 | show_probepoint(dlink->die, pp->offset, pf); | 626 | show_probe_point(sp_die, pf); |
647 | return 1; /* Exit; no same symbol in this CU. */ | ||
648 | } | ||
649 | } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) { | ||
650 | if (die_get_abstract_origin(dlink->die) == pf->inl_offs) { | ||
651 | /* Get probe address */ | ||
652 | pf->addr = die_get_entrypc(dlink->die); | ||
653 | pf->addr += pp->offset; | ||
654 | pr_debug("found inline addr: 0x%llx\n", pf->addr); | ||
655 | /* Inlined function. Get a real subprogram */ | ||
656 | for (lk = dlink->parent; lk != NULL; lk = lk->parent) { | ||
657 | tag = 0; | ||
658 | dwarf_tag(lk->die, &tag, &__dw_error); | ||
659 | DIE_IF(ret == DW_DLV_ERROR); | ||
660 | if (tag == DW_TAG_subprogram && | ||
661 | !die_inlined_subprogram(lk->die)) | ||
662 | goto found; | ||
663 | } | ||
664 | die("Failed to find real subprogram.\n"); | ||
665 | found: | ||
666 | /* Get offset from subprogram */ | ||
667 | ret = die_within_subprogram(lk->die, pf->addr, &offs); | ||
668 | DIE_IF(!ret); | ||
669 | show_probepoint(lk->die, offs, pf); | ||
670 | /* Continue to search */ | ||
671 | } | 627 | } |
672 | } | 628 | } else |
673 | return 0; | 629 | /* Inlined function: search instances */ |
630 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); | ||
631 | |||
632 | return 1; /* Exit; no same symbol in this CU. */ | ||
674 | } | 633 | } |
675 | 634 | ||
676 | static void find_by_func(struct probe_finder *pf) | 635 | static void find_probe_point_by_func(struct probe_finder *pf) |
677 | { | 636 | { |
678 | search_die_from_children(pf->cu_die, probefunc_callback, pf); | 637 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); |
679 | } | 638 | } |
680 | 639 | ||
681 | /* Find a probe point */ | 640 | /* Find a probe point */ |
682 | int find_probepoint(int fd, struct probe_point *pp) | 641 | int find_probe_point(int fd, struct probe_point *pp) |
683 | { | 642 | { |
684 | Dwarf_Half addr_size = 0; | ||
685 | Dwarf_Unsigned next_cuh = 0; | ||
686 | int cu_number = 0, ret; | ||
687 | struct probe_finder pf = {.pp = pp}; | 643 | struct probe_finder pf = {.pp = pp}; |
644 | int ret; | ||
645 | Dwarf_Off off, noff; | ||
646 | size_t cuhl; | ||
647 | Dwarf_Die *diep; | ||
648 | Dwarf *dbg; | ||
688 | 649 | ||
689 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 650 | dbg = dwarf_begin(fd, DWARF_C_READ); |
690 | if (ret != DW_DLV_OK) | 651 | if (!dbg) |
691 | return -ENOENT; | 652 | return -ENOENT; |
692 | 653 | ||
693 | pp->found = 0; | 654 | pp->found = 0; |
694 | while (++cu_number) { | 655 | off = 0; |
695 | /* Search CU (Compilation Unit) */ | 656 | line_list__init(&pf.lcache); |
696 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | 657 | /* Loop on CUs (Compilation Unit) */ |
697 | &addr_size, &next_cuh, &__dw_error); | 658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
698 | DIE_IF(ret == DW_DLV_ERROR); | ||
699 | if (ret == DW_DLV_NO_ENTRY) | ||
700 | break; | ||
701 | |||
702 | /* Get the DIE(Debugging Information Entry) of this CU */ | 659 | /* Get the DIE(Debugging Information Entry) of this CU */ |
703 | ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error); | 660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); |
704 | DIE_IF(ret != DW_DLV_OK); | 661 | if (!diep) |
662 | continue; | ||
705 | 663 | ||
706 | /* Check if target file is included. */ | 664 | /* Check if target file is included. */ |
707 | if (pp->file) | 665 | if (pp->file) |
708 | pf.fno = cu_find_fileno(pf.cu_die, pp->file); | 666 | pf.fname = cu_find_realpath(&pf.cu_die, pp->file); |
667 | else | ||
668 | pf.fname = NULL; | ||
709 | 669 | ||
710 | if (!pp->file || pf.fno) { | 670 | if (!pp->file || pf.fname) { |
711 | /* Save CU base address (for frame_base) */ | 671 | /* Save CU base address (for frame_base) */ |
712 | ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error); | 672 | ret = dwarf_lowpc(&pf.cu_die, &pf.cu_base); |
713 | DIE_IF(ret == DW_DLV_ERROR); | 673 | if (ret != 0) |
714 | if (ret == DW_DLV_NO_ENTRY) | ||
715 | pf.cu_base = 0; | 674 | pf.cu_base = 0; |
716 | if (pp->function) | 675 | if (pp->function) |
717 | find_by_func(&pf); | 676 | find_probe_point_by_func(&pf); |
677 | else if (pp->lazy_line) | ||
678 | find_probe_point_lazy(NULL, &pf); | ||
718 | else { | 679 | else { |
719 | pf.lno = pp->line; | 680 | pf.lno = pp->line; |
720 | find_by_line(&pf); | 681 | find_probe_point_by_line(&pf); |
721 | } | 682 | } |
722 | } | 683 | } |
723 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); | 684 | off = noff; |
724 | } | 685 | } |
725 | ret = dwarf_finish(__dw_debug, &__dw_error); | 686 | line_list__free(&pf.lcache); |
726 | DIE_IF(ret != DW_DLV_OK); | 687 | dwarf_end(dbg); |
727 | 688 | ||
728 | return pp->found; | 689 | return pp->found; |
729 | } | 690 | } |
730 | 691 | ||
692 | /* Find line range from its line number */ | ||
693 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | ||
694 | { | ||
695 | Dwarf_Lines *lines; | ||
696 | Dwarf_Line *line; | ||
697 | size_t nlines, i; | ||
698 | Dwarf_Addr addr; | ||
699 | int lineno; | ||
700 | int ret; | ||
701 | const char *src; | ||
702 | Dwarf_Die die_mem; | ||
703 | |||
704 | line_list__init(&lf->lr->line_list); | ||
705 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); | ||
706 | DIE_IF(ret != 0); | ||
707 | |||
708 | for (i = 0; i < nlines; i++) { | ||
709 | line = dwarf_onesrcline(lines, i); | ||
710 | ret = dwarf_lineno(line, &lineno); | ||
711 | DIE_IF(ret != 0); | ||
712 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
713 | continue; | ||
714 | |||
715 | if (sp_die) { | ||
716 | /* Address filtering 1: does sp_die include addr? */ | ||
717 | ret = dwarf_lineaddr(line, &addr); | ||
718 | DIE_IF(ret != 0); | ||
719 | if (!dwarf_haspc(sp_die, addr)) | ||
720 | continue; | ||
721 | |||
722 | /* Address filtering 2: No child include addr? */ | ||
723 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
724 | continue; | ||
725 | } | ||
726 | |||
727 | /* TODO: Get fileno from line, but how? */ | ||
728 | src = dwarf_linesrc(line, NULL, NULL); | ||
729 | if (strtailcmp(src, lf->fname) != 0) | ||
730 | continue; | ||
731 | |||
732 | /* Copy real path */ | ||
733 | if (!lf->lr->path) | ||
734 | lf->lr->path = strdup(src); | ||
735 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
736 | } | ||
737 | /* Update status */ | ||
738 | if (!list_empty(&lf->lr->line_list)) | ||
739 | lf->found = 1; | ||
740 | else { | ||
741 | free(lf->lr->path); | ||
742 | lf->lr->path = NULL; | ||
743 | } | ||
744 | } | ||
745 | |||
746 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | ||
747 | { | ||
748 | find_line_range_by_line(in_die, (struct line_finder *)data); | ||
749 | return DWARF_CB_ABORT; /* No need to find other instances */ | ||
750 | } | ||
751 | |||
752 | /* Search function from function name */ | ||
753 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | ||
754 | { | ||
755 | struct line_finder *lf = (struct line_finder *)data; | ||
756 | struct line_range *lr = lf->lr; | ||
757 | |||
758 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | ||
759 | die_compare_name(sp_die, lr->function) == 0) { | ||
760 | lf->fname = dwarf_decl_file(sp_die); | ||
761 | dwarf_decl_line(sp_die, &lr->offset); | ||
762 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | ||
763 | lf->lno_s = lr->offset + lr->start; | ||
764 | if (!lr->end) | ||
765 | lf->lno_e = INT_MAX; | ||
766 | else | ||
767 | lf->lno_e = lr->offset + lr->end; | ||
768 | lr->start = lf->lno_s; | ||
769 | lr->end = lf->lno_e; | ||
770 | if (dwarf_func_inline(sp_die)) | ||
771 | dwarf_func_inline_instances(sp_die, | ||
772 | line_range_inline_cb, lf); | ||
773 | else | ||
774 | find_line_range_by_line(sp_die, lf); | ||
775 | return 1; | ||
776 | } | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | static void find_line_range_by_func(struct line_finder *lf) | ||
781 | { | ||
782 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); | ||
783 | } | ||
784 | |||
785 | int find_line_range(int fd, struct line_range *lr) | ||
786 | { | ||
787 | struct line_finder lf = {.lr = lr, .found = 0}; | ||
788 | int ret; | ||
789 | Dwarf_Off off = 0, noff; | ||
790 | size_t cuhl; | ||
791 | Dwarf_Die *diep; | ||
792 | Dwarf *dbg; | ||
793 | |||
794 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
795 | if (!dbg) | ||
796 | return -ENOENT; | ||
797 | |||
798 | /* Loop on CUs (Compilation Unit) */ | ||
799 | while (!lf.found) { | ||
800 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); | ||
801 | if (ret != 0) | ||
802 | break; | ||
803 | |||
804 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
805 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | ||
806 | if (!diep) | ||
807 | continue; | ||
808 | |||
809 | /* Check if target file is included. */ | ||
810 | if (lr->file) | ||
811 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); | ||
812 | else | ||
813 | lf.fname = 0; | ||
814 | |||
815 | if (!lr->file || lf.fname) { | ||
816 | if (lr->function) | ||
817 | find_line_range_by_func(&lf); | ||
818 | else { | ||
819 | lf.lno_s = lr->start; | ||
820 | if (!lr->end) | ||
821 | lf.lno_e = INT_MAX; | ||
822 | else | ||
823 | lf.lno_e = lr->end; | ||
824 | find_line_range_by_line(NULL, &lf); | ||
825 | } | ||
826 | } | ||
827 | off = noff; | ||
828 | } | ||
829 | pr_debug("path: %lx\n", (unsigned long)lr->path); | ||
830 | dwarf_end(dbg); | ||
831 | return lf.found; | ||
832 | } | ||
833 | |||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index a4086aaddb73..d1a651793ba6 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef _PROBE_FINDER_H | 1 | #ifndef _PROBE_FINDER_H |
2 | #define _PROBE_FINDER_H | 2 | #define _PROBE_FINDER_H |
3 | 3 | ||
4 | #include <stdbool.h> | ||
5 | #include "util.h" | ||
6 | |||
4 | #define MAX_PATH_LEN 256 | 7 | #define MAX_PATH_LEN 256 |
5 | #define MAX_PROBE_BUFFER 1024 | 8 | #define MAX_PROBE_BUFFER 1024 |
6 | #define MAX_PROBES 128 | 9 | #define MAX_PROBES 128 |
@@ -18,6 +21,7 @@ struct probe_point { | |||
18 | /* Inputs */ | 21 | /* Inputs */ |
19 | char *file; /* File name */ | 22 | char *file; /* File name */ |
20 | int line; /* Line number */ | 23 | int line; /* Line number */ |
24 | char *lazy_line; /* Lazy line pattern */ | ||
21 | 25 | ||
22 | char *function; /* Function name */ | 26 | char *function; /* Function name */ |
23 | int offset; /* Offset bytes */ | 27 | int offset; /* Offset bytes */ |
@@ -32,34 +36,58 @@ struct probe_point { | |||
32 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | 36 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ |
33 | }; | 37 | }; |
34 | 38 | ||
35 | #ifndef NO_LIBDWARF | 39 | /* Line number container */ |
36 | extern int find_probepoint(int fd, struct probe_point *pp); | 40 | struct line_node { |
41 | struct list_head list; | ||
42 | unsigned int line; | ||
43 | }; | ||
44 | |||
45 | /* Line range */ | ||
46 | struct line_range { | ||
47 | char *file; /* File name */ | ||
48 | char *function; /* Function name */ | ||
49 | unsigned int start; /* Start line number */ | ||
50 | unsigned int end; /* End line number */ | ||
51 | int offset; /* Start line offset */ | ||
52 | char *path; /* Real path name */ | ||
53 | struct list_head line_list; /* Visible lines */ | ||
54 | }; | ||
37 | 55 | ||
38 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ | 56 | #ifndef NO_DWARF_SUPPORT |
39 | #ifndef _MIPS_SZLONG | 57 | extern int find_probe_point(int fd, struct probe_point *pp); |
40 | # define _MIPS_SZLONG 0 | 58 | extern int find_line_range(int fd, struct line_range *lr); |
41 | #endif | ||
42 | 59 | ||
43 | #include <dwarf.h> | 60 | #include <dwarf.h> |
44 | #include <libdwarf.h> | 61 | #include <libdw.h> |
45 | 62 | ||
46 | struct probe_finder { | 63 | struct probe_finder { |
47 | struct probe_point *pp; /* Target probe point */ | 64 | struct probe_point *pp; /* Target probe point */ |
48 | 65 | ||
49 | /* For function searching */ | 66 | /* For function searching */ |
50 | Dwarf_Addr addr; /* Address */ | 67 | Dwarf_Addr addr; /* Address */ |
51 | Dwarf_Unsigned fno; /* File number */ | 68 | const char *fname; /* File name */ |
52 | Dwarf_Unsigned lno; /* Line number */ | 69 | int lno; /* Line number */ |
53 | Dwarf_Off inl_offs; /* Inline offset */ | 70 | Dwarf_Die cu_die; /* Current CU */ |
54 | Dwarf_Die cu_die; /* Current CU */ | ||
55 | 71 | ||
56 | /* For variable searching */ | 72 | /* For variable searching */ |
57 | Dwarf_Addr cu_base; /* Current CU base address */ | 73 | Dwarf_Op *fb_ops; /* Frame base attribute */ |
58 | Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ | 74 | Dwarf_Addr cu_base; /* Current CU base address */ |
59 | const char *var; /* Current variable name */ | 75 | const char *var; /* Current variable name */ |
60 | char *buf; /* Current output buffer */ | 76 | char *buf; /* Current output buffer */ |
61 | int len; /* Length of output buffer */ | 77 | int len; /* Length of output buffer */ |
78 | struct list_head lcache; /* Line cache for lazy match */ | ||
62 | }; | 79 | }; |
63 | #endif /* NO_LIBDWARF */ | 80 | |
81 | struct line_finder { | ||
82 | struct line_range *lr; /* Target line range */ | ||
83 | |||
84 | const char *fname; /* File name */ | ||
85 | int lno_s; /* Start line number */ | ||
86 | int lno_e; /* End line number */ | ||
87 | Dwarf_Die cu_die; /* Current CU */ | ||
88 | int found; | ||
89 | }; | ||
90 | |||
91 | #endif /* NO_DWARF_SUPPORT */ | ||
64 | 92 | ||
65 | #endif /*_PROBE_FINDER_H */ | 93 | #endif /*_PROBE_FINDER_H */ |
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 6d6d76b8a21e..5376378e0cfc 100644 --- a/tools/perf/util/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -25,10 +25,16 @@ | |||
25 | #include <ctype.h> | 25 | #include <ctype.h> |
26 | #include <errno.h> | 26 | #include <errno.h> |
27 | 27 | ||
28 | #include "../perf.h" | 28 | #include "../../perf.h" |
29 | #include "util.h" | 29 | #include "../util.h" |
30 | #include "trace-event.h" | 30 | #include "../trace-event.h" |
31 | #include "trace-event-perl.h" | 31 | |
32 | #include <EXTERN.h> | ||
33 | #include <perl.h> | ||
34 | |||
35 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
36 | void boot_DynaLoader(pTHX_ CV *cv); | ||
37 | typedef PerlInterpreter * INTERP; | ||
32 | 38 | ||
33 | void xs_init(pTHX); | 39 | void xs_init(pTHX); |
34 | 40 | ||
@@ -49,7 +55,7 @@ INTERP my_perl; | |||
49 | 55 | ||
50 | struct event *events[FTRACE_MAX_EVENT]; | 56 | struct event *events[FTRACE_MAX_EVENT]; |
51 | 57 | ||
52 | static struct scripting_context *scripting_context; | 58 | extern struct scripting_context *scripting_context; |
53 | 59 | ||
54 | static char *cur_field_name; | 60 | static char *cur_field_name; |
55 | static int zero_flag_atom; | 61 | static int zero_flag_atom; |
@@ -239,33 +245,6 @@ static inline struct event *find_cache_event(int type) | |||
239 | return event; | 245 | return event; |
240 | } | 246 | } |
241 | 247 | ||
242 | int common_pc(struct scripting_context *context) | ||
243 | { | ||
244 | int pc; | ||
245 | |||
246 | pc = parse_common_pc(context->event_data); | ||
247 | |||
248 | return pc; | ||
249 | } | ||
250 | |||
251 | int common_flags(struct scripting_context *context) | ||
252 | { | ||
253 | int flags; | ||
254 | |||
255 | flags = parse_common_flags(context->event_data); | ||
256 | |||
257 | return flags; | ||
258 | } | ||
259 | |||
260 | int common_lock_depth(struct scripting_context *context) | ||
261 | { | ||
262 | int lock_depth; | ||
263 | |||
264 | lock_depth = parse_common_lock_depth(context->event_data); | ||
265 | |||
266 | return lock_depth; | ||
267 | } | ||
268 | |||
269 | static void perl_process_event(int cpu, void *data, | 248 | static void perl_process_event(int cpu, void *data, |
270 | int size __unused, | 249 | int size __unused, |
271 | unsigned long long nsecs, char *comm) | 250 | unsigned long long nsecs, char *comm) |
@@ -587,75 +566,3 @@ struct scripting_ops perl_scripting_ops = { | |||
587 | .process_event = perl_process_event, | 566 | .process_event = perl_process_event, |
588 | .generate_script = perl_generate_script, | 567 | .generate_script = perl_generate_script, |
589 | }; | 568 | }; |
590 | |||
591 | static void print_unsupported_msg(void) | ||
592 | { | ||
593 | fprintf(stderr, "Perl scripting not supported." | ||
594 | " Install libperl and rebuild perf to enable it.\n" | ||
595 | "For example:\n # apt-get install libperl-dev (ubuntu)" | ||
596 | "\n # yum install perl-ExtUtils-Embed (Fedora)" | ||
597 | "\n etc.\n"); | ||
598 | } | ||
599 | |||
600 | static int perl_start_script_unsupported(const char *script __unused, | ||
601 | int argc __unused, | ||
602 | const char **argv __unused) | ||
603 | { | ||
604 | print_unsupported_msg(); | ||
605 | |||
606 | return -1; | ||
607 | } | ||
608 | |||
609 | static int perl_stop_script_unsupported(void) | ||
610 | { | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static void perl_process_event_unsupported(int cpu __unused, | ||
615 | void *data __unused, | ||
616 | int size __unused, | ||
617 | unsigned long long nsecs __unused, | ||
618 | char *comm __unused) | ||
619 | { | ||
620 | } | ||
621 | |||
622 | static int perl_generate_script_unsupported(const char *outfile __unused) | ||
623 | { | ||
624 | print_unsupported_msg(); | ||
625 | |||
626 | return -1; | ||
627 | } | ||
628 | |||
629 | struct scripting_ops perl_scripting_unsupported_ops = { | ||
630 | .name = "Perl", | ||
631 | .start_script = perl_start_script_unsupported, | ||
632 | .stop_script = perl_stop_script_unsupported, | ||
633 | .process_event = perl_process_event_unsupported, | ||
634 | .generate_script = perl_generate_script_unsupported, | ||
635 | }; | ||
636 | |||
637 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | ||
638 | { | ||
639 | int err; | ||
640 | err = script_spec_register("Perl", scripting_ops); | ||
641 | if (err) | ||
642 | die("error registering Perl script extension"); | ||
643 | |||
644 | err = script_spec_register("pl", scripting_ops); | ||
645 | if (err) | ||
646 | die("error registering pl script extension"); | ||
647 | |||
648 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
649 | } | ||
650 | |||
651 | #ifdef NO_LIBPERL | ||
652 | void setup_perl_scripting(void) | ||
653 | { | ||
654 | register_perl_scripting(&perl_scripting_unsupported_ops); | ||
655 | } | ||
656 | #else | ||
657 | void setup_perl_scripting(void) | ||
658 | { | ||
659 | register_perl_scripting(&perl_scripting_ops); | ||
660 | } | ||
661 | #endif | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c new file mode 100644 index 000000000000..33a414bbba3e --- /dev/null +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -0,0 +1,573 @@ | |||
1 | /* | ||
2 | * trace-event-python. Feed trace events to an embedded Python interpreter. | ||
3 | * | ||
4 | * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <Python.h> | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <ctype.h> | ||
28 | #include <errno.h> | ||
29 | |||
30 | #include "../../perf.h" | ||
31 | #include "../util.h" | ||
32 | #include "../trace-event.h" | ||
33 | |||
34 | PyMODINIT_FUNC initperf_trace_context(void); | ||
35 | |||
36 | #define FTRACE_MAX_EVENT \ | ||
37 | ((1 << (sizeof(unsigned short) * 8)) - 1) | ||
38 | |||
39 | struct event *events[FTRACE_MAX_EVENT]; | ||
40 | |||
41 | #define MAX_FIELDS 64 | ||
42 | #define N_COMMON_FIELDS 7 | ||
43 | |||
44 | extern struct scripting_context *scripting_context; | ||
45 | |||
46 | static char *cur_field_name; | ||
47 | static int zero_flag_atom; | ||
48 | |||
49 | static PyObject *main_module, *main_dict; | ||
50 | |||
51 | static void handler_call_die(const char *handler_name) | ||
52 | { | ||
53 | PyErr_Print(); | ||
54 | Py_FatalError("problem in Python trace event handler"); | ||
55 | } | ||
56 | |||
57 | static void define_value(enum print_arg_type field_type, | ||
58 | const char *ev_name, | ||
59 | const char *field_name, | ||
60 | const char *field_value, | ||
61 | const char *field_str) | ||
62 | { | ||
63 | const char *handler_name = "define_flag_value"; | ||
64 | PyObject *handler, *t, *retval; | ||
65 | unsigned long long value; | ||
66 | unsigned n = 0; | ||
67 | |||
68 | if (field_type == PRINT_SYMBOL) | ||
69 | handler_name = "define_symbolic_value"; | ||
70 | |||
71 | t = PyTuple_New(4); | ||
72 | if (!t) | ||
73 | Py_FatalError("couldn't create Python tuple"); | ||
74 | |||
75 | value = eval_flag(field_value); | ||
76 | |||
77 | PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); | ||
78 | PyTuple_SetItem(t, n++, PyString_FromString(field_name)); | ||
79 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | ||
80 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | ||
81 | |||
82 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
83 | if (handler && PyCallable_Check(handler)) { | ||
84 | retval = PyObject_CallObject(handler, t); | ||
85 | if (retval == NULL) | ||
86 | handler_call_die(handler_name); | ||
87 | } | ||
88 | |||
89 | Py_DECREF(t); | ||
90 | } | ||
91 | |||
92 | static void define_values(enum print_arg_type field_type, | ||
93 | struct print_flag_sym *field, | ||
94 | const char *ev_name, | ||
95 | const char *field_name) | ||
96 | { | ||
97 | define_value(field_type, ev_name, field_name, field->value, | ||
98 | field->str); | ||
99 | |||
100 | if (field->next) | ||
101 | define_values(field_type, field->next, ev_name, field_name); | ||
102 | } | ||
103 | |||
104 | static void define_field(enum print_arg_type field_type, | ||
105 | const char *ev_name, | ||
106 | const char *field_name, | ||
107 | const char *delim) | ||
108 | { | ||
109 | const char *handler_name = "define_flag_field"; | ||
110 | PyObject *handler, *t, *retval; | ||
111 | unsigned n = 0; | ||
112 | |||
113 | if (field_type == PRINT_SYMBOL) | ||
114 | handler_name = "define_symbolic_field"; | ||
115 | |||
116 | if (field_type == PRINT_FLAGS) | ||
117 | t = PyTuple_New(3); | ||
118 | else | ||
119 | t = PyTuple_New(2); | ||
120 | if (!t) | ||
121 | Py_FatalError("couldn't create Python tuple"); | ||
122 | |||
123 | PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); | ||
124 | PyTuple_SetItem(t, n++, PyString_FromString(field_name)); | ||
125 | if (field_type == PRINT_FLAGS) | ||
126 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | ||
127 | |||
128 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
129 | if (handler && PyCallable_Check(handler)) { | ||
130 | retval = PyObject_CallObject(handler, t); | ||
131 | if (retval == NULL) | ||
132 | handler_call_die(handler_name); | ||
133 | } | ||
134 | |||
135 | Py_DECREF(t); | ||
136 | } | ||
137 | |||
138 | static void define_event_symbols(struct event *event, | ||
139 | const char *ev_name, | ||
140 | struct print_arg *args) | ||
141 | { | ||
142 | switch (args->type) { | ||
143 | case PRINT_NULL: | ||
144 | break; | ||
145 | case PRINT_ATOM: | ||
146 | define_value(PRINT_FLAGS, ev_name, cur_field_name, "0", | ||
147 | args->atom.atom); | ||
148 | zero_flag_atom = 0; | ||
149 | break; | ||
150 | case PRINT_FIELD: | ||
151 | if (cur_field_name) | ||
152 | free(cur_field_name); | ||
153 | cur_field_name = strdup(args->field.name); | ||
154 | break; | ||
155 | case PRINT_FLAGS: | ||
156 | define_event_symbols(event, ev_name, args->flags.field); | ||
157 | define_field(PRINT_FLAGS, ev_name, cur_field_name, | ||
158 | args->flags.delim); | ||
159 | define_values(PRINT_FLAGS, args->flags.flags, ev_name, | ||
160 | cur_field_name); | ||
161 | break; | ||
162 | case PRINT_SYMBOL: | ||
163 | define_event_symbols(event, ev_name, args->symbol.field); | ||
164 | define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL); | ||
165 | define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name, | ||
166 | cur_field_name); | ||
167 | break; | ||
168 | case PRINT_STRING: | ||
169 | break; | ||
170 | case PRINT_TYPE: | ||
171 | define_event_symbols(event, ev_name, args->typecast.item); | ||
172 | break; | ||
173 | case PRINT_OP: | ||
174 | if (strcmp(args->op.op, ":") == 0) | ||
175 | zero_flag_atom = 1; | ||
176 | define_event_symbols(event, ev_name, args->op.left); | ||
177 | define_event_symbols(event, ev_name, args->op.right); | ||
178 | break; | ||
179 | default: | ||
180 | /* we should warn... */ | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | if (args->next) | ||
185 | define_event_symbols(event, ev_name, args->next); | ||
186 | } | ||
187 | |||
188 | static inline struct event *find_cache_event(int type) | ||
189 | { | ||
190 | static char ev_name[256]; | ||
191 | struct event *event; | ||
192 | |||
193 | if (events[type]) | ||
194 | return events[type]; | ||
195 | |||
196 | events[type] = event = trace_find_event(type); | ||
197 | if (!event) | ||
198 | return NULL; | ||
199 | |||
200 | sprintf(ev_name, "%s__%s", event->system, event->name); | ||
201 | |||
202 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
203 | |||
204 | return event; | ||
205 | } | ||
206 | |||
207 | static void python_process_event(int cpu, void *data, | ||
208 | int size __unused, | ||
209 | unsigned long long nsecs, char *comm) | ||
210 | { | ||
211 | PyObject *handler, *retval, *context, *t; | ||
212 | static char handler_name[256]; | ||
213 | struct format_field *field; | ||
214 | unsigned long long val; | ||
215 | unsigned long s, ns; | ||
216 | struct event *event; | ||
217 | unsigned n = 0; | ||
218 | int type; | ||
219 | int pid; | ||
220 | |||
221 | t = PyTuple_New(MAX_FIELDS); | ||
222 | if (!t) | ||
223 | Py_FatalError("couldn't create Python tuple"); | ||
224 | |||
225 | type = trace_parse_common_type(data); | ||
226 | |||
227 | event = find_cache_event(type); | ||
228 | if (!event) | ||
229 | die("ug! no event found for type %d", type); | ||
230 | |||
231 | pid = trace_parse_common_pid(data); | ||
232 | |||
233 | sprintf(handler_name, "%s__%s", event->system, event->name); | ||
234 | |||
235 | s = nsecs / NSECS_PER_SEC; | ||
236 | ns = nsecs - s * NSECS_PER_SEC; | ||
237 | |||
238 | scripting_context->event_data = data; | ||
239 | |||
240 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | ||
241 | |||
242 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); | ||
243 | PyTuple_SetItem(t, n++, | ||
244 | PyCObject_FromVoidPtr(scripting_context, NULL)); | ||
245 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); | ||
246 | PyTuple_SetItem(t, n++, PyInt_FromLong(s)); | ||
247 | PyTuple_SetItem(t, n++, PyInt_FromLong(ns)); | ||
248 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); | ||
249 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); | ||
250 | |||
251 | for (field = event->format.fields; field; field = field->next) { | ||
252 | if (field->flags & FIELD_IS_STRING) { | ||
253 | int offset; | ||
254 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
255 | offset = *(int *)(data + field->offset); | ||
256 | offset &= 0xffff; | ||
257 | } else | ||
258 | offset = field->offset; | ||
259 | PyTuple_SetItem(t, n++, | ||
260 | PyString_FromString((char *)data + offset)); | ||
261 | } else { /* FIELD_IS_NUMERIC */ | ||
262 | val = read_size(data + field->offset, field->size); | ||
263 | if (field->flags & FIELD_IS_SIGNED) { | ||
264 | PyTuple_SetItem(t, n++, PyInt_FromLong(val)); | ||
265 | } else { | ||
266 | PyTuple_SetItem(t, n++, PyInt_FromLong(val)); | ||
267 | } | ||
268 | } | ||
269 | } | ||
270 | |||
271 | if (_PyTuple_Resize(&t, n) == -1) | ||
272 | Py_FatalError("error resizing Python tuple"); | ||
273 | |||
274 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
275 | if (handler && PyCallable_Check(handler)) { | ||
276 | retval = PyObject_CallObject(handler, t); | ||
277 | if (retval == NULL) | ||
278 | handler_call_die(handler_name); | ||
279 | } else { | ||
280 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | ||
281 | if (handler && PyCallable_Check(handler)) { | ||
282 | if (_PyTuple_Resize(&t, N_COMMON_FIELDS) == -1) | ||
283 | Py_FatalError("error resizing Python tuple"); | ||
284 | |||
285 | retval = PyObject_CallObject(handler, t); | ||
286 | if (retval == NULL) | ||
287 | handler_call_die("trace_unhandled"); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | Py_DECREF(t); | ||
292 | } | ||
293 | |||
294 | static int run_start_sub(void) | ||
295 | { | ||
296 | PyObject *handler, *retval; | ||
297 | int err = 0; | ||
298 | |||
299 | main_module = PyImport_AddModule("__main__"); | ||
300 | if (main_module == NULL) | ||
301 | return -1; | ||
302 | Py_INCREF(main_module); | ||
303 | |||
304 | main_dict = PyModule_GetDict(main_module); | ||
305 | if (main_dict == NULL) { | ||
306 | err = -1; | ||
307 | goto error; | ||
308 | } | ||
309 | Py_INCREF(main_dict); | ||
310 | |||
311 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | ||
312 | if (handler == NULL || !PyCallable_Check(handler)) | ||
313 | goto out; | ||
314 | |||
315 | retval = PyObject_CallObject(handler, NULL); | ||
316 | if (retval == NULL) | ||
317 | handler_call_die("trace_begin"); | ||
318 | |||
319 | Py_DECREF(retval); | ||
320 | return err; | ||
321 | error: | ||
322 | Py_XDECREF(main_dict); | ||
323 | Py_XDECREF(main_module); | ||
324 | out: | ||
325 | return err; | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Start trace script | ||
330 | */ | ||
331 | static int python_start_script(const char *script, int argc, const char **argv) | ||
332 | { | ||
333 | const char **command_line; | ||
334 | char buf[PATH_MAX]; | ||
335 | int i, err = 0; | ||
336 | FILE *fp; | ||
337 | |||
338 | command_line = malloc((argc + 1) * sizeof(const char *)); | ||
339 | command_line[0] = script; | ||
340 | for (i = 1; i < argc + 1; i++) | ||
341 | command_line[i] = argv[i - 1]; | ||
342 | |||
343 | Py_Initialize(); | ||
344 | |||
345 | initperf_trace_context(); | ||
346 | |||
347 | PySys_SetArgv(argc + 1, (char **)command_line); | ||
348 | |||
349 | fp = fopen(script, "r"); | ||
350 | if (!fp) { | ||
351 | sprintf(buf, "Can't open python script \"%s\"", script); | ||
352 | perror(buf); | ||
353 | err = -1; | ||
354 | goto error; | ||
355 | } | ||
356 | |||
357 | err = PyRun_SimpleFile(fp, script); | ||
358 | if (err) { | ||
359 | fprintf(stderr, "Error running python script %s\n", script); | ||
360 | goto error; | ||
361 | } | ||
362 | |||
363 | err = run_start_sub(); | ||
364 | if (err) { | ||
365 | fprintf(stderr, "Error starting python script %s\n", script); | ||
366 | goto error; | ||
367 | } | ||
368 | |||
369 | free(command_line); | ||
370 | fprintf(stderr, "perf trace started with Python script %s\n\n", | ||
371 | script); | ||
372 | |||
373 | return err; | ||
374 | error: | ||
375 | Py_Finalize(); | ||
376 | free(command_line); | ||
377 | |||
378 | return err; | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * Stop trace script | ||
383 | */ | ||
384 | static int python_stop_script(void) | ||
385 | { | ||
386 | PyObject *handler, *retval; | ||
387 | int err = 0; | ||
388 | |||
389 | handler = PyDict_GetItemString(main_dict, "trace_end"); | ||
390 | if (handler == NULL || !PyCallable_Check(handler)) | ||
391 | goto out; | ||
392 | |||
393 | retval = PyObject_CallObject(handler, NULL); | ||
394 | if (retval == NULL) | ||
395 | handler_call_die("trace_end"); | ||
396 | else | ||
397 | Py_DECREF(retval); | ||
398 | out: | ||
399 | Py_XDECREF(main_dict); | ||
400 | Py_XDECREF(main_module); | ||
401 | Py_Finalize(); | ||
402 | |||
403 | fprintf(stderr, "\nperf trace Python script stopped\n"); | ||
404 | |||
405 | return err; | ||
406 | } | ||
407 | |||
408 | static int python_generate_script(const char *outfile) | ||
409 | { | ||
410 | struct event *event = NULL; | ||
411 | struct format_field *f; | ||
412 | char fname[PATH_MAX]; | ||
413 | int not_first, count; | ||
414 | FILE *ofp; | ||
415 | |||
416 | sprintf(fname, "%s.py", outfile); | ||
417 | ofp = fopen(fname, "w"); | ||
418 | if (ofp == NULL) { | ||
419 | fprintf(stderr, "couldn't open %s\n", fname); | ||
420 | return -1; | ||
421 | } | ||
422 | fprintf(ofp, "# perf trace event handlers, " | ||
423 | "generated by perf trace -g python\n"); | ||
424 | |||
425 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | ||
426 | " License version 2\n\n"); | ||
427 | |||
428 | fprintf(ofp, "# The common_* event handler fields are the most useful " | ||
429 | "fields common to\n"); | ||
430 | |||
431 | fprintf(ofp, "# all events. They don't necessarily correspond to " | ||
432 | "the 'common_*' fields\n"); | ||
433 | |||
434 | fprintf(ofp, "# in the format files. Those fields not available as " | ||
435 | "handler params can\n"); | ||
436 | |||
437 | fprintf(ofp, "# be retrieved using Python functions of the form " | ||
438 | "common_*(context).\n"); | ||
439 | |||
440 | fprintf(ofp, "# See the perf-trace-python Documentation for the list " | ||
441 | "of available functions.\n\n"); | ||
442 | |||
443 | fprintf(ofp, "import os\n"); | ||
444 | fprintf(ofp, "import sys\n\n"); | ||
445 | |||
446 | fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n"); | ||
447 | fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n"); | ||
448 | fprintf(ofp, "\nfrom perf_trace_context import *\n"); | ||
449 | fprintf(ofp, "from Core import *\n\n\n"); | ||
450 | |||
451 | fprintf(ofp, "def trace_begin():\n"); | ||
452 | fprintf(ofp, "\tprint \"in trace_begin\"\n\n"); | ||
453 | |||
454 | fprintf(ofp, "def trace_end():\n"); | ||
455 | fprintf(ofp, "\tprint \"in trace_end\"\n\n"); | ||
456 | |||
457 | while ((event = trace_find_next_event(event))) { | ||
458 | fprintf(ofp, "def %s__%s(", event->system, event->name); | ||
459 | fprintf(ofp, "event_name, "); | ||
460 | fprintf(ofp, "context, "); | ||
461 | fprintf(ofp, "common_cpu,\n"); | ||
462 | fprintf(ofp, "\tcommon_secs, "); | ||
463 | fprintf(ofp, "common_nsecs, "); | ||
464 | fprintf(ofp, "common_pid, "); | ||
465 | fprintf(ofp, "common_comm,\n\t"); | ||
466 | |||
467 | not_first = 0; | ||
468 | count = 0; | ||
469 | |||
470 | for (f = event->format.fields; f; f = f->next) { | ||
471 | if (not_first++) | ||
472 | fprintf(ofp, ", "); | ||
473 | if (++count % 5 == 0) | ||
474 | fprintf(ofp, "\n\t"); | ||
475 | |||
476 | fprintf(ofp, "%s", f->name); | ||
477 | } | ||
478 | fprintf(ofp, "):\n"); | ||
479 | |||
480 | fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " | ||
481 | "common_secs, common_nsecs,\n\t\t\t" | ||
482 | "common_pid, common_comm)\n\n"); | ||
483 | |||
484 | fprintf(ofp, "\t\tprint \""); | ||
485 | |||
486 | not_first = 0; | ||
487 | count = 0; | ||
488 | |||
489 | for (f = event->format.fields; f; f = f->next) { | ||
490 | if (not_first++) | ||
491 | fprintf(ofp, ", "); | ||
492 | if (count && count % 3 == 0) { | ||
493 | fprintf(ofp, "\" \\\n\t\t\""); | ||
494 | } | ||
495 | count++; | ||
496 | |||
497 | fprintf(ofp, "%s=", f->name); | ||
498 | if (f->flags & FIELD_IS_STRING || | ||
499 | f->flags & FIELD_IS_FLAG || | ||
500 | f->flags & FIELD_IS_SYMBOLIC) | ||
501 | fprintf(ofp, "%%s"); | ||
502 | else if (f->flags & FIELD_IS_SIGNED) | ||
503 | fprintf(ofp, "%%d"); | ||
504 | else | ||
505 | fprintf(ofp, "%%u"); | ||
506 | } | ||
507 | |||
508 | fprintf(ofp, "\\n\" %% \\\n\t\t("); | ||
509 | |||
510 | not_first = 0; | ||
511 | count = 0; | ||
512 | |||
513 | for (f = event->format.fields; f; f = f->next) { | ||
514 | if (not_first++) | ||
515 | fprintf(ofp, ", "); | ||
516 | |||
517 | if (++count % 5 == 0) | ||
518 | fprintf(ofp, "\n\t\t"); | ||
519 | |||
520 | if (f->flags & FIELD_IS_FLAG) { | ||
521 | if ((count - 1) % 5 != 0) { | ||
522 | fprintf(ofp, "\n\t\t"); | ||
523 | count = 4; | ||
524 | } | ||
525 | fprintf(ofp, "flag_str(\""); | ||
526 | fprintf(ofp, "%s__%s\", ", event->system, | ||
527 | event->name); | ||
528 | fprintf(ofp, "\"%s\", %s)", f->name, | ||
529 | f->name); | ||
530 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | ||
531 | if ((count - 1) % 5 != 0) { | ||
532 | fprintf(ofp, "\n\t\t"); | ||
533 | count = 4; | ||
534 | } | ||
535 | fprintf(ofp, "symbol_str(\""); | ||
536 | fprintf(ofp, "%s__%s\", ", event->system, | ||
537 | event->name); | ||
538 | fprintf(ofp, "\"%s\", %s)", f->name, | ||
539 | f->name); | ||
540 | } else | ||
541 | fprintf(ofp, "%s", f->name); | ||
542 | } | ||
543 | |||
544 | fprintf(ofp, "),\n\n"); | ||
545 | } | ||
546 | |||
547 | fprintf(ofp, "def trace_unhandled(event_name, context, " | ||
548 | "common_cpu, common_secs, common_nsecs,\n\t\t" | ||
549 | "common_pid, common_comm):\n"); | ||
550 | |||
551 | fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " | ||
552 | "common_secs, common_nsecs,\n\t\tcommon_pid, " | ||
553 | "common_comm)\n\n"); | ||
554 | |||
555 | fprintf(ofp, "def print_header(" | ||
556 | "event_name, cpu, secs, nsecs, pid, comm):\n" | ||
557 | "\tprint \"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t" | ||
558 | "(event_name, cpu, secs, nsecs, pid, comm),\n"); | ||
559 | |||
560 | fclose(ofp); | ||
561 | |||
562 | fprintf(stderr, "generated Python script: %s\n", fname); | ||
563 | |||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | struct scripting_ops python_scripting_ops = { | ||
568 | .name = "Python", | ||
569 | .start_script = python_start_script, | ||
570 | .stop_script = python_stop_script, | ||
571 | .process_event = python_process_event, | ||
572 | .generate_script = python_generate_script, | ||
573 | }; | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ce3a6c8abe76..0de7258e70a5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
1 | #include <linux/kernel.h> | 3 | #include <linux/kernel.h> |
2 | 4 | ||
5 | #include <byteswap.h> | ||
3 | #include <unistd.h> | 6 | #include <unistd.h> |
4 | #include <sys/types.h> | 7 | #include <sys/types.h> |
5 | 8 | ||
@@ -49,6 +52,11 @@ out_close: | |||
49 | return -1; | 52 | return -1; |
50 | } | 53 | } |
51 | 54 | ||
55 | static inline int perf_session__create_kernel_maps(struct perf_session *self) | ||
56 | { | ||
57 | return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); | ||
58 | } | ||
59 | |||
52 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | 60 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) |
53 | { | 61 | { |
54 | size_t len = filename ? strlen(filename) + 1 : 0; | 62 | size_t len = filename ? strlen(filename) + 1 : 0; |
@@ -66,13 +74,22 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
66 | self->mmap_window = 32; | 74 | self->mmap_window = 32; |
67 | self->cwd = NULL; | 75 | self->cwd = NULL; |
68 | self->cwdlen = 0; | 76 | self->cwdlen = 0; |
77 | self->unknown_events = 0; | ||
69 | map_groups__init(&self->kmaps); | 78 | map_groups__init(&self->kmaps); |
70 | 79 | ||
71 | if (perf_session__create_kernel_maps(self) < 0) | 80 | if (mode == O_RDONLY) { |
72 | goto out_delete; | 81 | if (perf_session__open(self, force) < 0) |
82 | goto out_delete; | ||
83 | } else if (mode == O_WRONLY) { | ||
84 | /* | ||
85 | * In O_RDONLY mode this will be performed when reading the | ||
86 | * kernel MMAP event, in event__process_mmap(). | ||
87 | */ | ||
88 | if (perf_session__create_kernel_maps(self) < 0) | ||
89 | goto out_delete; | ||
90 | } | ||
73 | 91 | ||
74 | if (mode == O_RDONLY && perf_session__open(self, force) < 0) | 92 | self->sample_type = perf_header__sample_type(&self->header); |
75 | goto out_delete; | ||
76 | out: | 93 | out: |
77 | return self; | 94 | return self; |
78 | out_free: | 95 | out_free: |
@@ -148,3 +165,409 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, | |||
148 | 165 | ||
149 | return syms; | 166 | return syms; |
150 | } | 167 | } |
168 | |||
169 | static int process_event_stub(event_t *event __used, | ||
170 | struct perf_session *session __used) | ||
171 | { | ||
172 | dump_printf(": unhandled!\n"); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
177 | { | ||
178 | if (handler->sample == NULL) | ||
179 | handler->sample = process_event_stub; | ||
180 | if (handler->mmap == NULL) | ||
181 | handler->mmap = process_event_stub; | ||
182 | if (handler->comm == NULL) | ||
183 | handler->comm = process_event_stub; | ||
184 | if (handler->fork == NULL) | ||
185 | handler->fork = process_event_stub; | ||
186 | if (handler->exit == NULL) | ||
187 | handler->exit = process_event_stub; | ||
188 | if (handler->lost == NULL) | ||
189 | handler->lost = process_event_stub; | ||
190 | if (handler->read == NULL) | ||
191 | handler->read = process_event_stub; | ||
192 | if (handler->throttle == NULL) | ||
193 | handler->throttle = process_event_stub; | ||
194 | if (handler->unthrottle == NULL) | ||
195 | handler->unthrottle = process_event_stub; | ||
196 | } | ||
197 | |||
198 | static const char *event__name[] = { | ||
199 | [0] = "TOTAL", | ||
200 | [PERF_RECORD_MMAP] = "MMAP", | ||
201 | [PERF_RECORD_LOST] = "LOST", | ||
202 | [PERF_RECORD_COMM] = "COMM", | ||
203 | [PERF_RECORD_EXIT] = "EXIT", | ||
204 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
205 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
206 | [PERF_RECORD_FORK] = "FORK", | ||
207 | [PERF_RECORD_READ] = "READ", | ||
208 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
209 | }; | ||
210 | |||
211 | unsigned long event__total[PERF_RECORD_MAX]; | ||
212 | |||
213 | void event__print_totals(void) | ||
214 | { | ||
215 | int i; | ||
216 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
217 | pr_info("%10s events: %10ld\n", | ||
218 | event__name[i], event__total[i]); | ||
219 | } | ||
220 | |||
221 | void mem_bswap_64(void *src, int byte_size) | ||
222 | { | ||
223 | u64 *m = src; | ||
224 | |||
225 | while (byte_size > 0) { | ||
226 | *m = bswap_64(*m); | ||
227 | byte_size -= sizeof(u64); | ||
228 | ++m; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | static void event__all64_swap(event_t *self) | ||
233 | { | ||
234 | struct perf_event_header *hdr = &self->header; | ||
235 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | ||
236 | } | ||
237 | |||
238 | static void event__comm_swap(event_t *self) | ||
239 | { | ||
240 | self->comm.pid = bswap_32(self->comm.pid); | ||
241 | self->comm.tid = bswap_32(self->comm.tid); | ||
242 | } | ||
243 | |||
244 | static void event__mmap_swap(event_t *self) | ||
245 | { | ||
246 | self->mmap.pid = bswap_32(self->mmap.pid); | ||
247 | self->mmap.tid = bswap_32(self->mmap.tid); | ||
248 | self->mmap.start = bswap_64(self->mmap.start); | ||
249 | self->mmap.len = bswap_64(self->mmap.len); | ||
250 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | ||
251 | } | ||
252 | |||
253 | static void event__task_swap(event_t *self) | ||
254 | { | ||
255 | self->fork.pid = bswap_32(self->fork.pid); | ||
256 | self->fork.tid = bswap_32(self->fork.tid); | ||
257 | self->fork.ppid = bswap_32(self->fork.ppid); | ||
258 | self->fork.ptid = bswap_32(self->fork.ptid); | ||
259 | self->fork.time = bswap_64(self->fork.time); | ||
260 | } | ||
261 | |||
262 | static void event__read_swap(event_t *self) | ||
263 | { | ||
264 | self->read.pid = bswap_32(self->read.pid); | ||
265 | self->read.tid = bswap_32(self->read.tid); | ||
266 | self->read.value = bswap_64(self->read.value); | ||
267 | self->read.time_enabled = bswap_64(self->read.time_enabled); | ||
268 | self->read.time_running = bswap_64(self->read.time_running); | ||
269 | self->read.id = bswap_64(self->read.id); | ||
270 | } | ||
271 | |||
272 | typedef void (*event__swap_op)(event_t *self); | ||
273 | |||
274 | static event__swap_op event__swap_ops[] = { | ||
275 | [PERF_RECORD_MMAP] = event__mmap_swap, | ||
276 | [PERF_RECORD_COMM] = event__comm_swap, | ||
277 | [PERF_RECORD_FORK] = event__task_swap, | ||
278 | [PERF_RECORD_EXIT] = event__task_swap, | ||
279 | [PERF_RECORD_LOST] = event__all64_swap, | ||
280 | [PERF_RECORD_READ] = event__read_swap, | ||
281 | [PERF_RECORD_SAMPLE] = event__all64_swap, | ||
282 | [PERF_RECORD_MAX] = NULL, | ||
283 | }; | ||
284 | |||
285 | static int perf_session__process_event(struct perf_session *self, | ||
286 | event_t *event, | ||
287 | struct perf_event_ops *ops, | ||
288 | u64 offset, u64 head) | ||
289 | { | ||
290 | trace_event(event); | ||
291 | |||
292 | if (event->header.type < PERF_RECORD_MAX) { | ||
293 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | ||
294 | offset + head, event->header.size, | ||
295 | event__name[event->header.type]); | ||
296 | ++event__total[0]; | ||
297 | ++event__total[event->header.type]; | ||
298 | } | ||
299 | |||
300 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | ||
301 | event__swap_ops[event->header.type](event); | ||
302 | |||
303 | switch (event->header.type) { | ||
304 | case PERF_RECORD_SAMPLE: | ||
305 | return ops->sample(event, self); | ||
306 | case PERF_RECORD_MMAP: | ||
307 | return ops->mmap(event, self); | ||
308 | case PERF_RECORD_COMM: | ||
309 | return ops->comm(event, self); | ||
310 | case PERF_RECORD_FORK: | ||
311 | return ops->fork(event, self); | ||
312 | case PERF_RECORD_EXIT: | ||
313 | return ops->exit(event, self); | ||
314 | case PERF_RECORD_LOST: | ||
315 | return ops->lost(event, self); | ||
316 | case PERF_RECORD_READ: | ||
317 | return ops->read(event, self); | ||
318 | case PERF_RECORD_THROTTLE: | ||
319 | return ops->throttle(event, self); | ||
320 | case PERF_RECORD_UNTHROTTLE: | ||
321 | return ops->unthrottle(event, self); | ||
322 | default: | ||
323 | self->unknown_events++; | ||
324 | return -1; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | void perf_event_header__bswap(struct perf_event_header *self) | ||
329 | { | ||
330 | self->type = bswap_32(self->type); | ||
331 | self->misc = bswap_16(self->misc); | ||
332 | self->size = bswap_16(self->size); | ||
333 | } | ||
334 | |||
335 | int perf_header__read_build_ids(struct perf_header *self, | ||
336 | int input, u64 offset, u64 size) | ||
337 | { | ||
338 | struct build_id_event bev; | ||
339 | char filename[PATH_MAX]; | ||
340 | u64 limit = offset + size; | ||
341 | int err = -1; | ||
342 | |||
343 | while (offset < limit) { | ||
344 | struct dso *dso; | ||
345 | ssize_t len; | ||
346 | struct list_head *head = &dsos__user; | ||
347 | |||
348 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
349 | goto out; | ||
350 | |||
351 | if (self->needs_swap) | ||
352 | perf_event_header__bswap(&bev.header); | ||
353 | |||
354 | len = bev.header.size - sizeof(bev); | ||
355 | if (read(input, filename, len) != len) | ||
356 | goto out; | ||
357 | |||
358 | if (bev.header.misc & PERF_RECORD_MISC_KERNEL) | ||
359 | head = &dsos__kernel; | ||
360 | |||
361 | dso = __dsos__findnew(head, filename); | ||
362 | if (dso != NULL) { | ||
363 | dso__set_build_id(dso, &bev.build_id); | ||
364 | if (head == &dsos__kernel && filename[0] == '[') | ||
365 | dso->kernel = 1; | ||
366 | } | ||
367 | |||
368 | offset += bev.header.size; | ||
369 | } | ||
370 | err = 0; | ||
371 | out: | ||
372 | return err; | ||
373 | } | ||
374 | |||
375 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
376 | { | ||
377 | struct thread *thread = perf_session__findnew(self, 0); | ||
378 | |||
379 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | ||
380 | pr_err("problem inserting idle task.\n"); | ||
381 | thread = NULL; | ||
382 | } | ||
383 | |||
384 | return thread; | ||
385 | } | ||
386 | |||
387 | int __perf_session__process_events(struct perf_session *self, | ||
388 | u64 data_offset, u64 data_size, | ||
389 | u64 file_size, struct perf_event_ops *ops) | ||
390 | { | ||
391 | int err, mmap_prot, mmap_flags; | ||
392 | u64 head, shift; | ||
393 | u64 offset = 0; | ||
394 | size_t page_size; | ||
395 | event_t *event; | ||
396 | uint32_t size; | ||
397 | char *buf; | ||
398 | |||
399 | perf_event_ops__fill_defaults(ops); | ||
400 | |||
401 | page_size = sysconf(_SC_PAGESIZE); | ||
402 | |||
403 | head = data_offset; | ||
404 | shift = page_size * (head / page_size); | ||
405 | offset += shift; | ||
406 | head -= shift; | ||
407 | |||
408 | mmap_prot = PROT_READ; | ||
409 | mmap_flags = MAP_SHARED; | ||
410 | |||
411 | if (self->header.needs_swap) { | ||
412 | mmap_prot |= PROT_WRITE; | ||
413 | mmap_flags = MAP_PRIVATE; | ||
414 | } | ||
415 | remap: | ||
416 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, | ||
417 | mmap_flags, self->fd, offset); | ||
418 | if (buf == MAP_FAILED) { | ||
419 | pr_err("failed to mmap file\n"); | ||
420 | err = -errno; | ||
421 | goto out_err; | ||
422 | } | ||
423 | |||
424 | more: | ||
425 | event = (event_t *)(buf + head); | ||
426 | |||
427 | if (self->header.needs_swap) | ||
428 | perf_event_header__bswap(&event->header); | ||
429 | size = event->header.size; | ||
430 | if (size == 0) | ||
431 | size = 8; | ||
432 | |||
433 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
434 | int munmap_ret; | ||
435 | |||
436 | shift = page_size * (head / page_size); | ||
437 | |||
438 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
439 | assert(munmap_ret == 0); | ||
440 | |||
441 | offset += shift; | ||
442 | head -= shift; | ||
443 | goto remap; | ||
444 | } | ||
445 | |||
446 | size = event->header.size; | ||
447 | |||
448 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
449 | offset + head, event->header.size, event->header.type); | ||
450 | |||
451 | if (size == 0 || | ||
452 | perf_session__process_event(self, event, ops, offset, head) < 0) { | ||
453 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
454 | offset + head, event->header.size, | ||
455 | event->header.type); | ||
456 | /* | ||
457 | * assume we lost track of the stream, check alignment, and | ||
458 | * increment a single u64 in the hope to catch on again 'soon'. | ||
459 | */ | ||
460 | if (unlikely(head & 7)) | ||
461 | head &= ~7ULL; | ||
462 | |||
463 | size = 8; | ||
464 | } | ||
465 | |||
466 | head += size; | ||
467 | |||
468 | if (offset + head >= data_offset + data_size) | ||
469 | goto done; | ||
470 | |||
471 | if (offset + head < file_size) | ||
472 | goto more; | ||
473 | done: | ||
474 | err = 0; | ||
475 | out_err: | ||
476 | return err; | ||
477 | } | ||
478 | |||
479 | int perf_session__process_events(struct perf_session *self, | ||
480 | struct perf_event_ops *ops) | ||
481 | { | ||
482 | int err; | ||
483 | |||
484 | if (perf_session__register_idle_thread(self) == NULL) | ||
485 | return -ENOMEM; | ||
486 | |||
487 | if (!symbol_conf.full_paths) { | ||
488 | char bf[PATH_MAX]; | ||
489 | |||
490 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
491 | err = -errno; | ||
492 | out_getcwd_err: | ||
493 | pr_err("failed to get the current directory\n"); | ||
494 | goto out_err; | ||
495 | } | ||
496 | self->cwd = strdup(bf); | ||
497 | if (self->cwd == NULL) { | ||
498 | err = -ENOMEM; | ||
499 | goto out_getcwd_err; | ||
500 | } | ||
501 | self->cwdlen = strlen(self->cwd); | ||
502 | } | ||
503 | |||
504 | err = __perf_session__process_events(self, self->header.data_offset, | ||
505 | self->header.data_size, | ||
506 | self->size, ops); | ||
507 | out_err: | ||
508 | return err; | ||
509 | } | ||
510 | |||
511 | bool perf_session__has_traces(struct perf_session *self, const char *msg) | ||
512 | { | ||
513 | if (!(self->sample_type & PERF_SAMPLE_RAW)) { | ||
514 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | ||
515 | return false; | ||
516 | } | ||
517 | |||
518 | return true; | ||
519 | } | ||
520 | |||
521 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
522 | const char *symbol_name, | ||
523 | u64 addr) | ||
524 | { | ||
525 | char *bracket; | ||
526 | enum map_type i; | ||
527 | |||
528 | self->ref_reloc_sym.name = strdup(symbol_name); | ||
529 | if (self->ref_reloc_sym.name == NULL) | ||
530 | return -ENOMEM; | ||
531 | |||
532 | bracket = strchr(self->ref_reloc_sym.name, ']'); | ||
533 | if (bracket) | ||
534 | *bracket = '\0'; | ||
535 | |||
536 | self->ref_reloc_sym.addr = addr; | ||
537 | |||
538 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
539 | struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); | ||
540 | kmap->ref_reloc_sym = &self->ref_reloc_sym; | ||
541 | } | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
547 | { | ||
548 | return ip + (s64)map->pgoff; | ||
549 | } | ||
550 | |||
551 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
552 | { | ||
553 | return ip - (s64)map->pgoff; | ||
554 | } | ||
555 | |||
556 | void map__reloc_vmlinux(struct map *self) | ||
557 | { | ||
558 | struct kmap *kmap = map__kmap(self); | ||
559 | s64 reloc; | ||
560 | |||
561 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | ||
562 | return; | ||
563 | |||
564 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - | ||
565 | kmap->ref_reloc_sym->addr); | ||
566 | |||
567 | if (!reloc) | ||
568 | return; | ||
569 | |||
570 | self->map_ip = map__reloc_map_ip; | ||
571 | self->unmap_ip = map__reloc_unmap_ip; | ||
572 | self->pgoff = reloc; | ||
573 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 32eaa1bada06..31950fcd8a4d 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -3,13 +3,13 @@ | |||
3 | 3 | ||
4 | #include "event.h" | 4 | #include "event.h" |
5 | #include "header.h" | 5 | #include "header.h" |
6 | #include "symbol.h" | ||
6 | #include "thread.h" | 7 | #include "thread.h" |
7 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
8 | #include "../../../include/linux/perf_event.h" | 9 | #include "../../../include/linux/perf_event.h" |
9 | 10 | ||
10 | struct ip_callchain; | 11 | struct ip_callchain; |
11 | struct thread; | 12 | struct thread; |
12 | struct symbol; | ||
13 | 13 | ||
14 | struct perf_session { | 14 | struct perf_session { |
15 | struct perf_header header; | 15 | struct perf_header header; |
@@ -18,10 +18,13 @@ struct perf_session { | |||
18 | struct map_groups kmaps; | 18 | struct map_groups kmaps; |
19 | struct rb_root threads; | 19 | struct rb_root threads; |
20 | struct thread *last_match; | 20 | struct thread *last_match; |
21 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
21 | struct events_stats events_stats; | 22 | struct events_stats events_stats; |
22 | unsigned long event_total[PERF_RECORD_MAX]; | 23 | unsigned long event_total[PERF_RECORD_MAX]; |
24 | unsigned long unknown_events; | ||
23 | struct rb_root hists; | 25 | struct rb_root hists; |
24 | u64 sample_type; | 26 | u64 sample_type; |
27 | struct ref_reloc_sym ref_reloc_sym; | ||
25 | int fd; | 28 | int fd; |
26 | int cwdlen; | 29 | int cwdlen; |
27 | char *cwd; | 30 | char *cwd; |
@@ -31,23 +34,25 @@ struct perf_session { | |||
31 | typedef int (*event_op)(event_t *self, struct perf_session *session); | 34 | typedef int (*event_op)(event_t *self, struct perf_session *session); |
32 | 35 | ||
33 | struct perf_event_ops { | 36 | struct perf_event_ops { |
34 | event_op process_sample_event; | 37 | event_op sample, |
35 | event_op process_mmap_event; | 38 | mmap, |
36 | event_op process_comm_event; | 39 | comm, |
37 | event_op process_fork_event; | 40 | fork, |
38 | event_op process_exit_event; | 41 | exit, |
39 | event_op process_lost_event; | 42 | lost, |
40 | event_op process_read_event; | 43 | read, |
41 | event_op process_throttle_event; | 44 | throttle, |
42 | event_op process_unthrottle_event; | 45 | unthrottle; |
43 | int (*sample_type_check)(struct perf_session *session); | ||
44 | unsigned long total_unknown; | ||
45 | bool full_paths; | ||
46 | }; | 46 | }; |
47 | 47 | ||
48 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | 48 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); |
49 | void perf_session__delete(struct perf_session *self); | 49 | void perf_session__delete(struct perf_session *self); |
50 | 50 | ||
51 | void perf_event_header__bswap(struct perf_event_header *self); | ||
52 | |||
53 | int __perf_session__process_events(struct perf_session *self, | ||
54 | u64 data_offset, u64 data_size, u64 size, | ||
55 | struct perf_event_ops *ops); | ||
51 | int perf_session__process_events(struct perf_session *self, | 56 | int perf_session__process_events(struct perf_session *self, |
52 | struct perf_event_ops *event_ops); | 57 | struct perf_event_ops *event_ops); |
53 | 58 | ||
@@ -56,6 +61,28 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, | |||
56 | struct ip_callchain *chain, | 61 | struct ip_callchain *chain, |
57 | struct symbol **parent); | 62 | struct symbol **parent); |
58 | 63 | ||
59 | int perf_header__read_build_ids(int input, u64 offset, u64 file_size); | 64 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
65 | |||
66 | int perf_header__read_build_ids(struct perf_header *self, int input, | ||
67 | u64 offset, u64 file_size); | ||
68 | |||
69 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
70 | const char *symbol_name, | ||
71 | u64 addr); | ||
72 | |||
73 | void mem_bswap_64(void *src, int byte_size); | ||
74 | |||
75 | static inline int __perf_session__create_kernel_maps(struct perf_session *self, | ||
76 | struct dso *kernel) | ||
77 | { | ||
78 | return __map_groups__create_kernel_maps(&self->kmaps, | ||
79 | self->vmlinux_maps, kernel); | ||
80 | } | ||
60 | 81 | ||
82 | static inline struct map * | ||
83 | perf_session__new_module_map(struct perf_session *self, | ||
84 | u64 start, const char *filename) | ||
85 | { | ||
86 | return map_groups__new_module(&self->kmaps, start, filename); | ||
87 | } | ||
61 | #endif /* __PERF_SESSION_H */ | 88 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 5352d7dccc61..a175949ed216 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -227,16 +227,73 @@ fail: | |||
227 | return NULL; | 227 | return NULL; |
228 | } | 228 | } |
229 | 229 | ||
230 | /* Glob expression pattern matching */ | 230 | /* Character class matching */ |
231 | bool strglobmatch(const char *str, const char *pat) | 231 | static bool __match_charclass(const char *pat, char c, const char **npat) |
232 | { | ||
233 | bool complement = false, ret = true; | ||
234 | |||
235 | if (*pat == '!') { | ||
236 | complement = true; | ||
237 | pat++; | ||
238 | } | ||
239 | if (*pat++ == c) /* First character is special */ | ||
240 | goto end; | ||
241 | |||
242 | while (*pat && *pat != ']') { /* Matching */ | ||
243 | if (*pat == '-' && *(pat + 1) != ']') { /* Range */ | ||
244 | if (*(pat - 1) <= c && c <= *(pat + 1)) | ||
245 | goto end; | ||
246 | if (*(pat - 1) > *(pat + 1)) | ||
247 | goto error; | ||
248 | pat += 2; | ||
249 | } else if (*pat++ == c) | ||
250 | goto end; | ||
251 | } | ||
252 | if (!*pat) | ||
253 | goto error; | ||
254 | ret = false; | ||
255 | |||
256 | end: | ||
257 | while (*pat && *pat != ']') /* Searching closing */ | ||
258 | pat++; | ||
259 | if (!*pat) | ||
260 | goto error; | ||
261 | *npat = pat + 1; | ||
262 | return complement ? !ret : ret; | ||
263 | |||
264 | error: | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | /* Glob/lazy pattern matching */ | ||
269 | static bool __match_glob(const char *str, const char *pat, bool ignore_space) | ||
232 | { | 270 | { |
233 | while (*str && *pat && *pat != '*') { | 271 | while (*str && *pat && *pat != '*') { |
234 | if (*pat == '?') { | 272 | if (ignore_space) { |
273 | /* Ignore spaces for lazy matching */ | ||
274 | if (isspace(*str)) { | ||
275 | str++; | ||
276 | continue; | ||
277 | } | ||
278 | if (isspace(*pat)) { | ||
279 | pat++; | ||
280 | continue; | ||
281 | } | ||
282 | } | ||
283 | if (*pat == '?') { /* Matches any single character */ | ||
235 | str++; | 284 | str++; |
236 | pat++; | 285 | pat++; |
237 | } else | 286 | continue; |
238 | if (*str++ != *pat++) | 287 | } else if (*pat == '[') /* Character classes/Ranges */ |
288 | if (__match_charclass(pat + 1, *str, &pat)) { | ||
289 | str++; | ||
290 | continue; | ||
291 | } else | ||
239 | return false; | 292 | return false; |
293 | else if (*pat == '\\') /* Escaped char match as normal char */ | ||
294 | pat++; | ||
295 | if (*str++ != *pat++) | ||
296 | return false; | ||
240 | } | 297 | } |
241 | /* Check wild card */ | 298 | /* Check wild card */ |
242 | if (*pat == '*') { | 299 | if (*pat == '*') { |
@@ -251,3 +308,32 @@ bool strglobmatch(const char *str, const char *pat) | |||
251 | return !*str && !*pat; | 308 | return !*str && !*pat; |
252 | } | 309 | } |
253 | 310 | ||
311 | /** | ||
312 | * strglobmatch - glob expression pattern matching | ||
313 | * @str: the target string to match | ||
314 | * @pat: the pattern string to match | ||
315 | * | ||
316 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
317 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
318 | * also supported). Also, this supports escape character ('\') to use special | ||
319 | * characters as normal character. | ||
320 | * | ||
321 | * Note: if @pat syntax is broken, this always returns false. | ||
322 | */ | ||
323 | bool strglobmatch(const char *str, const char *pat) | ||
324 | { | ||
325 | return __match_glob(str, pat, false); | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * strlazymatch - matching pattern strings lazily with glob pattern | ||
330 | * @str: the target string to match | ||
331 | * @pat: the pattern string to match | ||
332 | * | ||
333 | * This is similar to strglobmatch, except this ignores spaces in | ||
334 | * the target string. | ||
335 | */ | ||
336 | bool strlazymatch(const char *str, const char *pat) | ||
337 | { | ||
338 | return __match_glob(str, pat, true); | ||
339 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 02ede58c54b4..542e44de3719 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
@@ -10,6 +10,7 @@ s64 perf_atoll(const char *str); | |||
10 | char **argv_split(const char *str, int *argcp); | 10 | char **argv_split(const char *str, int *argcp); |
11 | void argv_free(char **argv); | 11 | void argv_free(char **argv); |
12 | bool strglobmatch(const char *str, const char *pat); | 12 | bool strglobmatch(const char *str, const char *pat); |
13 | bool strlazymatch(const char *str, const char *pat); | ||
13 | 14 | ||
14 | #define _STR(x) #x | 15 | #define _STR(x) #x |
15 | #define STR(x) _STR(x) | 16 | #define STR(x) _STR(x) |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ab92763edb03..323c0aea0a91 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "../perf.h" | 2 | #include "../perf.h" |
3 | #include "session.h" | ||
4 | #include "sort.h" | 3 | #include "sort.h" |
5 | #include "string.h" | 4 | #include "string.h" |
6 | #include "symbol.h" | 5 | #include "symbol.h" |
@@ -22,6 +21,7 @@ | |||
22 | enum dso_origin { | 21 | enum dso_origin { |
23 | DSO__ORIG_KERNEL = 0, | 22 | DSO__ORIG_KERNEL = 0, |
24 | DSO__ORIG_JAVA_JIT, | 23 | DSO__ORIG_JAVA_JIT, |
24 | DSO__ORIG_BUILD_ID_CACHE, | ||
25 | DSO__ORIG_FEDORA, | 25 | DSO__ORIG_FEDORA, |
26 | DSO__ORIG_UBUNTU, | 26 | DSO__ORIG_UBUNTU, |
27 | DSO__ORIG_BUILDID, | 27 | DSO__ORIG_BUILDID, |
@@ -33,7 +33,7 @@ enum dso_origin { | |||
33 | static void dsos__add(struct list_head *head, struct dso *dso); | 33 | static void dsos__add(struct list_head *head, struct dso *dso); |
34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
36 | struct perf_session *session, symbol_filter_t filter); | 36 | symbol_filter_t filter); |
37 | static int vmlinux_path__nr_entries; | 37 | static int vmlinux_path__nr_entries; |
38 | static char **vmlinux_path; | 38 | static char **vmlinux_path; |
39 | 39 | ||
@@ -53,17 +53,12 @@ bool dso__sorted_by_name(const struct dso *self, enum map_type type) | |||
53 | return self->sorted_by_name & (1 << type); | 53 | return self->sorted_by_name & (1 << type); |
54 | } | 54 | } |
55 | 55 | ||
56 | static void dso__set_loaded(struct dso *self, enum map_type type) | ||
57 | { | ||
58 | self->loaded |= (1 << type); | ||
59 | } | ||
60 | |||
61 | static void dso__set_sorted_by_name(struct dso *self, enum map_type type) | 56 | static void dso__set_sorted_by_name(struct dso *self, enum map_type type) |
62 | { | 57 | { |
63 | self->sorted_by_name |= (1 << type); | 58 | self->sorted_by_name |= (1 << type); |
64 | } | 59 | } |
65 | 60 | ||
66 | static bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 61 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
67 | { | 62 | { |
68 | switch (map_type) { | 63 | switch (map_type) { |
69 | case MAP__FUNCTION: | 64 | case MAP__FUNCTION: |
@@ -142,14 +137,14 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) | |||
142 | self->start = start; | 137 | self->start = start; |
143 | self->end = len ? start + len - 1 : start; | 138 | self->end = len ? start + len - 1 : start; |
144 | 139 | ||
145 | pr_debug3("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); | 140 | pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); |
146 | 141 | ||
147 | memcpy(self->name, name, namelen); | 142 | memcpy(self->name, name, namelen); |
148 | 143 | ||
149 | return self; | 144 | return self; |
150 | } | 145 | } |
151 | 146 | ||
152 | static void symbol__delete(struct symbol *self) | 147 | void symbol__delete(struct symbol *self) |
153 | { | 148 | { |
154 | free(((void *)self) - symbol_conf.priv_size); | 149 | free(((void *)self) - symbol_conf.priv_size); |
155 | } | 150 | } |
@@ -160,7 +155,7 @@ static size_t symbol__fprintf(struct symbol *self, FILE *fp) | |||
160 | self->start, self->end, self->name); | 155 | self->start, self->end, self->name); |
161 | } | 156 | } |
162 | 157 | ||
163 | static void dso__set_long_name(struct dso *self, char *name) | 158 | void dso__set_long_name(struct dso *self, char *name) |
164 | { | 159 | { |
165 | if (name == NULL) | 160 | if (name == NULL) |
166 | return; | 161 | return; |
@@ -175,7 +170,7 @@ static void dso__set_basename(struct dso *self) | |||
175 | 170 | ||
176 | struct dso *dso__new(const char *name) | 171 | struct dso *dso__new(const char *name) |
177 | { | 172 | { |
178 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | 173 | struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); |
179 | 174 | ||
180 | if (self != NULL) { | 175 | if (self != NULL) { |
181 | int i; | 176 | int i; |
@@ -344,10 +339,10 @@ void dso__sort_by_name(struct dso *self, enum map_type type) | |||
344 | &self->symbols[type]); | 339 | &self->symbols[type]); |
345 | } | 340 | } |
346 | 341 | ||
347 | int build_id__sprintf(u8 *self, int len, char *bf) | 342 | int build_id__sprintf(const u8 *self, int len, char *bf) |
348 | { | 343 | { |
349 | char *bid = bf; | 344 | char *bid = bf; |
350 | u8 *raw = self; | 345 | const u8 *raw = self; |
351 | int i; | 346 | int i; |
352 | 347 | ||
353 | for (i = 0; i < len; ++i) { | 348 | for (i = 0; i < len; ++i) { |
@@ -372,6 +367,10 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | |||
372 | struct rb_node *nd; | 367 | struct rb_node *nd; |
373 | size_t ret = fprintf(fp, "dso: %s (", self->short_name); | 368 | size_t ret = fprintf(fp, "dso: %s (", self->short_name); |
374 | 369 | ||
370 | if (self->short_name != self->long_name) | ||
371 | ret += fprintf(fp, "%s, ", self->long_name); | ||
372 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
373 | self->loaded ? "" : "NOT "); | ||
375 | ret += dso__fprintf_buildid(self, fp); | 374 | ret += dso__fprintf_buildid(self, fp); |
376 | ret += fprintf(fp, ")\n"); | 375 | ret += fprintf(fp, ")\n"); |
377 | for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) { | 376 | for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) { |
@@ -382,24 +381,20 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | |||
382 | return ret; | 381 | return ret; |
383 | } | 382 | } |
384 | 383 | ||
385 | /* | 384 | int kallsyms__parse(const char *filename, void *arg, |
386 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | 385 | int (*process_symbol)(void *arg, const char *name, |
387 | * so that we can in the next step set the symbol ->end address and then | 386 | char type, u64 start)) |
388 | * call kernel_maps__split_kallsyms. | ||
389 | */ | ||
390 | static int dso__load_all_kallsyms(struct dso *self, struct map *map) | ||
391 | { | 387 | { |
392 | char *line = NULL; | 388 | char *line = NULL; |
393 | size_t n; | 389 | size_t n; |
394 | struct rb_root *root = &self->symbols[map->type]; | 390 | int err = 0; |
395 | FILE *file = fopen("/proc/kallsyms", "r"); | 391 | FILE *file = fopen(filename, "r"); |
396 | 392 | ||
397 | if (file == NULL) | 393 | if (file == NULL) |
398 | goto out_failure; | 394 | goto out_failure; |
399 | 395 | ||
400 | while (!feof(file)) { | 396 | while (!feof(file)) { |
401 | u64 start; | 397 | u64 start; |
402 | struct symbol *sym; | ||
403 | int line_len, len; | 398 | int line_len, len; |
404 | char symbol_type; | 399 | char symbol_type; |
405 | char *symbol_name; | 400 | char *symbol_name; |
@@ -420,43 +415,72 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map) | |||
420 | continue; | 415 | continue; |
421 | 416 | ||
422 | symbol_type = toupper(line[len]); | 417 | symbol_type = toupper(line[len]); |
423 | if (!symbol_type__is_a(symbol_type, map->type)) | ||
424 | continue; | ||
425 | |||
426 | symbol_name = line + len + 2; | 418 | symbol_name = line + len + 2; |
427 | /* | ||
428 | * Will fix up the end later, when we have all symbols sorted. | ||
429 | */ | ||
430 | sym = symbol__new(start, 0, symbol_name); | ||
431 | 419 | ||
432 | if (sym == NULL) | 420 | err = process_symbol(arg, symbol_name, symbol_type, start); |
433 | goto out_delete_line; | 421 | if (err) |
434 | /* | 422 | break; |
435 | * We will pass the symbols to the filter later, in | ||
436 | * map__split_kallsyms, when we have split the maps per module | ||
437 | */ | ||
438 | symbols__insert(root, sym); | ||
439 | } | 423 | } |
440 | 424 | ||
441 | free(line); | 425 | free(line); |
442 | fclose(file); | 426 | fclose(file); |
427 | return err; | ||
443 | 428 | ||
444 | return 0; | ||
445 | |||
446 | out_delete_line: | ||
447 | free(line); | ||
448 | out_failure: | 429 | out_failure: |
449 | return -1; | 430 | return -1; |
450 | } | 431 | } |
451 | 432 | ||
433 | struct process_kallsyms_args { | ||
434 | struct map *map; | ||
435 | struct dso *dso; | ||
436 | }; | ||
437 | |||
438 | static int map__process_kallsym_symbol(void *arg, const char *name, | ||
439 | char type, u64 start) | ||
440 | { | ||
441 | struct symbol *sym; | ||
442 | struct process_kallsyms_args *a = arg; | ||
443 | struct rb_root *root = &a->dso->symbols[a->map->type]; | ||
444 | |||
445 | if (!symbol_type__is_a(type, a->map->type)) | ||
446 | return 0; | ||
447 | |||
448 | /* | ||
449 | * Will fix up the end later, when we have all symbols sorted. | ||
450 | */ | ||
451 | sym = symbol__new(start, 0, name); | ||
452 | |||
453 | if (sym == NULL) | ||
454 | return -ENOMEM; | ||
455 | /* | ||
456 | * We will pass the symbols to the filter later, in | ||
457 | * map__split_kallsyms, when we have split the maps per module | ||
458 | */ | ||
459 | symbols__insert(root, sym); | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | ||
465 | * so that we can in the next step set the symbol ->end address and then | ||
466 | * call kernel_maps__split_kallsyms. | ||
467 | */ | ||
468 | static int dso__load_all_kallsyms(struct dso *self, const char *filename, | ||
469 | struct map *map) | ||
470 | { | ||
471 | struct process_kallsyms_args args = { .map = map, .dso = self, }; | ||
472 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | ||
473 | } | ||
474 | |||
452 | /* | 475 | /* |
453 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | 476 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
454 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 477 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
455 | * the original ELF section names vmlinux have. | 478 | * the original ELF section names vmlinux have. |
456 | */ | 479 | */ |
457 | static int dso__split_kallsyms(struct dso *self, struct map *map, | 480 | static int dso__split_kallsyms(struct dso *self, struct map *map, |
458 | struct perf_session *session, symbol_filter_t filter) | 481 | symbol_filter_t filter) |
459 | { | 482 | { |
483 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
460 | struct map *curr_map = map; | 484 | struct map *curr_map = map; |
461 | struct symbol *pos; | 485 | struct symbol *pos; |
462 | int count = 0; | 486 | int count = 0; |
@@ -477,13 +501,17 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
477 | 501 | ||
478 | *module++ = '\0'; | 502 | *module++ = '\0'; |
479 | 503 | ||
480 | if (strcmp(self->name, module)) { | 504 | if (strcmp(curr_map->dso->short_name, module)) { |
481 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); | 505 | curr_map = map_groups__find_by_name(kmaps, map->type, module); |
482 | if (curr_map == NULL) { | 506 | if (curr_map == NULL) { |
483 | pr_debug("/proc/{kallsyms,modules} " | 507 | pr_debug("/proc/{kallsyms,modules} " |
484 | "inconsistency!\n"); | 508 | "inconsistency while looking " |
509 | "for \"%s\" module!\n", module); | ||
485 | return -1; | 510 | return -1; |
486 | } | 511 | } |
512 | |||
513 | if (curr_map->dso->loaded) | ||
514 | goto discard_symbol; | ||
487 | } | 515 | } |
488 | /* | 516 | /* |
489 | * So that we look just like we get from .ko files, | 517 | * So that we look just like we get from .ko files, |
@@ -503,13 +531,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
503 | return -1; | 531 | return -1; |
504 | 532 | ||
505 | curr_map = map__new2(pos->start, dso, map->type); | 533 | curr_map = map__new2(pos->start, dso, map->type); |
506 | if (map == NULL) { | 534 | if (curr_map == NULL) { |
507 | dso__delete(dso); | 535 | dso__delete(dso); |
508 | return -1; | 536 | return -1; |
509 | } | 537 | } |
510 | 538 | ||
511 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | 539 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; |
512 | map_groups__insert(&session->kmaps, curr_map); | 540 | map_groups__insert(kmaps, curr_map); |
513 | ++kernel_range; | 541 | ++kernel_range; |
514 | } | 542 | } |
515 | 543 | ||
@@ -528,17 +556,16 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
528 | return count; | 556 | return count; |
529 | } | 557 | } |
530 | 558 | ||
531 | 559 | int dso__load_kallsyms(struct dso *self, const char *filename, | |
532 | static int dso__load_kallsyms(struct dso *self, struct map *map, | 560 | struct map *map, symbol_filter_t filter) |
533 | struct perf_session *session, symbol_filter_t filter) | ||
534 | { | 561 | { |
535 | if (dso__load_all_kallsyms(self, map) < 0) | 562 | if (dso__load_all_kallsyms(self, filename, map) < 0) |
536 | return -1; | 563 | return -1; |
537 | 564 | ||
538 | symbols__fixup_end(&self->symbols[map->type]); | 565 | symbols__fixup_end(&self->symbols[map->type]); |
539 | self->origin = DSO__ORIG_KERNEL; | 566 | self->origin = DSO__ORIG_KERNEL; |
540 | 567 | ||
541 | return dso__split_kallsyms(self, map, session, filter); | 568 | return dso__split_kallsyms(self, map, filter); |
542 | } | 569 | } |
543 | 570 | ||
544 | static int dso__load_perf_map(struct dso *self, struct map *map, | 571 | static int dso__load_perf_map(struct dso *self, struct map *map, |
@@ -864,10 +891,10 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type | |||
864 | } | 891 | } |
865 | } | 892 | } |
866 | 893 | ||
867 | static int dso__load_sym(struct dso *self, struct map *map, | 894 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, |
868 | struct perf_session *session, const char *name, int fd, | 895 | int fd, symbol_filter_t filter, int kmodule) |
869 | symbol_filter_t filter, int kernel, int kmodule) | ||
870 | { | 896 | { |
897 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | ||
871 | struct map *curr_map = map; | 898 | struct map *curr_map = map; |
872 | struct dso *curr_dso = self; | 899 | struct dso *curr_dso = self; |
873 | size_t dso_name_len = strlen(self->short_name); | 900 | size_t dso_name_len = strlen(self->short_name); |
@@ -924,7 +951,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
924 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 951 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
925 | 952 | ||
926 | memset(&sym, 0, sizeof(sym)); | 953 | memset(&sym, 0, sizeof(sym)); |
927 | if (!kernel) { | 954 | if (!self->kernel) { |
928 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 955 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || |
929 | elf_section_by_name(elf, &ehdr, &shdr, | 956 | elf_section_by_name(elf, &ehdr, &shdr, |
930 | ".gnu.prelink_undo", | 957 | ".gnu.prelink_undo", |
@@ -933,11 +960,15 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
933 | 960 | ||
934 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 961 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
935 | struct symbol *f; | 962 | struct symbol *f; |
936 | const char *elf_name; | 963 | const char *elf_name = elf_sym__name(&sym, symstrs); |
937 | char *demangled = NULL; | 964 | char *demangled = NULL; |
938 | int is_label = elf_sym__is_label(&sym); | 965 | int is_label = elf_sym__is_label(&sym); |
939 | const char *section_name; | 966 | const char *section_name; |
940 | 967 | ||
968 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
969 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
970 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
971 | |||
941 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 972 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
942 | continue; | 973 | continue; |
943 | 974 | ||
@@ -950,10 +981,9 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
950 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) | 981 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
951 | continue; | 982 | continue; |
952 | 983 | ||
953 | elf_name = elf_sym__name(&sym, symstrs); | ||
954 | section_name = elf_sec__name(&shdr, secstrs); | 984 | section_name = elf_sec__name(&shdr, secstrs); |
955 | 985 | ||
956 | if (kernel || kmodule) { | 986 | if (self->kernel || kmodule) { |
957 | char dso_name[PATH_MAX]; | 987 | char dso_name[PATH_MAX]; |
958 | 988 | ||
959 | if (strcmp(section_name, | 989 | if (strcmp(section_name, |
@@ -969,7 +999,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
969 | snprintf(dso_name, sizeof(dso_name), | 999 | snprintf(dso_name, sizeof(dso_name), |
970 | "%s%s", self->short_name, section_name); | 1000 | "%s%s", self->short_name, section_name); |
971 | 1001 | ||
972 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name); | 1002 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); |
973 | if (curr_map == NULL) { | 1003 | if (curr_map == NULL) { |
974 | u64 start = sym.st_value; | 1004 | u64 start = sym.st_value; |
975 | 1005 | ||
@@ -980,7 +1010,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
980 | if (curr_dso == NULL) | 1010 | if (curr_dso == NULL) |
981 | goto out_elf_end; | 1011 | goto out_elf_end; |
982 | curr_map = map__new2(start, curr_dso, | 1012 | curr_map = map__new2(start, curr_dso, |
983 | MAP__FUNCTION); | 1013 | map->type); |
984 | if (curr_map == NULL) { | 1014 | if (curr_map == NULL) { |
985 | dso__delete(curr_dso); | 1015 | dso__delete(curr_dso); |
986 | goto out_elf_end; | 1016 | goto out_elf_end; |
@@ -988,8 +1018,9 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
988 | curr_map->map_ip = identity__map_ip; | 1018 | curr_map->map_ip = identity__map_ip; |
989 | curr_map->unmap_ip = identity__map_ip; | 1019 | curr_map->unmap_ip = identity__map_ip; |
990 | curr_dso->origin = DSO__ORIG_KERNEL; | 1020 | curr_dso->origin = DSO__ORIG_KERNEL; |
991 | map_groups__insert(&session->kmaps, curr_map); | 1021 | map_groups__insert(kmap->kmaps, curr_map); |
992 | dsos__add(&dsos__kernel, curr_dso); | 1022 | dsos__add(&dsos__kernel, curr_dso); |
1023 | dso__set_loaded(curr_dso, map->type); | ||
993 | } else | 1024 | } else |
994 | curr_dso = curr_map->dso; | 1025 | curr_dso = curr_map->dso; |
995 | 1026 | ||
@@ -997,9 +1028,10 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
997 | } | 1028 | } |
998 | 1029 | ||
999 | if (curr_dso->adjust_symbols) { | 1030 | if (curr_dso->adjust_symbols) { |
1000 | pr_debug2("adjusting symbol: st_value: %Lx sh_addr: " | 1031 | pr_debug4("%s: adjusting symbol: st_value: %#Lx " |
1001 | "%Lx sh_offset: %Lx\n", (u64)sym.st_value, | 1032 | "sh_addr: %#Lx sh_offset: %#Lx\n", __func__, |
1002 | (u64)shdr.sh_addr, (u64)shdr.sh_offset); | 1033 | (u64)sym.st_value, (u64)shdr.sh_addr, |
1034 | (u64)shdr.sh_offset); | ||
1003 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 1035 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; |
1004 | } | 1036 | } |
1005 | /* | 1037 | /* |
@@ -1027,8 +1059,16 @@ new_symbol: | |||
1027 | /* | 1059 | /* |
1028 | * For misannotated, zeroed, ASM function sizes. | 1060 | * For misannotated, zeroed, ASM function sizes. |
1029 | */ | 1061 | */ |
1030 | if (nr > 0) | 1062 | if (nr > 0) { |
1031 | symbols__fixup_end(&self->symbols[map->type]); | 1063 | symbols__fixup_end(&self->symbols[map->type]); |
1064 | if (kmap) { | ||
1065 | /* | ||
1066 | * We need to fixup this here too because we create new | ||
1067 | * maps here, for things like vsyscall sections. | ||
1068 | */ | ||
1069 | __map_groups__fixup_end(kmap->kmaps, map->type); | ||
1070 | } | ||
1071 | } | ||
1032 | err = nr; | 1072 | err = nr; |
1033 | out_elf_end: | 1073 | out_elf_end: |
1034 | elf_end(elf); | 1074 | elf_end(elf); |
@@ -1041,25 +1081,28 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id) | |||
1041 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; | 1081 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; |
1042 | } | 1082 | } |
1043 | 1083 | ||
1044 | static bool __dsos__read_build_ids(struct list_head *head) | 1084 | static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) |
1045 | { | 1085 | { |
1046 | bool have_build_id = false; | 1086 | bool have_build_id = false; |
1047 | struct dso *pos; | 1087 | struct dso *pos; |
1048 | 1088 | ||
1049 | list_for_each_entry(pos, head, node) | 1089 | list_for_each_entry(pos, head, node) { |
1090 | if (with_hits && !pos->hit) | ||
1091 | continue; | ||
1050 | if (filename__read_build_id(pos->long_name, pos->build_id, | 1092 | if (filename__read_build_id(pos->long_name, pos->build_id, |
1051 | sizeof(pos->build_id)) > 0) { | 1093 | sizeof(pos->build_id)) > 0) { |
1052 | have_build_id = true; | 1094 | have_build_id = true; |
1053 | pos->has_build_id = true; | 1095 | pos->has_build_id = true; |
1054 | } | 1096 | } |
1097 | } | ||
1055 | 1098 | ||
1056 | return have_build_id; | 1099 | return have_build_id; |
1057 | } | 1100 | } |
1058 | 1101 | ||
1059 | bool dsos__read_build_ids(void) | 1102 | bool dsos__read_build_ids(bool with_hits) |
1060 | { | 1103 | { |
1061 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel), | 1104 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), |
1062 | ubuildids = __dsos__read_build_ids(&dsos__user); | 1105 | ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); |
1063 | return kbuildids || ubuildids; | 1106 | return kbuildids || ubuildids; |
1064 | } | 1107 | } |
1065 | 1108 | ||
@@ -1191,6 +1234,7 @@ char dso__symtab_origin(const struct dso *self) | |||
1191 | static const char origin[] = { | 1234 | static const char origin[] = { |
1192 | [DSO__ORIG_KERNEL] = 'k', | 1235 | [DSO__ORIG_KERNEL] = 'k', |
1193 | [DSO__ORIG_JAVA_JIT] = 'j', | 1236 | [DSO__ORIG_JAVA_JIT] = 'j', |
1237 | [DSO__ORIG_BUILD_ID_CACHE] = 'B', | ||
1194 | [DSO__ORIG_FEDORA] = 'f', | 1238 | [DSO__ORIG_FEDORA] = 'f', |
1195 | [DSO__ORIG_UBUNTU] = 'u', | 1239 | [DSO__ORIG_UBUNTU] = 'u', |
1196 | [DSO__ORIG_BUILDID] = 'b', | 1240 | [DSO__ORIG_BUILDID] = 'b', |
@@ -1203,19 +1247,19 @@ char dso__symtab_origin(const struct dso *self) | |||
1203 | return origin[self->origin]; | 1247 | return origin[self->origin]; |
1204 | } | 1248 | } |
1205 | 1249 | ||
1206 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, | 1250 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) |
1207 | symbol_filter_t filter) | ||
1208 | { | 1251 | { |
1209 | int size = PATH_MAX; | 1252 | int size = PATH_MAX; |
1210 | char *name; | 1253 | char *name; |
1211 | u8 build_id[BUILD_ID_SIZE]; | 1254 | u8 build_id[BUILD_ID_SIZE]; |
1255 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
1212 | int ret = -1; | 1256 | int ret = -1; |
1213 | int fd; | 1257 | int fd; |
1214 | 1258 | ||
1215 | dso__set_loaded(self, map->type); | 1259 | dso__set_loaded(self, map->type); |
1216 | 1260 | ||
1217 | if (self->kernel) | 1261 | if (self->kernel) |
1218 | return dso__load_kernel_sym(self, map, session, filter); | 1262 | return dso__load_kernel_sym(self, map, filter); |
1219 | 1263 | ||
1220 | name = malloc(size); | 1264 | name = malloc(size); |
1221 | if (!name) | 1265 | if (!name) |
@@ -1230,8 +1274,16 @@ int dso__load(struct dso *self, struct map *map, struct perf_session *session, | |||
1230 | return ret; | 1274 | return ret; |
1231 | } | 1275 | } |
1232 | 1276 | ||
1233 | self->origin = DSO__ORIG_FEDORA - 1; | 1277 | self->origin = DSO__ORIG_BUILD_ID_CACHE; |
1234 | 1278 | ||
1279 | if (self->has_build_id) { | ||
1280 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1281 | build_id_hex); | ||
1282 | snprintf(name, size, "%s/%s/.build-id/%.2s/%s", | ||
1283 | getenv("HOME"), DEBUG_CACHE_DIR, | ||
1284 | build_id_hex, build_id_hex + 2); | ||
1285 | goto open_file; | ||
1286 | } | ||
1235 | more: | 1287 | more: |
1236 | do { | 1288 | do { |
1237 | self->origin++; | 1289 | self->origin++; |
@@ -1247,8 +1299,6 @@ more: | |||
1247 | case DSO__ORIG_BUILDID: | 1299 | case DSO__ORIG_BUILDID: |
1248 | if (filename__read_build_id(self->long_name, build_id, | 1300 | if (filename__read_build_id(self->long_name, build_id, |
1249 | sizeof(build_id))) { | 1301 | sizeof(build_id))) { |
1250 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
1251 | |||
1252 | build_id__sprintf(build_id, sizeof(build_id), | 1302 | build_id__sprintf(build_id, sizeof(build_id), |
1253 | build_id_hex); | 1303 | build_id_hex); |
1254 | snprintf(name, size, | 1304 | snprintf(name, size, |
@@ -1276,11 +1326,11 @@ compare_build_id: | |||
1276 | if (!dso__build_id_equal(self, build_id)) | 1326 | if (!dso__build_id_equal(self, build_id)) |
1277 | goto more; | 1327 | goto more; |
1278 | } | 1328 | } |
1279 | 1329 | open_file: | |
1280 | fd = open(name, O_RDONLY); | 1330 | fd = open(name, O_RDONLY); |
1281 | } while (fd < 0); | 1331 | } while (fd < 0); |
1282 | 1332 | ||
1283 | ret = dso__load_sym(self, map, NULL, name, fd, filter, 0, 0); | 1333 | ret = dso__load_sym(self, map, name, fd, filter, 0); |
1284 | close(fd); | 1334 | close(fd); |
1285 | 1335 | ||
1286 | /* | 1336 | /* |
@@ -1309,14 +1359,34 @@ struct map *map_groups__find_by_name(struct map_groups *self, | |||
1309 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 1359 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
1310 | struct map *map = rb_entry(nd, struct map, rb_node); | 1360 | struct map *map = rb_entry(nd, struct map, rb_node); |
1311 | 1361 | ||
1312 | if (map->dso && strcmp(map->dso->name, name) == 0) | 1362 | if (map->dso && strcmp(map->dso->short_name, name) == 0) |
1313 | return map; | 1363 | return map; |
1314 | } | 1364 | } |
1315 | 1365 | ||
1316 | return NULL; | 1366 | return NULL; |
1317 | } | 1367 | } |
1318 | 1368 | ||
1319 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) | 1369 | static int dso__kernel_module_get_build_id(struct dso *self) |
1370 | { | ||
1371 | char filename[PATH_MAX]; | ||
1372 | /* | ||
1373 | * kernel module short names are of the form "[module]" and | ||
1374 | * we need just "module" here. | ||
1375 | */ | ||
1376 | const char *name = self->short_name + 1; | ||
1377 | |||
1378 | snprintf(filename, sizeof(filename), | ||
1379 | "/sys/module/%.*s/notes/.note.gnu.build-id", | ||
1380 | (int)strlen(name - 1), name); | ||
1381 | |||
1382 | if (sysfs__read_build_id(filename, self->build_id, | ||
1383 | sizeof(self->build_id)) == 0) | ||
1384 | self->has_build_id = true; | ||
1385 | |||
1386 | return 0; | ||
1387 | } | ||
1388 | |||
1389 | static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname) | ||
1320 | { | 1390 | { |
1321 | struct dirent *dent; | 1391 | struct dirent *dent; |
1322 | DIR *dir = opendir(dirname); | 1392 | DIR *dir = opendir(dirname); |
@@ -1336,7 +1406,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d | |||
1336 | 1406 | ||
1337 | snprintf(path, sizeof(path), "%s/%s", | 1407 | snprintf(path, sizeof(path), "%s/%s", |
1338 | dirname, dent->d_name); | 1408 | dirname, dent->d_name); |
1339 | if (perf_session__set_modules_path_dir(self, path) < 0) | 1409 | if (map_groups__set_modules_path_dir(self, path) < 0) |
1340 | goto failure; | 1410 | goto failure; |
1341 | } else { | 1411 | } else { |
1342 | char *dot = strrchr(dent->d_name, '.'), | 1412 | char *dot = strrchr(dent->d_name, '.'), |
@@ -1350,7 +1420,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d | |||
1350 | (int)(dot - dent->d_name), dent->d_name); | 1420 | (int)(dot - dent->d_name), dent->d_name); |
1351 | 1421 | ||
1352 | strxfrchar(dso_name, '-', '_'); | 1422 | strxfrchar(dso_name, '-', '_'); |
1353 | map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name); | 1423 | map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name); |
1354 | if (map == NULL) | 1424 | if (map == NULL) |
1355 | continue; | 1425 | continue; |
1356 | 1426 | ||
@@ -1361,6 +1431,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d | |||
1361 | if (long_name == NULL) | 1431 | if (long_name == NULL) |
1362 | goto failure; | 1432 | goto failure; |
1363 | dso__set_long_name(map->dso, long_name); | 1433 | dso__set_long_name(map->dso, long_name); |
1434 | dso__kernel_module_get_build_id(map->dso); | ||
1364 | } | 1435 | } |
1365 | } | 1436 | } |
1366 | 1437 | ||
@@ -1370,7 +1441,7 @@ failure: | |||
1370 | return -1; | 1441 | return -1; |
1371 | } | 1442 | } |
1372 | 1443 | ||
1373 | static int perf_session__set_modules_path(struct perf_session *self) | 1444 | static int map_groups__set_modules_path(struct map_groups *self) |
1374 | { | 1445 | { |
1375 | struct utsname uts; | 1446 | struct utsname uts; |
1376 | char modules_path[PATH_MAX]; | 1447 | char modules_path[PATH_MAX]; |
@@ -1381,7 +1452,7 @@ static int perf_session__set_modules_path(struct perf_session *self) | |||
1381 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", | 1452 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", |
1382 | uts.release); | 1453 | uts.release); |
1383 | 1454 | ||
1384 | return perf_session__set_modules_path_dir(self, modules_path); | 1455 | return map_groups__set_modules_path_dir(self, modules_path); |
1385 | } | 1456 | } |
1386 | 1457 | ||
1387 | /* | 1458 | /* |
@@ -1391,8 +1462,8 @@ static int perf_session__set_modules_path(struct perf_session *self) | |||
1391 | */ | 1462 | */ |
1392 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | 1463 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) |
1393 | { | 1464 | { |
1394 | struct map *self = malloc(sizeof(*self)); | 1465 | struct map *self = zalloc(sizeof(*self) + |
1395 | 1466 | (dso->kernel ? sizeof(struct kmap) : 0)); | |
1396 | if (self != NULL) { | 1467 | if (self != NULL) { |
1397 | /* | 1468 | /* |
1398 | * ->end will be filled after we load all the symbols | 1469 | * ->end will be filled after we load all the symbols |
@@ -1403,7 +1474,25 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
1403 | return self; | 1474 | return self; |
1404 | } | 1475 | } |
1405 | 1476 | ||
1406 | static int perf_session__create_module_maps(struct perf_session *self) | 1477 | struct map *map_groups__new_module(struct map_groups *self, u64 start, |
1478 | const char *filename) | ||
1479 | { | ||
1480 | struct map *map; | ||
1481 | struct dso *dso = __dsos__findnew(&dsos__kernel, filename); | ||
1482 | |||
1483 | if (dso == NULL) | ||
1484 | return NULL; | ||
1485 | |||
1486 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1487 | if (map == NULL) | ||
1488 | return NULL; | ||
1489 | |||
1490 | dso->origin = DSO__ORIG_KMODULE; | ||
1491 | map_groups__insert(self, map); | ||
1492 | return map; | ||
1493 | } | ||
1494 | |||
1495 | static int map_groups__create_modules(struct map_groups *self) | ||
1407 | { | 1496 | { |
1408 | char *line = NULL; | 1497 | char *line = NULL; |
1409 | size_t n; | 1498 | size_t n; |
@@ -1416,7 +1505,6 @@ static int perf_session__create_module_maps(struct perf_session *self) | |||
1416 | while (!feof(file)) { | 1505 | while (!feof(file)) { |
1417 | char name[PATH_MAX]; | 1506 | char name[PATH_MAX]; |
1418 | u64 start; | 1507 | u64 start; |
1419 | struct dso *dso; | ||
1420 | char *sep; | 1508 | char *sep; |
1421 | int line_len; | 1509 | int line_len; |
1422 | 1510 | ||
@@ -1442,32 +1530,16 @@ static int perf_session__create_module_maps(struct perf_session *self) | |||
1442 | *sep = '\0'; | 1530 | *sep = '\0'; |
1443 | 1531 | ||
1444 | snprintf(name, sizeof(name), "[%s]", line); | 1532 | snprintf(name, sizeof(name), "[%s]", line); |
1445 | dso = dso__new(name); | 1533 | map = map_groups__new_module(self, start, name); |
1446 | 1534 | if (map == NULL) | |
1447 | if (dso == NULL) | ||
1448 | goto out_delete_line; | ||
1449 | |||
1450 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1451 | if (map == NULL) { | ||
1452 | dso__delete(dso); | ||
1453 | goto out_delete_line; | 1535 | goto out_delete_line; |
1454 | } | 1536 | dso__kernel_module_get_build_id(map->dso); |
1455 | |||
1456 | snprintf(name, sizeof(name), | ||
1457 | "/sys/module/%s/notes/.note.gnu.build-id", line); | ||
1458 | if (sysfs__read_build_id(name, dso->build_id, | ||
1459 | sizeof(dso->build_id)) == 0) | ||
1460 | dso->has_build_id = true; | ||
1461 | |||
1462 | dso->origin = DSO__ORIG_KMODULE; | ||
1463 | map_groups__insert(&self->kmaps, map); | ||
1464 | dsos__add(&dsos__kernel, dso); | ||
1465 | } | 1537 | } |
1466 | 1538 | ||
1467 | free(line); | 1539 | free(line); |
1468 | fclose(file); | 1540 | fclose(file); |
1469 | 1541 | ||
1470 | return perf_session__set_modules_path(self); | 1542 | return map_groups__set_modules_path(self); |
1471 | 1543 | ||
1472 | out_delete_line: | 1544 | out_delete_line: |
1473 | free(line); | 1545 | free(line); |
@@ -1476,7 +1548,6 @@ out_failure: | |||
1476 | } | 1548 | } |
1477 | 1549 | ||
1478 | static int dso__load_vmlinux(struct dso *self, struct map *map, | 1550 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
1479 | struct perf_session *session, | ||
1480 | const char *vmlinux, symbol_filter_t filter) | 1551 | const char *vmlinux, symbol_filter_t filter) |
1481 | { | 1552 | { |
1482 | int err = -1, fd; | 1553 | int err = -1, fd; |
@@ -1510,51 +1581,124 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1510 | return -1; | 1581 | return -1; |
1511 | 1582 | ||
1512 | dso__set_loaded(self, map->type); | 1583 | dso__set_loaded(self, map->type); |
1513 | err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); | 1584 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0); |
1514 | close(fd); | 1585 | close(fd); |
1515 | 1586 | ||
1587 | if (err > 0) | ||
1588 | pr_debug("Using %s for symbols\n", vmlinux); | ||
1589 | |||
1590 | return err; | ||
1591 | } | ||
1592 | |||
1593 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
1594 | symbol_filter_t filter) | ||
1595 | { | ||
1596 | int i, err = 0; | ||
1597 | |||
1598 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1599 | vmlinux_path__nr_entries); | ||
1600 | |||
1601 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | ||
1602 | err = dso__load_vmlinux(self, map, vmlinux_path[i], filter); | ||
1603 | if (err > 0) { | ||
1604 | dso__set_long_name(self, strdup(vmlinux_path[i])); | ||
1605 | break; | ||
1606 | } | ||
1607 | } | ||
1608 | |||
1516 | return err; | 1609 | return err; |
1517 | } | 1610 | } |
1518 | 1611 | ||
1519 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 1612 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
1520 | struct perf_session *session, symbol_filter_t filter) | 1613 | symbol_filter_t filter) |
1521 | { | 1614 | { |
1522 | int err; | 1615 | int err; |
1523 | bool is_kallsyms; | 1616 | const char *kallsyms_filename = NULL; |
1617 | char *kallsyms_allocated_filename = NULL; | ||
1618 | /* | ||
1619 | * Step 1: if the user specified a vmlinux filename, use it and only | ||
1620 | * it, reporting errors to the user if it cannot be used. | ||
1621 | * | ||
1622 | * For instance, try to analyse an ARM perf.data file _without_ a | ||
1623 | * build-id, or if the user specifies the wrong path to the right | ||
1624 | * vmlinux file, obviously we can't fallback to another vmlinux (a | ||
1625 | * x86_86 one, on the machine where analysis is being performed, say), | ||
1626 | * or worse, /proc/kallsyms. | ||
1627 | * | ||
1628 | * If the specified file _has_ a build-id and there is a build-id | ||
1629 | * section in the perf.data file, we will still do the expected | ||
1630 | * validation in dso__load_vmlinux and will bail out if they don't | ||
1631 | * match. | ||
1632 | */ | ||
1633 | if (symbol_conf.vmlinux_name != NULL) { | ||
1634 | err = dso__load_vmlinux(self, map, | ||
1635 | symbol_conf.vmlinux_name, filter); | ||
1636 | goto out_try_fixup; | ||
1637 | } | ||
1524 | 1638 | ||
1525 | if (vmlinux_path != NULL) { | 1639 | if (vmlinux_path != NULL) { |
1526 | int i; | 1640 | err = dso__load_vmlinux_path(self, map, filter); |
1527 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1641 | if (err > 0) |
1528 | vmlinux_path__nr_entries); | 1642 | goto out_fixup; |
1529 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1643 | } |
1530 | err = dso__load_vmlinux(self, map, session, | 1644 | |
1531 | vmlinux_path[i], filter); | 1645 | /* |
1532 | if (err > 0) { | 1646 | * Say the kernel DSO was created when processing the build-id header table, |
1533 | pr_debug("Using %s for symbols\n", | 1647 | * we have a build-id, so check if it is the same as the running kernel, |
1534 | vmlinux_path[i]); | 1648 | * using it if it is. |
1535 | dso__set_long_name(self, | 1649 | */ |
1536 | strdup(vmlinux_path[i])); | 1650 | if (self->has_build_id) { |
1537 | goto out_fixup; | 1651 | u8 kallsyms_build_id[BUILD_ID_SIZE]; |
1652 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1653 | |||
1654 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
1655 | sizeof(kallsyms_build_id)) == 0) { | ||
1656 | if (dso__build_id_equal(self, kallsyms_build_id)) { | ||
1657 | kallsyms_filename = "/proc/kallsyms"; | ||
1658 | goto do_kallsyms; | ||
1538 | } | 1659 | } |
1539 | } | 1660 | } |
1540 | } | 1661 | /* |
1662 | * Now look if we have it on the build-id cache in | ||
1663 | * $HOME/.debug/[kernel.kallsyms]. | ||
1664 | */ | ||
1665 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1666 | sbuild_id); | ||
1541 | 1667 | ||
1542 | is_kallsyms = self->long_name[0] == '['; | 1668 | if (asprintf(&kallsyms_allocated_filename, |
1543 | if (is_kallsyms) | 1669 | "%s/.debug/[kernel.kallsyms]/%s", |
1544 | goto do_kallsyms; | 1670 | getenv("HOME"), sbuild_id) == -1) { |
1671 | pr_err("Not enough memory for kallsyms file lookup\n"); | ||
1672 | return -1; | ||
1673 | } | ||
1545 | 1674 | ||
1546 | err = dso__load_vmlinux(self, map, session, self->long_name, filter); | 1675 | kallsyms_filename = kallsyms_allocated_filename; |
1547 | if (err <= 0) { | 1676 | |
1548 | pr_info("The file %s cannot be used, " | 1677 | if (access(kallsyms_filename, F_OK)) { |
1549 | "trying to use /proc/kallsyms...", self->long_name); | 1678 | pr_err("No kallsyms or vmlinux with build-id %s " |
1550 | do_kallsyms: | 1679 | "was found\n", sbuild_id); |
1551 | err = dso__load_kallsyms(self, map, session, filter); | 1680 | free(kallsyms_allocated_filename); |
1552 | if (err > 0 && !is_kallsyms) | 1681 | return -1; |
1553 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | 1682 | } |
1683 | } else { | ||
1684 | /* | ||
1685 | * Last resort, if we don't have a build-id and couldn't find | ||
1686 | * any vmlinux file, try the running kernel kallsyms table. | ||
1687 | */ | ||
1688 | kallsyms_filename = "/proc/kallsyms"; | ||
1554 | } | 1689 | } |
1555 | 1690 | ||
1691 | do_kallsyms: | ||
1692 | err = dso__load_kallsyms(self, kallsyms_filename, map, filter); | ||
1693 | if (err > 0) | ||
1694 | pr_debug("Using %s for symbols\n", kallsyms_filename); | ||
1695 | free(kallsyms_allocated_filename); | ||
1696 | |||
1697 | out_try_fixup: | ||
1556 | if (err > 0) { | 1698 | if (err > 0) { |
1557 | out_fixup: | 1699 | out_fixup: |
1700 | if (kallsyms_filename != NULL) | ||
1701 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | ||
1558 | map__fixup_start(map); | 1702 | map__fixup_start(map); |
1559 | map__fixup_end(map); | 1703 | map__fixup_end(map); |
1560 | } | 1704 | } |
@@ -1564,7 +1708,6 @@ out_fixup: | |||
1564 | 1708 | ||
1565 | LIST_HEAD(dsos__user); | 1709 | LIST_HEAD(dsos__user); |
1566 | LIST_HEAD(dsos__kernel); | 1710 | LIST_HEAD(dsos__kernel); |
1567 | struct dso *vdso; | ||
1568 | 1711 | ||
1569 | static void dsos__add(struct list_head *head, struct dso *dso) | 1712 | static void dsos__add(struct list_head *head, struct dso *dso) |
1570 | { | 1713 | { |
@@ -1576,19 +1719,19 @@ static struct dso *dsos__find(struct list_head *head, const char *name) | |||
1576 | struct dso *pos; | 1719 | struct dso *pos; |
1577 | 1720 | ||
1578 | list_for_each_entry(pos, head, node) | 1721 | list_for_each_entry(pos, head, node) |
1579 | if (strcmp(pos->name, name) == 0) | 1722 | if (strcmp(pos->long_name, name) == 0) |
1580 | return pos; | 1723 | return pos; |
1581 | return NULL; | 1724 | return NULL; |
1582 | } | 1725 | } |
1583 | 1726 | ||
1584 | struct dso *dsos__findnew(const char *name) | 1727 | struct dso *__dsos__findnew(struct list_head *head, const char *name) |
1585 | { | 1728 | { |
1586 | struct dso *dso = dsos__find(&dsos__user, name); | 1729 | struct dso *dso = dsos__find(head, name); |
1587 | 1730 | ||
1588 | if (!dso) { | 1731 | if (!dso) { |
1589 | dso = dso__new(name); | 1732 | dso = dso__new(name); |
1590 | if (dso != NULL) { | 1733 | if (dso != NULL) { |
1591 | dsos__add(&dsos__user, dso); | 1734 | dsos__add(head, dso); |
1592 | dso__set_basename(dso); | 1735 | dso__set_basename(dso); |
1593 | } | 1736 | } |
1594 | } | 1737 | } |
@@ -1613,75 +1756,78 @@ void dsos__fprintf(FILE *fp) | |||
1613 | __dsos__fprintf(&dsos__user, fp); | 1756 | __dsos__fprintf(&dsos__user, fp); |
1614 | } | 1757 | } |
1615 | 1758 | ||
1616 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp) | 1759 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
1760 | bool with_hits) | ||
1617 | { | 1761 | { |
1618 | struct dso *pos; | 1762 | struct dso *pos; |
1619 | size_t ret = 0; | 1763 | size_t ret = 0; |
1620 | 1764 | ||
1621 | list_for_each_entry(pos, head, node) { | 1765 | list_for_each_entry(pos, head, node) { |
1766 | if (with_hits && !pos->hit) | ||
1767 | continue; | ||
1622 | ret += dso__fprintf_buildid(pos, fp); | 1768 | ret += dso__fprintf_buildid(pos, fp); |
1623 | ret += fprintf(fp, " %s\n", pos->long_name); | 1769 | ret += fprintf(fp, " %s\n", pos->long_name); |
1624 | } | 1770 | } |
1625 | return ret; | 1771 | return ret; |
1626 | } | 1772 | } |
1627 | 1773 | ||
1628 | size_t dsos__fprintf_buildid(FILE *fp) | 1774 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) |
1629 | { | 1775 | { |
1630 | return (__dsos__fprintf_buildid(&dsos__kernel, fp) + | 1776 | return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + |
1631 | __dsos__fprintf_buildid(&dsos__user, fp)); | 1777 | __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); |
1632 | } | 1778 | } |
1633 | 1779 | ||
1634 | static struct dso *dsos__create_kernel( const char *vmlinux) | 1780 | struct dso *dso__new_kernel(const char *name) |
1635 | { | 1781 | { |
1636 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); | 1782 | struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); |
1637 | 1783 | ||
1638 | if (kernel == NULL) | 1784 | if (self != NULL) { |
1639 | return NULL; | 1785 | self->short_name = "[kernel]"; |
1786 | self->kernel = 1; | ||
1787 | } | ||
1640 | 1788 | ||
1641 | kernel->short_name = "[kernel]"; | 1789 | return self; |
1642 | kernel->kernel = 1; | 1790 | } |
1643 | 1791 | ||
1644 | vdso = dso__new("[vdso]"); | 1792 | void dso__read_running_kernel_build_id(struct dso *self) |
1645 | if (vdso == NULL) | 1793 | { |
1646 | goto out_delete_kernel_dso; | 1794 | if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, |
1647 | dso__set_loaded(vdso, MAP__FUNCTION); | 1795 | sizeof(self->build_id)) == 0) |
1796 | self->has_build_id = true; | ||
1797 | } | ||
1648 | 1798 | ||
1649 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, | 1799 | static struct dso *dsos__create_kernel(const char *vmlinux) |
1650 | sizeof(kernel->build_id)) == 0) | 1800 | { |
1651 | kernel->has_build_id = true; | 1801 | struct dso *kernel = dso__new_kernel(vmlinux); |
1652 | 1802 | ||
1653 | dsos__add(&dsos__kernel, kernel); | 1803 | if (kernel != NULL) { |
1654 | dsos__add(&dsos__user, vdso); | 1804 | dso__read_running_kernel_build_id(kernel); |
1805 | dsos__add(&dsos__kernel, kernel); | ||
1806 | } | ||
1655 | 1807 | ||
1656 | return kernel; | 1808 | return kernel; |
1657 | |||
1658 | out_delete_kernel_dso: | ||
1659 | dso__delete(kernel); | ||
1660 | return NULL; | ||
1661 | } | 1809 | } |
1662 | 1810 | ||
1663 | static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux) | 1811 | int __map_groups__create_kernel_maps(struct map_groups *self, |
1812 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
1813 | struct dso *kernel) | ||
1664 | { | 1814 | { |
1665 | struct map *functions, *variables; | 1815 | enum map_type type; |
1666 | struct dso *kernel = dsos__create_kernel(vmlinux); | ||
1667 | 1816 | ||
1668 | if (kernel == NULL) | 1817 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
1669 | return -1; | 1818 | struct kmap *kmap; |
1670 | 1819 | ||
1671 | functions = map__new2(0, kernel, MAP__FUNCTION); | 1820 | vmlinux_maps[type] = map__new2(0, kernel, type); |
1672 | if (functions == NULL) | 1821 | if (vmlinux_maps[type] == NULL) |
1673 | return -1; | 1822 | return -1; |
1674 | 1823 | ||
1675 | variables = map__new2(0, kernel, MAP__VARIABLE); | 1824 | vmlinux_maps[type]->map_ip = |
1676 | if (variables == NULL) { | 1825 | vmlinux_maps[type]->unmap_ip = identity__map_ip; |
1677 | map__delete(functions); | ||
1678 | return -1; | ||
1679 | } | ||
1680 | 1826 | ||
1681 | functions->map_ip = functions->unmap_ip = | 1827 | kmap = map__kmap(vmlinux_maps[type]); |
1682 | variables->map_ip = variables->unmap_ip = identity__map_ip; | 1828 | kmap->kmaps = self; |
1683 | map_groups__insert(self, functions); | 1829 | map_groups__insert(self, vmlinux_maps[type]); |
1684 | map_groups__insert(self, variables); | 1830 | } |
1685 | 1831 | ||
1686 | return 0; | 1832 | return 0; |
1687 | } | 1833 | } |
@@ -1791,19 +1937,22 @@ out_free_comm_list: | |||
1791 | return -1; | 1937 | return -1; |
1792 | } | 1938 | } |
1793 | 1939 | ||
1794 | int perf_session__create_kernel_maps(struct perf_session *self) | 1940 | int map_groups__create_kernel_maps(struct map_groups *self, |
1941 | struct map *vmlinux_maps[MAP__NR_TYPES]) | ||
1795 | { | 1942 | { |
1796 | if (map_groups__create_kernel_maps(&self->kmaps, | 1943 | struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); |
1797 | symbol_conf.vmlinux_name) < 0) | 1944 | |
1945 | if (kernel == NULL) | ||
1946 | return -1; | ||
1947 | |||
1948 | if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) | ||
1798 | return -1; | 1949 | return -1; |
1799 | 1950 | ||
1800 | if (symbol_conf.use_modules && | 1951 | if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) |
1801 | perf_session__create_module_maps(self) < 0) | 1952 | pr_debug("Problems creating module maps, continuing anyway...\n"); |
1802 | pr_debug("Failed to load list of modules for session %s, " | ||
1803 | "continuing...\n", self->filename); | ||
1804 | /* | 1953 | /* |
1805 | * Now that we have all the maps created, just set the ->end of them: | 1954 | * Now that we have all the maps created, just set the ->end of them: |
1806 | */ | 1955 | */ |
1807 | map_groups__fixup_end(&self->kmaps); | 1956 | map_groups__fixup_end(self); |
1808 | return 0; | 1957 | return 0; |
1809 | } | 1958 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 8aded2356f79..280dadd32a08 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -8,6 +8,8 @@ | |||
8 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
9 | #include "event.h" | 9 | #include "event.h" |
10 | 10 | ||
11 | #define DEBUG_CACHE_DIR ".debug" | ||
12 | |||
11 | #ifdef HAVE_CPLUS_DEMANGLE | 13 | #ifdef HAVE_CPLUS_DEMANGLE |
12 | extern char *cplus_demangle(const char *, int); | 14 | extern char *cplus_demangle(const char *, int); |
13 | 15 | ||
@@ -49,6 +51,8 @@ struct symbol { | |||
49 | char name[0]; | 51 | char name[0]; |
50 | }; | 52 | }; |
51 | 53 | ||
54 | void symbol__delete(struct symbol *self); | ||
55 | |||
52 | struct strlist; | 56 | struct strlist; |
53 | 57 | ||
54 | struct symbol_conf { | 58 | struct symbol_conf { |
@@ -58,7 +62,8 @@ struct symbol_conf { | |||
58 | sort_by_name, | 62 | sort_by_name, |
59 | show_nr_samples, | 63 | show_nr_samples, |
60 | use_callchain, | 64 | use_callchain, |
61 | exclude_other; | 65 | exclude_other, |
66 | full_paths; | ||
62 | const char *vmlinux_name, | 67 | const char *vmlinux_name, |
63 | *field_sep; | 68 | *field_sep; |
64 | char *dso_list_str, | 69 | char *dso_list_str, |
@@ -77,6 +82,12 @@ static inline void *symbol__priv(struct symbol *self) | |||
77 | return ((void *)self) - symbol_conf.priv_size; | 82 | return ((void *)self) - symbol_conf.priv_size; |
78 | } | 83 | } |
79 | 84 | ||
85 | struct ref_reloc_sym { | ||
86 | const char *name; | ||
87 | u64 addr; | ||
88 | u64 unrelocated_addr; | ||
89 | }; | ||
90 | |||
80 | struct addr_location { | 91 | struct addr_location { |
81 | struct thread *thread; | 92 | struct thread *thread; |
82 | struct map *map; | 93 | struct map *map; |
@@ -94,6 +105,7 @@ struct dso { | |||
94 | u8 slen_calculated:1; | 105 | u8 slen_calculated:1; |
95 | u8 has_build_id:1; | 106 | u8 has_build_id:1; |
96 | u8 kernel:1; | 107 | u8 kernel:1; |
108 | u8 hit:1; | ||
97 | unsigned char origin; | 109 | unsigned char origin; |
98 | u8 sorted_by_name; | 110 | u8 sorted_by_name; |
99 | u8 loaded; | 111 | u8 loaded; |
@@ -105,37 +117,55 @@ struct dso { | |||
105 | }; | 117 | }; |
106 | 118 | ||
107 | struct dso *dso__new(const char *name); | 119 | struct dso *dso__new(const char *name); |
120 | struct dso *dso__new_kernel(const char *name); | ||
108 | void dso__delete(struct dso *self); | 121 | void dso__delete(struct dso *self); |
109 | 122 | ||
110 | bool dso__loaded(const struct dso *self, enum map_type type); | 123 | bool dso__loaded(const struct dso *self, enum map_type type); |
111 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | 124 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); |
112 | 125 | ||
126 | static inline void dso__set_loaded(struct dso *self, enum map_type type) | ||
127 | { | ||
128 | self->loaded |= (1 << type); | ||
129 | } | ||
130 | |||
113 | void dso__sort_by_name(struct dso *self, enum map_type type); | 131 | void dso__sort_by_name(struct dso *self, enum map_type type); |
114 | 132 | ||
115 | struct perf_session; | 133 | extern struct list_head dsos__user, dsos__kernel; |
134 | |||
135 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | ||
136 | |||
137 | static inline struct dso *dsos__findnew(const char *name) | ||
138 | { | ||
139 | return __dsos__findnew(&dsos__user, name); | ||
140 | } | ||
116 | 141 | ||
117 | struct dso *dsos__findnew(const char *name); | 142 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); |
118 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, | 143 | int dso__load_vmlinux_path(struct dso *self, struct map *map, |
119 | symbol_filter_t filter); | 144 | symbol_filter_t filter); |
145 | int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, | ||
146 | symbol_filter_t filter); | ||
120 | void dsos__fprintf(FILE *fp); | 147 | void dsos__fprintf(FILE *fp); |
121 | size_t dsos__fprintf_buildid(FILE *fp); | 148 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); |
122 | 149 | ||
123 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | 150 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
124 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 151 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
125 | char dso__symtab_origin(const struct dso *self); | 152 | char dso__symtab_origin(const struct dso *self); |
153 | void dso__set_long_name(struct dso *self, char *name); | ||
126 | void dso__set_build_id(struct dso *self, void *build_id); | 154 | void dso__set_build_id(struct dso *self, void *build_id); |
155 | void dso__read_running_kernel_build_id(struct dso *self); | ||
127 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); | 156 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); |
128 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | 157 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, |
129 | const char *name); | 158 | const char *name); |
130 | 159 | ||
131 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 160 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
132 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 161 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
133 | bool dsos__read_build_ids(void); | 162 | bool dsos__read_build_ids(bool with_hits); |
134 | int build_id__sprintf(u8 *self, int len, char *bf); | 163 | int build_id__sprintf(const u8 *self, int len, char *bf); |
164 | int kallsyms__parse(const char *filename, void *arg, | ||
165 | int (*process_symbol)(void *arg, const char *name, | ||
166 | char type, u64 start)); | ||
135 | 167 | ||
136 | int symbol__init(void); | 168 | int symbol__init(void); |
137 | int perf_session__create_kernel_maps(struct perf_session *self); | 169 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
138 | 170 | ||
139 | extern struct list_head dsos__user, dsos__kernel; | ||
140 | extern struct dso *vdso; | ||
141 | #endif /* __PERF_SYMBOL */ | 171 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 4a08dcf50b68..21b92162282b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -31,12 +31,41 @@ static struct thread *thread__new(pid_t pid) | |||
31 | return self; | 31 | return self; |
32 | } | 32 | } |
33 | 33 | ||
34 | static void map_groups__flush(struct map_groups *self) | ||
35 | { | ||
36 | int type; | ||
37 | |||
38 | for (type = 0; type < MAP__NR_TYPES; type++) { | ||
39 | struct rb_root *root = &self->maps[type]; | ||
40 | struct rb_node *next = rb_first(root); | ||
41 | |||
42 | while (next) { | ||
43 | struct map *pos = rb_entry(next, struct map, rb_node); | ||
44 | next = rb_next(&pos->rb_node); | ||
45 | rb_erase(&pos->rb_node, root); | ||
46 | /* | ||
47 | * We may have references to this map, for | ||
48 | * instance in some hist_entry instances, so | ||
49 | * just move them to a separate list. | ||
50 | */ | ||
51 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
34 | int thread__set_comm(struct thread *self, const char *comm) | 56 | int thread__set_comm(struct thread *self, const char *comm) |
35 | { | 57 | { |
58 | int err; | ||
59 | |||
36 | if (self->comm) | 60 | if (self->comm) |
37 | free(self->comm); | 61 | free(self->comm); |
38 | self->comm = strdup(comm); | 62 | self->comm = strdup(comm); |
39 | return self->comm ? 0 : -ENOMEM; | 63 | err = self->comm == NULL ? -ENOMEM : 0; |
64 | if (!err) { | ||
65 | self->comm_set = true; | ||
66 | map_groups__flush(&self->mg); | ||
67 | } | ||
68 | return err; | ||
40 | } | 69 | } |
41 | 70 | ||
42 | int thread__comm_len(struct thread *self) | 71 | int thread__comm_len(struct thread *self) |
@@ -50,11 +79,6 @@ int thread__comm_len(struct thread *self) | |||
50 | return self->comm_len; | 79 | return self->comm_len; |
51 | } | 80 | } |
52 | 81 | ||
53 | static const char *map_type__name[MAP__NR_TYPES] = { | ||
54 | [MAP__FUNCTION] = "Functions", | ||
55 | [MAP__VARIABLE] = "Variables", | ||
56 | }; | ||
57 | |||
58 | static size_t __map_groups__fprintf_maps(struct map_groups *self, | 82 | static size_t __map_groups__fprintf_maps(struct map_groups *self, |
59 | enum map_type type, FILE *fp) | 83 | enum map_type type, FILE *fp) |
60 | { | 84 | { |
@@ -255,11 +279,14 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
255 | { | 279 | { |
256 | int i; | 280 | int i; |
257 | 281 | ||
258 | if (self->comm) | 282 | if (parent->comm_set) { |
259 | free(self->comm); | 283 | if (self->comm) |
260 | self->comm = strdup(parent->comm); | 284 | free(self->comm); |
261 | if (!self->comm) | 285 | self->comm = strdup(parent->comm); |
262 | return -ENOMEM; | 286 | if (!self->comm) |
287 | return -ENOMEM; | ||
288 | self->comm_set = true; | ||
289 | } | ||
263 | 290 | ||
264 | for (i = 0; i < MAP__NR_TYPES; ++i) | 291 | for (i = 0; i < MAP__NR_TYPES; ++i) |
265 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | 292 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) |
@@ -282,14 +309,13 @@ size_t perf_session__fprintf(struct perf_session *self, FILE *fp) | |||
282 | } | 309 | } |
283 | 310 | ||
284 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 311 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
285 | struct perf_session *session, | ||
286 | enum map_type type, u64 addr, | 312 | enum map_type type, u64 addr, |
287 | symbol_filter_t filter) | 313 | symbol_filter_t filter) |
288 | { | 314 | { |
289 | struct map *map = map_groups__find(self, type, addr); | 315 | struct map *map = map_groups__find(self, type, addr); |
290 | 316 | ||
291 | if (map != NULL) | 317 | if (map != NULL) |
292 | return map__find_symbol(map, session, map->map_ip(map, addr), filter); | 318 | return map__find_symbol(map, map->map_ip(map, addr), filter); |
293 | 319 | ||
294 | return NULL; | 320 | return NULL; |
295 | } | 321 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index c206f72c8881..0a28f39de545 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -15,6 +15,7 @@ struct thread { | |||
15 | struct map_groups mg; | 15 | struct map_groups mg; |
16 | pid_t pid; | 16 | pid_t pid; |
17 | char shortname[3]; | 17 | char shortname[3]; |
18 | bool comm_set; | ||
18 | char *comm; | 19 | char *comm; |
19 | int comm_len; | 20 | int comm_len; |
20 | }; | 21 | }; |
@@ -48,23 +49,36 @@ static inline struct map *thread__find_map(struct thread *self, | |||
48 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | 49 | return self ? map_groups__find(&self->mg, type, addr) : NULL; |
49 | } | 50 | } |
50 | 51 | ||
52 | void thread__find_addr_map(struct thread *self, | ||
53 | struct perf_session *session, u8 cpumode, | ||
54 | enum map_type type, u64 addr, | ||
55 | struct addr_location *al); | ||
56 | |||
51 | void thread__find_addr_location(struct thread *self, | 57 | void thread__find_addr_location(struct thread *self, |
52 | struct perf_session *session, u8 cpumode, | 58 | struct perf_session *session, u8 cpumode, |
53 | enum map_type type, u64 addr, | 59 | enum map_type type, u64 addr, |
54 | struct addr_location *al, | 60 | struct addr_location *al, |
55 | symbol_filter_t filter); | 61 | symbol_filter_t filter); |
56 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 62 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
57 | struct perf_session *session, | ||
58 | enum map_type type, u64 addr, | 63 | enum map_type type, u64 addr, |
59 | symbol_filter_t filter); | 64 | symbol_filter_t filter); |
60 | 65 | ||
61 | static inline struct symbol * | 66 | static inline struct symbol *map_groups__find_function(struct map_groups *self, |
62 | map_groups__find_function(struct map_groups *self, struct perf_session *session, | 67 | u64 addr, |
63 | u64 addr, symbol_filter_t filter) | 68 | symbol_filter_t filter) |
64 | { | 69 | { |
65 | return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter); | 70 | return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); |
66 | } | 71 | } |
67 | 72 | ||
68 | struct map *map_groups__find_by_name(struct map_groups *self, | 73 | struct map *map_groups__find_by_name(struct map_groups *self, |
69 | enum map_type type, const char *name); | 74 | enum map_type type, const char *name); |
75 | |||
76 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
77 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
78 | struct dso *kernel); | ||
79 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
80 | struct map *vmlinux_maps[MAP__NR_TYPES]); | ||
81 | |||
82 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | ||
83 | const char *filename); | ||
70 | #endif /* __PERF_THREAD_H */ | 84 | #endif /* __PERF_THREAD_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index cace35595530..5ea8973ad331 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | #define _GNU_SOURCE | 21 | #define _GNU_SOURCE |
22 | #include <dirent.h> | 22 | #include <dirent.h> |
23 | #include <mntent.h> | ||
23 | #include <stdio.h> | 24 | #include <stdio.h> |
24 | #include <stdlib.h> | 25 | #include <stdlib.h> |
25 | #include <string.h> | 26 | #include <string.h> |
@@ -37,6 +38,7 @@ | |||
37 | 38 | ||
38 | #include "../perf.h" | 39 | #include "../perf.h" |
39 | #include "trace-event.h" | 40 | #include "trace-event.h" |
41 | #include "debugfs.h" | ||
40 | 42 | ||
41 | #define VERSION "0.5" | 43 | #define VERSION "0.5" |
42 | 44 | ||
@@ -101,32 +103,12 @@ void *malloc_or_die(unsigned int size) | |||
101 | 103 | ||
102 | static const char *find_debugfs(void) | 104 | static const char *find_debugfs(void) |
103 | { | 105 | { |
104 | static char debugfs[MAX_PATH+1]; | 106 | const char *path = debugfs_mount(NULL); |
105 | static int debugfs_found; | ||
106 | char type[100]; | ||
107 | FILE *fp; | ||
108 | |||
109 | if (debugfs_found) | ||
110 | return debugfs; | ||
111 | |||
112 | if ((fp = fopen("/proc/mounts","r")) == NULL) | ||
113 | die("Can't open /proc/mounts for read"); | ||
114 | |||
115 | while (fscanf(fp, "%*s %" | ||
116 | STR(MAX_PATH) | ||
117 | "s %99s %*s %*d %*d\n", | ||
118 | debugfs, type) == 2) { | ||
119 | if (strcmp(type, "debugfs") == 0) | ||
120 | break; | ||
121 | } | ||
122 | fclose(fp); | ||
123 | |||
124 | if (strcmp(type, "debugfs") != 0) | ||
125 | die("debugfs not mounted, please mount"); | ||
126 | 107 | ||
127 | debugfs_found = 1; | 108 | if (!path) |
109 | die("Your kernel not support debugfs filesystem"); | ||
128 | 110 | ||
129 | return debugfs; | 111 | return path; |
130 | } | 112 | } |
131 | 113 | ||
132 | /* | 114 | /* |
@@ -271,6 +253,8 @@ static void read_header_files(void) | |||
271 | write_or_die("header_page", 12); | 253 | write_or_die("header_page", 12); |
272 | write_or_die(&size, 8); | 254 | write_or_die(&size, 8); |
273 | check_size = copy_file_fd(fd); | 255 | check_size = copy_file_fd(fd); |
256 | close(fd); | ||
257 | |||
274 | if (size != check_size) | 258 | if (size != check_size) |
275 | die("wrong size for '%s' size=%lld read=%lld", | 259 | die("wrong size for '%s' size=%lld read=%lld", |
276 | path, size, check_size); | 260 | path, size, check_size); |
@@ -289,6 +273,7 @@ static void read_header_files(void) | |||
289 | if (size != check_size) | 273 | if (size != check_size) |
290 | die("wrong size for '%s'", path); | 274 | die("wrong size for '%s'", path); |
291 | put_tracing_file(path); | 275 | put_tracing_file(path); |
276 | close(fd); | ||
292 | } | 277 | } |
293 | 278 | ||
294 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 279 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -317,7 +302,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
317 | die("can't read directory '%s'", sys); | 302 | die("can't read directory '%s'", sys); |
318 | 303 | ||
319 | while ((dent = readdir(dir))) { | 304 | while ((dent = readdir(dir))) { |
320 | if (strcmp(dent->d_name, ".") == 0 || | 305 | if (dent->d_type != DT_DIR || |
306 | strcmp(dent->d_name, ".") == 0 || | ||
321 | strcmp(dent->d_name, "..") == 0 || | 307 | strcmp(dent->d_name, "..") == 0 || |
322 | !name_in_tp_list(dent->d_name, tps)) | 308 | !name_in_tp_list(dent->d_name, tps)) |
323 | continue; | 309 | continue; |
@@ -334,7 +320,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
334 | 320 | ||
335 | rewinddir(dir); | 321 | rewinddir(dir); |
336 | while ((dent = readdir(dir))) { | 322 | while ((dent = readdir(dir))) { |
337 | if (strcmp(dent->d_name, ".") == 0 || | 323 | if (dent->d_type != DT_DIR || |
324 | strcmp(dent->d_name, ".") == 0 || | ||
338 | strcmp(dent->d_name, "..") == 0 || | 325 | strcmp(dent->d_name, "..") == 0 || |
339 | !name_in_tp_list(dent->d_name, tps)) | 326 | !name_in_tp_list(dent->d_name, tps)) |
340 | continue; | 327 | continue; |
@@ -353,6 +340,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
353 | 340 | ||
354 | free(format); | 341 | free(format); |
355 | } | 342 | } |
343 | closedir(dir); | ||
356 | } | 344 | } |
357 | 345 | ||
358 | static void read_ftrace_files(struct tracepoint_path *tps) | 346 | static void read_ftrace_files(struct tracepoint_path *tps) |
@@ -394,26 +382,21 @@ static void read_event_files(struct tracepoint_path *tps) | |||
394 | die("can't read directory '%s'", path); | 382 | die("can't read directory '%s'", path); |
395 | 383 | ||
396 | while ((dent = readdir(dir))) { | 384 | while ((dent = readdir(dir))) { |
397 | if (strcmp(dent->d_name, ".") == 0 || | 385 | if (dent->d_type != DT_DIR || |
386 | strcmp(dent->d_name, ".") == 0 || | ||
398 | strcmp(dent->d_name, "..") == 0 || | 387 | strcmp(dent->d_name, "..") == 0 || |
399 | strcmp(dent->d_name, "ftrace") == 0 || | 388 | strcmp(dent->d_name, "ftrace") == 0 || |
400 | !system_in_tp_list(dent->d_name, tps)) | 389 | !system_in_tp_list(dent->d_name, tps)) |
401 | continue; | 390 | continue; |
402 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | 391 | count++; |
403 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
404 | ret = stat(sys, &st); | ||
405 | free(sys); | ||
406 | if (ret < 0) | ||
407 | continue; | ||
408 | if (S_ISDIR(st.st_mode)) | ||
409 | count++; | ||
410 | } | 392 | } |
411 | 393 | ||
412 | write_or_die(&count, 4); | 394 | write_or_die(&count, 4); |
413 | 395 | ||
414 | rewinddir(dir); | 396 | rewinddir(dir); |
415 | while ((dent = readdir(dir))) { | 397 | while ((dent = readdir(dir))) { |
416 | if (strcmp(dent->d_name, ".") == 0 || | 398 | if (dent->d_type != DT_DIR || |
399 | strcmp(dent->d_name, ".") == 0 || | ||
417 | strcmp(dent->d_name, "..") == 0 || | 400 | strcmp(dent->d_name, "..") == 0 || |
418 | strcmp(dent->d_name, "ftrace") == 0 || | 401 | strcmp(dent->d_name, "ftrace") == 0 || |
419 | !system_in_tp_list(dent->d_name, tps)) | 402 | !system_in_tp_list(dent->d_name, tps)) |
@@ -422,14 +405,13 @@ static void read_event_files(struct tracepoint_path *tps) | |||
422 | sprintf(sys, "%s/%s", path, dent->d_name); | 405 | sprintf(sys, "%s/%s", path, dent->d_name); |
423 | ret = stat(sys, &st); | 406 | ret = stat(sys, &st); |
424 | if (ret >= 0) { | 407 | if (ret >= 0) { |
425 | if (S_ISDIR(st.st_mode)) { | 408 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); |
426 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | 409 | copy_event_system(sys, tps); |
427 | copy_event_system(sys, tps); | ||
428 | } | ||
429 | } | 410 | } |
430 | free(sys); | 411 | free(sys); |
431 | } | 412 | } |
432 | 413 | ||
414 | closedir(dir); | ||
433 | put_tracing_file(path); | 415 | put_tracing_file(path); |
434 | } | 416 | } |
435 | 417 | ||
@@ -533,7 +515,7 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | |||
533 | write_or_die(buf, 1); | 515 | write_or_die(buf, 1); |
534 | 516 | ||
535 | /* save page_size */ | 517 | /* save page_size */ |
536 | page_size = getpagesize(); | 518 | page_size = sysconf(_SC_PAGESIZE); |
537 | write_or_die(&page_size, 4); | 519 | write_or_die(&page_size, 4); |
538 | 520 | ||
539 | read_header_files(); | 521 | read_header_files(); |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index c5c32be040bf..9b3c20f42f98 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -1925,6 +1925,15 @@ void *raw_field_ptr(struct event *event, const char *name, void *data) | |||
1925 | if (!field) | 1925 | if (!field) |
1926 | return NULL; | 1926 | return NULL; |
1927 | 1927 | ||
1928 | if (field->flags & FIELD_IS_STRING) { | ||
1929 | int offset; | ||
1930 | |||
1931 | offset = *(int *)(data + field->offset); | ||
1932 | offset &= 0xffff; | ||
1933 | |||
1934 | return data + offset; | ||
1935 | } | ||
1936 | |||
1928 | return data + field->offset; | 1937 | return data + field->offset; |
1929 | } | 1938 | } |
1930 | 1939 | ||
@@ -3277,3 +3286,18 @@ void parse_set_info(int nr_cpus, int long_sz) | |||
3277 | cpus = nr_cpus; | 3286 | cpus = nr_cpus; |
3278 | long_size = long_sz; | 3287 | long_size = long_sz; |
3279 | } | 3288 | } |
3289 | |||
3290 | int common_pc(struct scripting_context *context) | ||
3291 | { | ||
3292 | return parse_common_pc(context->event_data); | ||
3293 | } | ||
3294 | |||
3295 | int common_flags(struct scripting_context *context) | ||
3296 | { | ||
3297 | return parse_common_flags(context->event_data); | ||
3298 | } | ||
3299 | |||
3300 | int common_lock_depth(struct scripting_context *context) | ||
3301 | { | ||
3302 | return parse_common_lock_depth(context->event_data); | ||
3303 | } | ||
diff --git a/tools/perf/util/trace-event-perl.h b/tools/perf/util/trace-event-perl.h deleted file mode 100644 index e88fb26137bb..000000000000 --- a/tools/perf/util/trace-event-perl.h +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | #ifndef __PERF_TRACE_EVENT_PERL_H | ||
2 | #define __PERF_TRACE_EVENT_PERL_H | ||
3 | #ifdef NO_LIBPERL | ||
4 | typedef int INTERP; | ||
5 | #define dSP | ||
6 | #define ENTER | ||
7 | #define SAVETMPS | ||
8 | #define PUTBACK | ||
9 | #define SPAGAIN | ||
10 | #define FREETMPS | ||
11 | #define LEAVE | ||
12 | #define SP | ||
13 | #define ERRSV | ||
14 | #define G_SCALAR (0) | ||
15 | #define G_DISCARD (0) | ||
16 | #define G_NOARGS (0) | ||
17 | #define PUSHMARK(a) | ||
18 | #define SvTRUE(a) (0) | ||
19 | #define XPUSHs(s) | ||
20 | #define sv_2mortal(a) | ||
21 | #define newSVpv(a,b) | ||
22 | #define newSVuv(a) | ||
23 | #define newSViv(a) | ||
24 | #define get_cv(a,b) (0) | ||
25 | #define call_pv(a,b) (0) | ||
26 | #define perl_alloc() (0) | ||
27 | #define perl_construct(a) (0) | ||
28 | #define perl_parse(a,b,c,d,e) (0) | ||
29 | #define perl_run(a) (0) | ||
30 | #define perl_destruct(a) (0) | ||
31 | #define perl_free(a) (0) | ||
32 | #define pTHX void | ||
33 | #define CV void | ||
34 | #define dXSUB_SYS | ||
35 | #define pTHX_ | ||
36 | static inline void newXS(const char *a, void *b, const char *c) {} | ||
37 | static void boot_Perf__Trace__Context(pTHX_ CV *cv) {} | ||
38 | static void boot_DynaLoader(pTHX_ CV *cv) {} | ||
39 | #else | ||
40 | #include <EXTERN.h> | ||
41 | #include <perl.h> | ||
42 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
43 | void boot_DynaLoader(pTHX_ CV *cv); | ||
44 | typedef PerlInterpreter * INTERP; | ||
45 | #endif | ||
46 | |||
47 | struct scripting_context { | ||
48 | void *event_data; | ||
49 | }; | ||
50 | |||
51 | int common_pc(struct scripting_context *context); | ||
52 | int common_flags(struct scripting_context *context); | ||
53 | int common_lock_depth(struct scripting_context *context); | ||
54 | |||
55 | #endif /* __PERF_TRACE_EVENT_PERL_H */ | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 1744422cafcb..7cd1193918c7 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -18,7 +18,7 @@ | |||
18 | * | 18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #define _LARGEFILE64_SOURCE | 21 | #define _FILE_OFFSET_BITS 64 |
22 | 22 | ||
23 | #include <dirent.h> | 23 | #include <dirent.h> |
24 | #include <stdio.h> | 24 | #include <stdio.h> |
@@ -83,7 +83,7 @@ static char *read_string(void) | |||
83 | char *str = NULL; | 83 | char *str = NULL; |
84 | int size = 0; | 84 | int size = 0; |
85 | int i; | 85 | int i; |
86 | int r; | 86 | off_t r; |
87 | 87 | ||
88 | for (;;) { | 88 | for (;;) { |
89 | r = read(input_fd, buf, BUFSIZ); | 89 | r = read(input_fd, buf, BUFSIZ); |
@@ -118,7 +118,7 @@ static char *read_string(void) | |||
118 | 118 | ||
119 | /* move the file descriptor to the end of the string */ | 119 | /* move the file descriptor to the end of the string */ |
120 | r = lseek(input_fd, -(r - i), SEEK_CUR); | 120 | r = lseek(input_fd, -(r - i), SEEK_CUR); |
121 | if (r < 0) | 121 | if (r == (off_t)-1) |
122 | die("lseek"); | 122 | die("lseek"); |
123 | 123 | ||
124 | if (str) { | 124 | if (str) { |
@@ -282,8 +282,8 @@ static void update_cpu_data_index(int cpu) | |||
282 | 282 | ||
283 | static void get_next_page(int cpu) | 283 | static void get_next_page(int cpu) |
284 | { | 284 | { |
285 | off64_t save_seek; | 285 | off_t save_seek; |
286 | off64_t ret; | 286 | off_t ret; |
287 | 287 | ||
288 | if (!cpu_data[cpu].page) | 288 | if (!cpu_data[cpu].page) |
289 | return; | 289 | return; |
@@ -298,17 +298,17 @@ static void get_next_page(int cpu) | |||
298 | update_cpu_data_index(cpu); | 298 | update_cpu_data_index(cpu); |
299 | 299 | ||
300 | /* other parts of the code may expect the pointer to not move */ | 300 | /* other parts of the code may expect the pointer to not move */ |
301 | save_seek = lseek64(input_fd, 0, SEEK_CUR); | 301 | save_seek = lseek(input_fd, 0, SEEK_CUR); |
302 | 302 | ||
303 | ret = lseek64(input_fd, cpu_data[cpu].offset, SEEK_SET); | 303 | ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); |
304 | if (ret < 0) | 304 | if (ret == (off_t)-1) |
305 | die("failed to lseek"); | 305 | die("failed to lseek"); |
306 | ret = read(input_fd, cpu_data[cpu].page, page_size); | 306 | ret = read(input_fd, cpu_data[cpu].page, page_size); |
307 | if (ret < 0) | 307 | if (ret < 0) |
308 | die("failed to read page"); | 308 | die("failed to read page"); |
309 | 309 | ||
310 | /* reset the file pointer back */ | 310 | /* reset the file pointer back */ |
311 | lseek64(input_fd, save_seek, SEEK_SET); | 311 | lseek(input_fd, save_seek, SEEK_SET); |
312 | 312 | ||
313 | return; | 313 | return; |
314 | } | 314 | } |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c new file mode 100644 index 000000000000..7ea983acfaea --- /dev/null +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * trace-event-scripting. Scripting engine common and initialization code. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include <errno.h> | ||
27 | |||
28 | #include "../perf.h" | ||
29 | #include "util.h" | ||
30 | #include "trace-event.h" | ||
31 | |||
32 | struct scripting_context *scripting_context; | ||
33 | |||
34 | static int stop_script_unsupported(void) | ||
35 | { | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static void process_event_unsupported(int cpu __unused, | ||
40 | void *data __unused, | ||
41 | int size __unused, | ||
42 | unsigned long long nsecs __unused, | ||
43 | char *comm __unused) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | static void print_python_unsupported_msg(void) | ||
48 | { | ||
49 | fprintf(stderr, "Python scripting not supported." | ||
50 | " Install libpython and rebuild perf to enable it.\n" | ||
51 | "For example:\n # apt-get install python-dev (ubuntu)" | ||
52 | "\n # yum install python-devel (Fedora)" | ||
53 | "\n etc.\n"); | ||
54 | } | ||
55 | |||
56 | static int python_start_script_unsupported(const char *script __unused, | ||
57 | int argc __unused, | ||
58 | const char **argv __unused) | ||
59 | { | ||
60 | print_python_unsupported_msg(); | ||
61 | |||
62 | return -1; | ||
63 | } | ||
64 | |||
65 | static int python_generate_script_unsupported(const char *outfile __unused) | ||
66 | { | ||
67 | print_python_unsupported_msg(); | ||
68 | |||
69 | return -1; | ||
70 | } | ||
71 | |||
72 | struct scripting_ops python_scripting_unsupported_ops = { | ||
73 | .name = "Python", | ||
74 | .start_script = python_start_script_unsupported, | ||
75 | .stop_script = stop_script_unsupported, | ||
76 | .process_event = process_event_unsupported, | ||
77 | .generate_script = python_generate_script_unsupported, | ||
78 | }; | ||
79 | |||
80 | static void register_python_scripting(struct scripting_ops *scripting_ops) | ||
81 | { | ||
82 | int err; | ||
83 | err = script_spec_register("Python", scripting_ops); | ||
84 | if (err) | ||
85 | die("error registering Python script extension"); | ||
86 | |||
87 | err = script_spec_register("py", scripting_ops); | ||
88 | if (err) | ||
89 | die("error registering py script extension"); | ||
90 | |||
91 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
92 | } | ||
93 | |||
94 | #ifdef NO_LIBPYTHON | ||
95 | void setup_python_scripting(void) | ||
96 | { | ||
97 | register_python_scripting(&python_scripting_unsupported_ops); | ||
98 | } | ||
99 | #else | ||
100 | struct scripting_ops python_scripting_ops; | ||
101 | |||
102 | void setup_python_scripting(void) | ||
103 | { | ||
104 | register_python_scripting(&python_scripting_ops); | ||
105 | } | ||
106 | #endif | ||
107 | |||
108 | static void print_perl_unsupported_msg(void) | ||
109 | { | ||
110 | fprintf(stderr, "Perl scripting not supported." | ||
111 | " Install libperl and rebuild perf to enable it.\n" | ||
112 | "For example:\n # apt-get install libperl-dev (ubuntu)" | ||
113 | "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)" | ||
114 | "\n etc.\n"); | ||
115 | } | ||
116 | |||
117 | static int perl_start_script_unsupported(const char *script __unused, | ||
118 | int argc __unused, | ||
119 | const char **argv __unused) | ||
120 | { | ||
121 | print_perl_unsupported_msg(); | ||
122 | |||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | static int perl_generate_script_unsupported(const char *outfile __unused) | ||
127 | { | ||
128 | print_perl_unsupported_msg(); | ||
129 | |||
130 | return -1; | ||
131 | } | ||
132 | |||
133 | struct scripting_ops perl_scripting_unsupported_ops = { | ||
134 | .name = "Perl", | ||
135 | .start_script = perl_start_script_unsupported, | ||
136 | .stop_script = stop_script_unsupported, | ||
137 | .process_event = process_event_unsupported, | ||
138 | .generate_script = perl_generate_script_unsupported, | ||
139 | }; | ||
140 | |||
141 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | ||
142 | { | ||
143 | int err; | ||
144 | err = script_spec_register("Perl", scripting_ops); | ||
145 | if (err) | ||
146 | die("error registering Perl script extension"); | ||
147 | |||
148 | err = script_spec_register("pl", scripting_ops); | ||
149 | if (err) | ||
150 | die("error registering pl script extension"); | ||
151 | |||
152 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
153 | } | ||
154 | |||
155 | #ifdef NO_LIBPERL | ||
156 | void setup_perl_scripting(void) | ||
157 | { | ||
158 | register_perl_scripting(&perl_scripting_unsupported_ops); | ||
159 | } | ||
160 | #else | ||
161 | struct scripting_ops perl_scripting_ops; | ||
162 | |||
163 | void setup_perl_scripting(void) | ||
164 | { | ||
165 | register_perl_scripting(&perl_scripting_ops); | ||
166 | } | ||
167 | #endif | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 6ad405620c9b..c3269b937db4 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -279,7 +279,15 @@ struct scripting_ops { | |||
279 | 279 | ||
280 | int script_spec_register(const char *spec, struct scripting_ops *ops); | 280 | int script_spec_register(const char *spec, struct scripting_ops *ops); |
281 | 281 | ||
282 | extern struct scripting_ops perl_scripting_ops; | ||
283 | void setup_perl_scripting(void); | 282 | void setup_perl_scripting(void); |
283 | void setup_python_scripting(void); | ||
284 | |||
285 | struct scripting_context { | ||
286 | void *event_data; | ||
287 | }; | ||
288 | |||
289 | int common_pc(struct scripting_context *context); | ||
290 | int common_flags(struct scripting_context *context); | ||
291 | int common_lock_depth(struct scripting_context *context); | ||
284 | 292 | ||
285 | #endif /* __PERF_TRACE_EVENTS_H */ | 293 | #endif /* __PERF_TRACE_EVENTS_H */ |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c new file mode 100644 index 000000000000..f9b890fde681 --- /dev/null +++ b/tools/perf/util/util.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include "util.h" | ||
2 | #include <sys/mman.h> | ||
3 | |||
4 | int mkdir_p(char *path, mode_t mode) | ||
5 | { | ||
6 | struct stat st; | ||
7 | int err; | ||
8 | char *d = path; | ||
9 | |||
10 | if (*d != '/') | ||
11 | return -1; | ||
12 | |||
13 | if (stat(path, &st) == 0) | ||
14 | return 0; | ||
15 | |||
16 | while (*++d == '/'); | ||
17 | |||
18 | while ((d = strchr(d, '/'))) { | ||
19 | *d = '\0'; | ||
20 | err = stat(path, &st) && mkdir(path, mode); | ||
21 | *d++ = '/'; | ||
22 | if (err) | ||
23 | return -1; | ||
24 | while (*d == '/') | ||
25 | ++d; | ||
26 | } | ||
27 | return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; | ||
28 | } | ||
29 | |||
30 | static int slow_copyfile(const char *from, const char *to) | ||
31 | { | ||
32 | int err = 0; | ||
33 | char *line = NULL; | ||
34 | size_t n; | ||
35 | FILE *from_fp = fopen(from, "r"), *to_fp; | ||
36 | |||
37 | if (from_fp == NULL) | ||
38 | goto out; | ||
39 | |||
40 | to_fp = fopen(to, "w"); | ||
41 | if (to_fp == NULL) | ||
42 | goto out_fclose_from; | ||
43 | |||
44 | while (getline(&line, &n, from_fp) > 0) | ||
45 | if (fputs(line, to_fp) == EOF) | ||
46 | goto out_fclose_to; | ||
47 | err = 0; | ||
48 | out_fclose_to: | ||
49 | fclose(to_fp); | ||
50 | free(line); | ||
51 | out_fclose_from: | ||
52 | fclose(from_fp); | ||
53 | out: | ||
54 | return err; | ||
55 | } | ||
56 | |||
57 | int copyfile(const char *from, const char *to) | ||
58 | { | ||
59 | int fromfd, tofd; | ||
60 | struct stat st; | ||
61 | void *addr; | ||
62 | int err = -1; | ||
63 | |||
64 | if (stat(from, &st)) | ||
65 | goto out; | ||
66 | |||
67 | if (st.st_size == 0) /* /proc? do it slowly... */ | ||
68 | return slow_copyfile(from, to); | ||
69 | |||
70 | fromfd = open(from, O_RDONLY); | ||
71 | if (fromfd < 0) | ||
72 | goto out; | ||
73 | |||
74 | tofd = creat(to, 0755); | ||
75 | if (tofd < 0) | ||
76 | goto out_close_from; | ||
77 | |||
78 | addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0); | ||
79 | if (addr == MAP_FAILED) | ||
80 | goto out_close_to; | ||
81 | |||
82 | if (write(tofd, addr, st.st_size) == st.st_size) | ||
83 | err = 0; | ||
84 | |||
85 | munmap(addr, st.st_size); | ||
86 | out_close_to: | ||
87 | close(tofd); | ||
88 | if (err) | ||
89 | unlink(to); | ||
90 | out_close_from: | ||
91 | close(fromfd); | ||
92 | out: | ||
93 | return err; | ||
94 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c673d8825883..0f5b2a6f1080 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -403,4 +403,7 @@ void git_qsort(void *base, size_t nmemb, size_t size, | |||
403 | #endif | 403 | #endif |
404 | #endif | 404 | #endif |
405 | 405 | ||
406 | int mkdir_p(char *path, mode_t mode); | ||
407 | int copyfile(const char *from, const char *to); | ||
408 | |||
406 | #endif | 409 | #endif |
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 1c15e39f99e3..cfa55d686e3b 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c | |||
@@ -169,6 +169,7 @@ static void perf_read_values__display_pretty(FILE *fp, | |||
169 | counterwidth[j], values->value[i][j]); | 169 | counterwidth[j], values->value[i][j]); |
170 | fprintf(fp, "\n"); | 170 | fprintf(fp, "\n"); |
171 | } | 171 | } |
172 | free(counterwidth); | ||
172 | } | 173 | } |
173 | 174 | ||
174 | static void perf_read_values__display_raw(FILE *fp, | 175 | static void perf_read_values__display_raw(FILE *fp, |