diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-28 13:20:25 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-28 13:20:25 -0500 |
commit | 6556a6743549defc32e5f90ee2cb1ecd833a44c3 (patch) | |
tree | 622306583d4a3c13235a8bfc012854c125c597f1 /tools/perf/util/event.c | |
parent | e0d272429a34ff143bfa04ee8e29dd4eed2964c7 (diff) | |
parent | 1dd2980d990068e20045b90c424518cc7f3657ff (diff) |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (172 commits)
perf_event, amd: Fix spinlock initialization
perf_event: Fix preempt warning in perf_clock()
perf tools: Flush maps on COMM events
perf_events, x86: Split PMU definitions into separate files
perf annotate: Handle samples not at objdump output addr boundaries
perf_events, x86: Remove superflous MSR writes
perf_events: Simplify code by removing cpu argument to hw_perf_group_sched_in()
perf_events, x86: AMD event scheduling
perf_events: Add new start/stop PMU callbacks
perf_events: Report the MMAP pgoff value in bytes
perf annotate: Defer allocating sym_priv->hist array
perf symbols: Improve debugging information about symtab origins
perf top: Use a macro instead of a constant variable
perf symbols: Check the right return variable
perf/scripts: Tag syscall_name helper as not yet available
perf/scripts: Add perf-trace-python Documentation
perf/scripts: Remove unnecessary PyTuple resizes
perf/scripts: Add syscall tracing scripts
perf/scripts: Add Python scripting engine
perf/scripts: Remove check-perf-trace from listed scripts
...
Fix trivial conflict in tools/perf/util/probe-event.c
Diffstat (limited to 'tools/perf/util/event.c')
-rw-r--r-- | tools/perf/util/event.c | 220 |
1 files changed, 188 insertions, 32 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8a9e6baa3099..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,11 +432,10 @@ 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 | ||
@@ -303,7 +450,6 @@ void thread__find_addr_location(struct thread *self, | |||
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) |