From cc65466f0de34d946d9311517f4e7781b7294ef8 Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Tue, 17 Sep 2013 23:04:59 +0200 Subject: [PATCH] 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..... --- build/jam/BuildSetup | 2 + headers/private/kernel/arch/arm/arch_cpu.h | 1 + src/system/kernel/arch/arm/arch_asm.S | 6 + src/system/kernel/arch/arm/arch_debug.cpp | 436 ++++++++++++++++----- src/system/kernel/arch/arm/arch_int.cpp | 36 ++ src/system/kernel/arch/arm/arch_thread.cpp | 17 + 6 files changed, 390 insertions(+), 108 deletions(-) diff --git a/build/jam/BuildSetup b/build/jam/BuildSetup index 47f346fb9a..6eb5b937aa 100644 --- a/build/jam/BuildSetup +++ b/build/jam/BuildSetup @@ -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 ; diff --git a/headers/private/kernel/arch/arm/arch_cpu.h b/headers/private/kernel/arch/arm/arch_cpu.h index a70398aa64..fea6c68979 100644 --- a/headers/private/kernel/arch/arm/arch_cpu.h +++ b/headers/private/kernel/arch/arm/arch_cpu.h @@ -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); diff --git a/src/system/kernel/arch/arm/arch_asm.S b/src/system/kernel/arch/arm/arch_asm.S index 689f615646..50e9508274 100644 --- a/src/system/kernel/arch/arm/arch_asm.S +++ b/src/system/kernel/arch/arm/arch_asm.S @@ -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 } diff --git a/src/system/kernel/arch/arm/arch_debug.cpp b/src/system/kernel/arch/arm/arch_debug.cpp index daae859148..1832ca2962 100644 --- a/src/system/kernel/arch/arm/arch_debug.cpp +++ b/src/system/kernel/arch/arm/arch_debug.cpp @@ -6,6 +6,8 @@ * Axel Dörfler * Ingo Weinhold * François Revol + * Ithamar R. Adema + * */ @@ -13,25 +15,22 @@ #include #include +#include #include #include #include #include - - -struct stack_frame { - struct stack_frame *previous; - addr_t return_address; -}; +#include +#include +#include #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 +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(arg, valueKnown); + if (valueKnown) + kprintf("int64: \33[34m%Ld\33[0m", value); + break; + case B_INT32_TYPE: + value = read_function_argument_value(arg, valueKnown); + if (valueKnown) + kprintf("int32: \33[34m%ld\33[0m", (int32)value); + break; + case B_INT16_TYPE: + value = read_function_argument_value(arg, valueKnown); + if (valueKnown) + kprintf("int16: \33[34m%d\33[0m", (int16)value); + break; + case B_INT8_TYPE: + value = read_function_argument_value(arg, valueKnown); + if (valueKnown) + kprintf("int8: \33[34m%d\33[0m", (int8)value); + break; + case B_UINT64_TYPE: + value = read_function_argument_value(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(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(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(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(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(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(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\"\"\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> ", 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] [ ]\n" + "Prints a stack trace for the current, respectively the specified\n" + "thread.\n" + " -d - Disables the demangling of the symbols.\n" + " - 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 :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; } diff --git a/src/system/kernel/arch/arm/arch_int.cpp b/src/system/kernel/arch/arm/arch_int.cpp index b41cc7154d..0dc4fdcd9f 100644 --- a/src/system/kernel/arch/arm/arch_int.cpp +++ b/src/system/kernel/arch/arm/arch_int.cpp @@ -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 " diff --git a/src/system/kernel/arch/arm/arch_thread.cpp b/src/system/kernel/arch/arm/arch_thread.cpp index 3f7950e380..2de87612e8 100644 --- a/src/system/kernel/arch/arm/arch_thread.cpp +++ b/src/system/kernel/arch/arm/arch_thread.cpp @@ -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) {