Blackfin arch: Add ability to expend the hardware trace buffer
authorRobin Getz <robin.getz@analog.com>
Wed, 25 Jul 2007 03:03:28 +0000 (11:03 +0800)
committerBryan Wu <bryan.wu@analog.com>
Wed, 25 Jul 2007 03:03:28 +0000 (11:03 +0800)
Add ability to expend the hardware trace buffer via a configurable
software buffer - so you can have lots of history when a crash occurs.

The interesting way we do printk in the traps.c confusese the checking
script

Signed-off-by: Robin Getz <robin.getz@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
arch/blackfin/Kconfig
arch/blackfin/kernel/irqchip.c
arch/blackfin/kernel/traps.c
arch/blackfin/mach-bf533/head.S
arch/blackfin/mach-bf537/head.S
arch/blackfin/mach-bf548/head.S
arch/blackfin/mach-bf561/head.S
arch/blackfin/mach-common/entry.S
include/asm-blackfin/trace.h

index 9ce675e..a7a6e0c 100644 (file)
@@ -1024,8 +1024,89 @@ config DEBUG_HUNT_FOR_ZERO
          Enabling this option will take up an extra entry in CPLB table.
          Otherwise, there is no extra overhead.
 
          Enabling this option will take up an extra entry in CPLB table.
          Otherwise, there is no extra overhead.
 
+config DEBUG_BFIN_HWTRACE_ON
+       bool "Turn on Blackfin's Hardware Trace"
+       default y
+       help
+         All Blackfins include a Trace Unit which stores a history of the last
+         16 changes in program flow taken by the program sequencer. The history
+         allows the user to recreate the program sequencer’s recent path. This
+         can be handy when an application dies - we print out the execution
+         path of how it got to the offending instruction.
+
+         By turning this off, you may save a tiny amount of power.
+
+choice
+       prompt "Omit loop Tracing"
+       default DEBUG_BFIN_HWTRACE_COMPRESSION_OFF
+       depends on DEBUG_BFIN_HWTRACE_ON
+       help
+         The trace buffer can be configured to omit recording of changes in
+         program flow that match either the last entry or one of the last
+         two entries. Omitting one of these entries from the record prevents
+         the trace buffer from overflowing because of any sort of loop (for, do
+         while, etc) in the program.
+
+         Because zero-overhead Hardware loops are not recorded in the trace buffer,
+         this feature can be used to prevent trace overflow from loops that
+         are nested four deep.
+
+config DEBUG_BFIN_HWTRACE_COMPRESSION_OFF
+       bool "Trace all Loops"
+       help
+         The trace buffer records all changes of flow 
+
+config DEBUG_BFIN_HWTRACE_COMPRESSION_ONE
+       bool "Compress single-level loops"
+       help
+         The trace buffer does not record single loops - helpful if trace 
+         is spinning on a while or do loop.
+
+config DEBUG_BFIN_HWTRACE_COMPRESSION_TWO
+       bool "Compress two-level loops"
+       help
+         The trace buffer does not record loops two levels deep. Helpful if
+         the trace is spinning in a nested loop
+
+endchoice
+
+config DEBUG_BFIN_HWTRACE_COMPRESSION
+       int
+       depends on DEBUG_BFIN_HWTRACE_ON
+       default 0 if DEBUG_BFIN_HWTRACE_COMPRESSION_OFF
+       default 1 if DEBUG_BFIN_HWTRACE_COMPRESSION_ONE
+       default 2 if DEBUG_BFIN_HWTRACE_COMPRESSION_TWO
+
+
+config DEBUG_BFIN_HWTRACE_EXPAND
+       bool "Expand Trace Buffer greater than 16 entries"
+       depends on DEBUG_BFIN_HWTRACE_ON
+       default n
+       help
+         By selecting this option, every time the 16 hardware entries in
+         the Blackfin's HW Trace buffer are full, the kernel will move them
+         into a software buffer, for dumping when there is an issue. This 
+         has a great impact on performance, (an interrupt every 16 change of 
+         flows) and should normally be turned off, except in those nasty
+         debugging sessions
+
+config DEBUG_BFIN_HWTRACE_EXPAND_LEN
+       int "Size of Trace buffer (in power of 2k)"
+       range 0 4
+       depends on DEBUG_BFIN_HWTRACE_EXPAND
+       default 1
+       help
+         This sets the size of the software buffer that the trace information
+         is kept in.
+         0 for (2^0)  1k, or 256 entries,
+         1 for (2^1)  2k, or 512 entries,
+         2 for (2^2)  4k, or 1024 entries,
+         3 for (2^3)  8k, or 2048 entries,
+         4 for (2^4) 16k, or 4096 entries
+
 config DEBUG_BFIN_NO_KERN_HWTRACE
        bool "Trace user apps (turn off hwtrace in kernel)"
 config DEBUG_BFIN_NO_KERN_HWTRACE
        bool "Trace user apps (turn off hwtrace in kernel)"
+       depends on DEBUG_BFIN_HWTRACE_ON
        default n
        help
          Some pieces of the kernel contain a lot of flow changes which can
        default n
        help
          Some pieces of the kernel contain a lot of flow changes which can
index 1fc001c..462ae41 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/kallsyms.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/kallsyms.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <asm/trace.h>
 
 static unsigned long irq_err_count;
 static spinlock_t irq_controller_lock;
 
 static unsigned long irq_err_count;
 static spinlock_t irq_controller_lock;
@@ -144,4 +145,12 @@ void __init init_IRQ(void)
        }
 
        init_arch_irq();
        }
 
        init_arch_irq();
+
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       /* Now that evt_ivhw is set up, turn this on */
+       trace_buff_offset = 0;
+       bfin_write_TBUFCTL(BFIN_TRACE_ON);
+       printk(KERN_INFO "Hardware Trace expanded to %ik\n",
+         1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN);
+#endif
 }
 }
index 792a841..0ec02fe 100644 (file)
@@ -55,6 +55,7 @@ asmlinkage void trap_c(struct pt_regs *fp);
 
 int kstack_depth_to_print = 48;
 
 
 int kstack_depth_to_print = 48;
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
 static int printk_address(unsigned long address)
 {
        struct vm_list_struct *vml;
 static int printk_address(unsigned long address)
 {
        struct vm_list_struct *vml;
@@ -131,10 +132,14 @@ static int printk_address(unsigned long address)
        /* we were unable to find this address anywhere */
        return printk("[<0x%p>]", (void *)address);
 }
        /* we were unable to find this address anywhere */
        return printk("[<0x%p>]", (void *)address);
 }
+#endif
 
 asmlinkage void trap_c(struct pt_regs *fp)
 {
 
 asmlinkage void trap_c(struct pt_regs *fp)
 {
-       int j, sig = 0;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+       int j;
+#endif
+       int sig = 0;
        siginfo_t info;
        unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
 
        siginfo_t info;
        unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
 
@@ -429,24 +434,56 @@ asmlinkage void trap_c(struct pt_regs *fp)
 
 /* Typical exception handling routines */
 
 
 /* Typical exception handling routines */
 
+#define EXPAND_LEN ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 256 - 1)
+
 void dump_bfin_trace_buffer(void)
 {
 void dump_bfin_trace_buffer(void)
 {
-       int tflags;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+       int tflags, i = 0;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       int j, index;
+#endif
+
        trace_buffer_save(tflags);
 
        trace_buffer_save(tflags);
 
+       printk(KERN_EMERG "Hardware Trace:\n");
+
        if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) {
        if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) {
-               int i;
-               printk(KERN_EMERG "Hardware Trace:\n");
-               for (i = 0; bfin_read_TBUFSTAT() & TBUFCNT; i++) {
-                       printk(KERN_EMERG "%2i Target : ", i);
+               for (; bfin_read_TBUFSTAT() & TBUFCNT; i++) {
+                       printk(KERN_EMERG "%4i Target : ", i);
                        printk_address((unsigned long)bfin_read_TBUF());
                        printk_address((unsigned long)bfin_read_TBUF());
-                       printk("\n" KERN_EMERG "   Source : ");
+                       printk("\n" KERN_EMERG "     Source : ");
                        printk_address((unsigned long)bfin_read_TBUF());
                        printk("\n");
                }
        }
 
                        printk_address((unsigned long)bfin_read_TBUF());
                        printk("\n");
                }
        }
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       if (trace_buff_offset)
+               index = trace_buff_offset/4 - 1;
+       else
+               index = EXPAND_LEN;
+
+       j = (1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 128;
+       while (j) {
+               printk(KERN_EMERG "%4i Target : ", i);
+               printk_address(software_trace_buff[index]);
+               index -= 1;
+               if (index < 0 )
+                       index = EXPAND_LEN;
+               printk("\n" KERN_EMERG "     Source : ");
+               printk_address(software_trace_buff[index]);
+               index -= 1;
+               if (index < 0)
+                       index = EXPAND_LEN;
+               printk("\n");
+               j--;
+               i++;
+       }
+#endif
+
        trace_buffer_restore(tflags);
        trace_buffer_restore(tflags);
+#endif
 }
 EXPORT_SYMBOL(dump_bfin_trace_buffer);
 
 }
 EXPORT_SYMBOL(dump_bfin_trace_buffer);
 
@@ -510,7 +547,9 @@ void show_stack(struct task_struct *task, unsigned long *stack)
 void dump_stack(void)
 {
        unsigned long stack;
 void dump_stack(void)
 {
        unsigned long stack;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
        int tflags;
        int tflags;
+#endif
        trace_buffer_save(tflags);
        dump_bfin_trace_buffer();
        show_stack(current, &stack);
        trace_buffer_save(tflags);
        dump_bfin_trace_buffer();
        show_stack(current, &stack);
index 9c5378b..1d5b9db 100644 (file)
@@ -98,7 +98,7 @@ ENTRY(__start)
        M2 = r0;
        M3 = r0;
 
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
        P0 = R1;
        R0 = R1;
 
index 82ea047..6dbcb77 100644 (file)
@@ -96,7 +96,7 @@ ENTRY(__start)
        M2 = r0;
        M3 = r0;
 
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
        P0 = R1;
        R0 = R1;
 
index 72087c2..e53d74d 100644 (file)
@@ -93,7 +93,7 @@ ENTRY(__stext)
        M2 = r0;
        M3 = r0;
 
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
        P0 = R1;
        R0 = R1;
 
index 83cd3f9..8c9f73b 100644 (file)
@@ -96,7 +96,7 @@ ENTRY(__start)
        M2 = r0;
        M3 = r0;
 
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
        P0 = R1;
        R0 = R1;
 
index 207e697..ab278a7 100644 (file)
@@ -731,6 +731,75 @@ ENTRY(_init_exception_buff)
        rts;
 ENDPROC(_init_exception_buff)
 
        rts;
 ENDPROC(_init_exception_buff)
 
+/* We handle this 100% in exception space - to reduce overhead
+ * Only potiential problem is if the software buffer gets swapped out of the
+ * CPLB table - then double fault. - so we don't let this happen in other places
+ */
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+ENTRY(_ex_trace_buff_full)
+       [--sp] = P3;
+       [--sp] = P2;
+       [--sp] = LC0;
+       [--sp] = LT0;
+       [--sp] = LB0;
+       P5.L = _trace_buff_offset;
+       P5.H = _trace_buff_offset;
+       P3 = [P5];              /* trace_buff_offset */
+       P5.L = lo(TBUFSTAT);
+       P5.H = hi(TBUFSTAT);
+       R7 = [P5];
+       R7 <<= 1;               /* double, since we need to read twice */
+       LC0 = R7;
+       R7 <<= 2;               /* need to shift over again,
+                                * to get the number of bytes */
+       P5.L = lo(TBUF);
+       P5.H = hi(TBUF);
+       R6 = ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN)*1024) - 1;
+
+       P2 = R7;
+       P3 = P3 + P2;
+       R7 = P3;
+       R7 = R7 & R6;
+       P3 = R7;
+       P2.L = _trace_buff_offset;
+       P2.H = _trace_buff_offset;
+       [P2] = P3;
+
+       P2.L = _software_trace_buff;
+       P2.H = _software_trace_buff;
+
+       LSETUP (.Lstart, .Lend) LC0;
+.Lstart:
+       R7 = [P5];      /* read TBUF */
+       P4 = P3 + P2;
+       [P4] = R7;
+       P3 += -4;
+       R7 = P3;
+       R7 = R7 & R6;
+.Lend:
+       P3 = R7;
+
+       LB0 = [sp++];
+       LT0 = [sp++];
+       LC0 = [sp++];
+       P2 = [sp++];
+       P3 = [sp++];
+       jump _return_from_exception;
+
+#if CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN == 4
+.data
+#else
+.section .l1.data.B
+#endif
+ENTRY(_trace_buff_offset)
+        .long 0;
+ALIGN
+ENTRY(_software_trace_buff)
+       .rept ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN)*256);
+       .long 0
+       .endr
+#endif
+
 /*
  * Put these in the kernel data section - that should always be covered by
  * a CPLB. This is needed to ensure we don't get double fault conditions
 /*
  * Put these in the kernel data section - that should always be covered by
  * a CPLB. This is needed to ensure we don't get double fault conditions
@@ -764,7 +833,11 @@ _extable:
        .long _ex_trap_c        /* 0x0E - User Defined */
        .long _ex_trap_c        /* 0x0F - User Defined */
        .long _ex_single_step   /* 0x10 - HW Single step */
        .long _ex_trap_c        /* 0x0E - User Defined */
        .long _ex_trap_c        /* 0x0F - User Defined */
        .long _ex_single_step   /* 0x10 - HW Single step */
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       .long _ex_trace_buff_full /* 0x11 - Trace Buffer Full */
+#else
        .long _ex_trap_c        /* 0x11 - Trace Buffer Full */
        .long _ex_trap_c        /* 0x11 - Trace Buffer Full */
+#endif
        .long _ex_trap_c        /* 0x12 - Reserved */
        .long _ex_trap_c        /* 0x13 - Reserved */
        .long _ex_trap_c        /* 0x14 - Reserved */
        .long _ex_trap_c        /* 0x12 - Reserved */
        .long _ex_trap_c        /* 0x13 - Reserved */
        .long _ex_trap_c        /* 0x14 - Reserved */
index 9c2474c..6313aac 100644 (file)
@@ -6,23 +6,46 @@
 #ifndef _BLACKFIN_TRACE_
 #define _BLACKFIN_TRACE_
 
 #ifndef _BLACKFIN_TRACE_
 #define _BLACKFIN_TRACE_
 
+/* Normally, we use ON, but you can't turn on software expansion until
+ * interrupts subsystem is ready
+ */
+
+#define BFIN_TRACE_INIT ((CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION << 4) | 0x03)
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+#define BFIN_TRACE_ON   (BFIN_TRACE_INIT | (CONFIG_DEBUG_BFIN_HWTRACE_EXPAND << 2))
+#else
+#define BFIN_TRACE_ON   (BFIN_TRACE_INIT)
+#endif
+
 #ifndef __ASSEMBLY__
 #ifndef __ASSEMBLY__
+extern unsigned long trace_buff_offset;
+extern unsigned long software_trace_buff[];
+
 /* Trace Macros for C files */
 
 /* Trace Macros for C files */
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+
 #define trace_buffer_save(x) \
 #define trace_buffer_save(x) \
-        do { \
-                (x) = bfin_read_TBUFCTL(); \
-                bfin_write_TBUFCTL((x) & ~TBUFEN); \
-        } while (0)
+       do { \
+               (x) = bfin_read_TBUFCTL(); \
+               bfin_write_TBUFCTL((x) & ~TBUFEN); \
+       } while (0)
 
 #define trace_buffer_restore(x) \
 
 #define trace_buffer_restore(x) \
-        do { \
-                bfin_write_TBUFCTL((x));        \
-        } while (0)
+       do { \
+               bfin_write_TBUFCTL((x));        \
+       } while (0)
+#else /* DEBUG_BFIN_HWTRACE_ON */
+
+#define trace_buffer_save(x)
+#define trace_buffer_restore(x)
+#endif /* CONFIG_DEBUG_BFIN_HWTRACE_ON */
 
 #else
 /* Trace Macros for Assembly files */
 
 
 #else
 /* Trace Macros for Assembly files */
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+
 #define TRACE_BUFFER_START(preg, dreg) trace_buffer_start(preg, dreg)
 #define TRACE_BUFFER_STOP(preg, dreg)  trace_buffer_stop(preg, dreg)
 
 #define TRACE_BUFFER_START(preg, dreg) trace_buffer_start(preg, dreg)
 #define TRACE_BUFFER_STOP(preg, dreg)  trace_buffer_stop(preg, dreg)
 
        dreg = 0x1;                     \
        [preg] = dreg;
 
        dreg = 0x1;                     \
        [preg] = dreg;
 
-#define trace_buffer_start(preg, dreg) \
+#define trace_buffer_start(preg, dreg) \
        preg.L = LO(TBUFCTL);           \
        preg.H = HI(TBUFCTL);           \
        preg.L = LO(TBUFCTL);           \
        preg.H = HI(TBUFCTL);           \
-       dreg = 0x13;                    \
+       dreg = BFIN_TRACE_ON;           \
+       [preg] = dreg;
+
+#define trace_buffer_init(preg, dreg) \
+       preg.L = LO(TBUFCTL);         \
+       preg.H = HI(TBUFCTL);         \
+       dreg = BFIN_TRACE_INIT;       \
        [preg] = dreg;
 
        [preg] = dreg;
 
+#else /* CONFIG_DEBUG_BFIN_HWTRACE_ON */
+
+#define trace_buffer_stop(preg, dreg)
+#define trace_buffer_start(preg, dreg)
+#define trace_buffer_init(preg, dreg)
+
+#endif /* CONFIG_DEBUG_BFIN_HWTRACE_ON */
+
 #ifdef CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE
 # define DEBUG_START_HWTRACE(preg, dreg) trace_buffer_start(preg, dreg)
 # define DEBUG_STOP_HWTRACE(preg, dreg) trace_buffer_stop(preg, dreg)
 #ifdef CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE
 # define DEBUG_START_HWTRACE(preg, dreg) trace_buffer_start(preg, dreg)
 # define DEBUG_STOP_HWTRACE(preg, dreg) trace_buffer_stop(preg, dreg)