ARM: kernel: Make KDL more useful on ARM

This adds the -mapcs-frame compiler flag for ARM to have "stable"
stack frames, adds support to the kernel for dumping stack crawls,
and initial support for iframes. There' much more functionality
to unlock in KDL, but this makes debugging already a lot more
comfortable.....
This commit is contained in:
Ithamar R. Adema
2013-09-17 23:04:59 +02:00
parent 740490ba82
commit cc65466f0d
6 changed files with 390 additions and 108 deletions
+2
View File
@@ -242,6 +242,8 @@ switch $(HAIKU_CPU) {
case arm :
{
HAIKU_DEFINES += __ARM__ ;
HAIKU_CCFLAGS += -mapcs-frame ; # For stackcrawls
HAIKU_C++FLAGS += -mapcs-frame ;
HAIKU_BOOT_PLATFORM ?= u-boot ;
HAIKU_BOOT_BOARD ?= verdex ;
HAIKU_BOOT_FLOPPY_IMAGE_SIZE = 1440 ;
@@ -63,6 +63,7 @@ extern "C" {
extern addr_t arm_get_far(void);
extern int32 arm_get_fsr(void);
extern addr_t arm_get_fp(void);
extern int mmu_read_c1(void);
extern int mmu_write_c1(int val);
+6
View File
@@ -94,6 +94,12 @@ FUNCTION(arm_get_far):
bx lr
FUNCTION_END(arm_get_far)
/* addr_t arm_get_fp(void); */
FUNCTION(arm_get_fp):
mov r0, fp @ get framepointer
bx lr
FUNCTION_END(arm_get_fp);
/* status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */
FUNCTION(arch_cpu_user_memcpy):
stmfd sp!, { r4-r6 }
+328 -108
View File
@@ -6,6 +6,8 @@
* Axel Dörfler <[email protected]>
* Ingo Weinhold <[email protected]>
* François Revol <[email protected]>
* Ithamar R. Adema <[email protected]>
*
*/
@@ -13,25 +15,22 @@
#include <arch_cpu.h>
#include <debug.h>
#include <debug_heap.h>
#include <elf.h>
#include <kernel.h>
#include <kimage.h>
#include <thread.h>
struct stack_frame {
struct stack_frame *previous;
addr_t return_address;
};
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>
#include <vm/VMArea.h>
#define NUM_PREVIOUS_LOCATIONS 32
extern struct iframe_stack gBootFrameStack;
/*
static bool
already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 framePointer)
already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 fp)
{
int32 last = *_last;
int32 num = *_num;
@@ -39,13 +38,13 @@ already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 framePointer)
for (i = 0; i < num; i++) {
if (visited[(NUM_PREVIOUS_LOCATIONS + last - i)
% NUM_PREVIOUS_LOCATIONS] == framePointer) {
% NUM_PREVIOUS_LOCATIONS] == fp) {
return true;
}
}
*_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS;
visited[last] = framePointer;
visited[last] = fp;
if (num < NUM_PREVIOUS_LOCATIONS)
*_num = num + 1;
@@ -54,99 +53,327 @@ already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 framePointer)
}
static inline stack_frame *
get_current_stack_frame()
static status_t
get_next_frame(addr_t fp, addr_t *next, addr_t *ip)
{
stack_frame *frame;
asm volatile("move.l %%fp,%0" : "=r"(frame));
return frame;
if (fp != 0) {
addr_t _fp = *(((addr_t*)fp) -3);
addr_t _sp = *(((addr_t*)fp) -2);
addr_t _lr = *(((addr_t*)fp) -1);
addr_t _pc = *(((addr_t*)fp) -0);
*ip = (_fp != 0) ? _lr : _pc;
*next = _fp;
return B_OK;
}
return B_BAD_VALUE;
}
static status_t
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
lookup_symbol(Thread* thread, addr_t address, addr_t* _baseAddress,
const char** _symbolName, const char** _imageName, bool* _exactMatch)
{
Thread *thread = thread_get_current_thread();
addr_t oldFaultHandler = thread->fault_handler;
status_t status = B_ENTRY_NOT_FOUND;
// set fault handler, so that we can safely access user stacks
if (thread) {
if (m68k_set_fault_handler(&thread->fault_handler, (addr_t)&&error))
goto error;
if (IS_KERNEL_ADDRESS(address)) {
// a kernel symbol
status = elf_debug_lookup_symbol_address(address, _baseAddress,
_symbolName, _imageName, _exactMatch);
} else if (thread != NULL && thread->team != NULL) {
// try a lookup using the userland runtime loader structures
status = elf_debug_lookup_user_symbol_address(thread->team, address,
_baseAddress, _symbolName, _imageName, _exactMatch);
if (status != B_OK) {
// try to locate the image in the images loaded into user space
status = image_debug_lookup_user_symbol_address(thread->team,
address, _baseAddress, _symbolName, _imageName, _exactMatch);
}
}
*ip = ((struct stack_frame *)framePointer)->return_address;
*next = (addr_t)((struct stack_frame *)framePointer)->previous;
if (thread)
thread->fault_handler = oldFaultHandler;
return B_OK;
error:
thread->fault_handler = oldFaultHandler;
return B_BAD_ADDRESS;
return status;
}
static void
print_stack_frame(Thread *thread, addr_t ip, addr_t framePointer,
addr_t nextFramePointer)
set_debug_argument_variable(int32 index, uint64 value)
{
addr_t diff = nextFramePointer - framePointer;
// kernel space/user space switch
if (diff & 0x80000000)
diff = 0;
// lookup symbol
const char *symbol, *image;
addr_t baseAddress;
bool exactMatch;
status_t status = elf_debug_lookup_symbol_address(ip, &baseAddress, &symbol,
&image, &exactMatch);
if (status != B_OK && !IS_KERNEL_ADDRESS(ip) && thread) {
// try to locate the image in the images loaded into user space
status = image_debug_lookup_user_symbol_address(thread->team, ip,
&baseAddress, &symbol, &image, &exactMatch);
}
if (status == B_OK) {
if (symbol != NULL) {
kprintf("%08lx (+%4ld) %08lx <%s>:%s + 0x%04lx%s\n", framePointer,
diff, ip, image, symbol, ip - baseAddress,
(exactMatch ? "" : " (nearest)"));
} else {
kprintf("%08lx (+%4ld) %08lx <%s@%p>:unknown + 0x%04lx\n",
framePointer, diff, ip, image, (void *)baseAddress,
ip - baseAddress);
}
} else
kprintf("%08lx (+%4ld) %08lx\n", framePointer, diff, ip);
char name[8];
snprintf(name, sizeof(name), "_arg%ld", index);
set_debug_variable(name, value);
}
template<typename Type>
static Type
read_function_argument_value(void* argument, bool& _valueKnown)
{
Type value;
if (debug_memcpy(B_CURRENT_TEAM, &value, argument, sizeof(Type)) == B_OK) {
_valueKnown = true;
return value;
}
_valueKnown = false;
return 0;
}
static status_t
print_demangled_call(const char* image, const char* symbol, addr_t args,
bool noObjectMethod, bool addDebugVariables)
{
static const size_t kBufferSize = 256;
char* buffer = (char*)debug_malloc(kBufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
bool isObjectMethod;
const char* name = debug_demangle_symbol(symbol, buffer, kBufferSize,
&isObjectMethod);
if (name == NULL) {
debug_free(buffer);
return B_ERROR;
}
uint32* arg = (uint32*)args;
if (noObjectMethod)
isObjectMethod = false;
if (isObjectMethod) {
const char* lastName = strrchr(name, ':') - 1;
int namespaceLength = lastName - name;
uint32 argValue = 0;
if (debug_memcpy(B_CURRENT_TEAM, &argValue, arg, 4) == B_OK) {
kprintf("<%s> %.*s<\33[32m%#" B_PRIx32 "\33[0m>%s", image,
namespaceLength, name, argValue, lastName);
} else
kprintf("<%s> %.*s<???>%s", image, namespaceLength, name, lastName);
if (addDebugVariables)
set_debug_variable("_this", argValue);
arg++;
} else
kprintf("<%s> %s", image, name);
kprintf("(");
size_t length;
int32 type, i = 0;
uint32 cookie = 0;
while (debug_get_next_demangled_argument(&cookie, symbol, buffer,
kBufferSize, &type, &length) == B_OK) {
if (i++ > 0)
kprintf(", ");
// retrieve value and type identifier
uint64 value;
bool valueKnown = false;
switch (type) {
case B_INT64_TYPE:
value = read_function_argument_value<int64>(arg, valueKnown);
if (valueKnown)
kprintf("int64: \33[34m%Ld\33[0m", value);
break;
case B_INT32_TYPE:
value = read_function_argument_value<int32>(arg, valueKnown);
if (valueKnown)
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
break;
case B_INT16_TYPE:
value = read_function_argument_value<int16>(arg, valueKnown);
if (valueKnown)
kprintf("int16: \33[34m%d\33[0m", (int16)value);
break;
case B_INT8_TYPE:
value = read_function_argument_value<int8>(arg, valueKnown);
if (valueKnown)
kprintf("int8: \33[34m%d\33[0m", (int8)value);
break;
case B_UINT64_TYPE:
value = read_function_argument_value<uint64>(arg, valueKnown);
if (valueKnown) {
kprintf("uint64: \33[34m%#Lx\33[0m", value);
if (value < 0x100000)
kprintf(" (\33[34m%Lu\33[0m)", value);
}
break;
case B_UINT32_TYPE:
value = read_function_argument_value<uint32>(arg, valueKnown);
if (valueKnown) {
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
if (value < 0x100000)
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
}
break;
case B_UINT16_TYPE:
value = read_function_argument_value<uint16>(arg, valueKnown);
if (valueKnown) {
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
(uint16)value, (uint16)value);
}
break;
case B_UINT8_TYPE:
value = read_function_argument_value<uint8>(arg, valueKnown);
if (valueKnown) {
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
(uint8)value, (uint8)value);
}
break;
case B_BOOL_TYPE:
value = read_function_argument_value<uint8>(arg, valueKnown);
if (valueKnown)
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
break;
default:
if (buffer[0])
kprintf("%s: ", buffer);
if (length == 4) {
value = read_function_argument_value<uint32>(arg,
valueKnown);
if (valueKnown) {
if (value == 0
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
kprintf("NULL");
else
kprintf("\33[34m%#lx\33[0m", (uint32)value);
}
break;
}
if (length == 8) {
value = read_function_argument_value<uint64>(arg,
valueKnown);
} else
value = (uint64)arg;
if (valueKnown)
kprintf("\33[34m%#Lx\33[0m", value);
break;
}
if (!valueKnown)
kprintf("???");
if (valueKnown && type == B_STRING_TYPE) {
if (value == 0)
kprintf(" \33[31m\"<NULL>\"\33[0m");
else if (debug_strlcpy(B_CURRENT_TEAM, buffer, (char*)(addr_t)value,
kBufferSize) < B_OK) {
kprintf(" \33[31m\"<???>\"\33[0m");
} else
kprintf(" \33[36m\"%s\"\33[0m", buffer);
}
if (addDebugVariables)
set_debug_argument_variable(i, value);
arg = (uint32*)((uint8*)arg + length);
}
debug_free(buffer);
kprintf(")");
return B_OK;
}
static void
print_stack_frame(Thread *thread, addr_t ip, addr_t fp, addr_t next,
int32 callIndex, bool demangle)
{
const char* symbol;
const char* image;
addr_t baseAddress;
bool exactMatch;
status_t status;
addr_t diff;
diff = next - fp;
// MSB set = kernel space/user space switch
if (diff & ~((addr_t)-1 >> 1))
diff = 0;
status = lookup_symbol(thread, ip, &baseAddress, &symbol, &image,
&exactMatch);
kprintf("%2" B_PRId32 " %0*lx (+%4ld) %0*lx ", callIndex,
B_PRINTF_POINTER_WIDTH, fp, diff, B_PRINTF_POINTER_WIDTH, ip);
if (status == B_OK) {
if (exactMatch && demangle) {
status = print_demangled_call(image, symbol,
next, false, false);
}
if (!exactMatch || !demangle || status != B_OK) {
if (symbol != NULL) {
kprintf("<%s> %s%s", image, symbol,
exactMatch ? "" : " (nearest)");
} else
kprintf("<%s@%p> <unknown>", image, (void*)baseAddress);
}
kprintf(" + %#04lx\n", ip - baseAddress);
} else {
VMArea *area = NULL;
if (thread != NULL && thread->team != NULL
&& thread->team->address_space != NULL) {
area = thread->team->address_space->LookupArea(ip);
}
if (area != NULL) {
kprintf("%" B_PRId32 ":%s@%p + %#lx\n", area->id, area->name,
(void*)area->Base(), ip - area->Base());
} else
kprintf("\n");
}
}
static int
stack_trace(int argc, char **argv)
{
uint32 previousLocations[NUM_PREVIOUS_LOCATIONS];
struct iframe_stack *frameStack;
Thread *thread;
addr_t framePointer;
int32 i, num = 0, last = 0;
if (argc < 2) {
thread = thread_get_current_thread();
framePointer = (addr_t)get_current_stack_frame();
} else {
kprintf("Stack traces of other threads not supported yet!\n");
return 0;
static const char* usage = "usage: %s [-d] [ <thread id> ]\n"
"Prints a stack trace for the current, respectively the specified\n"
"thread.\n"
" -d - Disables the demangling of the symbols.\n"
" <thread id> - The ID of the thread for which to print the stack\n"
" trace.\n";
bool demangle = true;
int32 threadIndex = 1;
if (argc > 1 && !strcmp(argv[1], "-d")) {
demangle = false;
threadIndex++;
}
if (argc > threadIndex + 1
|| (argc == 2 && strcmp(argv[1], "--help") == 0)) {
kprintf(usage, argv[0]);
return 0;
}
addr_t previousLocations[NUM_PREVIOUS_LOCATIONS];
Thread* thread = NULL;
phys_addr_t oldPageDirectory = 0;
addr_t fp = arm_get_fp();
int32 num = 0, last = 0;
struct iframe_stack *frameStack;
// We don't have a thread pointer early in the boot process
if (thread != NULL)
frameStack = &thread->arch_info.iframes;
else
frameStack = &gBootFrameStack;
int32 i;
for (i = 0; i < frameStack->index; i++) {
kprintf("iframe %p (end = %p)\n",
frameStack->frames[i], frameStack->frames[i] + 1);
@@ -168,11 +395,11 @@ return 0;
kprintf("frame caller <image>:function + offset\n");
for (;;) {
for (int32 callIndex = 0;; callIndex++) {
// see if the frame pointer matches the iframe
struct iframe *frame = NULL;
for (i = 0; i < frameStack->index; i++) {
if (framePointer == (addr_t)frameStack->frames[i]) {
if (fp == (addr_t)frameStack->frames[i]) {
// it's an iframe
frame = frameStack->frames[i];
break;
@@ -181,49 +408,43 @@ return 0;
if (frame) {
kprintf("iframe at %p\n", frame);
kprintf(" d0 0x%08lx d1 0x%08lx d2 0x%08lx d3 0x%08lx\n",
frame->d[0], frame->d[1], frame->d[2], frame->d[3]);
kprintf(" d4 0x%08lx d5 0x%08lx d6 0x%08lx d7 0x%08lx\n",
frame->d[4], frame->d[5], frame->d[6], frame->d[7]);
kprintf(" a0 0x%08lx a1 0x%08lx a2 0x%08lx a3 0x%08lx\n",
frame->a[0], frame->a[1], frame->a[2], frame->a[3]);
kprintf(" a4 0x%08lx a5 0x%08lx a6 0x%08lx a7 0x%08lx (sp)\n",
#warning M68K: a7 in iframe ??
frame->a[4], frame->a[5], frame->a[6], -1L);
kprintf(" pc 0x%08lx sr 0x%04x\n",
frame->cpu.pc, frame->cpu.sr);
#warning M68K: missing regs
kprintf(" r0 0x%08lx r1 0x%08lx r2 0x%08lx r3 0x%08lx\n",
frame->r0, frame->r1, frame->r2, frame->r3);
kprintf(" r4 0x%08lx r5 0x%08lx r6 0x%08lx r7 0x%08lx\n",
frame->r4, frame->r5, frame->r6, frame->r7);
kprintf(" r8 0x%08lx r9 0x%08lx r10 0x%08lx r11 0x%08lx\n",
frame->r8, frame->r9, frame->r10, frame->r11);
kprintf(" r12 0x%08lx sp 0x%08lx lr 0x%08lx pc 0x%08lx\n",
frame->r12, frame->svc_sp, frame->svc_lr, frame->pc);
print_stack_frame(thread, frame->cpu.pc, framePointer, frame->a[6]);
framePointer = frame->a[6];
fp = frame->svc_sp;
print_stack_frame(thread, frame->pc, frame->svc_sp, frame->svc_lr, callIndex, demangle);
} else {
addr_t ip, nextFramePointer;
addr_t ip, next;
if (get_next_frame(framePointer, &nextFramePointer, &ip) != B_OK) {
kprintf("%08lx -- read fault\n", framePointer);
if (get_next_frame(fp, &next, &ip) != B_OK) {
kprintf("%08lx -- read fault\n", fp);
break;
}
if (ip == 0 || framePointer == 0)
if (ip == 0 || fp == 0)
break;
print_stack_frame(thread, ip, framePointer, nextFramePointer);
framePointer = nextFramePointer;
print_stack_frame(thread, ip, fp, next, callIndex, demangle);
fp = next;
}
if (already_visited(previousLocations, &last, &num, framePointer)) {
kprintf("circular stack frame: %p!\n", (void *)framePointer);
if (already_visited(previousLocations, &last, &num, fp)) {
kprintf("circular stack frame: %p!\n", (void *)fp);
break;
}
if (framePointer == 0)
if (fp == 0)
break;
}
return 0;
}
*/
// #pragma mark -
@@ -245,6 +466,7 @@ arch_debug_contains_call(Thread *thread, const char *symbol,
void
arch_debug_stack_trace(void)
{
stack_trace(0, NULL);
}
@@ -261,8 +483,7 @@ int32
arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
int32 skipIframes, int32 skipFrames, uint32 flags)
{
#warning ARM:IMPLEMENT
// TODO: Implement!
return 0;
}
@@ -302,10 +523,9 @@ arch_get_debug_variable(const char* variableName, uint64* value)
status_t
arch_debug_init(kernel_args *args)
{
// add_debugger_command("where", &stack_trace, "Same as \"sc\"");
// add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
// add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
#warning ARM:IMPLEMENT
add_debugger_command("where", &stack_trace, "Same as \"sc\"");
add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
return B_NO_ERROR;
}
+36
View File
@@ -188,9 +188,35 @@ arch_int_init_post_device_manager(struct kernel_args *args)
}
// Little helper class for handling the
// iframe stack as used by KDL.
class IFrameScope {
public:
IFrameScope(struct iframe *iframe) {
fThread = thread_get_current_thread();
if (fThread)
arm_push_iframe(&fThread->arch_info.iframes, iframe);
else
arm_push_iframe(&gBootFrameStack, iframe);
}
virtual ~IFrameScope() {
// pop iframe
if (fThread)
arm_pop_iframe(&fThread->arch_info.iframes);
else
arm_pop_iframe(&gBootFrameStack);
}
private:
Thread* fThread;
};
extern "C" void
arch_arm_undefined(struct iframe *iframe)
{
IFrameScope scope(iframe); // push/pop iframe
print_iframe("Undefined Instruction", iframe);
panic("not handled!");
}
@@ -199,6 +225,8 @@ arch_arm_undefined(struct iframe *iframe)
extern "C" void
arch_arm_syscall(struct iframe *iframe)
{
IFrameScope scope(iframe); // push/pop iframe
print_iframe("Software interrupt", iframe);
}
@@ -206,6 +234,8 @@ arch_arm_syscall(struct iframe *iframe)
extern "C" void
arch_arm_data_abort(struct iframe *frame)
{
IFrameScope scope(iframe);
Thread *thread = thread_get_current_thread();
bool isUser = (frame->spsr & 0x1f) == 0x10;
addr_t far = arm_get_far();
@@ -290,6 +320,8 @@ arch_arm_data_abort(struct iframe *frame)
extern "C" void
arch_arm_prefetch_abort(struct iframe *iframe)
{
IFrameScope scope(iframe);
print_iframe("Prefetch Abort", iframe);
panic("not handled!");
}
@@ -298,6 +330,8 @@ arch_arm_prefetch_abort(struct iframe *iframe)
extern "C" void
arch_arm_irq(struct iframe *iframe)
{
IFrameScope scope(iframe);
for (int i=0; i < 32; i++) {
if (sPxaInterruptBase[PXA_ICIP] & (1 << i))
int_io_interrupt_handler(i, true);
@@ -308,6 +342,8 @@ arch_arm_irq(struct iframe *iframe)
extern "C" void
arch_arm_fiq(struct iframe *iframe)
{
IFrameScope scope(iframe);
for (int i=0; i < 32; i++) {
if (sPxaInterruptBase[PXA_ICIP] & (1 << i)) {
dprintf("arch_arm_fiq: help me, FIQ %d was triggered but no "
@@ -42,6 +42,23 @@ static struct arch_thread sInitialState;
Thread *gCurrentThread;
void
arm_push_iframe(struct iframe_stack *stack, struct iframe *frame)
{
ASSERT(stack->index < IFRAME_TRACE_DEPTH);
stack->frames[stack->index++] = frame;
}
void
arm_pop_iframe(struct iframe_stack *stack)
{
ASSERT(stack->index > 0);
stack->index--;
}
status_t
arch_thread_init(struct kernel_args *args)
{