diff --git a/arch/i386/oprofile/Makefile b/arch/i386/oprofile/Makefile index a6945077e804a963c8e6b3f92b2278d16ef1ef81..30f3eb3666675e4b8a90bff8f0edfd43cfe99f6f 100644 --- a/arch/i386/oprofile/Makefile +++ b/arch/i386/oprofile/Makefile @@ -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 diff --git a/arch/i386/oprofile/backtrace.c b/arch/i386/oprofile/backtrace.c new file mode 100644 index 0000000000000000000000000000000000000000..49a06053f70f0b5bc894d8276ca4ab7e133e3430 --- /dev/null +++ b/arch/i386/oprofile/backtrace.c @@ -0,0 +1,124 @@ +/** + * @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(¤t->mm->page_table_lock)) + return; +#endif + + while (depth-- && head && pages_present(head)) + head = dump_backtrace(head); + +#ifdef CONFIG_SMP + spin_unlock(¤t->mm->page_table_lock); +#endif +} diff --git a/arch/i386/oprofile/init.c b/arch/i386/oprofile/init.c index 1b7747ca726a676097ea0c23ba91619e96db221b..672eed39d53e3c2ab8f64ea9b6811023ad28a3a6 100644 --- a/arch/i386/oprofile/init.c +++ b/arch/i386/oprofile/init.c @@ -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; } diff --git a/arch/i386/oprofile/nmi_int.c b/arch/i386/oprofile/nmi_int.c index d3450dcc53e0eeb3b425d8f3ada63ad6c5087d4b..d4ffd3f9c33bae8aab5be49183bd96604beb3d30 100644 --- a/arch/i386/oprofile/nmi_int.c +++ b/arch/i386/oprofile/nmi_int.c @@ -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; } diff --git a/arch/i386/oprofile/nmi_timer_int.c b/arch/i386/oprofile/nmi_timer_int.c index c3351b824187427499501195e53f4f80bddfaf83..b2e462abf337e44ffe7f2417b26b865542f193b9 100644 --- a/arch/i386/oprofile/nmi_timer_int.c +++ b/arch/i386/oprofile/nmi_timer_int.c @@ -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; } diff --git a/arch/i386/oprofile/op_model_athlon.c b/arch/i386/oprofile/op_model_athlon.c index 0d4e00f42995ff2a9ed5836637177a7be3f4c764..3ad9a72a5036b324d32a976b034dedd03fbc09f0 100644 --- a/arch/i386/oprofile/op_model_athlon.c +++ b/arch/i386/oprofile/op_model_athlon.c @@ -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); } } diff --git a/arch/i386/oprofile/op_model_p4.c b/arch/i386/oprofile/op_model_p4.c index 1b3626df5dd227cbfafaaf6814d2d6f48f47d551..ac8a066035c24a1f9a9cc3f3d63e57ce29eee33d 100644 --- a/arch/i386/oprofile/op_model_p4.c +++ b/arch/i386/oprofile/op_model_p4.c @@ -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); diff --git a/arch/i386/oprofile/op_model_ppro.c b/arch/i386/oprofile/op_model_ppro.c index 0cc80654db626cbc2f374880e395dbadcefd3594..d719015fc0444a8e229448654f16d7351bb41798 100644 --- a/arch/i386/oprofile/op_model_ppro.c +++ b/arch/i386/oprofile/op_model_ppro.c @@ -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); } } diff --git a/arch/i386/oprofile/op_x86_model.h b/arch/i386/oprofile/op_x86_model.h index 5cc2514670f3fecedf969297aab1097203cd238f..123b7e90a9eed803699470e6eb44010f8f2428db 100644 --- a/arch/i386/oprofile/op_x86_model.h +++ b/arch/i386/oprofile/op_x86_model.h @@ -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); }; diff --git a/arch/x86_64/oprofile/Makefile b/arch/x86_64/oprofile/Makefile index f4e3ccb8b7258ba161cf9526340f6a5538429976..6be32683e1bc71081fc8b8a888a38f2e2b58a884 100644 --- a/arch/x86_64/oprofile/Makefile +++ b/arch/x86_64/oprofile/Makefile @@ -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