Commit 6640317b authored by Greg Banks's avatar Greg Banks Committed by Linus Torvalds

[PATCH] oprofile: i386 support for stack trace sampling

oprofile i386 arch updates, including some internal API changes and support
for stack trace sampling.

(akpm: I added a nasty hack to fix the x86_64 build.  However the feature is
untested on x86_64 and probably doesn't work yet).
Signed-off-by: default avatarJohn Levon <levon@movementarian.org>
Signed-off-by: default avatarGreg Banks <gnb@melbourne.sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 8404d51c
......@@ -6,7 +6,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofilefs.o oprofile_stats.o \
timer_int.o )
oprofile-y := $(DRIVER_OBJS) init.o
oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \
op_model_ppro.o op_model_p4.o
oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o
/**
* @file backtrace.c
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon
* @author David Smith
*/
#include <linux/oprofile.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/ptrace.h>
struct frame_head {
struct frame_head * ebp;
unsigned long ret;
} __attribute__((packed));
static struct frame_head *
dump_backtrace(struct frame_head * head)
{
oprofile_add_trace(head->ret);
/* frame pointers should strictly progress back up the stack
* (towards higher addresses) */
if (head >= head->ebp)
return 0;
return head->ebp;
}
#ifdef CONFIG_X86_4G
/* With a 4G kernel/user split, user pages are not directly
* accessible from the kernel, so don't try
*/
static int pages_present(struct frame_head * head)
{
return 0;
}
#else
/* check that the page(s) containing the frame head are present */
static int pages_present(struct frame_head * head)
{
struct mm_struct * mm = current->mm;
/* FIXME: only necessary once per page */
if (!check_user_page_readable(mm, (unsigned long)head))
return 0;
return check_user_page_readable(mm, (unsigned long)(head + 1));
}
#endif /* CONFIG_X86_4G */
/*
* | | /\ Higher addresses
* | |
* --------------- stack base (address of current_thread_info)
* | thread info |
* . .
* | stack |
* --------------- saved regs->ebp value if valid (frame_head address)
* . .
* --------------- struct pt_regs stored on stack (struct pt_regs *)
* | |
* . .
* | |
* --------------- %esp
* | |
* | | \/ Lower addresses
*
* Thus, &pt_regs <-> stack base restricts the valid(ish) ebp values
*/
#ifdef CONFIG_FRAME_POINTER
static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
{
unsigned long headaddr = (unsigned long)head;
unsigned long stack = (unsigned long)regs;
unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
return headaddr > stack && headaddr < stack_base;
}
#else
/* without fp, it's just junk */
static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
{
return 0;
}
#endif
void
x86_backtrace(struct pt_regs * const regs, unsigned int depth)
{
struct frame_head *head;
#ifdef CONFIG_X86_64
head = (struct frame_head *)regs->rbp;
#else
head = (struct frame_head *)regs->ebp;
#endif
if (!user_mode(regs)) {
while (depth-- && valid_kernel_stack(head, regs))
head = dump_backtrace(head);
return;
}
#ifdef CONFIG_SMP
if (!spin_trylock(&current->mm->page_table_lock))
return;
#endif
while (depth-- && head && pages_present(head))
head = dump_backtrace(head);
#ifdef CONFIG_SMP
spin_unlock(&current->mm->page_table_lock);
#endif
}
......@@ -15,22 +15,26 @@
* with the NMI mode driver.
*/
extern int nmi_init(struct oprofile_operations ** ops);
extern int nmi_timer_init(struct oprofile_operations **ops);
extern int nmi_init(struct oprofile_operations * ops);
extern int nmi_timer_init(struct oprofile_operations * ops);
extern void nmi_exit(void);
extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
int __init oprofile_arch_init(struct oprofile_operations ** ops)
void __init oprofile_arch_init(struct oprofile_operations * ops)
{
int ret = -ENODEV;
int ret;
ret = -ENODEV;
#ifdef CONFIG_X86_LOCAL_APIC
ret = nmi_init(ops);
#endif
#ifdef CONFIG_X86_IO_APIC
if (ret < 0)
ret = nmi_timer_init(ops);
#endif
return ret;
ops->backtrace = x86_backtrace;
}
......
......@@ -84,7 +84,7 @@ static void __exit exit_driverfs(void)
static int nmi_callback(struct pt_regs * regs, int cpu)
{
return model->check_ctrs(cpu, &cpu_msrs[cpu], regs);
return model->check_ctrs(regs, &cpu_msrs[cpu]);
}
......@@ -300,16 +300,7 @@ static int nmi_create_files(struct super_block * sb, struct dentry * root)
}
struct oprofile_operations nmi_ops = {
.create_files = nmi_create_files,
.setup = nmi_setup,
.shutdown = nmi_shutdown,
.start = nmi_start,
.stop = nmi_stop
};
static int __init p4_init(void)
static int __init p4_init(char ** cpu_type)
{
__u8 cpu_model = current_cpu_data.x86_model;
......@@ -317,18 +308,18 @@ static int __init p4_init(void)
return 0;
#ifndef CONFIG_SMP
nmi_ops.cpu_type = "i386/p4";
*cpu_type = "i386/p4";
model = &op_p4_spec;
return 1;
#else
switch (smp_num_siblings) {
case 1:
nmi_ops.cpu_type = "i386/p4";
*cpu_type = "i386/p4";
model = &op_p4_spec;
return 1;
case 2:
nmi_ops.cpu_type = "i386/p4-ht";
*cpu_type = "i386/p4-ht";
model = &op_p4_ht2_spec;
return 1;
}
......@@ -340,7 +331,7 @@ static int __init p4_init(void)
}
static int __init ppro_init(void)
static int __init ppro_init(char ** cpu_type)
{
__u8 cpu_model = current_cpu_data.x86_model;
......@@ -348,13 +339,13 @@ static int __init ppro_init(void)
return 0;
if (cpu_model == 9) {
nmi_ops.cpu_type = "i386/p6_mobile";
*cpu_type = "i386/p6_mobile";
} else if (cpu_model > 5) {
nmi_ops.cpu_type = "i386/piii";
*cpu_type = "i386/piii";
} else if (cpu_model > 2) {
nmi_ops.cpu_type = "i386/pii";
*cpu_type = "i386/pii";
} else {
nmi_ops.cpu_type = "i386/ppro";
*cpu_type = "i386/ppro";
}
model = &op_ppro_spec;
......@@ -364,11 +355,12 @@ static int __init ppro_init(void)
/* in order to get driverfs right */
static int using_nmi;
int __init nmi_init(struct oprofile_operations ** ops)
int __init nmi_init(struct oprofile_operations *ops)
{
__u8 vendor = current_cpu_data.x86_vendor;
__u8 family = current_cpu_data.x86;
char *cpu_type;
if (!cpu_has_apic)
return -ENODEV;
......@@ -381,13 +373,13 @@ int __init nmi_init(struct oprofile_operations ** ops)
return -ENODEV;
case 6:
model = &op_athlon_spec;
nmi_ops.cpu_type = "i386/athlon";
cpu_type = "i386/athlon";
break;
case 0xf:
model = &op_athlon_spec;
/* Actually it could be i386/hammer too, but give
user space an consistent name. */
nmi_ops.cpu_type = "x86-64/hammer";
cpu_type = "x86-64/hammer";
break;
}
break;
......@@ -396,13 +388,13 @@ int __init nmi_init(struct oprofile_operations ** ops)
switch (family) {
/* Pentium IV */
case 0xf:
if (!p4_init())
if (!p4_init(&cpu_type))
return -ENODEV;
break;
/* A P6-class processor */
case 6:
if (!ppro_init())
if (!ppro_init(&cpu_type))
return -ENODEV;
break;
......@@ -417,7 +409,12 @@ int __init nmi_init(struct oprofile_operations ** ops)
init_driverfs();
using_nmi = 1;
*ops = &nmi_ops;
ops->create_files = nmi_create_files;
ops->setup = nmi_setup;
ops->shutdown = nmi_shutdown;
ops->start = nmi_start;
ops->stop = nmi_stop;
ops->cpu_type = cpu_type;
printk(KERN_INFO "oprofile: using NMI interrupt.\n");
return 0;
}
......
......@@ -20,9 +20,7 @@
static int nmi_timer_callback(struct pt_regs * regs, int cpu)
{
unsigned long eip = instruction_pointer(regs);
oprofile_add_sample(eip, !user_mode(regs), 0, cpu);
oprofile_add_sample(regs, 0);
return 1;
}
......@@ -42,20 +40,16 @@ static void timer_stop(void)
}
static struct oprofile_operations nmi_timer_ops = {
.start = timer_start,
.stop = timer_stop,
.cpu_type = "timer"
};
int __init nmi_timer_init(struct oprofile_operations ** ops)
int __init nmi_timer_init(struct oprofile_operations * ops)
{
extern int nmi_active;
if (nmi_active <= 0)
return -ENODEV;
*ops = &nmi_timer_ops;
ops->start = timer_start;
ops->stop = timer_stop;
ops->cpu_type = "timer";
printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
return 0;
}
......@@ -90,19 +90,16 @@ static void athlon_setup_ctrs(struct op_msrs const * const msrs)
}
static int athlon_check_ctrs(unsigned int const cpu,
struct op_msrs const * const msrs,
struct pt_regs * const regs)
static int athlon_check_ctrs(struct pt_regs * const regs,
struct op_msrs const * const msrs)
{
unsigned int low, high;
int i;
unsigned long eip = profile_pc(regs);
int is_kernel = !user_mode(regs);
for (i = 0 ; i < NUM_COUNTERS; ++i) {
CTR_READ(low, high, msrs, i);
if (CTR_OVERFLOWED(low)) {
oprofile_add_sample(eip, is_kernel, i, cpu);
oprofile_add_sample(regs, i);
CTR_WRITE(reset_value[i], msrs, i);
}
}
......
......@@ -619,14 +619,11 @@ static void p4_setup_ctrs(struct op_msrs const * const msrs)
}
static int p4_check_ctrs(unsigned int const cpu,
struct op_msrs const * const msrs,
struct pt_regs * const regs)
static int p4_check_ctrs(struct pt_regs * const regs,
struct op_msrs const * const msrs)
{
unsigned long ctr, low, high, stag, real;
int i;
unsigned long eip = profile_pc(regs);
int is_kernel = !user_mode(regs);
stag = get_stagger();
......@@ -657,7 +654,7 @@ static int p4_check_ctrs(unsigned int const cpu,
CCCR_READ(low, high, real);
CTR_READ(ctr, high, real);
if (CCCR_OVF_P(low) || CTR_OVERFLOW_P(ctr)) {
oprofile_add_sample(eip, is_kernel, i, cpu);
oprofile_add_sample(regs, i);
CTR_WRITE(reset_value[i], real);
CCCR_CLEAR_OVF(low);
CCCR_WRITE(low, high, real);
......
......@@ -85,19 +85,16 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs)
}
static int ppro_check_ctrs(unsigned int const cpu,
struct op_msrs const * const msrs,
struct pt_regs * const regs)
static int ppro_check_ctrs(struct pt_regs * const regs,
struct op_msrs const * const msrs)
{
unsigned int low, high;
int i;
unsigned long eip = profile_pc(regs);
int is_kernel = !user_mode(regs);
for (i = 0 ; i < NUM_COUNTERS; ++i) {
CTR_READ(low, high, msrs, i);
if (CTR_OVERFLOWED(low)) {
oprofile_add_sample(eip, is_kernel, i, cpu);
oprofile_add_sample(regs, i);
CTR_WRITE(reset_value[i], msrs, i);
}
}
......
......@@ -36,9 +36,8 @@ struct op_x86_model_spec {
unsigned int const num_controls;
void (*fill_in_addresses)(struct op_msrs * const msrs);
void (*setup_ctrs)(struct op_msrs const * const msrs);
int (*check_ctrs)(unsigned int const cpu,
struct op_msrs const * const msrs,
struct pt_regs * const regs);
int (*check_ctrs)(struct pt_regs * const regs,
struct op_msrs const * const msrs);
void (*start)(struct op_msrs const * const msrs);
void (*stop)(struct op_msrs const * const msrs);
};
......
......@@ -11,7 +11,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofilefs.o oprofile_stats.o \
timer_int.o )
OPROFILE-y := init.o
OPROFILE-y := init.o backtrace.o
OPROFILE-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o op_model_p4.o \
op_model_ppro.o
OPROFILE-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment