]> Zhao Yanbai Git Server - minix.git/commitdiff
NMI sampling
authorTomas Hruby <tom@minix3.org>
Thu, 23 Sep 2010 10:49:45 +0000 (10:49 +0000)
committerTomas Hruby <tom@minix3.org>
Thu, 23 Sep 2010 10:49:45 +0000 (10:49 +0000)
- if profile --nmi kernel uses NMI watchdog based sampling based on
  Intel architecture performance counters

- using NMI makes kernel profiling possible

- watchdog kernel lockup detection is disabled while sampling as we
  may get unpredictable interrupts in kernel and thus possibly many
  false positives

- if watchdog is not enabled at boot time, profiling enables it and
  turns it of again when done

kernel/arch/i386/arch_watchdog.c
kernel/arch/i386/include/arch_watchdog.h
kernel/arch/i386/memory.c
kernel/profile.c
kernel/profile.h
kernel/system/do_sprofile.c
kernel/watchdog.c
kernel/watchdog.h

index 3c454d6ce1d10413e21f4d9a39371c316c0004e9..f05dae872f025b5d877c302d0ebefbc6f61d68dc 100644 (file)
@@ -18,7 +18,9 @@
  * Intel architecture performance counters watchdog
  */
 
-PRIVATE void intel_arch_watchdog_init(int cpu)
+PRIVATE struct arch_watchdog intel_arch_watchdog;
+
+PRIVATE void intel_arch_watchdog_init(const unsigned cpu)
 {
        u64_t cpuf;
        u32_t val;
@@ -36,7 +38,7 @@ PRIVATE void intel_arch_watchdog_init(int cpu)
        cpuf = cpu_get_freq(cpu);
        while (cpuf.hi || cpuf.lo > 0x7fffffffU)
                cpuf = div64u64(cpuf, 2);
-       watchdog->resetval = cpuf.lo;
+       watchdog->resetval = watchdog->watchdog_resetval = cpuf.lo;
 
        ia32_msr_write(MSR_PERFMON_CRT0, 0, -cpuf.lo);
 
@@ -46,21 +48,21 @@ PRIVATE void intel_arch_watchdog_init(int cpu)
        lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
 }
 
-PRIVATE void intel_arch_watchdog_reinit(const int cpu)
+PRIVATE void intel_arch_watchdog_reinit(const unsigned cpu)
 {
        lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
        ia32_msr_write(MSR_PERFMON_CRT0, 0, -watchdog->resetval);
 }
 
-PRIVATE struct arch_watchdog intel_arch_watchdog = {
-       /*.init = */    intel_arch_watchdog_init,
-       /*.reinit = */  intel_arch_watchdog_reinit
-};
-
-int arch_watchdog_init(void)
+PUBLIC int arch_watchdog_init(void)
 {
        u32_t eax, ebx, ecx, edx;
 
+       if (!lapic_addr) {
+               printf("ERROR : Cannot use NMI watchdog if APIC is not enabled\n");
+               return -1;
+       }
+
        eax = 0xA;
 
        _cpuid(&eax, &ebx, &ecx, &edx);
@@ -81,14 +83,18 @@ int arch_watchdog_init(void)
        (void) lapic_read(LAPIC_LVTPCR);
 
        /* double check if LAPIC is enabled */
-       if (lapic_addr && watchdog_enabled && watchdog->init) {
+       if (lapic_addr && watchdog->init) {
                watchdog->init(cpuid);
        }
 
        return 0;
 }
 
-void arch_watchdog_lockup(const struct nmi_frame * frame)
+PUBLIC void arch_watchdog_stop(void)
+{
+}
+
+PUBLIC void arch_watchdog_lockup(const struct nmi_frame * frame)
 {
        printf("KERNEL LOCK UP\n"
                        "eax    0x%08x\n"
@@ -123,15 +129,44 @@ void arch_watchdog_lockup(const struct nmi_frame * frame)
        panic("Kernel lockup");
 }
 
-void i386_watchdog_start(void)
+PUBLIC int i386_watchdog_start(void)
 {
-       if (watchdog_enabled) {
-               if (arch_watchdog_init()) {
-                       printf("WARNING watchdog initialization "
-                                       "failed! Disabled\n");
-                       watchdog_enabled = 0;
-               }
-               else
-                       BOOT_VERBOSE(printf("Watchdog enabled\n"););
+       if (arch_watchdog_init()) {
+               printf("WARNING watchdog initialization "
+                               "failed! Disabled\n");
+               watchdog_enabled = 0;
+               return -1;
        }
+       else
+               BOOT_VERBOSE(printf("Watchdog enabled\n"););
+
+       return 0;
 }
+
+PRIVATE int intel_arch_watchdog_profile_init(const unsigned freq)
+{
+       u64_t cpuf;
+
+       /* FIXME works only if all CPUs have the same freq */
+       cpuf = cpu_get_freq(cpuid);
+       cpuf = div64u64(cpuf, freq);
+
+       /*
+        * if freq is too low and the cpu freq too high we may get in a range of
+        * insane value which cannot be handled by the 31bit CPU perf counter
+        */
+       if (cpuf.hi != 0 || cpuf.lo > 0x7fffffffU) {
+               printf("ERROR : nmi watchdog ticks exceed 31bits, use higher frequency\n");
+               return EINVAL;
+       }
+
+       watchdog->profile_resetval = cpuf.lo;
+
+       return OK;
+}
+
+PRIVATE struct arch_watchdog intel_arch_watchdog = {
+       /*.init = */            intel_arch_watchdog_init,
+       /*.reinit = */          intel_arch_watchdog_reinit,
+       /*.profile_init = */    intel_arch_watchdog_profile_init
+};
index c205094e1fd89aa3321b0a8822fa14935ddc80ae..24c429804516de26321182e0a81c846341fe0ff0 100644 (file)
@@ -21,6 +21,8 @@ struct nmi_frame {
        reg_t   eflags;
 };
 
-void i386_watchdog_start(void);
+_PROTOTYPE(int i386_watchdog_start, (void));
+
+#define nmi_in_kernel(f)       ((f)->cs == CS_SELECTOR)
 
 #endif /* __I386_WATCHDOG_H__ */
index 3650e900d0cc461c04b3cc9848865a6011b5e13a..4d7417ae88a9b7c41f9daca2c3b92b61fa16e7a7 100644 (file)
@@ -1103,11 +1103,12 @@ PUBLIC int arch_enable_paging(struct proc * caller, const message * m_ptr)
 #ifdef CONFIG_WATCHDOG
        /*
         * We make sure that we don't enable the watchdog until paging is turned
-        * on as we might get a NMI while switching and we might still use wrong
+        * on as we might get an NMI while switching and we might still use wrong
         * lapic address. Bad things would happen. It is unfortunate but such is
         * life
         */
-       i386_watchdog_start();
+       if (watchdog_enabled)
+               i386_watchdog_start();
 #endif
 
        return OK;
index c1c3c653a7843d1ef7d3e0e5db1ad02adc6e12ab..626202338784cdbe0f91eb559c77b79cc566dbb9 100644 (file)
@@ -26,6 +26,7 @@
 #if SPROFILE
 
 #include <string.h>
+#include "watchdog.h"
 
 /* Function prototype for the profiling clock handler. */ 
 FORWARD _PROTOTYPE( int profile_clock_handler, (irq_hook_t *hook) );
@@ -90,25 +91,20 @@ PRIVATE sprof_save_proc(struct proc * p)
        sprof_info.mem_used += sizeof(s);
 }
 
-/*===========================================================================*
- *                     profile_clock_handler                           *
- *===========================================================================*/
-PRIVATE int profile_clock_handler(irq_hook_t *hook)
+PRIVATE void profile_sample(struct proc * p)
 {
-  struct proc * p;
 /* This executes on every tick of the CMOS timer. */
 
   /* Are we profiling, and profiling memory not full? */
-  if (!sprofiling || sprof_info.mem_used == -1) return (1);
+  if (!sprofiling || sprof_info.mem_used == -1)
+         return;
 
   /* Check if enough memory available before writing sample. */
   if (sprof_info.mem_used + sizeof(sprof_info) > sprof_mem_size) {
        sprof_info.mem_used = -1;
-       return(1);
+       return;
   }
 
-  p = get_cpulocal_var(proc_ptr);
-
   if (!(p->p_misc_flags & MF_SPROF_SEEN)) {
          p->p_misc_flags |= MF_SPROF_SEEN;
          sprof_save_proc(p);
@@ -126,6 +122,17 @@ PRIVATE int profile_clock_handler(irq_hook_t *hook)
   }
   
   sprof_info.total_samples++;
+}
+
+/*===========================================================================*
+ *                     profile_clock_handler                           *
+ *===========================================================================*/
+PRIVATE int profile_clock_handler(irq_hook_t *hook)
+{
+  struct proc * p;
+  p = get_cpulocal_var(proc_ptr);
+
+  profile_sample(p);
 
   /* Acknowledge interrupt if necessary. */
   arch_ack_profile_clock();
@@ -133,6 +140,25 @@ PRIVATE int profile_clock_handler(irq_hook_t *hook)
   return(1);                                    /* reenable interrupts */
 }
 
+PUBLIC void nmi_sprofile_handler(struct nmi_frame * frame)
+{
+       /*
+        * test if the kernel was interrupted. If so, save first a sample fo
+        * kernel and than for the current process, otherwise save just the
+        * process
+        */
+       if (nmi_in_kernel(frame)) {
+               struct proc *kern;
+
+               kern = proc_addr(KERNEL);
+               kern->p_reg.pc = frame->pc;
+
+               profile_sample(kern);
+       }
+       
+       profile_sample(get_cpulocal_var(proc_ptr));
+}
+
 #endif /* SPROFILE */
 
 #if CPROFILE
index e09847f35857f2ebdb614b540ce80be674ea42be..275ec87117bfd5d1ca2d700f875f3a44f6ebc6b9 100644 (file)
@@ -5,12 +5,17 @@
 
 #if SPROFILE   /* statistical profiling */
 
+#include "arch_watchdog.h"
+
 EXTERN int sprofiling;                 /* whether profiling is running */
+EXTERN int sprofiling_type;                    /* whether profiling is running */
 EXTERN int sprof_mem_size;             /* available user memory for data */
 EXTERN struct sprof_info_s sprof_info; /* profiling info for user program */
 EXTERN vir_bytes sprof_data_addr_vir;  /* user address to write data */
 EXTERN endpoint_t sprof_ep;            /* user process */
 
+_PROTOTYPE(void nmi_sprofile_handler, (struct nmi_frame * frame));
+
 #endif /* SPROFILE */
 
 
index 92a8b15180b777d1e02e238dfb5516dedd3c1f5a..d4f4b404df78bd1a48fafdeb68005e77344edfef 100644 (file)
@@ -34,6 +34,7 @@ PRIVATE clean_seen_flag(void)
 PUBLIC int do_sprofile(struct proc * caller, message * m_ptr)
 {
   int proc_nr;
+  int err;
 
   switch(m_ptr->PROF_ACTION) {
 
@@ -66,7 +67,19 @@ PUBLIC int do_sprofile(struct proc * caller, message * m_ptr)
 
        sprof_mem_size = m_ptr->PROF_MEM_SIZE;
 
-       init_profile_clock(m_ptr->PROF_FREQ);
+       switch (sprofiling_type = m_ptr->PROF_INTR_TYPE) {
+               case PROF_RTC:
+                       init_profile_clock(m_ptr->PROF_FREQ);
+                       break;
+               case PROF_NMI:
+                       err = nmi_watchdog_start_profiling(m_ptr->PROF_FREQ);
+                       if (err)
+                               return err;
+                       break;
+               default:
+                       printf("ERROR : unknown profiling interrupt type\n");
+                       return EINVAL;
+       }
        
        sprofiling = 1;
 
@@ -87,7 +100,14 @@ PUBLIC int do_sprofile(struct proc * caller, message * m_ptr)
 
        sprofiling = 0;
 
-       stop_profile_clock();
+       switch (sprofiling_type) {
+               case PROF_RTC:
+                       stop_profile_clock();
+                       break;
+               case PROF_NMI:
+                       nmi_watchdog_stop_profiling();
+                       break;
+       }
 
        data_copy(KERNEL, (vir_bytes) &sprof_info,
                sprof_ep, sprof_info_addr_vir, sizeof(sprof_info));
index b4c19c8b6625329f2fbac11a4c570f8ec4017db8..739aa3e5d3536bf86e91839c094403b0d8848d52 100644 (file)
@@ -6,12 +6,13 @@
 
 #include "watchdog.h"
 #include "arch/i386/glo.h"
+#include "profile.h"
 
 unsigned watchdog_local_timer_ticks = 0U;
 struct arch_watchdog *watchdog;
 int watchdog_enabled;
 
-void nmi_watchdog_handler(struct nmi_frame * frame)
+PRIVATE void lockup_check(struct nmi_frame * frame)
 {
        /* FIXME this should be CPU local */
        static unsigned no_ticks;
@@ -23,7 +24,7 @@ void nmi_watchdog_handler(struct nmi_frame * frame)
         * report a lockup in such situation
         */
        if (serial_debug_active)
-               goto reset_and_continue;
+               return;
 
        if (last_tick_count != watchdog_local_timer_ticks) {
                if (no_ticks == 1) {
@@ -32,7 +33,7 @@ void nmi_watchdog_handler(struct nmi_frame * frame)
                }
                /* we are still ticking, everything seems good */
                last_tick_count = watchdog_local_timer_ticks;
-               goto reset_and_continue;
+               return;
        }
 
        /*
@@ -42,12 +43,63 @@ void nmi_watchdog_handler(struct nmi_frame * frame)
        if (++no_ticks < 10) {
                if (no_ticks == 1)
                        printf("WARNING watchdog : possible kernel lockup\n");
-               goto reset_and_continue;
+               return;
        }
 
+       /* if we get this far, the kernel is locked up */
        arch_watchdog_lockup(frame);
+}
 
-reset_and_continue:
-       if (watchdog->reinit)
+PUBLIC void nmi_watchdog_handler(struct nmi_frame * frame)
+{
+       /*
+        * Do not check for lockups while profiling, it is extremely likely that
+        * a false positive is detected if the frequency is high
+        */
+       if (watchdog_enabled && !sprofiling)
+               lockup_check(frame);
+       if (sprofiling)
+               nmi_sprofile_handler(frame);
+       
+       if ((watchdog_enabled || sprofiling) && watchdog->reinit)
                watchdog->reinit(cpuid);
 }
+
+int nmi_watchdog_start_profiling(const unsigned freq)
+{
+       int err;
+       
+       /* if watchdog hasn't been enabled, we must enable it now */
+       if (!watchdog_enabled) {
+               if (arch_watchdog_init())
+                       return ENODEV;
+       }
+
+       if (!watchdog->profile_init) {
+               printf("WARNING NMI watchdog profiling not supported\n");
+               nmi_watchdog_stop_profiling();
+               return ENODEV;
+       }
+
+       err = watchdog->profile_init(freq);
+       if (err != OK)
+               return err;
+
+       watchdog->resetval = watchdog->profile_resetval;
+
+       return OK;
+}
+
+void nmi_watchdog_stop_profiling(void)
+{
+       /*
+        * if we do not rearm the NMI source, we are done, if we want to keep
+        * the watchdog running, we reset is to its normal value
+        */
+
+       if (watchdog)
+               watchdog->resetval = watchdog->watchdog_resetval;
+
+       if (!watchdog_enabled)
+               arch_watchdog_stop();
+}
index 04cc0987b6456f702c58e6a80a3a355ed9f8bc22..521fd2fa6affc299df7afea4642c87e88cfe3a5f 100644 (file)
@@ -13,18 +13,23 @@ extern unsigned watchdog_local_timer_ticks; /* is timer still ticking? */
  * implement it in runtime after the correct arch/model was detected
  */
 
-typedef void (* arch_watchdog_method_t)(int);
+typedef void (* arch_watchdog_method_t)(const unsigned);
+typedef int (* arch_watchdog_profile_init_t)(const unsigned);
 
 struct arch_watchdog {
-       arch_watchdog_method_t  init;   /* initial setup */
-       arch_watchdog_method_t  reinit; /* reinitialization after a tick */
-       unsigned                resetval;
+       arch_watchdog_method_t          init;   /* initial setup */
+       arch_watchdog_method_t          reinit; /* reinit after a tick */
+       arch_watchdog_profile_init_t    profile_init;
+       unsigned                        resetval;
+       unsigned                        watchdog_resetval;
+       unsigned                        profile_resetval;
 };
 
 extern struct arch_watchdog *watchdog;
 
-/* let the arch code do whatever it needs to setup the watchdog */
+/* let the arch code do whatever it needs to setup or quit the watchdog */
 int arch_watchdog_init(void);
+void arch_watchdog_stop(void);
 /* if the watchdog detects lockup, let the arch code to handle it */
 void arch_watchdog_lockup(const struct nmi_frame * frame);
 
@@ -33,4 +38,10 @@ void arch_watchdog_lockup(const struct nmi_frame * frame);
  * arch specific code of the watchdog implementaion */
 void nmi_watchdog_handler(struct nmi_frame * frame);
 
+/*
+ * start and stop profiling using the NMI watchdog
+ */
+int nmi_watchdog_start_profiling(const unsigned freq);
+void nmi_watchdog_stop_profiling(void);
+
 #endif /* __WATCHDOG_H__ */