Commit 518039bc authored by Robin Getz's avatar Robin Getz Committed by Bryan Wu

Blackfin arch: Add ability to expend the hardware trace buffer

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: default avatarRobin Getz <robin.getz@analog.com>
Signed-off-by: default avatarBryan Wu <bryan.wu@analog.com>
parent f16295e7
......@@ -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
......
......@@ -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
}
......@@ -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,16 +434,23 @@ 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);
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);
if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) {
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_address((unsigned long)bfin_read_TBUF());
......@@ -446,7 +458,32 @@ void dump_bfin_trace_buffer(void)
}
}
#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);
......
......@@ -98,7 +98,7 @@ ENTRY(__start)
M2 = r0;
M3 = r0;
trace_buffer_start(p0,r0);
trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
......
......@@ -96,7 +96,7 @@ ENTRY(__start)
M2 = r0;
M3 = r0;
trace_buffer_start(p0,r0);
trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
......
......@@ -93,7 +93,7 @@ ENTRY(__stext)
M2 = r0;
M3 = r0;
trace_buffer_start(p0,r0);
trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
......
......@@ -96,7 +96,7 @@ ENTRY(__start)
M2 = r0;
M3 = r0;
trace_buffer_start(p0,r0);
trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
......
......@@ -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 */
......
......@@ -6,9 +6,25 @@
#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(); \
......@@ -19,10 +35,17 @@
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)
......@@ -35,9 +58,23 @@
#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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment