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/, \ ...@@ -6,7 +6,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofilefs.o oprofile_stats.o \ oprofilefs.o oprofile_stats.o \
timer_int.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 \ oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \
op_model_ppro.o op_model_p4.o op_model_ppro.o op_model_p4.o
oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.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 @@ ...@@ -15,22 +15,26 @@
* with the NMI mode driver. * with the NMI mode driver.
*/ */
extern int nmi_init(struct oprofile_operations ** ops); extern int nmi_init(struct oprofile_operations * ops);
extern int nmi_timer_init(struct oprofile_operations **ops); extern int nmi_timer_init(struct oprofile_operations * ops);
extern void nmi_exit(void); 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 #ifdef CONFIG_X86_LOCAL_APIC
ret = nmi_init(ops); ret = nmi_init(ops);
#endif #endif
#ifdef CONFIG_X86_IO_APIC #ifdef CONFIG_X86_IO_APIC
if (ret < 0) if (ret < 0)
ret = nmi_timer_init(ops); ret = nmi_timer_init(ops);
#endif #endif
return ret; ops->backtrace = x86_backtrace;
} }
......
...@@ -84,7 +84,7 @@ static void __exit exit_driverfs(void) ...@@ -84,7 +84,7 @@ static void __exit exit_driverfs(void)
static int nmi_callback(struct pt_regs * regs, int cpu) 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) ...@@ -300,16 +300,7 @@ static int nmi_create_files(struct super_block * sb, struct dentry * root)
} }
struct oprofile_operations nmi_ops = { static int __init p4_init(char ** cpu_type)
.create_files = nmi_create_files,
.setup = nmi_setup,
.shutdown = nmi_shutdown,
.start = nmi_start,
.stop = nmi_stop
};
static int __init p4_init(void)
{ {
__u8 cpu_model = current_cpu_data.x86_model; __u8 cpu_model = current_cpu_data.x86_model;
...@@ -317,18 +308,18 @@ static int __init p4_init(void) ...@@ -317,18 +308,18 @@ static int __init p4_init(void)
return 0; return 0;
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
nmi_ops.cpu_type = "i386/p4"; *cpu_type = "i386/p4";
model = &op_p4_spec; model = &op_p4_spec;
return 1; return 1;
#else #else
switch (smp_num_siblings) { switch (smp_num_siblings) {
case 1: case 1:
nmi_ops.cpu_type = "i386/p4"; *cpu_type = "i386/p4";
model = &op_p4_spec; model = &op_p4_spec;
return 1; return 1;
case 2: case 2:
nmi_ops.cpu_type = "i386/p4-ht"; *cpu_type = "i386/p4-ht";
model = &op_p4_ht2_spec; model = &op_p4_ht2_spec;
return 1; return 1;
} }
...@@ -340,7 +331,7 @@ static int __init p4_init(void) ...@@ -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; __u8 cpu_model = current_cpu_data.x86_model;
...@@ -348,13 +339,13 @@ static int __init ppro_init(void) ...@@ -348,13 +339,13 @@ static int __init ppro_init(void)
return 0; return 0;
if (cpu_model == 9) { if (cpu_model == 9) {
nmi_ops.cpu_type = "i386/p6_mobile"; *cpu_type = "i386/p6_mobile";
} else if (cpu_model > 5) { } else if (cpu_model > 5) {
nmi_ops.cpu_type = "i386/piii"; *cpu_type = "i386/piii";
} else if (cpu_model > 2) { } else if (cpu_model > 2) {
nmi_ops.cpu_type = "i386/pii"; *cpu_type = "i386/pii";
} else { } else {
nmi_ops.cpu_type = "i386/ppro"; *cpu_type = "i386/ppro";
} }
model = &op_ppro_spec; model = &op_ppro_spec;
...@@ -364,11 +355,12 @@ static int __init ppro_init(void) ...@@ -364,11 +355,12 @@ static int __init ppro_init(void)
/* in order to get driverfs right */ /* in order to get driverfs right */
static int using_nmi; 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 vendor = current_cpu_data.x86_vendor;
__u8 family = current_cpu_data.x86; __u8 family = current_cpu_data.x86;
char *cpu_type;
if (!cpu_has_apic) if (!cpu_has_apic)
return -ENODEV; return -ENODEV;
...@@ -381,13 +373,13 @@ int __init nmi_init(struct oprofile_operations ** ops) ...@@ -381,13 +373,13 @@ int __init nmi_init(struct oprofile_operations ** ops)
return -ENODEV; return -ENODEV;
case 6: case 6:
model = &op_athlon_spec; model = &op_athlon_spec;
nmi_ops.cpu_type = "i386/athlon"; cpu_type = "i386/athlon";
break; break;
case 0xf: case 0xf:
model = &op_athlon_spec; model = &op_athlon_spec;
/* Actually it could be i386/hammer too, but give /* Actually it could be i386/hammer too, but give
user space an consistent name. */ user space an consistent name. */
nmi_ops.cpu_type = "x86-64/hammer"; cpu_type = "x86-64/hammer";
break; break;
} }
break; break;
...@@ -396,13 +388,13 @@ int __init nmi_init(struct oprofile_operations ** ops) ...@@ -396,13 +388,13 @@ int __init nmi_init(struct oprofile_operations ** ops)
switch (family) { switch (family) {
/* Pentium IV */ /* Pentium IV */
case 0xf: case 0xf:
if (!p4_init()) if (!p4_init(&cpu_type))
return -ENODEV; return -ENODEV;
break; break;
/* A P6-class processor */ /* A P6-class processor */
case 6: case 6:
if (!ppro_init()) if (!ppro_init(&cpu_type))
return -ENODEV; return -ENODEV;
break; break;
...@@ -417,7 +409,12 @@ int __init nmi_init(struct oprofile_operations ** ops) ...@@ -417,7 +409,12 @@ int __init nmi_init(struct oprofile_operations ** ops)
init_driverfs(); init_driverfs();
using_nmi = 1; 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"); printk(KERN_INFO "oprofile: using NMI interrupt.\n");
return 0; return 0;
} }
......
...@@ -20,9 +20,7 @@ ...@@ -20,9 +20,7 @@
static int nmi_timer_callback(struct pt_regs * regs, int cpu) static int nmi_timer_callback(struct pt_regs * regs, int cpu)
{ {
unsigned long eip = instruction_pointer(regs); oprofile_add_sample(regs, 0);
oprofile_add_sample(eip, !user_mode(regs), 0, cpu);
return 1; return 1;
} }
...@@ -42,20 +40,16 @@ static void timer_stop(void) ...@@ -42,20 +40,16 @@ static void timer_stop(void)
} }
static struct oprofile_operations nmi_timer_ops = { int __init nmi_timer_init(struct oprofile_operations * ops)
.start = timer_start,
.stop = timer_stop,
.cpu_type = "timer"
};
int __init nmi_timer_init(struct oprofile_operations ** ops)
{ {
extern int nmi_active; extern int nmi_active;
if (nmi_active <= 0) if (nmi_active <= 0)
return -ENODEV; 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"); printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
return 0; return 0;
} }
...@@ -90,19 +90,16 @@ static void athlon_setup_ctrs(struct op_msrs const * const msrs) ...@@ -90,19 +90,16 @@ static void athlon_setup_ctrs(struct op_msrs const * const msrs)
} }
static int athlon_check_ctrs(unsigned int const cpu, static int athlon_check_ctrs(struct pt_regs * const regs,
struct op_msrs const * const msrs, struct op_msrs const * const msrs)
struct pt_regs * const regs)
{ {
unsigned int low, high; unsigned int low, high;
int i; int i;
unsigned long eip = profile_pc(regs);
int is_kernel = !user_mode(regs);
for (i = 0 ; i < NUM_COUNTERS; ++i) { for (i = 0 ; i < NUM_COUNTERS; ++i) {
CTR_READ(low, high, msrs, i); CTR_READ(low, high, msrs, i);
if (CTR_OVERFLOWED(low)) { if (CTR_OVERFLOWED(low)) {
oprofile_add_sample(eip, is_kernel, i, cpu); oprofile_add_sample(regs, i);
CTR_WRITE(reset_value[i], msrs, i); CTR_WRITE(reset_value[i], msrs, i);
} }
} }
......
...@@ -619,14 +619,11 @@ static void p4_setup_ctrs(struct op_msrs const * const msrs) ...@@ -619,14 +619,11 @@ static void p4_setup_ctrs(struct op_msrs const * const msrs)
} }
static int p4_check_ctrs(unsigned int const cpu, static int p4_check_ctrs(struct pt_regs * const regs,
struct op_msrs const * const msrs, struct op_msrs const * const msrs)
struct pt_regs * const regs)
{ {
unsigned long ctr, low, high, stag, real; unsigned long ctr, low, high, stag, real;
int i; int i;
unsigned long eip = profile_pc(regs);
int is_kernel = !user_mode(regs);
stag = get_stagger(); stag = get_stagger();
...@@ -657,7 +654,7 @@ static int p4_check_ctrs(unsigned int const cpu, ...@@ -657,7 +654,7 @@ static int p4_check_ctrs(unsigned int const cpu,
CCCR_READ(low, high, real); CCCR_READ(low, high, real);
CTR_READ(ctr, high, real); CTR_READ(ctr, high, real);
if (CCCR_OVF_P(low) || CTR_OVERFLOW_P(ctr)) { 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); CTR_WRITE(reset_value[i], real);
CCCR_CLEAR_OVF(low); CCCR_CLEAR_OVF(low);
CCCR_WRITE(low, high, real); CCCR_WRITE(low, high, real);
......
...@@ -85,19 +85,16 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs) ...@@ -85,19 +85,16 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs)
} }
static int ppro_check_ctrs(unsigned int const cpu, static int ppro_check_ctrs(struct pt_regs * const regs,
struct op_msrs const * const msrs, struct op_msrs const * const msrs)
struct pt_regs * const regs)
{ {
unsigned int low, high; unsigned int low, high;
int i; int i;
unsigned long eip = profile_pc(regs);
int is_kernel = !user_mode(regs);
for (i = 0 ; i < NUM_COUNTERS; ++i) { for (i = 0 ; i < NUM_COUNTERS; ++i) {
CTR_READ(low, high, msrs, i); CTR_READ(low, high, msrs, i);
if (CTR_OVERFLOWED(low)) { if (CTR_OVERFLOWED(low)) {
oprofile_add_sample(eip, is_kernel, i, cpu); oprofile_add_sample(regs, i);
CTR_WRITE(reset_value[i], msrs, i); CTR_WRITE(reset_value[i], msrs, i);
} }
} }
......
...@@ -36,9 +36,8 @@ struct op_x86_model_spec { ...@@ -36,9 +36,8 @@ struct op_x86_model_spec {
unsigned int const num_controls; unsigned int const num_controls;
void (*fill_in_addresses)(struct op_msrs * const msrs); void (*fill_in_addresses)(struct op_msrs * const msrs);
void (*setup_ctrs)(struct op_msrs const * const msrs); void (*setup_ctrs)(struct op_msrs const * const msrs);
int (*check_ctrs)(unsigned int const cpu, int (*check_ctrs)(struct pt_regs * const regs,
struct op_msrs const * const msrs, struct op_msrs const * const msrs);
struct pt_regs * const regs);
void (*start)(struct op_msrs const * const msrs); void (*start)(struct op_msrs const * const msrs);
void (*stop)(struct op_msrs const * const msrs); void (*stop)(struct op_msrs const * const msrs);
}; };
......
...@@ -11,7 +11,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ ...@@ -11,7 +11,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofilefs.o oprofile_stats.o \ oprofilefs.o oprofile_stats.o \
timer_int.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 \ OPROFILE-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o op_model_p4.o \
op_model_ppro.o op_model_ppro.o
OPROFILE-$(CONFIG_X86_IO_APIC) += nmi_timer_int.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