mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-16 01:04:08 +08:00
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: Robin Getz <robin.getz@analog.com> Signed-off-by: Bryan Wu <bryan.wu@analog.com>
This commit is contained in:
parent
f16295e7e7
commit
518039bc24
@ -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,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);
|
||||
|
@ -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,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)
|
||||
|
Loading…
Reference in New Issue
Block a user