diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-01-14 09:23:10 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-01-16 04:58:45 -0500 |
commit | ba21594cddee0a3af582971656702b1c4509d8f5 (patch) | |
tree | 2526e75ba7479a5fd59f979962f1937c3104ad99 /tools/perf/util/session.c | |
parent | 0d755034dbd01e240eadf2d31f4f75d3088ccd21 (diff) |
perf tools: Cross platform perf.data analysis support
There are still some problems related to loading vmlinux files,
but those are unrelated to the feature implemented in this
patch, so will get fixed in the next patches, but here are some
results:
1. collect perf.data file on a Fedora 12 machine, x86_64, 64-bit
userland
2. transfer it to a Debian Testing machine, PARISC64, 32-bit
userland
acme@parisc:~/git/linux-2.6-tip$ perf buildid-list | head -5
74f9930ee94475b6b3238caf3725a50d59cb994b [kernel.kallsyms]
55fdd56670453ea66c011158c4b9d30179c1d049 /lib/modules/2.6.33-rc4-tip+/kernel/net/ipv4/netfilter/ipt_MASQUERADE.ko
41adff63c730890480980d5d8ba513f1c216a858 /lib/modules/2.6.33-rc4-tip+/kernel/net/ipv4/netfilter/iptable_nat.ko
90a33def1077bb8e97b8a78546dc96c2de62df46 /lib/modules/2.6.33-rc4-tip+/kernel/net/ipv4/netfilter/nf_nat.ko
984c7bea90ce1376d5c8e7ef43a781801286e62d /lib/modules/2.6.33-rc4-tip+/kernel/drivers/net/tun.ko
acme@parisc:~/git/linux-2.6-tip$ perf buildid-list | tail -5
22492f3753c6a67de5c7ccbd6b863390c92c0723 /usr/lib64/libXt.so.6.0.0
353802bb7e1b895ba43507cc678f951e778e4c6f /usr/lib64/libMagickCore.so.2.0.0
d10c2897558595efe7be8b0584cf7e6398bc776c /usr/lib64/libfprint.so.0.0.0
a83ecfb519a788774a84d5ddde633c9ba56c03ab /home/acme/bin/perf
d3ca765a8ecf257d263801d7ad8c49c189082317 /usr/lib64/libdwarf.so.0.0
acme@parisc:~/git/linux-2.6-tip$
acme@parisc:~/git/linux-2.6-tip$ perf report --sort comm
The file [kernel.kallsyms] cannot be used, trying to use /proc/kallsyms...
^^^^ The problem related to vmlinux handling, it shouldn't be trying this
^^^^ rather alien /proc/kallsyms at all...
/lib64/libpthread-2.10.2.so with build id 5c68f7afeb33309c78037e374b0deee84dd441f6 not found, continuing without symbols
/lib64/libc-2.10.2.so with build id eb4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 not found, continuing without symbols
/home/acme/bin/perf with build id a83ecfb519a788774a84d5ddde633c9ba56c03ab not found, continuing without symbols
/usr/sbin/openvpn with build id f2037a091ef36b591187a858d75e203690ea9409 not found, continuing without symbols
Failed to open /lib/modules/2.6.33-rc4-tip+/kernel/drivers/net/e1000e/e1000e.ko, continuing without symbols
Failed to open /lib/modules/2.6.33-rc4-tip+/kernel/drivers/net/wireless/iwlwifi/iwlcore.ko, continuing without symbols
<SNIP more complaints about not finding the right build-ids,
those will have to wait for 'perf archive' or plain
copying what was collected by 'perf record' on the x86_64,
source machine, see further below for an example of this >
# Samples: 293085637
#
# Overhead Command
# ........ ...............
#
61.70% find
23.50% perf
5.86% swapper
3.12% sshd
2.39% init
0.87% bash
0.86% sleep
0.59% dbus-daemon
0.25% hald
0.24% NetworkManager
0.19% hald-addon-rfki
0.15% openvpn
0.07% phy0
0.07% events/0
0.05% iwl3945
0.05% events/1
0.03% kondemand/0
acme@parisc:~/git/linux-2.6-tip$
Which matches what we get when running the same command for the
same perf.data file on the F12, x86_64, source machine:
[root@doppio linux-2.6-tip]# perf report --sort comm
# Samples: 293085637
#
# Overhead Command
# ........ ...............
#
61.70% find
23.50% perf
5.86% swapper
3.12% sshd
2.39% init
0.87% bash
0.86% sleep
0.59% dbus-daemon
0.25% hald
0.24% NetworkManager
0.19% hald-addon-rfki
0.15% openvpn
0.07% phy0
0.07% events/0
0.05% iwl3945
0.05% events/1
0.03% kondemand/0
[root@doppio linux-2.6-tip]#
The other modes work as well, modulo the problem with vmlinux:
acme@parisc:~/git/linux-2.6-tip$ perf report --sort comm,dso 2> /dev/null | head -15
# Samples: 293085637
#
# Overhead Command Shared Object
# ........ ............... .................................
#
35.11% find ffffffff81002b5a
18.25% perf ffffffff8102235f
16.17% find libc-2.10.2.so
9.07% find find
5.80% swapper ffffffff8102235f
3.95% perf libc-2.10.2.so
2.33% init ffffffff810091b9
1.65% sshd libcrypto.so.0.9.8k
1.35% find [e1000e]
0.68% sleep libc-2.10.2.so
acme@parisc:~/git/linux-2.6-tip$
And the lack of the right buildids:
acme@parisc:~/git/linux-2.6-tip$ perf report --sort comm,dso,symbol 2> /dev/null | head -15
# Samples: 293085637
#
# Overhead Command Shared Object Symbol
# ........ ............... ................................. ......
#
35.11% find ffffffff81002b5a [k] 0xffffffff81002b5a
18.25% perf ffffffff8102235f [k] 0xffffffff8102235f
16.17% find libc-2.10.2.so [.] 0x00000000045782
9.07% find find [.] 0x0000000000fb0e
5.80% swapper ffffffff8102235f [k] 0xffffffff8102235f
3.95% perf libc-2.10.2.so [.] 0x0000000007f398
2.33% init ffffffff810091b9 [k] 0xffffffff810091b9
1.65% sshd libcrypto.so.0.9.8k [.] 0x00000000105440
1.35% find [e1000e] [k] 0x00000000010948
0.68% sleep libc-2.10.2.so [.] 0x0000000011ad5b
acme@parisc:~/git/linux-2.6-tip$
But if we:
acme@parisc:~/git/linux-2.6-tip$ ls ~/.debug
ls: cannot access /home/acme/.debug: No such file or directory
acme@parisc:~/git/linux-2.6-tip$ mkdir -p ~/.debug/lib64/libc-2.10.2.so/
acme@parisc:~/git/linux-2.6-tip$ scp doppio:.debug/lib64/libc-2.10.2.so/* ~/.debug/lib64/libc-2.10.2.so/
acme@doppio's password:
eb4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 100% 1783KB 714.7KB/s 00:02
acme@parisc:~/git/linux-2.6-tip$ mkdir -p ~/.debug/.build-id/eb
acme@parisc:~/git/linux-2.6-tip$ ln -s ../../lib64/libc-2.10.2.so/eb4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 ~/.debug/.build-id/eb/4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1
acme@parisc:~/git/linux-2.6-tip$ perf report --dsos libc-2.10.2.so 2> /dev/null
# dso: libc-2.10.2.so
# Samples: 64281170
#
# Overhead Command Symbol
# ........ ............... ......
#
14.98% perf [.] __GI_strcmp
12.30% find [.] __GI_memmove
9.25% find [.] _int_malloc
7.60% find [.] _IO_vfprintf_internal
6.10% find [.] _IO_new_file_xsputn
6.02% find [.] __GI_close
3.08% find [.] _IO_file_overflow_internal
3.08% find [.] malloc_consolidate
3.08% find [.] _int_free
3.08% find [.] __strchrnul
3.08% find [.] __getdents64
3.08% find [.] __write_nocancel
3.08% sleep [.] __GI__dl_addr
3.08% sshd [.] __libc_select
3.08% find [.] _IO_new_file_write
3.07% find [.] _IO_new_do_write
3.06% find [.] __GI___errno_location
3.05% find [.] __GI___libc_malloc
3.04% perf [.] __GI_memcpy
1.71% find [.] __fprintf_chk
1.29% bash [.] __gconv_transform_utf8_internal
0.79% dbus-daemon [.] __GI_strlen
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
acme@parisc:~/git/linux-2.6-tip$
Which matches what we get on the source, F12, x86_64 machine:
[root@doppio linux-2.6-tip]# perf report --dsos libc-2.10.2.so
# dso: libc-2.10.2.so
# Samples: 64281170
#
# Overhead Command Symbol
# ........ ............... ......
#
14.98% perf [.] __GI_strcmp
12.30% find [.] __GI_memmove
9.25% find [.] _int_malloc
7.60% find [.] _IO_vfprintf_internal
6.10% find [.] _IO_new_file_xsputn
6.02% find [.] __GI_close
3.08% find [.] _IO_file_overflow_internal
3.08% find [.] malloc_consolidate
3.08% find [.] _int_free
3.08% find [.] __strchrnul
3.08% find [.] __getdents64
3.08% find [.] __write_nocancel
3.08% sleep [.] __GI__dl_addr
3.08% sshd [.] __libc_select
3.08% find [.] _IO_new_file_write
3.07% find [.] _IO_new_do_write
3.06% find [.] __GI___errno_location
3.05% find [.] __GI___libc_malloc
3.04% perf [.] __GI_memcpy
1.71% find [.] __fprintf_chk
1.29% bash [.] __gconv_transform_utf8_internal
0.79% dbus-daemon [.] __GI_strlen
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@doppio linux-2.6-tip]#
So I think this is really, really nice in that it demonstrates
the portability of perf.data files and the use of build-ids
accross such aliens worlds :-)
There are some things to fix tho, like the bitmap on the header,
but things are looking good.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1263478990-8200-2-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r-- | tools/perf/util/session.c | 108 |
1 files changed, 98 insertions, 10 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e3ccdb46d6c4..604e14f6a6f9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | 2 | ||
3 | #include <byteswap.h> | ||
3 | #include <unistd.h> | 4 | #include <unistd.h> |
4 | #include <sys/types.h> | 5 | #include <sys/types.h> |
5 | 6 | ||
@@ -201,21 +202,88 @@ void event__print_totals(void) | |||
201 | event__name[i], event__total[i]); | 202 | event__name[i], event__total[i]); |
202 | } | 203 | } |
203 | 204 | ||
205 | void mem_bswap_64(void *src, int byte_size) | ||
206 | { | ||
207 | u64 *m = src; | ||
208 | |||
209 | while (byte_size > 0) { | ||
210 | *m = bswap_64(*m); | ||
211 | byte_size -= sizeof(u64); | ||
212 | ++m; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static void event__all64_swap(event_t *self) | ||
217 | { | ||
218 | struct perf_event_header *hdr = &self->header; | ||
219 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | ||
220 | } | ||
221 | |||
222 | static void event__comm_swap(event_t *self) | ||
223 | { | ||
224 | self->comm.pid = bswap_32(self->comm.pid); | ||
225 | self->comm.tid = bswap_32(self->comm.tid); | ||
226 | } | ||
227 | |||
228 | static void event__mmap_swap(event_t *self) | ||
229 | { | ||
230 | self->mmap.pid = bswap_32(self->mmap.pid); | ||
231 | self->mmap.tid = bswap_32(self->mmap.tid); | ||
232 | self->mmap.start = bswap_64(self->mmap.start); | ||
233 | self->mmap.len = bswap_64(self->mmap.len); | ||
234 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | ||
235 | } | ||
236 | |||
237 | static void event__task_swap(event_t *self) | ||
238 | { | ||
239 | self->fork.pid = bswap_32(self->fork.pid); | ||
240 | self->fork.tid = bswap_32(self->fork.tid); | ||
241 | self->fork.ppid = bswap_32(self->fork.ppid); | ||
242 | self->fork.ptid = bswap_32(self->fork.ptid); | ||
243 | self->fork.time = bswap_64(self->fork.time); | ||
244 | } | ||
245 | |||
246 | static void event__read_swap(event_t *self) | ||
247 | { | ||
248 | self->read.pid = bswap_32(self->read.pid); | ||
249 | self->read.tid = bswap_32(self->read.tid); | ||
250 | self->read.value = bswap_64(self->read.value); | ||
251 | self->read.time_enabled = bswap_64(self->read.time_enabled); | ||
252 | self->read.time_running = bswap_64(self->read.time_running); | ||
253 | self->read.id = bswap_64(self->read.id); | ||
254 | } | ||
255 | |||
256 | typedef void (*event__swap_op)(event_t *self); | ||
257 | |||
258 | static event__swap_op event__swap_ops[] = { | ||
259 | [PERF_RECORD_MMAP] = event__mmap_swap, | ||
260 | [PERF_RECORD_COMM] = event__comm_swap, | ||
261 | [PERF_RECORD_FORK] = event__task_swap, | ||
262 | [PERF_RECORD_EXIT] = event__task_swap, | ||
263 | [PERF_RECORD_LOST] = event__all64_swap, | ||
264 | [PERF_RECORD_READ] = event__read_swap, | ||
265 | [PERF_RECORD_SAMPLE] = event__all64_swap, | ||
266 | [PERF_RECORD_MAX] = NULL, | ||
267 | }; | ||
268 | |||
204 | static int perf_session__process_event(struct perf_session *self, | 269 | static int perf_session__process_event(struct perf_session *self, |
205 | event_t *event, | 270 | event_t *event, |
206 | struct perf_event_ops *ops, | 271 | struct perf_event_ops *ops, |
207 | unsigned long offset, unsigned long head) | 272 | u64 offset, u64 head) |
208 | { | 273 | { |
209 | trace_event(event); | 274 | trace_event(event); |
210 | 275 | ||
211 | if (event->header.type < PERF_RECORD_MAX) { | 276 | if (event->header.type < PERF_RECORD_MAX) { |
212 | dump_printf("%#lx [%#x]: PERF_RECORD_%s", | 277 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", |
213 | offset + head, event->header.size, | 278 | offset + head, event->header.size, |
214 | event__name[event->header.type]); | 279 | event__name[event->header.type]); |
215 | ++event__total[0]; | 280 | ++event__total[0]; |
216 | ++event__total[event->header.type]; | 281 | ++event__total[event->header.type]; |
217 | } | 282 | } |
218 | 283 | ||
284 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | ||
285 | event__swap_ops[event->header.type](event); | ||
286 | |||
219 | switch (event->header.type) { | 287 | switch (event->header.type) { |
220 | case PERF_RECORD_SAMPLE: | 288 | case PERF_RECORD_SAMPLE: |
221 | return ops->sample(event, self); | 289 | return ops->sample(event, self); |
@@ -241,7 +309,15 @@ static int perf_session__process_event(struct perf_session *self, | |||
241 | } | 309 | } |
242 | } | 310 | } |
243 | 311 | ||
244 | int perf_header__read_build_ids(int input, u64 offset, u64 size) | 312 | void perf_event_header__bswap(struct perf_event_header *self) |
313 | { | ||
314 | self->type = bswap_32(self->type); | ||
315 | self->misc = bswap_16(self->misc); | ||
316 | self->size = bswap_16(self->size); | ||
317 | } | ||
318 | |||
319 | int perf_header__read_build_ids(struct perf_header *self, | ||
320 | int input, u64 offset, u64 size) | ||
245 | { | 321 | { |
246 | struct build_id_event bev; | 322 | struct build_id_event bev; |
247 | char filename[PATH_MAX]; | 323 | char filename[PATH_MAX]; |
@@ -256,6 +332,9 @@ int perf_header__read_build_ids(int input, u64 offset, u64 size) | |||
256 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | 332 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) |
257 | goto out; | 333 | goto out; |
258 | 334 | ||
335 | if (self->needs_swap) | ||
336 | perf_event_header__bswap(&bev.header); | ||
337 | |||
259 | len = bev.header.size - sizeof(bev); | 338 | len = bev.header.size - sizeof(bev); |
260 | if (read(input, filename, len) != len) | 339 | if (read(input, filename, len) != len) |
261 | goto out; | 340 | goto out; |
@@ -292,9 +371,9 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
292 | int perf_session__process_events(struct perf_session *self, | 371 | int perf_session__process_events(struct perf_session *self, |
293 | struct perf_event_ops *ops) | 372 | struct perf_event_ops *ops) |
294 | { | 373 | { |
295 | int err; | 374 | int err, mmap_prot, mmap_flags; |
296 | unsigned long head, shift; | 375 | u64 head, shift; |
297 | unsigned long offset = 0; | 376 | u64 offset = 0; |
298 | size_t page_size; | 377 | size_t page_size; |
299 | event_t *event; | 378 | event_t *event; |
300 | uint32_t size; | 379 | uint32_t size; |
@@ -330,9 +409,16 @@ out_getcwd_err: | |||
330 | offset += shift; | 409 | offset += shift; |
331 | head -= shift; | 410 | head -= shift; |
332 | 411 | ||
412 | mmap_prot = PROT_READ; | ||
413 | mmap_flags = MAP_SHARED; | ||
414 | |||
415 | if (self->header.needs_swap) { | ||
416 | mmap_prot |= PROT_WRITE; | ||
417 | mmap_flags = MAP_PRIVATE; | ||
418 | } | ||
333 | remap: | 419 | remap: |
334 | buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, | 420 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, |
335 | MAP_SHARED, self->fd, offset); | 421 | mmap_flags, self->fd, offset); |
336 | if (buf == MAP_FAILED) { | 422 | if (buf == MAP_FAILED) { |
337 | pr_err("failed to mmap file\n"); | 423 | pr_err("failed to mmap file\n"); |
338 | err = -errno; | 424 | err = -errno; |
@@ -342,6 +428,8 @@ remap: | |||
342 | more: | 428 | more: |
343 | event = (event_t *)(buf + head); | 429 | event = (event_t *)(buf + head); |
344 | 430 | ||
431 | if (self->header.needs_swap) | ||
432 | perf_event_header__bswap(&event->header); | ||
345 | size = event->header.size; | 433 | size = event->header.size; |
346 | if (size == 0) | 434 | if (size == 0) |
347 | size = 8; | 435 | size = 8; |
@@ -361,12 +449,12 @@ more: | |||
361 | 449 | ||
362 | size = event->header.size; | 450 | size = event->header.size; |
363 | 451 | ||
364 | dump_printf("\n%#lx [%#x]: event: %d\n", | 452 | dump_printf("\n%#Lx [%#x]: event: %d\n", |
365 | offset + head, event->header.size, event->header.type); | 453 | offset + head, event->header.size, event->header.type); |
366 | 454 | ||
367 | if (size == 0 || | 455 | if (size == 0 || |
368 | perf_session__process_event(self, event, ops, offset, head) < 0) { | 456 | perf_session__process_event(self, event, ops, offset, head) < 0) { |
369 | dump_printf("%#lx [%#x]: skipping unknown header type: %d\n", | 457 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", |
370 | offset + head, event->header.size, | 458 | offset + head, event->header.size, |
371 | event->header.type); | 459 | event->header.type); |
372 | /* | 460 | /* |