lkml.org 
[lkml]   [2010]   [Mar]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH] perf: x86: fix callgraphs of 32-bit processes on 64-bit kernels V4.
    Date
    When profiling a 32-bit process on a 64-bit kernel, callgraph tracing
    stopped after the first function, because it has seen a garbage memory address
    (tried to interpret the frame pointer, and return address as a 64-bit pointer).

    Fix this by using a struct stack_frame with 32-bit pointers when the TIF_IA32 flag is set.

    Note that TIF_IA32 flag must be used, and not is_compat_task(), because the
    latter is only set when the 32-bit process is executing a syscall,
    which may not always be the case (when tracing page fault events for example).

    Cc: "H. Peter Anvin" <hpa@zytor.com>
    Cc: Frederic Weisbecker <fweisbec@gmail.com>
    Cc: Ingo Molnar <mingo@redhat.com>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
    Cc: Paul Mackerras <paulus@samba.org>
    Cc: x86@kernel.org
    Cc: linux-kernel@vger.kernel.org
    Signed-off-by: Török Edwin <edwintorok@gmail.com>
    ---
    arch/x86/kernel/cpu/perf_event.c | 44 +++++++++++++++++++++++++++++++++----
    arch/x86/kernel/dumpstack.h | 5 ++++
    2 files changed, 44 insertions(+), 5 deletions(-)

    diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
    index 8c1c070..5f97f29 100644
    --- a/arch/x86/kernel/cpu/perf_event.c
    +++ b/arch/x86/kernel/cpu/perf_event.c
    @@ -26,6 +26,7 @@
    #include <asm/apic.h>
    #include <asm/stacktrace.h>
    #include <asm/nmi.h>
    +#include <asm/compat.h>

    static u64 perf_event_mask __read_mostly;

    @@ -2392,14 +2393,42 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n)
    return len;
    }

    -static int copy_stack_frame(const void __user *fp, struct stack_frame *frame)
    +#ifdef CONFIG_COMPAT
    +static inline int
    +perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
    {
    - unsigned long bytes;
    + /* 32-bit process in 64-bit kernel. */
    + struct stack_frame_ia32 frame;
    + const void __user *fp;
    +
    + if (!test_thread_flag(TIF_IA32))
    + return 0;
    +
    + fp = compat_ptr(regs->bp);
    + while (entry->nr < PERF_MAX_STACK_DEPTH) {
    + unsigned long bytes;
    + frame.next_frame = 0;
    + frame.return_address = 0;

    - bytes = copy_from_user_nmi(frame, fp, sizeof(*frame));
    + bytes = copy_from_user_nmi(&frame, fp, sizeof(frame));
    + if (bytes != sizeof(frame))
    + break;
    +
    + if (fp < compat_ptr(regs->sp))
    + break;

    - return bytes == sizeof(*frame);
    + callchain_store(entry, frame.return_address);
    + fp = compat_ptr(frame.next_frame);
    + }
    + return 1;
    }
    +#else
    +static inline int
    +perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
    +{
    + return 0;
    +}
    +#endif

    static void
    perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry)
    @@ -2415,11 +2444,16 @@ perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry)
    callchain_store(entry, PERF_CONTEXT_USER);
    callchain_store(entry, regs->ip);

    + if (perf_callchain_user32(regs, entry))
    + return;
    +
    while (entry->nr < PERF_MAX_STACK_DEPTH) {
    + unsigned long bytes;
    frame.next_frame = NULL;
    frame.return_address = 0;

    - if (!copy_stack_frame(fp, &frame))
    + bytes = copy_from_user_nmi(&frame, fp, sizeof(frame));
    + if (bytes != sizeof(frame))
    break;

    if ((unsigned long)fp < regs->sp)
    diff --git a/arch/x86/kernel/dumpstack.h b/arch/x86/kernel/dumpstack.h
    index 4fd1420..3f0a242 100644
    --- a/arch/x86/kernel/dumpstack.h
    +++ b/arch/x86/kernel/dumpstack.h
    @@ -29,4 +29,9 @@ struct stack_frame {
    struct stack_frame *next_frame;
    unsigned long return_address;
    };
    +
    +struct stack_frame_ia32 {
    + u32 next_frame;
    + u32 return_address;
    +};
    #endif
    --
    1.7.0
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2010-03-17 11:09    [W:0.034 / U:0.248 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site