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.
 
+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)"
+       depends on DEBUG_BFIN_HWTRACE_ON
        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 <asm/trace.h>
 
 static unsigned long irq_err_count;
 static spinlock_t irq_controller_lock;
@@ -144,4 +145,12 @@ void __init init_IRQ(void)
        }
 
        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;
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
 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);
 }
+#endif
 
 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;
 
@@ -429,24 +434,56 @@ asmlinkage void trap_c(struct pt_regs *fp)
 
 /* Typical exception handling routines */
 
+#define EXPAND_LEN ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 256 - 1)
+
 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);
 
+       printk(KERN_EMERG "Hardware Trace:\n");
+
        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("\n" KERN_EMERG "   Source : ");
+                       printk("\n" KERN_EMERG "     Source : ");
                        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);
+#endif
 }
 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;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
        int tflags;
+#endif
        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;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
index 82ea047..6dbcb77 100644 (file)
@@ -96,7 +96,7 @@ ENTRY(__start)
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
index 72087c2..e53d74d 100644 (file)
@@ -93,7 +93,7 @@ ENTRY(__stext)
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
index 83cd3f9..8c9f73b 100644 (file)
@@ -96,7 +96,7 @@ ENTRY(__start)
        M2 = r0;
        M3 = r0;
 
-       trace_buffer_start(p0,r0);
+       trace_buffer_init(p0,r0);
        P0 = R1;
        R0 = R1;
 
index 207e697..ab278a7 100644 (file)
@@ -731,6 +731,75 @@ ENTRY(_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
@@ -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 */
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       .long _ex_trace_buff_full /* 0x11 - Trace Buffer Full */
+#else
        .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 */
index 9c2474c..6313aac 100644 (file)
@@ -6,23 +6,46 @@
 #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__
+extern unsigned long trace_buff_offset;
+extern unsigned long software_trace_buff[];
+
 /* Trace Macros for C files */
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+
 #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) \
-        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 */
 
+#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)
 
        dreg = 0x1;                     \
        [preg] = dreg;
 
-#define trace_buffer_start(preg, dreg) \
+#define trace_buffer_start(preg, dreg) \
        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;
 
+#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)