diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-15 03:57:59 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-15 03:08:08 -0400 |
commit | 038e836e97e70c4ad2b5058b07fc7207f50b59dd (patch) | |
tree | 37a9d0a2d95f768593a6594134af98160890ae60 | |
parent | 5a6cec3abbdb74244caab68db100825a8c4ac02d (diff) |
perf_counter, x86: Fix kernel-space call-chains
Kernel-space call-chains were trimmed at the first entry because
we never processed anything beyond the first stack context.
Allow the backtrace to jump from NMI to IRQ stack then to task stack
and finally user-space stack.
Also calculate the stack and bp variables correctly so that the
stack walker does not exit early.
We can get deep traces as a result, visible in perf report -D output:
0x32af0 [0xe0]: PERF_EVENT (IP, 5): 15134: 0xffffffff815225fd period: 1
... chain: u:2, k:22, nr:24
..... 0: 0xffffffff815225fd
..... 1: 0xffffffff810ac51c
..... 2: 0xffffffff81018e29
..... 3: 0xffffffff81523939
..... 4: 0xffffffff81524b8f
..... 5: 0xffffffff81524bd9
..... 6: 0xffffffff8105e498
..... 7: 0xffffffff8152315a
..... 8: 0xffffffff81522c3a
..... 9: 0xffffffff810d9b74
..... 10: 0xffffffff810dbeec
..... 11: 0xffffffff810dc3fb
This is a 22-entries kernel-space chain.
(We still only record reliable stack entries.)
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 22 |
1 files changed, 9 insertions, 13 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 09d8cb69c3f3..6d5e7cfd97e7 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -1575,8 +1575,8 @@ static void backtrace_warning(void *data, char *msg) | |||
1575 | 1575 | ||
1576 | static int backtrace_stack(void *data, char *name) | 1576 | static int backtrace_stack(void *data, char *name) |
1577 | { | 1577 | { |
1578 | /* Don't bother with IRQ stacks for now */ | 1578 | /* Process all stacks: */ |
1579 | return -1; | 1579 | return 0; |
1580 | } | 1580 | } |
1581 | 1581 | ||
1582 | static void backtrace_address(void *data, unsigned long addr, int reliable) | 1582 | static void backtrace_address(void *data, unsigned long addr, int reliable) |
@@ -1594,6 +1594,8 @@ static const struct stacktrace_ops backtrace_ops = { | |||
1594 | .address = backtrace_address, | 1594 | .address = backtrace_address, |
1595 | }; | 1595 | }; |
1596 | 1596 | ||
1597 | #include "../dumpstack.h" | ||
1598 | |||
1597 | static void | 1599 | static void |
1598 | perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) | 1600 | perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) |
1599 | { | 1601 | { |
@@ -1601,26 +1603,20 @@ perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) | |||
1601 | char *stack; | 1603 | char *stack; |
1602 | int nr = entry->nr; | 1604 | int nr = entry->nr; |
1603 | 1605 | ||
1604 | callchain_store(entry, instruction_pointer(regs)); | 1606 | callchain_store(entry, regs->ip); |
1605 | 1607 | ||
1606 | stack = ((char *)regs + sizeof(struct pt_regs)); | 1608 | stack = ((char *)regs + sizeof(struct pt_regs)); |
1607 | #ifdef CONFIG_FRAME_POINTER | 1609 | #ifdef CONFIG_FRAME_POINTER |
1608 | bp = frame_pointer(regs); | 1610 | get_bp(bp); |
1609 | #else | 1611 | #else |
1610 | bp = 0; | 1612 | bp = 0; |
1611 | #endif | 1613 | #endif |
1612 | 1614 | ||
1613 | dump_trace(NULL, regs, (void *)stack, bp, &backtrace_ops, entry); | 1615 | dump_trace(NULL, regs, (void *)&stack, bp, &backtrace_ops, entry); |
1614 | 1616 | ||
1615 | entry->kernel = entry->nr - nr; | 1617 | entry->kernel = entry->nr - nr; |
1616 | } | 1618 | } |
1617 | 1619 | ||
1618 | |||
1619 | struct stack_frame { | ||
1620 | const void __user *next_fp; | ||
1621 | unsigned long return_address; | ||
1622 | }; | ||
1623 | |||
1624 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) | 1620 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) |
1625 | { | 1621 | { |
1626 | int ret; | 1622 | int ret; |
@@ -1652,7 +1648,7 @@ perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) | |||
1652 | callchain_store(entry, regs->ip); | 1648 | callchain_store(entry, regs->ip); |
1653 | 1649 | ||
1654 | while (entry->nr < MAX_STACK_DEPTH) { | 1650 | while (entry->nr < MAX_STACK_DEPTH) { |
1655 | frame.next_fp = NULL; | 1651 | frame.next_frame = NULL; |
1656 | frame.return_address = 0; | 1652 | frame.return_address = 0; |
1657 | 1653 | ||
1658 | if (!copy_stack_frame(fp, &frame)) | 1654 | if (!copy_stack_frame(fp, &frame)) |
@@ -1662,7 +1658,7 @@ perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) | |||
1662 | break; | 1658 | break; |
1663 | 1659 | ||
1664 | callchain_store(entry, frame.return_address); | 1660 | callchain_store(entry, frame.return_address); |
1665 | fp = frame.next_fp; | 1661 | fp = frame.next_frame; |
1666 | } | 1662 | } |
1667 | 1663 | ||
1668 | entry->user = entry->nr - nr; | 1664 | entry->user = entry->nr - nr; |