Skip to content

Commit

Permalink
sparc64: Add perf callchain support.
Browse files Browse the repository at this point in the history
Pretty straightforward, and it should be easy to add accurate
walk through of signal stack frames in userspace.

Signed-off-by: David S. Miller <davem@davemloft.net>
Tested-by: Jens Axboe <jens.axboe@oracle.com>
  • Loading branch information
David S. Miller committed Jan 21, 2010
1 parent 0299b13 commit 4f6dbe4
Showing 1 changed file with 119 additions and 1 deletion.
120 changes: 119 additions & 1 deletion arch/sparc/kernel/perf_event.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Performance event support for sparc64.
*
* Copyright (C) 2009 David S. Miller <davem@davemloft.net>
* Copyright (C) 2009, 2010 David S. Miller <davem@davemloft.net>
*
* This code is based almost entirely upon the x86 perf event
* code, which is:
Expand All @@ -18,11 +18,15 @@
#include <linux/kdebug.h>
#include <linux/mutex.h>

#include <asm/stacktrace.h>
#include <asm/cpudata.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/nmi.h>
#include <asm/pcr.h>

#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.
Expand Down Expand Up @@ -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;
}

0 comments on commit 4f6dbe4

Please sign in to comment.