OProfile annotated call graphs
RFC PATCH 1/1 OProfile annotated call graphs
Below is a simple patch for backtracing for x86. It basically takes an optimistic approach towards whether ebp has useful values. It seems to work reasonably well already :
77.79 442.20 6/58 kmem_cache_free [9]
661.24 3758.69 51/58 _pte_chain_free [12]
[10] 4.6 752.00 4274.59 58 page_remove_rmap [10]
1596.54 1578.45 594/679 zap_pte_range [14]
973.46 112.77 189/1594 do_wp_page [3]
8.73 3.54 17/2542 zap_pmd_range [27]
0.28 0.49 8/16796 handle_mm_fault [31]
0.00 0.32 2/2 copy_one_pte [802]Obviously you'll need CONFIG_FRAME_POINTER=y for the kernel to get useful results (without, it still "works" but you only get traces for sched.c as the frame pointer is enabled there).
If you're bored enough to actually try it out, get
http://movementarian.org/oprofile-0.6cvs.tar.gz
then
opcontrol --init
echo 20 >/dev/oprofile/backtrace_depth
start the profiler
opcontrol --dump
opgprof /boot/2.5.72/vmlinux # or whatever, generates gmon.out gprof /boot/2.5.72/vmlinux # gives gprof output
The userspace is broken for doing anything other than the above at this point.
Any comments ?
regards,
john
arch/alpha/oprofile/op_model_ev4.c +1 -2
@@ -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); }
arch/alpha/oprofile/op_model_ev5.c +1 -2
@@ -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); }
arch/alpha/oprofile/op_model_ev67.c +3 -5
@@ -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 ))
arch/alpha/oprofile/op_model_ev6.c +1 -2
@@ -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); }
arch/i386/oprofile/backtrace.c +98 -0
98 lines not on this page · raw message
arch/i386/oprofile/init.c +5 -1
@@ -18,18 +18,22 @@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) {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 + (*ops)->backtrace = x86_backtrace;return ret; }
arch/i386/oprofile/Makefile +1 -1
@@ -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
arch/i386/oprofile/nmi_int.c +1 -1
@@ -83,7 +83,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]); }
arch/i386/oprofile/nmi_timer_int.c +1 -3
@@ -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 ; }
arch/i386/oprofile/op_model_athlon.c +3 -6
@@ -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 =instruction_pointer (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); } }
arch/i386/oprofile/op_model_p4.c +3 -6
@@ -592,14 +592,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 =instruction_pointer (regs); -int is_kernel = !user_mode (regs); stag =get_stagger ();
@@ -630,7 +627,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);
arch/i386/oprofile/op_model_ppro.c +3 -6
@@ -84,19 +84,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 =instruction_pointer (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); } }
arch/i386/oprofile/op_x86_model.h +2 -3
@@ -39,9 +39,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); };
drivers/oprofile/buffer_sync.c +42 -20
91 lines not on this page · raw message
drivers/oprofile/cpu_buffer.c +110 -26
197 lines not on this page · raw message
drivers/oprofile/cpu_buffer.h +6 -0
@@ -35,6 +35,7 @@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;
@@ -45,4 +46,9 @@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 +#define CPU_TRACE_END 3 +#endif /* OPROFILE_CPU_BUFFER_H */
drivers/oprofile/event_buffer.h +2 -0
@@ -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);
drivers/oprofile/oprof.c +30 -12
69 lines not on this page · raw message
drivers/oprofile/oprof.h +3 -0
@@ -26,10 +26,13 @@extern unsigned long fs_buffer_watershed;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 */
drivers/oprofile/oprofile_files.c +33 -1
@@ -18,7 +18,38 @@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 cpu_type_read (struct file * file,char * buf,size_t count,loff_t * offset) {return oprofilefs_str_to_user (oprofile_ops->cpu_type, buf, count, offset);
@@ -36,7 +67,7 @@ } -static ssize_t enable_write (struct file *file,char const * buf,size_t count,loff_t * offset) +static ssize_t enable_write (struct file * file,char const * buf,size_t count,loff_t * offset) {unsigned long val;int retval;
@@ -85,6 +116,7 @@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);oprofile_create_stats_files (sb, root);if (oprofile_ops->create_files) oprofile_ops->create_files (sb, root);
drivers/oprofile/timer_int.c +1 -6
@@ -18,11 +18,7 @@static int timer_notify (struct notifier_block * self,unsigned long val,void * data) { -struct pt_regs * regs = (struct pt_regs *)data; -int cpu =smp_processor_id (); -unsigned long eip =instruction_pointer (regs); - -oprofile_add_sample (eip, !user_mode (regs),0 , cpu); +oprofile_add_sample ((struct pt_regs *)data,0 );return 0 ; }
@@ -54,5 +50,4 @@void __init timer_init (struct oprofile_operations ** ops) { *ops = &timer_ops; -printk (KERN_INFO "oprofile: using timer interrupt.\n" ); }
include/linux/oprofile.h +13 -3
@@ -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. */ +void (*backtrace)(struct pt_regs *const regs,unsigned int depth);/* CPU identification string. */ char * cpu_type; };
@@ -41,7 +44,7 @@/** * 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. */
@@ -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
kernel/ksyms.c +1 -0
@@ -134,6 +134,7 @@EXPORT_SYMBOL (page_address);#endif EXPORT_SYMBOL (get_user_pages); +EXPORT_SYMBOL (follow_page);/* filesystem internal functions */ EXPORT_SYMBOL (def_blk_fops);