]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] oprofile - timer hook
authorJohn Levon <levon@movementarian.org>
Tue, 15 Oct 2002 11:30:38 +0000 (04:30 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Tue, 15 Oct 2002 11:30:38 +0000 (04:30 -0700)
This implements a simple hook into the profiling timer for x86 so that
non-perfctr machines can still use oprofile.  This has proven useful for
laptops and the like.

It also reduces header dependencies a bit by centralising readprofile
code

14 files changed:
arch/i386/kernel/Makefile
arch/i386/kernel/apic.c
arch/i386/kernel/i386_ksyms.c
arch/i386/kernel/profile.c [new file with mode: 0644]
arch/i386/kernel/time.c
arch/i386/mach-generic/do_timer.h
arch/i386/mach-visws/do_timer.h
fs/proc/proc_misc.c
include/asm-i386/hw_irq.h
include/linux/profile.h
include/linux/sched.h
init/main.c
kernel/profile.c
kernel/timer.c

index d201c60ac5c2f2cfcbddb4a0e1bfbc5275daa9de..55f9312b7f39baf5fa50b02ac3c2917d72be98be 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_X86_LOCAL_APIC)  += apic.o nmi.o
 obj-$(CONFIG_X86_IO_APIC)      += io_apic.o
 obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o
 obj-$(CONFIG_X86_NUMAQ)                += numaq.o
+obj-$(CONFIG_PROFILING)                += profile.o
 
 EXTRA_AFLAGS   := -traditional
 
index c2f56438f74943a037f397e1d9efdf48ad51af8f..bff34a4d1dcfcd096d140414c32dfc454ee9a875 100644 (file)
@@ -1008,17 +1008,9 @@ int setup_profiling_timer(unsigned int multiplier)
 
 inline void smp_local_timer_interrupt(struct pt_regs * regs)
 {
-       int user = user_mode(regs);
        int cpu = smp_processor_id();
 
-       /*
-        * The profiling function is SMP safe. (nothing can mess
-        * around with "current", and the profiling counters are
-        * updated with atomic operations). This is especially
-        * useful with a profiling multiplier != 1
-        */
-       if (!user)
-               x86_do_profile(regs->eip);
+       x86_do_profile(regs);
 
        if (--prof_counter[cpu] <= 0) {
                /*
@@ -1036,7 +1028,7 @@ inline void smp_local_timer_interrupt(struct pt_regs * regs)
                }
 
 #ifdef CONFIG_SMP
-               update_process_times(user);
+               update_process_times(user_mode(regs));
 #endif
        }
 
index 79c204a1f47657d2d510bd7618f7e6ee5585b4cb..9314e0b9f880513921d144611b6740c36268a778 100644 (file)
@@ -167,6 +167,9 @@ EXPORT_SYMBOL(get_wchan);
 
 EXPORT_SYMBOL(rtc_lock);
 
+EXPORT_SYMBOL_GPL(register_profile_notifier);
+EXPORT_SYMBOL_GPL(unregister_profile_notifier);
 #undef memcpy
 #undef memset
 extern void * memset(void *,int,__kernel_size_t);
diff --git a/arch/i386/kernel/profile.c b/arch/i386/kernel/profile.c
new file mode 100644 (file)
index 0000000..334af20
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *     linux/arch/i386/kernel/profile.c
+ *
+ *     (C) 2002 John Levon <levon@movementarian.org>
+ *
+ */
+
+#include <linux/profile.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/irq.h>
+#include <asm/hw_irq.h> 
+static struct notifier_block * profile_listeners;
+static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
+int register_profile_notifier(struct notifier_block * nb)
+{
+       int err;
+       write_lock_irq(&profile_lock);
+       err = notifier_chain_register(&profile_listeners, nb);
+       write_unlock_irq(&profile_lock);
+       return err;
+}
+
+
+int unregister_profile_notifier(struct notifier_block * nb)
+{
+       int err;
+       write_lock_irq(&profile_lock);
+       err = notifier_chain_unregister(&profile_listeners, nb);
+       write_unlock_irq(&profile_lock);
+       return err;
+}
+
+
+void x86_profile_hook(struct pt_regs * regs)
+{
+       /* we would not even need this lock if
+        * we had a global cli() on register/unregister
+        */ 
+       read_lock(&profile_lock);
+       notifier_call_chain(&profile_listeners, 0, regs);
+       read_unlock(&profile_lock);
+}
index 4e3b4f1cb4b31d98fc1819a197fd759da76ac10b..cf53d2c1d50a23cd1ac223f48bcf77609a7f7cff 100644 (file)
@@ -64,11 +64,6 @@ extern spinlock_t i8259A_lock;
 
 #include "do_timer.h"
 
-/*
- * for x86_do_profile()
- */
-#include <linux/irq.h>
-
 u64 jiffies_64;
 
 unsigned long cpu_khz; /* Detected as we calibrate the TSC */
index 7ee964b2ebf20cb254a4ade7fc7f5c501659f1d3..4a24f8ad0635d8911f39a41444d591efbb684782 100644 (file)
@@ -20,8 +20,7 @@ static inline void do_timer_interrupt_hook(struct pt_regs *regs)
  * system, in that case we have to call the local interrupt handler.
  */
 #ifndef CONFIG_X86_LOCAL_APIC
-       if (!user_mode(regs))
-               x86_do_profile(regs->eip);
+       x86_do_profile(regs);
 #else
        if (!using_apic_timer)
                smp_local_timer_interrupt(regs);
index b2c1cbed5cb9c59ac7b5b4926e61a1f8087fcf3f..d19c7063e17d633482045607ecb7bf9128b9e11f 100644 (file)
@@ -15,8 +15,7 @@ static inline void do_timer_interrupt_hook(struct pt_regs *regs)
  * system, in that case we have to call the local interrupt handler.
  */
 #ifndef CONFIG_X86_LOCAL_APIC
-       if (!user_mode(regs))
-               x86_do_profile(regs->eip);
+       x86_do_profile(regs);
 #else
        if (!using_apic_timer)
                smp_local_timer_interrupt(regs);
index 7bdea5bbe922d94801c57da6c465d4011ec24301..cbafa4129498c0f244299342b3e6d0124f2dd65f 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/smp_lock.h>
 #include <linux/seq_file.h>
 #include <linux/times.h>
+#include <linux/profile.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
index f23f4f75ce65b37df682eca5caf81483b5cd4b8a..1a60daa9172e497990a3dfb75f6e59b250b54642 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/profile.h>
 #include <asm/atomic.h>
 #include <asm/irq.h>
 
@@ -65,20 +66,31 @@ extern char _stext, _etext;
 
 #define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
 
-extern unsigned long prof_cpu_mask;
-extern unsigned int * prof_buffer;
-extern unsigned long prof_len;
-extern unsigned long prof_shift;
-
 /*
- * x86 profiling function, SMP safe. We might want to do this in
- * assembly totally?
+ * The profiling function is SMP safe. (nothing can mess
+ * around with "current", and the profiling counters are
+ * updated with atomic operations). This is especially
+ * useful with a profiling multiplier != 1
  */
-static inline void x86_do_profile (unsigned long eip)
+static inline void x86_do_profile(struct pt_regs * regs)
 {
+       unsigned long eip;
+       extern unsigned long prof_cpu_mask;
+       extern char _stext;
+#ifdef CONFIG_PROFILING
+       extern void x86_profile_hook(struct pt_regs *);
+       x86_profile_hook(regs);
+#endif
+       if (user_mode(regs))
+               return;
        if (!prof_buffer)
                return;
 
+       eip = regs->eip;
        /*
         * Only measure the CPUs specified by /proc/irq/prof_cpu_mask.
         * (default is all CPUs.)
@@ -97,7 +109,28 @@ static inline void x86_do_profile (unsigned long eip)
                eip = prof_len-1;
        atomic_inc((atomic_t *)&prof_buffer[eip]);
 }
+struct notifier_block;
+#ifdef CONFIG_PROFILING
+int register_profile_notifier(struct notifier_block * nb);
+int unregister_profile_notifier(struct notifier_block * nb);
+
+#else
+
+static inline int register_profile_notifier(struct notifier_block * nb)
+{
+       return -ENOSYS;
+}
+
+static inline int unregister_profile_notifier(struct notifier_block * nb)
+{
+       return -ENOSYS;
+}
 
+#endif /* CONFIG_PROFILING */
 #ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */
 static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {
        if (IO_APIC_IRQ(i))
index 15c1e91198b002dc4a5a39f4531c75a1e8e690d0..11fbe9cec5728d24ecfe8b989487df37f579e62f 100644 (file)
@@ -8,6 +8,17 @@
 #include <linux/init.h>
 #include <asm/errno.h>
  
+/* parse command line */
+int __init profile_setup(char * str);
+/* init basic kernel profiler */
+void __init profile_init(void);
+
+extern unsigned int * prof_buffer;
+extern unsigned long prof_len;
+extern unsigned long prof_shift;
+
+
 enum profile_type {
        EXIT_TASK,
        EXIT_MMAP,
index 89c4ead4cf4baf6e42550245e70e25c12b341e7e..764a3ebf3c24573e67c24527123fe794edb55288 100644 (file)
@@ -492,10 +492,6 @@ extern unsigned long itimer_ticks;
 extern unsigned long itimer_next;
 extern void do_timer(struct pt_regs *);
 
-extern unsigned int * prof_buffer;
-extern unsigned long prof_len;
-extern unsigned long prof_shift;
-
 extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr));
 extern void FASTCALL(__wake_up_locked(wait_queue_head_t *q, unsigned int mode));
 extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr));
index c6023edc03f322ff86dc31620334d6d6c9b89cf2..1850a1c3686d75e9d8e3f4040561ed0cce5be65a 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/security.h>
 #include <linux/workqueue.h>
+#include <linux/profile.h>
 
 #include <asm/io.h>
 #include <asm/bugs.h>
@@ -52,7 +53,6 @@
 #error Sorry, your GCC is too old. It builds incorrect kernels.
 #endif
 
-extern char _stext, _etext;
 extern char *linux_banner;
 
 static int init(void *);
@@ -130,13 +130,6 @@ __setup("maxcpus=", maxcpus);
 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
 
-static int __init profile_setup(char *str)
-{
-    int par;
-    if (get_option(&str,&par)) prof_shift = par;
-       return 1;
-}
-
 __setup("profile=", profile_setup);
 
 static int __init checksetup(char *line)
@@ -411,16 +404,7 @@ asmlinkage void __init start_kernel(void)
 #ifdef CONFIG_MODULES
        init_modules();
 #endif
-       if (prof_shift) {
-               unsigned int size;
-               /* only text is profiled */
-               prof_len = (unsigned long) &_etext - (unsigned long) &_stext;
-               prof_len >>= prof_shift;
-               
-               size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1;
-               prof_buffer = (unsigned int *) alloc_bootmem(size);
-       }
-
+       profile_init();
        kmem_cache_init();
        local_irq_enable();
        calibrate_delay();
index 7ebffe971ca86054b036530f58f5f112912ac4df..756f142b1f35224a5d7b0c877ca1cac9325a6158 100644 (file)
@@ -9,6 +9,36 @@
 #include <linux/notifier.h>
 #include <linux/mm.h>
 
+extern char _stext, _etext;
+
+unsigned int * prof_buffer;
+unsigned long prof_len;
+unsigned long prof_shift;
+
+int __init profile_setup(char * str)
+{
+       int par;
+       if (get_option(&str,&par))
+               prof_shift = par;
+       return 1;
+}
+
+
+void __init profile_init(void)
+{
+       unsigned int size;
+       if (!prof_shift) 
+               return;
+       /* only text is profiled */
+       prof_len = (unsigned long) &_etext - (unsigned long) &_stext;
+       prof_len >>= prof_shift;
+               
+       size = prof_len * sizeof(unsigned int) + PAGE_SIZE - 1;
+       prof_buffer = (unsigned int *) alloc_bootmem(size);
+}
+
 /* Profile event notifications */
  
 #ifdef CONFIG_PROFILING
index bf0077634c93247f2cac8442a40947a5f267826e..2d30f7fd0ecb6389f6f2990bfef93743efb3b06b 100644 (file)
@@ -406,10 +406,6 @@ long time_adj;                             /* tick adjust (scaled 1 / HZ)  */
 long time_reftime;                     /* time at last adjustment (s)  */
 long time_adjust;
 
-unsigned int * prof_buffer;
-unsigned long prof_len;
-unsigned long prof_shift;
-
 /*
  * this routine handles the overflow of the microsecond field
  *