aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/blackfin/Kconfig81
-rw-r--r--arch/blackfin/kernel/irqchip.c9
-rw-r--r--arch/blackfin/kernel/traps.c53
-rw-r--r--arch/blackfin/mach-bf533/head.S2
-rw-r--r--arch/blackfin/mach-bf537/head.S2
-rw-r--r--arch/blackfin/mach-bf548/head.S2
-rw-r--r--arch/blackfin/mach-bf561/head.S2
-rw-r--r--arch/blackfin/mach-common/entry.S73
-rw-r--r--include/asm-blackfin/trace.h55
9 files changed, 259 insertions, 20 deletions
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index 9ce675e8026..a7a6e0c5827 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -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
diff --git a/arch/blackfin/kernel/irqchip.c b/arch/blackfin/kernel/irqchip.c
index 1fc001c7abd..462ae41144c 100644
--- a/arch/blackfin/kernel/irqchip.c
+++ b/arch/blackfin/kernel/irqchip.c
@@ -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
}
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c
index 792a8416fe1..0ec02fe663e 100644
--- a/arch/blackfin/kernel/traps.c
+++ b/arch/blackfin/kernel/traps.c
@@ -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);
diff --git a/arch/blackfin/mach-bf533/head.S b/arch/blackfin/mach-bf533/head.S
index 9c5378bb8a2..1d5b9dbbbaa 100644
--- a/arch/blackfin/mach-bf533/head.S
+++ b/arch/blackfin/mach-bf533/head.S
@@ -98,7 +98,7 @@ ENTRY(__start)
M2 = r0;
M3 = r0;
- trace_buffer_start(p0,r0);
+ trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
diff --git a/arch/blackfin/mach-bf537/head.S b/arch/blackfin/mach-bf537/head.S
index 82ea0470591..6dbcb77c8d3 100644
--- a/arch/blackfin/mach-bf537/head.S
+++ b/arch/blackfin/mach-bf537/head.S
@@ -96,7 +96,7 @@ ENTRY(__start)
M2 = r0;
M3 = r0;
- trace_buffer_start(p0,r0);
+ trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
diff --git a/arch/blackfin/mach-bf548/head.S b/arch/blackfin/mach-bf548/head.S
index 72087c2e6a8..e53d74d4c0a 100644
--- a/arch/blackfin/mach-bf548/head.S
+++ b/arch/blackfin/mach-bf548/head.S
@@ -93,7 +93,7 @@ ENTRY(__stext)
M2 = r0;
M3 = r0;
- trace_buffer_start(p0,r0);
+ trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
diff --git a/arch/blackfin/mach-bf561/head.S b/arch/blackfin/mach-bf561/head.S
index 83cd3f9bbf2..8c9f73b8e57 100644
--- a/arch/blackfin/mach-bf561/head.S
+++ b/arch/blackfin/mach-bf561/head.S
@@ -96,7 +96,7 @@ ENTRY(__start)
M2 = r0;
M3 = r0;
- trace_buffer_start(p0,r0);
+ trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index 207e69786b4..ab278a72f28 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -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 */
diff --git a/include/asm-blackfin/trace.h b/include/asm-blackfin/trace.h
index 9c2474c9a58..6313aace9d5 100644
--- a/include/asm-blackfin/trace.h
+++ b/include/asm-blackfin/trace.h
@@ -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)
@@ -32,12 +55,26 @@
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)