diff -ur linux-2.6.9/arch/alpha/oprofile/common.c linux-2.6.9-callgraph/arch/alpha/oprofile/common.c --- linux-2.6.9/arch/alpha/oprofile/common.c 2004-10-25 19:41:35.000000000 -0400 +++ linux-2.6.9-callgraph/arch/alpha/oprofile/common.c 2004-10-20 13:11:42.000000000 -0400 @@ -138,17 +138,8 @@ return 0; } -static struct oprofile_operations oprof_axp_ops = { - .create_files = op_axp_create_files, - .setup = op_axp_setup, - .shutdown = op_axp_shutdown, - .start = op_axp_start, - .stop = op_axp_stop, - .cpu_type = NULL /* To be filled in below. */ -}; - -int __init -oprofile_arch_init(struct oprofile_operations **ops) +void __init +oprofile_arch_init(struct oprofile_operations *ops) { struct op_axp_model *lmodel = NULL; @@ -175,16 +166,18 @@ } if (!lmodel) - return -ENODEV; + return; model = lmodel; - oprof_axp_ops.cpu_type = lmodel->cpu_type; - *ops = &oprof_axp_ops; - + ops->create_files = op_axp_create_files; + ops->setup = op_axp_setup; + ops->shutdown = op_axp_shutdown; + ops->start = op_axp_start; + ops->stop = op_axp_stop; + ops->cpu_type = lmodel->cpu_type; + printk(KERN_INFO "oprofile: using %s performance monitoring.\n", lmodel->cpu_type); - - return 0; } diff -ur linux-2.6.9/arch/alpha/oprofile/op_model_ev4.c linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev4.c --- linux-2.6.9/arch/alpha/oprofile/op_model_ev4.c 2004-01-09 01:59:26.000000000 -0500 +++ linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev4.c 2004-10-20 13:11:42.000000000 -0400 @@ -101,8 +101,7 @@ return; /* Record the sample. */ - oprofile_add_sample(regs->pc, !user_mode(regs), - which, smp_processor_id()); + oprofile_add_sample(regs, which); } diff -ur linux-2.6.9/arch/alpha/oprofile/op_model_ev5.c linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev5.c --- linux-2.6.9/arch/alpha/oprofile/op_model_ev5.c 2004-01-09 01:59:26.000000000 -0500 +++ linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev5.c 2004-10-20 13:11:42.000000000 -0400 @@ -186,8 +186,7 @@ struct op_counter_config *ctr) { /* Record the sample. */ - oprofile_add_sample(regs->pc, !user_mode(regs), - which, smp_processor_id()); + oprofile_add_sample(regs, which); } diff -ur linux-2.6.9/arch/alpha/oprofile/op_model_ev67.c linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev67.c --- linux-2.6.9/arch/alpha/oprofile/op_model_ev67.c 2004-01-09 02:00:04.000000000 -0500 +++ linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev67.c 2004-10-20 13:11:42.000000000 -0400 @@ -138,8 +138,7 @@ if (counter == 1) fake_counter += PM_NUM_COUNTERS; if (ctr[fake_counter].enabled) - oprofile_add_sample(pc, kern, fake_counter, - smp_processor_id()); + oprofile_add_pc(pc, kern, fake_counter); } static void @@ -197,8 +196,7 @@ to PALcode. Recognize ITB miss by PALcode offset address, and get actual PC from EXC_ADDR. */ - oprofile_add_sample(regs->pc, kern, which, - smp_processor_id()); + oprofile_add_pc(regs->pc, kern, which); if ((pmpc & ((1 << 15) - 1)) == 581) op_add_pm(regs->pc, kern, which, ctr, PM_ITB_MISS); @@ -241,7 +239,7 @@ } } - oprofile_add_sample(pmpc, kern, which, smp_processor_id()); + oprofile_add_pc(pmpc, kern, which); pctr_ctl = wrperfmon(5, 0); if (pctr_ctl & (1UL << 27)) diff -ur linux-2.6.9/arch/alpha/oprofile/op_model_ev6.c linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev6.c --- linux-2.6.9/arch/alpha/oprofile/op_model_ev6.c 2004-01-09 01:59:44.000000000 -0500 +++ linux-2.6.9-callgraph/arch/alpha/oprofile/op_model_ev6.c 2004-10-20 13:11:42.000000000 -0400 @@ -88,8 +88,7 @@ struct op_counter_config *ctr) { /* Record the sample. */ - oprofile_add_sample(regs->pc, !user_mode(regs), - which, smp_processor_id()); + oprofile_add_sample(regs, which); } diff -ur linux-2.6.9/arch/arm/oprofile/common.c linux-2.6.9-callgraph/arch/arm/oprofile/common.c --- linux-2.6.9/arch/arm/oprofile/common.c 2004-10-25 19:43:32.000000000 -0400 +++ linux-2.6.9-callgraph/arch/arm/oprofile/common.c 2004-10-20 13:11:42.000000000 -0400 @@ -24,14 +24,6 @@ static void pmu_stop(void); static int pmu_create_files(struct super_block *, struct dentry *); -static struct oprofile_operations pmu_ops = { - .create_files = pmu_create_files, - .setup = pmu_setup, - .shutdown = pmu_stop, - .start = pmu_start, - .stop = pmu_stop, -}; - #ifdef CONFIG_PM static struct sys_device device_oprofile = { .id = 0, @@ -113,19 +105,22 @@ up(&pmu_sem); } -int __init pmu_init(struct oprofile_operations **ops, struct op_arm_model_spec *spec) +void __init pmu_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec) { init_MUTEX(&pmu_sem); if (spec->init() < 0) - return -ENODEV; + return; pmu_model = spec; init_driverfs(); - *ops = &pmu_ops; - pmu_ops.cpu_type = pmu_model->name; + ops->create_files = pmu_create_files; + ops->setup = pmu_setup; + ops->shutdown = pmu_stop; + ops->start = pmu_start; + ops->stop = pmu_stop; + ops->cpu_type = pmu_model->name; printk(KERN_INFO "oprofile: using %s PMU\n", spec->name); - return 0; } void pmu_exit(void) diff -ur linux-2.6.9/arch/arm/oprofile/init.c linux-2.6.9-callgraph/arch/arm/oprofile/init.c --- linux-2.6.9/arch/arm/oprofile/init.c 2004-10-25 19:43:32.000000000 -0400 +++ linux-2.6.9-callgraph/arch/arm/oprofile/init.c 2004-10-20 13:11:42.000000000 -0400 @@ -12,14 +12,11 @@ #include #include "op_arm_model.h" -int __init oprofile_arch_init(struct oprofile_operations **ops) +void __init oprofile_arch_init(struct oprofile_operations *ops) { - int ret = -ENODEV; - #ifdef CONFIG_CPU_XSCALE - ret = pmu_init(ops, &op_xscale_spec); + pmu_init(ops, &op_xscale_spec); #endif - return ret; } void oprofile_arch_exit(void) diff -ur linux-2.6.9/arch/arm/oprofile/op_model_xscale.c linux-2.6.9-callgraph/arch/arm/oprofile/op_model_xscale.c --- linux-2.6.9/arch/arm/oprofile/op_model_xscale.c 2004-10-25 19:46:31.000000000 -0400 +++ linux-2.6.9-callgraph/arch/arm/oprofile/op_model_xscale.c 2004-10-20 13:13:43.000000000 -0400 @@ -343,8 +343,7 @@ static irqreturn_t xscale_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) { - unsigned long pc = profile_pc(regs); - int i, is_kernel = !user_mode(regs); + int i; u32 pmnc; if (pmu->id == PMU_XSC1) @@ -357,7 +356,7 @@ continue; write_counter(i, -(u32)results[i].reset_counter); - oprofile_add_sample(pc, is_kernel, i, smp_processor_id()); + oprofile_add_sample(regs, i); results[i].ovf--; } diff -ur linux-2.6.9/arch/i386/oprofile/init.c linux-2.6.9-callgraph/arch/i386/oprofile/init.c --- linux-2.6.9/arch/i386/oprofile/init.c 2004-01-09 01:59:44.000000000 -0500 +++ linux-2.6.9-callgraph/arch/i386/oprofile/init.c 2004-10-20 13:11:42.000000000 -0400 @@ -15,22 +15,24 @@ * 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; + #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 -ur linux-2.6.9/arch/i386/oprofile/Makefile linux-2.6.9-callgraph/arch/i386/oprofile/Makefile --- linux-2.6.9/arch/i386/oprofile/Makefile 2004-01-09 01:59:57.000000000 -0500 +++ linux-2.6.9-callgraph/arch/i386/oprofile/Makefile 2004-10-20 13:11:42.000000000 -0400 @@ -6,7 +6,7 @@ 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 -ur linux-2.6.9/arch/i386/oprofile/nmi_int.c linux-2.6.9-callgraph/arch/i386/oprofile/nmi_int.c --- linux-2.6.9/arch/i386/oprofile/nmi_int.c 2004-10-25 19:43:33.000000000 -0400 +++ linux-2.6.9-callgraph/arch/i386/oprofile/nmi_int.c 2004-10-20 13:11:42.000000000 -0400 @@ -84,7 +84,7 @@ 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 @@ } -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 @@ 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 ppro_init(void) +static int __init ppro_init(char ** cpu_type) { __u8 cpu_model = current_cpu_data.x86_model; @@ -348,13 +339,13 @@ 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,10 +355,11 @@ /* 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 @@ 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 @@ 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 @@ 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 -ur linux-2.6.9/arch/i386/oprofile/nmi_timer_int.c linux-2.6.9-callgraph/arch/i386/oprofile/nmi_timer_int.c --- linux-2.6.9/arch/i386/oprofile/nmi_timer_int.c 2004-10-25 19:42:11.000000000 -0400 +++ linux-2.6.9-callgraph/arch/i386/oprofile/nmi_timer_int.c 2004-10-20 13:11:42.000000000 -0400 @@ -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 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 -ur linux-2.6.9/arch/i386/oprofile/op_model_athlon.c linux-2.6.9-callgraph/arch/i386/oprofile/op_model_athlon.c --- linux-2.6.9/arch/i386/oprofile/op_model_athlon.c 2004-10-25 19:46:32.000000000 -0400 +++ linux-2.6.9-callgraph/arch/i386/oprofile/op_model_athlon.c 2004-10-20 13:15:26.000000000 -0400 @@ -90,19 +90,16 @@ } -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 -ur linux-2.6.9/arch/i386/oprofile/op_model_p4.c linux-2.6.9-callgraph/arch/i386/oprofile/op_model_p4.c --- linux-2.6.9/arch/i386/oprofile/op_model_p4.c 2004-10-25 19:46:32.000000000 -0400 +++ linux-2.6.9-callgraph/arch/i386/oprofile/op_model_p4.c 2004-10-20 13:16:26.000000000 -0400 @@ -619,14 +619,11 @@ } -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 @@ 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 -ur linux-2.6.9/arch/i386/oprofile/op_model_ppro.c linux-2.6.9-callgraph/arch/i386/oprofile/op_model_ppro.c --- linux-2.6.9/arch/i386/oprofile/op_model_ppro.c 2004-10-25 19:46:32.000000000 -0400 +++ linux-2.6.9-callgraph/arch/i386/oprofile/op_model_ppro.c 2004-10-20 13:18:00.000000000 -0400 @@ -85,19 +85,16 @@ } -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 -ur linux-2.6.9/arch/i386/oprofile/op_x86_model.h linux-2.6.9-callgraph/arch/i386/oprofile/op_x86_model.h --- linux-2.6.9/arch/i386/oprofile/op_x86_model.h 2004-01-09 01:59:18.000000000 -0500 +++ linux-2.6.9-callgraph/arch/i386/oprofile/op_x86_model.h 2004-10-20 13:11:42.000000000 -0400 @@ -36,9 +36,8 @@ 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 -ur linux-2.6.9/arch/ia64/oprofile/init.c linux-2.6.9-callgraph/arch/ia64/oprofile/init.c --- linux-2.6.9/arch/ia64/oprofile/init.c 2004-10-25 19:46:36.000000000 -0400 +++ linux-2.6.9-callgraph/arch/ia64/oprofile/init.c 2004-10-20 13:20:10.000000000 -0400 @@ -15,12 +15,11 @@ extern int perfmon_init(struct oprofile_operations ** ops); extern void perfmon_exit(void); -int __init oprofile_arch_init(struct oprofile_operations ** ops) +void __init oprofile_arch_init(struct oprofile_operations ** ops) { #ifdef CONFIG_PERFMON - return perfmon_init(ops); + perfmon_init(ops); #endif - return -ENODEV; } diff -ur linux-2.6.9/arch/parisc/oprofile/init.c linux-2.6.9-callgraph/arch/parisc/oprofile/init.c --- linux-2.6.9/arch/parisc/oprofile/init.c 2004-01-09 01:59:34.000000000 -0500 +++ linux-2.6.9-callgraph/arch/parisc/oprofile/init.c 2004-10-20 13:11:42.000000000 -0400 @@ -12,11 +12,8 @@ #include #include -extern void timer_init(struct oprofile_operations ** ops); - -int __init oprofile_arch_init(struct oprofile_operations ** ops) +int __init oprofile_arch_init(struct oprofile_operations * ops) { - return -ENODEV; } diff -ur linux-2.6.9/arch/ppc64/oprofile/common.c linux-2.6.9-callgraph/arch/ppc64/oprofile/common.c --- linux-2.6.9/arch/ppc64/oprofile/common.c 2004-10-25 19:46:42.000000000 -0400 +++ linux-2.6.9-callgraph/arch/ppc64/oprofile/common.c 2004-10-20 13:11:42.000000000 -0400 @@ -120,16 +120,7 @@ return 0; } -static struct oprofile_operations oprof_ppc64_ops = { - .create_files = op_ppc64_create_files, - .setup = op_ppc64_setup, - .shutdown = op_ppc64_shutdown, - .start = op_ppc64_start, - .stop = op_ppc64_stop, - .cpu_type = NULL /* To be filled in below. */ -}; - -int __init oprofile_arch_init(struct oprofile_operations **ops) +void __init oprofile_arch_init(struct oprofile_operations *ops) { unsigned int pvr; @@ -140,7 +131,7 @@ case PV_630p: model = &op_model_rs64; model->num_counters = 8; - oprof_ppc64_ops.cpu_type = "ppc64/power3"; + ops->cpu_type = "ppc64/power3"; break; case PV_NORTHSTAR: @@ -149,40 +140,42 @@ case PV_SSTAR: model = &op_model_rs64; model->num_counters = 8; - oprof_ppc64_ops.cpu_type = "ppc64/rs64"; + ops->cpu_type = "ppc64/rs64"; break; case PV_POWER4: case PV_POWER4p: model = &op_model_power4; model->num_counters = 8; - oprof_ppc64_ops.cpu_type = "ppc64/power4"; + ops->cpu_type = "ppc64/power4"; break; case PV_970: case PV_970FX: model = &op_model_power4; model->num_counters = 8; - oprof_ppc64_ops.cpu_type = "ppc64/970"; + ops->cpu_type = "ppc64/970"; break; case PV_POWER5: case PV_POWER5p: model = &op_model_power4; model->num_counters = 6; - oprof_ppc64_ops.cpu_type = "ppc64/power5"; + ops->cpu_type = "ppc64/power5"; break; default: - return -ENODEV; + return; } - *ops = &oprof_ppc64_ops; + ops->create_files = op_ppc64_create_files; + ops->setup = op_ppc64_setup; + ops->shutdown = op_ppc64_shutdown; + ops->start = op_ppc64_start; + ops->stop = op_ppc64_stop; printk(KERN_INFO "oprofile: using %s performance monitoring.\n", - oprof_ppc64_ops.cpu_type); - - return 0; + ops->cpu_type); } void oprofile_arch_exit(void) diff -ur linux-2.6.9/arch/ppc64/oprofile/op_model_power4.c linux-2.6.9-callgraph/arch/ppc64/oprofile/op_model_power4.c --- linux-2.6.9/arch/ppc64/oprofile/op_model_power4.c 2004-10-25 19:46:42.000000000 -0400 +++ linux-2.6.9-callgraph/arch/ppc64/oprofile/op_model_power4.c 2004-10-20 13:11:42.000000000 -0400 @@ -264,11 +264,9 @@ int is_kernel; int val; int i; - unsigned int cpu = smp_processor_id(); unsigned int mmcr0; pc = get_pc(regs); - is_kernel = get_kernel(pc); /* set the PMM bit (see comment below) */ mtmsrd(mfmsr() | MSR_PMM); @@ -277,7 +275,7 @@ val = ctr_read(i); if (val < 0) { if (oprofile_running && ctr[i].enabled) { - oprofile_add_sample(pc, is_kernel, i, cpu); + oprofile_add_pc(pc, get_kernel(pc), i); ctr_write(i, reset_value[i]); } else { ctr_write(i, 0); diff -ur linux-2.6.9/arch/ppc64/oprofile/op_model_rs64.c linux-2.6.9-callgraph/arch/ppc64/oprofile/op_model_rs64.c --- linux-2.6.9/arch/ppc64/oprofile/op_model_rs64.c 2004-10-25 19:46:42.000000000 -0400 +++ linux-2.6.9-callgraph/arch/ppc64/oprofile/op_model_rs64.c 2004-10-20 13:11:42.000000000 -0400 @@ -180,7 +180,6 @@ int i; unsigned long pc = mfspr(SPRN_SIAR); int is_kernel = (pc >= KERNELBASE); - unsigned int cpu = smp_processor_id(); /* set the PMM bit (see comment below) */ mtmsrd(mfmsr() | MSR_PMM); @@ -189,7 +188,7 @@ val = ctr_read(i); if (val < 0) { if (ctr[i].enabled) { - oprofile_add_sample(pc, is_kernel, i, cpu); + oprofile_add_pc(pc, is_kernel, i); ctr_write(i, reset_value[i]); } else { ctr_write(i, 0); diff -ur linux-2.6.9/arch/s390/oprofile/init.c linux-2.6.9-callgraph/arch/s390/oprofile/init.c --- linux-2.6.9/arch/s390/oprofile/init.c 2004-10-25 19:43:37.000000000 -0400 +++ linux-2.6.9-callgraph/arch/s390/oprofile/init.c 2004-10-20 13:11:42.000000000 -0400 @@ -12,13 +12,8 @@ #include #include -//extern int irq_init(struct oprofile_operations** ops); -extern void timer_init(struct oprofile_operations** ops); - -int __init oprofile_arch_init(struct oprofile_operations** ops) +void __init oprofile_arch_init(struct oprofile_operations* ops) { - timer_init(ops); - return 0; } void oprofile_arch_exit(void) diff -ur linux-2.6.9/arch/sh/oprofile/op_model_null.c linux-2.6.9-callgraph/arch/sh/oprofile/op_model_null.c --- linux-2.6.9/arch/sh/oprofile/op_model_null.c 2004-10-25 19:41:12.000000000 -0400 +++ linux-2.6.9-callgraph/arch/sh/oprofile/op_model_null.c 2004-10-20 13:11:42.000000000 -0400 @@ -12,9 +12,8 @@ #include #include -int __init oprofile_arch_init(struct oprofile_operations **ops) +void __init oprofile_arch_init(struct oprofile_operations *ops) { - return -ENODEV; } void oprofile_arch_exit(void) diff -ur linux-2.6.9/arch/sparc64/oprofile/init.c linux-2.6.9-callgraph/arch/sparc64/oprofile/init.c --- linux-2.6.9/arch/sparc64/oprofile/init.c 2004-01-09 01:59:45.000000000 -0500 +++ linux-2.6.9-callgraph/arch/sparc64/oprofile/init.c 2004-10-20 13:11:42.000000000 -0400 @@ -12,11 +12,8 @@ #include #include -extern void timer_init(struct oprofile_operations ** ops); - -int __init oprofile_arch_init(struct oprofile_operations ** ops) +void __init oprofile_arch_init(struct oprofile_operations * ops) { - return -ENODEV; } diff -ur linux-2.6.9/drivers/oprofile/buffer_sync.c linux-2.6.9-callgraph/drivers/oprofile/buffer_sync.c --- linux-2.6.9/drivers/oprofile/buffer_sync.c 2004-10-25 19:47:02.000000000 -0400 +++ linux-2.6.9-callgraph/drivers/oprofile/buffer_sync.c 2004-10-20 13:23:54.000000000 -0400 @@ -296,6 +296,13 @@ } +static void add_trace_begin(void) +{ + add_event_entry(ESCAPE_CODE); + add_event_entry(TRACE_BEGIN_CODE); +} + + static void add_sample_entry(unsigned long offset, unsigned long event) { add_event_entry(offset); @@ -303,7 +310,7 @@ } -static void add_us_sample(struct mm_struct * mm, struct op_sample * s) +static int add_us_sample(struct mm_struct * mm, struct op_sample * s) { unsigned long cookie; off_t offset; @@ -312,7 +319,7 @@ if (!cookie) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); - return; + return 0; } if (cookie != last_cookie) { @@ -321,6 +328,8 @@ } add_sample_entry(offset, s->event); + + return 1; } @@ -328,15 +337,18 @@ * sample is converted into a persistent dentry/offset pair * for later lookup from userspace. */ -static void add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) +static int +add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) { if (in_kernel) { add_sample_entry(s->eip, s->event); + return 1; } else if (mm) { - add_us_sample(mm, s); + return add_us_sample(mm, s); } else { atomic_inc(&oprofile_stats.sample_lost_no_mm); } + return 0; } @@ -358,9 +370,9 @@ } -static inline int is_ctx_switch(unsigned long val) +static inline int is_code(unsigned long val) { - return val == ~0UL; + return val == ESCAPE_CODE; } @@ -397,12 +409,23 @@ rmb(); - if (new_tail < (b->buffer_size)) + if (new_tail < b->buffer_size) b->tail_pos = new_tail; else b->tail_pos = 0; } +/* FIXME: this is not sufficient if we implement syscall barrier backtrace + * traversal, the code switch to sb_sample_start at first kernel enter/exit + * switch so we need a fifth state and some special handling in sync_buffer() + */ +typedef enum { + sb_bt_ignore = -2, + sb_buffer_start, + sb_bt_start, + sb_sample_start, +} sync_buffer_state; + /* Move tasks along towards death. Any tasks on dead_tasks * will definitely have no remaining references in any @@ -469,6 +492,7 @@ int in_kernel = 1; unsigned int i; unsigned long available; + sync_buffer_state state = sb_buffer_start; down(&buffer_sem); @@ -478,14 +502,19 @@ available = get_slots(cpu_buf); - for (i=0; i < available; ++i) { + for (i = 0; i < available; ++i) { struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; - if (is_ctx_switch(s->eip)) { - if (s->event <= 1) { + if (is_code(s->eip)) { + if (s->event <= CPU_IS_KERNEL) { /* kernel/userspace switch */ in_kernel = s->event; + if (state == sb_buffer_start) + state = sb_sample_start; add_kernel_ctx_switch(s->event); + } else if (s->event == CPU_TRACE_BEGIN) { + state = sb_bt_start; + add_trace_begin(); } else { struct mm_struct * oldmm = mm; @@ -499,7 +528,13 @@ add_user_ctx_switch(new, cookie); } } else { - add_sample(mm, s, in_kernel); + if (state >= sb_bt_start && + !add_sample(mm, s, in_kernel)) { + if (state == sb_bt_start) { + state = sb_bt_ignore; + atomic_inc(&oprofile_stats.bt_lost_no_mapping); + } + } } increment_tail(cpu_buf); diff -ur linux-2.6.9/drivers/oprofile/cpu_buffer.c linux-2.6.9-callgraph/drivers/oprofile/cpu_buffer.c --- linux-2.6.9/drivers/oprofile/cpu_buffer.c 2004-10-25 19:47:02.000000000 -0400 +++ linux-2.6.9-callgraph/drivers/oprofile/cpu_buffer.c 2004-10-20 13:35:38.000000000 -0400 @@ -18,9 +18,11 @@ */ #include +#include #include #include +#include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" #include "oprof.h" @@ -58,6 +60,7 @@ b->last_task = NULL; b->last_is_kernel = -1; + b->tracing = 0; b->buffer_size = buffer_size; b->tail_pos = 0; b->head_pos = 0; @@ -142,55 +145,138 @@ } +inline static void +add_sample(struct oprofile_cpu_buffer * cpu_buf, + unsigned long pc, unsigned long event) +{ + struct op_sample * entry = &cpu_buf->buffer[cpu_buf->head_pos]; + entry->eip = pc; + entry->event = event; + increment_head(cpu_buf); +} + + +inline static void +add_code(struct oprofile_cpu_buffer * buffer, unsigned long value) +{ + add_sample(buffer, ESCAPE_CODE, value); +} + + /* This must be safe from any context. It's safe writing here * because of the head/tail separation of the writer and reader * of the CPU buffer. * * is_kernel is needed because on some architectures you cannot * tell if you are in kernel or user space simply by looking at - * eip. We tag this in the buffer by generating kernel enter/exit + * pc. We tag this in the buffer by generating kernel enter/exit * events whenever is_kernel changes */ -void oprofile_add_sample(unsigned long eip, unsigned int is_kernel, - unsigned long event, int cpu) +static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, + int is_kernel, unsigned long event) { - struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[cpu]; struct task_struct * task; - is_kernel = !!is_kernel; - cpu_buf->sample_received++; - if (nr_available_slots(cpu_buf) < 3) { cpu_buf->sample_lost_overflow++; - return; + return 0; } + is_kernel = !!is_kernel; + task = current; /* notice a switch from user->kernel or vice versa */ if (cpu_buf->last_is_kernel != is_kernel) { cpu_buf->last_is_kernel = is_kernel; - cpu_buf->buffer[cpu_buf->head_pos].eip = ~0UL; - cpu_buf->buffer[cpu_buf->head_pos].event = is_kernel; - increment_head(cpu_buf); + add_code(cpu_buf, is_kernel); } /* notice a task switch */ if (cpu_buf->last_task != task) { cpu_buf->last_task = task; - cpu_buf->buffer[cpu_buf->head_pos].eip = ~0UL; - cpu_buf->buffer[cpu_buf->head_pos].event = (unsigned long)task; - increment_head(cpu_buf); + add_code(cpu_buf, (unsigned long)task); } - - cpu_buf->buffer[cpu_buf->head_pos].eip = eip; - cpu_buf->buffer[cpu_buf->head_pos].event = event; - increment_head(cpu_buf); + + add_sample(cpu_buf, pc, event); + return 1; +} + + +static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) +{ + if (nr_available_slots(cpu_buf) < 4) { + cpu_buf->sample_lost_overflow++; + return 0; + } + + add_code(cpu_buf, CPU_TRACE_BEGIN); + cpu_buf->tracing = 1; + return 1; } +static void oprofile_end_trace(struct oprofile_cpu_buffer * cpu_buf) +{ + cpu_buf->tracing = 0; +} + + +void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) +{ + struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[smp_processor_id()]; + unsigned long pc = instruction_pointer(regs); + int is_kernel = !user_mode(regs); + + if (!backtrace_depth) { + log_sample(cpu_buf, pc, is_kernel, event); + return; + } + + if (!oprofile_begin_trace(cpu_buf)) + return; + + /* if log_sample() fail we can't backtrace since we lost the source + * of this event */ + if (log_sample(cpu_buf, pc, is_kernel, event)) + oprofile_ops.backtrace(regs, backtrace_depth); + oprofile_end_trace(cpu_buf); +} + + +void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) +{ + struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[smp_processor_id()]; + log_sample(cpu_buf, pc, is_kernel, event); +} + + +void oprofile_add_trace(unsigned long pc) +{ + struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[smp_processor_id()]; + + if (!cpu_buf->tracing) + return; + + if (nr_available_slots(cpu_buf) < 1) { + cpu_buf->tracing = 0; + cpu_buf->sample_lost_overflow++; + return; + } + + /* broken frame can give an eip with the same value as an escape code, + * abort the trace if we get it */ + if (pc == ESCAPE_CODE) { + cpu_buf->tracing = 0; + cpu_buf->backtrace_aborted++; + return; + } + + add_sample(cpu_buf, pc, 0); + } + /* Resets the cpu buffer to a sane state. */ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf) { diff -ur linux-2.6.9/drivers/oprofile/cpu_buffer.h linux-2.6.9-callgraph/drivers/oprofile/cpu_buffer.h --- linux-2.6.9/drivers/oprofile/cpu_buffer.h 2004-10-25 19:47:02.000000000 -0400 +++ linux-2.6.9-callgraph/drivers/oprofile/cpu_buffer.h 2004-10-20 13:36:55.000000000 -0400 @@ -37,9 +37,11 @@ unsigned long buffer_size; struct task_struct * last_task; int last_is_kernel; + int tracing; struct op_sample * buffer; unsigned long sample_received; unsigned long sample_lost_overflow; + unsigned long backtrace_aborted; int cpu; struct work_struct work; } ____cacheline_aligned; @@ -48,4 +50,8 @@ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); +/* transient events for the CPU buffer -> event buffer */ +#define CPU_IS_KERNEL 1 +#define CPU_TRACE_BEGIN 2 + #endif /* OPROFILE_CPU_BUFFER_H */ diff -ur linux-2.6.9/drivers/oprofile/event_buffer.h linux-2.6.9-callgraph/drivers/oprofile/event_buffer.h --- linux-2.6.9/drivers/oprofile/event_buffer.h 2004-01-09 01:59:09.000000000 -0500 +++ linux-2.6.9-callgraph/drivers/oprofile/event_buffer.h 2004-10-20 13:11:42.000000000 -0400 @@ -32,6 +32,8 @@ #define KERNEL_EXIT_SWITCH_CODE 5 #define MODULE_LOADED_CODE 6 #define CTX_TGID_CODE 7 +#define TRACE_BEGIN_CODE 8 +#define TRACE_END_CODE 9 /* add data to the event buffer */ void add_event_entry(unsigned long data); diff -ur linux-2.6.9/drivers/oprofile/oprof.c linux-2.6.9-callgraph/drivers/oprofile/oprof.c --- linux-2.6.9/drivers/oprofile/oprof.c 2004-01-09 01:59:19.000000000 -0500 +++ linux-2.6.9-callgraph/drivers/oprofile/oprof.c 2004-10-20 13:11:42.000000000 -0400 @@ -20,8 +20,10 @@ #include "buffer_sync.h" #include "oprofile_stats.h" -struct oprofile_operations * oprofile_ops; +struct oprofile_operations oprofile_ops; + unsigned long oprofile_started; +unsigned long backtrace_depth; static unsigned long is_setup; static DECLARE_MUTEX(start_sem); @@ -43,7 +45,7 @@ if ((err = alloc_event_buffer())) goto out1; - if (oprofile_ops->setup && (err = oprofile_ops->setup())) + if (oprofile_ops.setup && (err = oprofile_ops.setup())) goto out2; /* Note even though this starts part of the @@ -59,8 +61,8 @@ return 0; out3: - if (oprofile_ops->shutdown) - oprofile_ops->shutdown(); + if (oprofile_ops.shutdown) + oprofile_ops.shutdown(); out2: free_event_buffer(); out1: @@ -88,7 +90,7 @@ oprofile_reset_stats(); - if ((err = oprofile_ops->start())) + if ((err = oprofile_ops.start())) goto out; oprofile_started = 1; @@ -104,7 +106,7 @@ down(&start_sem); if (!oprofile_started) goto out; - oprofile_ops->stop(); + oprofile_ops.stop(); oprofile_started = 0; /* wake up the daemon to read what remains */ wake_up_buffer_waiter(); @@ -117,8 +119,8 @@ { down(&start_sem); sync_stop(); - if (oprofile_ops->shutdown) - oprofile_ops->shutdown(); + if (oprofile_ops.shutdown) + oprofile_ops.shutdown(); is_setup = 0; free_event_buffer(); free_cpu_buffers(); @@ -126,38 +128,50 @@ } -extern void timer_init(struct oprofile_operations ** ops); +int oprofile_set_backtrace(unsigned long val) +{ + int err = 0; + down(&start_sem); -static int __init oprofile_init(void) -{ - /* Architecture must fill in the interrupt ops and the - * logical CPU type, or we can fall back to the timer - * interrupt profiler. - */ - int err = oprofile_arch_init(&oprofile_ops); + if (oprofile_started) { + err = -EBUSY; + goto out; + } - if (err == -ENODEV || timer) { - timer_init(&oprofile_ops); - err = 0; - } else if (err) { + if (!oprofile_ops.backtrace) { + err = -EINVAL; goto out; } - if (!oprofile_ops->cpu_type) { - printk(KERN_ERR "oprofile: cpu_type not set !\n"); - err = -EFAULT; + backtrace_depth = val; + +out: + up(&start_sem); + return err; +} + + +extern void timer_init(struct oprofile_operations * ops); + +static int __init oprofile_init(void) +{ + int err = 0; + + /* this is our fallback case */ + timer_init(&oprofile_ops); + + if (timer) { + printk(KERN_INFO "oprofile: using timer interrupt.\n"); } else { - err = oprofilefs_register(); + oprofile_arch_init(&oprofile_ops); } - + + err = oprofilefs_register(); if (err) - goto out_exit; -out: + oprofile_arch_exit(); + return err; -out_exit: - oprofile_arch_exit(); - goto out; } diff -ur linux-2.6.9/drivers/oprofile/oprof.h linux-2.6.9-callgraph/drivers/oprofile/oprof.h --- linux-2.6.9/drivers/oprofile/oprof.h 2004-01-09 01:59:10.000000000 -0500 +++ linux-2.6.9-callgraph/drivers/oprofile/oprof.h 2004-10-20 13:11:42.000000000 -0400 @@ -24,12 +24,15 @@ extern unsigned long fs_buffer_size; extern unsigned long fs_cpu_buffer_size; extern unsigned long fs_buffer_watershed; -extern struct oprofile_operations * oprofile_ops; +extern struct oprofile_operations oprofile_ops; extern unsigned long oprofile_started; +extern unsigned long backtrace_depth; struct super_block; struct dentry; void oprofile_create_files(struct super_block * sb, struct dentry * root); + +int oprofile_set_backtrace(unsigned long depth); #endif /* OPROF_H */ diff -ur linux-2.6.9/drivers/oprofile/oprofile_files.c linux-2.6.9-callgraph/drivers/oprofile/oprofile_files.c --- linux-2.6.9/drivers/oprofile/oprofile_files.c 2004-10-25 19:45:47.000000000 -0400 +++ linux-2.6.9-callgraph/drivers/oprofile/oprofile_files.c 2004-10-20 13:11:42.000000000 -0400 @@ -18,6 +18,37 @@ unsigned long fs_cpu_buffer_size = 8192; unsigned long fs_buffer_watershed = 32768; /* FIXME: tune */ +static ssize_t depth_read(struct file * file, char * buf, size_t count, loff_t * offset) +{ + return oprofilefs_ulong_to_user(backtrace_depth, buf, count, offset); +} + + +static ssize_t depth_write(struct file * file, char const * buf, size_t count, loff_t * offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = oprofilefs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + retval = oprofile_set_backtrace(val); + + if (retval) + return retval; + return count; +} + + +static struct file_operations depth_fops = { + .read = depth_read, + .write = depth_write +}; + static ssize_t pointer_size_read(struct file * file, char __user * buf, size_t count, loff_t * offset) { @@ -32,7 +63,7 @@ static ssize_t cpu_type_read(struct file * file, char __user * buf, size_t count, loff_t * offset) { - return oprofilefs_str_to_user(oprofile_ops->cpu_type, buf, count, offset); + return oprofilefs_str_to_user(oprofile_ops.cpu_type, buf, count, offset); } @@ -47,7 +78,7 @@ } -static ssize_t enable_write(struct file *file, char const __user * buf, size_t count, loff_t * offset) +static ssize_t enable_write(struct file * file, char const __user * buf, size_t count, loff_t * offset) { unsigned long val; int retval; @@ -76,7 +107,7 @@ }; -static ssize_t dump_write(struct file *file, char const __user * buf, size_t count, loff_t * offset) +static ssize_t dump_write(struct file * file, char const __user * buf, size_t count, loff_t * offset) { wake_up_buffer_waiter(); return count; @@ -96,8 +127,9 @@ oprofilefs_create_ulong(sb, root, "buffer_watershed", &fs_buffer_watershed); oprofilefs_create_ulong(sb, root, "cpu_buffer_size", &fs_cpu_buffer_size); oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops); + oprofilefs_create_file(sb, root, "backtrace_depth", &depth_fops); oprofilefs_create_file(sb, root, "pointer_size", &pointer_size_fops); oprofile_create_stats_files(sb, root); - if (oprofile_ops->create_files) - oprofile_ops->create_files(sb, root); + if (oprofile_ops.create_files) + oprofile_ops.create_files(sb, root); } diff -ur linux-2.6.9/drivers/oprofile/oprofile_stats.c linux-2.6.9-callgraph/drivers/oprofile/oprofile_stats.c --- linux-2.6.9/drivers/oprofile/oprofile_stats.c 2004-10-25 19:47:02.000000000 -0400 +++ linux-2.6.9-callgraph/drivers/oprofile/oprofile_stats.c 2004-10-20 13:38:00.000000000 -0400 @@ -59,6 +59,8 @@ &cpu_buf->sample_received); oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_overflow", &cpu_buf->sample_lost_overflow); + oprofilefs_create_ro_ulong(sb, cpudir, "backtrace_aborted", + &cpu_buf->backtrace_aborted); } oprofilefs_create_ro_atomic(sb, dir, "sample_lost_no_mm", @@ -67,4 +69,6 @@ &oprofile_stats.sample_lost_no_mapping); oprofilefs_create_ro_atomic(sb, dir, "event_lost_overflow", &oprofile_stats.event_lost_overflow); + oprofilefs_create_ro_atomic(sb, dir, "bt_lost_no_mapping", + &oprofile_stats.bt_lost_no_mapping); } diff -ur linux-2.6.9/drivers/oprofile/oprofile_stats.h linux-2.6.9-callgraph/drivers/oprofile/oprofile_stats.h --- linux-2.6.9/drivers/oprofile/oprofile_stats.h 2004-01-09 02:00:13.000000000 -0500 +++ linux-2.6.9-callgraph/drivers/oprofile/oprofile_stats.h 2004-10-20 13:11:42.000000000 -0400 @@ -15,6 +15,7 @@ struct oprofile_stat_struct { atomic_t sample_lost_no_mm; atomic_t sample_lost_no_mapping; + atomic_t bt_lost_no_mapping; atomic_t event_lost_overflow; }; diff -ur linux-2.6.9/drivers/oprofile/timer_int.c linux-2.6.9-callgraph/drivers/oprofile/timer_int.c --- linux-2.6.9/drivers/oprofile/timer_int.c 2004-10-25 19:47:02.000000000 -0400 +++ linux-2.6.9-callgraph/drivers/oprofile/timer_int.c 2004-10-20 13:38:42.000000000 -0400 @@ -17,10 +17,7 @@ static int timer_notify(struct pt_regs *regs) { - int cpu = smp_processor_id(); - unsigned long eip = profile_pc(regs); - - oprofile_add_sample(eip, !user_mode(regs), 0, cpu); + oprofile_add_sample(regs, 0); return 0; } @@ -36,15 +33,9 @@ } -static struct oprofile_operations timer_ops = { - .start = timer_start, - .stop = timer_stop, - .cpu_type = "timer" -}; - - -void __init timer_init(struct oprofile_operations ** ops) +void __init timer_init(struct oprofile_operations * ops) { - *ops = &timer_ops; - printk(KERN_INFO "oprofile: using timer interrupt.\n"); + ops->start = timer_start; + ops->stop = timer_stop; + ops->cpu_type = "timer"; } diff -ur linux-2.6.9/include/linux/oprofile.h linux-2.6.9-callgraph/include/linux/oprofile.h --- linux-2.6.9/include/linux/oprofile.h 2004-10-25 19:46:13.000000000 -0400 +++ linux-2.6.9-callgraph/include/linux/oprofile.h 2004-10-20 13:11:42.000000000 -0400 @@ -20,6 +20,7 @@ struct super_block; struct dentry; struct file_operations; +struct pt_regs; /* Operations structure to be filled in */ struct oprofile_operations { @@ -34,6 +35,8 @@ int (*start)(void); /* Stop delivering interrupts. */ void (*stop)(void); + /* Initiate a stack backtrace. Optional. */ + void (*backtrace)(struct pt_regs * const regs, unsigned int depth); /* CPU identification string. */ char * cpu_type; }; @@ -41,11 +44,11 @@ /** * One-time initialisation. *ops must be set to a filled-in * operations structure. This is called even in timer interrupt - * mode. + * mode so an arch can set a backtrace callback. * - * Return 0 on success. + * If an error occurs, the fields should be left untouched. */ -int oprofile_arch_init(struct oprofile_operations ** ops); +void oprofile_arch_init(struct oprofile_operations * ops); /** * One-time exit/cleanup for the arch. @@ -56,8 +59,15 @@ * Add a sample. This may be called from any context. Pass * smp_processor_id() as cpu. */ -extern void oprofile_add_sample(unsigned long eip, unsigned int is_kernel, - unsigned long event, int cpu); +void oprofile_add_sample(struct pt_regs * const regs, unsigned long event); + +/* Use this instead when the PC value is not from the regs. Doesn't + * backtrace. */ +void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event); + +/* add a backtrace entry, to be called from the ->backtrace callback */ +void oprofile_add_trace(unsigned long eip); + /** * Create a file of the given name as a child of the given root, with diff -ur linux-2.6.9/mm/memory.c linux-2.6.9-callgraph/mm/memory.c --- linux-2.6.9/mm/memory.c 2004-10-25 19:47:30.000000000 -0400 +++ linux-2.6.9-callgraph/mm/memory.c 2004-10-20 13:11:42.000000000 -0400 @@ -660,6 +660,8 @@ return NULL; } +EXPORT_SYMBOL(follow_page); + /* * Given a physical address, is there a useful struct page pointing to * it? This may become more complex in the future if we start dealing diff -N linux-2.6.9-callgraph/arch/i386/oprofile/backtrace.c --- /dev/null 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-callgraph/arch/i386/oprofile/backtrace.c 2004-10-20 13:11:42.000000000 -0400 @@ -0,0 +1,118 @@ +/** + * @file backtrace.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * @author David Smith + */ + +#include +#include +#include +#include + +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 (!follow_page(mm, (unsigned long)head, 0)) + return 0; + + return (int)follow_page(mm, (unsigned long)(head + 1), 0); +} +#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 = (struct frame_head *)regs->ebp; + + 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 +}