From patchwork Tue Jan 19 08:26:45 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Miller X-Patchwork-Id: 43171 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 2C3DAB7CB6 for ; Tue, 19 Jan 2010 19:26:42 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754533Ab0ASI0j (ORCPT ); Tue, 19 Jan 2010 03:26:39 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754120Ab0ASI0j (ORCPT ); Tue, 19 Jan 2010 03:26:39 -0500 Received: from 74-93-104-97-Washington.hfc.comcastbusiness.net ([74.93.104.97]:37440 "EHLO sunset.davemloft.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754968Ab0ASI0f (ORCPT ); Tue, 19 Jan 2010 03:26:35 -0500 Received: from localhost (localhost [127.0.0.1]) by sunset.davemloft.net (Postfix) with ESMTP id 5E51524C056; Tue, 19 Jan 2010 00:26:45 -0800 (PST) Date: Tue, 19 Jan 2010 00:26:45 -0800 (PST) Message-Id: <20100119.002645.70252139.davem@davemloft.net> To: sparclinux@vger.kernel.org CC: jens.axboe@oracle.com Subject: [PATCH]: sparc64: Add perf callchain support. From: David Miller X-Mailer: Mew version 6.3 on Emacs 23.1 / Mule 6.0 (HANACHIRUSATO) Mime-Version: 1.0 Sender: sparclinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: sparclinux@vger.kernel.org Pretty straightforward, and it should be easy to add accurate walk through of signal stack frames in userspace. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 120 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 119 insertions(+), 1 deletions(-) diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 198fb4e..2386ac6 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -1,6 +1,6 @@ /* Performance event support for sparc64. * - * Copyright (C) 2009 David S. Miller + * Copyright (C) 2009, 2010 David S. Miller * * This code is based almost entirely upon the x86 perf event * code, which is: @@ -18,11 +18,15 @@ #include #include +#include #include +#include #include #include #include +#include "kstack.h" + /* Sparc64 chips have two performance counters, 32-bits each, with * overflow interrupts generated on transition from 0xffffffff to 0. * The counters are accessed in one go using a 64-bit register. @@ -1062,3 +1066,117 @@ void __init init_hw_perf_events(void) register_die_notifier(&perf_event_nmi_notifier); } + +static inline void callchain_store(struct perf_callchain_entry *entry, u64 ip) +{ + if (entry->nr < PERF_MAX_STACK_DEPTH) + entry->ip[entry->nr++] = ip; +} + +static void perf_callchain_kernel(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long ksp, fp; + + callchain_store(entry, PERF_CONTEXT_KERNEL); + callchain_store(entry, regs->tpc); + + ksp = regs->u_regs[UREG_I6]; + fp = ksp + STACK_BIAS; + do { + struct sparc_stackf *sf; + struct pt_regs *regs; + unsigned long pc; + + if (!kstack_valid(current_thread_info(), fp)) + break; + + sf = (struct sparc_stackf *) fp; + regs = (struct pt_regs *) (sf + 1); + + if (kstack_is_trap_frame(current_thread_info(), regs)) { + if (user_mode(regs)) + break; + pc = regs->tpc; + fp = regs->u_regs[UREG_I6] + STACK_BIAS; + } else { + pc = sf->callers_pc; + fp = (unsigned long)sf->fp + STACK_BIAS; + } + callchain_store(entry, pc); + } while (entry->nr < PERF_MAX_STACK_DEPTH); +} + +static void perf_callchain_user_64(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long ufp; + + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, regs->tpc); + + ufp = regs->u_regs[UREG_I6] + STACK_BIAS; + do { + struct sparc_stackf *usf, sf; + unsigned long pc; + + usf = (struct sparc_stackf *) ufp; + if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) + break; + + pc = sf.callers_pc; + ufp = (unsigned long)sf.fp + STACK_BIAS; + callchain_store(entry, pc); + } while (entry->nr < PERF_MAX_STACK_DEPTH); +} + +static void perf_callchain_user_32(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long ufp; + + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, regs->tpc); + + ufp = regs->u_regs[UREG_I6]; + do { + struct sparc_stackf32 *usf, sf; + unsigned long pc; + + usf = (struct sparc_stackf32 *) ufp; + if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) + break; + + pc = sf.callers_pc; + ufp = (unsigned long)sf.fp; + callchain_store(entry, pc); + } while (entry->nr < PERF_MAX_STACK_DEPTH); +} + +/* Like powerpc we can't get PMU interrupts within the PMU handler, + * so no need for seperate NMI and IRQ chains as on x86. + */ +static DEFINE_PER_CPU(struct perf_callchain_entry, callchain); + +struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) +{ + struct perf_callchain_entry *entry = &__get_cpu_var(callchain); + + entry->nr = 0; + if (!user_mode(regs)) { + stack_trace_flush(); + perf_callchain_kernel(regs, entry); + if (current->mm) + regs = task_pt_regs(current); + else + regs = NULL; + } + if (regs) { + flushw_user(); + if (test_thread_flag(TIF_32BIT)) + perf_callchain_user_32(regs, entry); + else + perf_callchain_user_64(regs, entry); + } + return entry; +}