Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 147315
b: refs/heads/master
c: d7d59fb
h: refs/heads/master
i:
  147313: 4c8f3cc
  147311: 3e01645
v: v3
  • Loading branch information
Peter Zijlstra authored and Ingo Molnar committed Apr 6, 2009
1 parent 261f55a commit bf7c339
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 394ee07623cf556c8daae2b3c00cf5fea47f0811
refs/heads/master: d7d59fb323833682b117b528d77eeb8ef587036a
154 changes: 154 additions & 0 deletions trunk/arch/x86/kernel/cpu/perf_counter.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
#include <linux/module.h>
#include <linux/kdebug.h>
#include <linux/sched.h>
#include <linux/uaccess.h>

#include <asm/apic.h>
#include <asm/stacktrace.h>

static bool perf_counters_initialized __read_mostly;

Expand Down Expand Up @@ -958,3 +960,155 @@ hw_perf_counter_init(struct perf_counter *counter)

return &x86_perf_counter_ops;
}

/*
* callchain support
*/

static inline
void callchain_store(struct perf_callchain_entry *entry, unsigned long ip)
{
if (entry->nr < MAX_STACK_DEPTH)
entry->ip[entry->nr++] = ip;
}

static DEFINE_PER_CPU(struct perf_callchain_entry, irq_entry);
static DEFINE_PER_CPU(struct perf_callchain_entry, nmi_entry);


static void
backtrace_warning_symbol(void *data, char *msg, unsigned long symbol)
{
/* Ignore warnings */
}

static void backtrace_warning(void *data, char *msg)
{
/* Ignore warnings */
}

static int backtrace_stack(void *data, char *name)
{
/* Don't bother with IRQ stacks for now */
return -1;
}

static void backtrace_address(void *data, unsigned long addr, int reliable)
{
struct perf_callchain_entry *entry = data;

if (reliable)
callchain_store(entry, addr);
}

static const struct stacktrace_ops backtrace_ops = {
.warning = backtrace_warning,
.warning_symbol = backtrace_warning_symbol,
.stack = backtrace_stack,
.address = backtrace_address,
};

static void
perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry)
{
unsigned long bp;
char *stack;

callchain_store(entry, instruction_pointer(regs));

stack = ((char *)regs + sizeof(struct pt_regs));
#ifdef CONFIG_FRAME_POINTER
bp = frame_pointer(regs);
#else
bp = 0;
#endif

dump_trace(NULL, regs, (void *)stack, bp, &backtrace_ops, entry);
}


struct stack_frame {
const void __user *next_fp;
unsigned long return_address;
};

static int copy_stack_frame(const void __user *fp, struct stack_frame *frame)
{
int ret;

if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
return 0;

ret = 1;
pagefault_disable();
if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
ret = 0;
pagefault_enable();

return ret;
}

static void
perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry)
{
struct stack_frame frame;
const void __user *fp;

regs = (struct pt_regs *)current->thread.sp0 - 1;
fp = (void __user *)regs->bp;

callchain_store(entry, regs->ip);

while (entry->nr < MAX_STACK_DEPTH) {
frame.next_fp = NULL;
frame.return_address = 0;

if (!copy_stack_frame(fp, &frame))
break;

if ((unsigned long)fp < user_stack_pointer(regs))
break;

callchain_store(entry, frame.return_address);
fp = frame.next_fp;
}
}

static void
perf_do_callchain(struct pt_regs *regs, struct perf_callchain_entry *entry)
{
int is_user;

if (!regs)
return;

is_user = user_mode(regs);

if (!current || current->pid == 0)
return;

if (is_user && current->state != TASK_RUNNING)
return;

if (!is_user)
perf_callchain_kernel(regs, entry);

if (current->mm)
perf_callchain_user(regs, entry);
}

struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
{
struct perf_callchain_entry *entry;

if (in_nmi())
entry = &__get_cpu_var(nmi_entry);
else
entry = &__get_cpu_var(irq_entry);

entry->nr = 0;

perf_do_callchain(regs, entry);

return entry;
}

0 comments on commit bf7c339

Please sign in to comment.