diff --git a/headers/private/debug/debug_support.h b/headers/private/debug/debug_support.h index e8ce99876f..05c8e81b93 100644 --- a/headers/private/debug/debug_support.h +++ b/headers/private/debug/debug_support.h @@ -13,12 +13,14 @@ extern "C" { #endif typedef struct debug_context { + team_id team; port_id nub_port; port_id reply_port; } debug_context; -status_t init_debug_context(debug_context *context, port_id nubPort); +status_t init_debug_context(debug_context *context, team_id team, + port_id nubPort); void destroy_debug_context(debug_context *context); status_t send_debug_message(debug_context *context, int32 messageCode, @@ -50,6 +52,21 @@ status_t debug_get_stack_frame(debug_context *context, void *stackFrameAddress, debug_stack_frame_info *stackFrameInfo); +// symbol lookup support + +typedef struct debug_symbol_lookup_context debug_symbol_lookup_context; + +status_t debug_create_symbol_lookup_context(debug_context *debugContext, + debug_symbol_lookup_context **lookupContext); +void debug_delete_symbol_lookup_context( + debug_symbol_lookup_context *lookupContext); + +status_t debug_lookup_symbol_address(debug_symbol_lookup_context *lookupContext, + const void *address, void **baseAddress, char *symbolName, + int32 symbolNameSize, char *imageName, int32 imageNameSize, + bool *exactMatch); + + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/kits/debug/Jamfile b/src/kits/debug/Jamfile index 90321c661c..a762c4a876 100644 --- a/src/kits/debug/Jamfile +++ b/src/kits/debug/Jamfile @@ -1,6 +1,9 @@ SubDir OBOS_TOP src kits debug ; +UseArchHeaders $(OBOS_ARCH) ; UsePrivateHeaders debug ; +UsePrivateHeaders kernel ; +UsePrivateHeaders runtime_loader ; UsePrivateHeaders shared ; SubDirHdrs [ FDirName $(SUBDIR) arch ] ; @@ -8,7 +11,9 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) arch $(OBOS_ARCH) ] ; SharedLibrary debug : debug_support.cpp + SymbolLookup.cpp + # architecture specific arch_debug_support.cpp ; diff --git a/src/kits/debug/SymbolLookup.cpp b/src/kits/debug/SymbolLookup.cpp new file mode 100644 index 0000000000..4621292a6a --- /dev/null +++ b/src/kits/debug/SymbolLookup.cpp @@ -0,0 +1,278 @@ +/* + * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +#include +#include + +#include "SymbolLookup.h" + +using namespace BPrivate; + +// PrepareAddress +const void * +Area::PrepareAddress(const void *address) +{ + TRACE(("Area::PrepareAddress(%p): area: %ld\n", address, fRemoteID)); + + // clone the area, if not done already + if (fLocalID < 0) { + fLocalID = clone_area("cloned area", &fLocalAddress, B_ANY_ADDRESS, + B_READ_AREA, fRemoteID); + if (fLocalID < 0) { + TRACE(("Area::PrepareAddress(): Failed to clone area %ld: %s\n", + fRemoteID, strerror(fLocalID))); + throw Exception(fLocalID); + } + } + + // translate the address + const void *result = (const void*)((addr_t)address - (addr_t)fRemoteAddress + + (addr_t)fLocalAddress); + + TRACE(("Area::PrepareAddress(%p) done: %p\n", address, result)); + + return result; +} + + +// #pragma mark - + +// constructor +RemoteMemoryAccessor::RemoteMemoryAccessor(team_id team) + : fTeam(team), + fAreas() +{ +} + +// destructor +RemoteMemoryAccessor::~RemoteMemoryAccessor() +{ + // delete the areas + while (Area *area = fAreas.Head()) { + fAreas.Remove(area); + delete area; + } +} + +// Init +status_t +RemoteMemoryAccessor::Init() +{ + // get a list of the team's areas + area_info areaInfo; + int32 cookie = 0; + status_t error; + while ((error = get_next_area_info(fTeam, &cookie, &areaInfo)) == B_OK) { + + TRACE(("area %ld: address: %p, size: %ld, name: %s\n", areaInfo.area, + areaInfo.address, areaInfo.size, areaInfo.name)); + + Area *area = new(nothrow) Area(areaInfo.area, areaInfo.address, + areaInfo.size); + if (!area) + return B_NO_MEMORY; + + fAreas.Add(area); + } + + if (fAreas.IsEmpty()) + return error; + + return B_OK; +} + +// PrepareAddress +const void * +RemoteMemoryAccessor::PrepareAddress(const void *remoteAddress, int32 size) +{ + TRACE(("RemoteMemoryAccessor::PrepareAddress(%p, %ld)\n", remoteAddress, + size)); + + if (!remoteAddress) { + TRACE(("RemoteMemoryAccessor::PrepareAddress(): Got null address!\n")); + throw Exception(B_BAD_VALUE); + } + + return _FindArea(remoteAddress, size).PrepareAddress(remoteAddress); +} + +// _FindArea +Area & +RemoteMemoryAccessor::_FindArea(const void *address, int32 size) +{ + TRACE(("RemoteMemoryAccessor::_FindArea(%p, %ld)\n", address, size)); + + for (AreaList::Iterator it = fAreas.GetIterator(); it.HasNext();) { + Area *area = it.Next(); + if (area->ContainsAddress(address, size)) + return *area; + } + + TRACE(("RemoteMemoryAccessor::_FindArea(): No area found for address %p\n", + address)); + throw Exception(B_ENTRY_NOT_FOUND); +} + + +// #pragma mark - + +// constructor +SymbolLookup::SymbolLookup(team_id team) + : RemoteMemoryAccessor(team), + fDebugArea(NULL) +{ +} + +// destructor +SymbolLookup::~SymbolLookup() +{ +} + +// Init +status_t +SymbolLookup::Init() +{ + TRACE(("SymbolLookup::Init()\n")); + + status_t error = RemoteMemoryAccessor::Init(); + if (error != B_OK) + return error; + + TRACE(("SymbolLookup::Init(): searching debug area...\n")); + + // find the runtime loader debug area + runtime_loader_debug_area *remoteDebugArea = NULL; + int32 cookie = 0; + area_info areaInfo; + while (get_next_area_info(fTeam, &cookie, &areaInfo) == B_OK) { + if (strcmp(areaInfo.name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) { + remoteDebugArea = (runtime_loader_debug_area*)areaInfo.address; + break; + } + } + + if (!remoteDebugArea) { + TRACE(("SymbolLookup::Init(): Couldn't find debug area!\n")); + return B_ERROR; + } + + TRACE(("SymbolLookup::Init(): found debug area, translating address...\n")); + + // translate the address + try { + fDebugArea = &Read(*remoteDebugArea); + + TRACE(("SymbolLookup::Init(): translated debug area is at: %p, " + "loaded_images: %p\n", fDebugArea, fDebugArea->loaded_images)); + + } catch (Exception exception) { + return exception.Error(); + } + + return B_OK; +} + +// LookupSymbolAddress +status_t +SymbolLookup::LookupSymbolAddress(addr_t address, addr_t *_baseAddress, + const char **_symbolName, const char **_imageName, bool *_exactMatch) +{ + TRACE(("SymbolLookup::LookupSymbolAddress(%p)\n", (void*)address)); + + // get the image for the address + const image_t *image = _FindImageAtAddress(address); + if (!image) + return B_ENTRY_NOT_FOUND; + + TRACE(("SymbolLookup::LookupSymbolAddress(): found image: ID: %ld, text: " + "address: %p, size: %ld\n", + image->id, (void*)image->regions[0].vmstart, image->regions[0].size)); + + // search the image for the symbol + const struct Elf32_Sym *symbolFound = NULL; + addr_t deltaFound = INT_MAX; + bool exactMatch = false; + const char *symbolName = NULL; // remote + + int32 hashTabSize = Read(image->symhash[0]); + const uint32 *hashBuckets = image->symhash + 2; // remote + const uint32 *hashChains = image->symhash + 2 + hashTabSize; // remote + const elf_region_t *textRegion = image->regions; // local + + for (int32 i = 0; i < hashTabSize; i++) { + for (int32 j = Read(hashBuckets[i]); + j != STN_UNDEF; + j = Read(hashChains[j])) { + const struct Elf32_Sym *symbol = &Read(image->syms[j]); + + // skip invalid symbols + if (symbol->st_value == 0 + || symbol->st_value + symbol->st_size + textRegion->delta + > textRegion->vmstart + textRegion->size) { + continue; + } + + // skip symbols starting after the given address + addr_t symbolAddress = symbol->st_value + textRegion->delta; + + if (symbolAddress > address) + continue; + addr_t symbolDelta = address - symbolAddress; + + if (!symbolFound || symbolDelta < deltaFound) { + deltaFound = symbolDelta; + symbolFound = symbol; + symbolName = SYMNAME(image, symbol); + + if (symbolDelta >= 0 && symbolDelta < symbol->st_size) { + // exact match + exactMatch = true; + break; + } + } + } + } + + TRACE(("SymbolLookup::LookupSymbolAddress(): done: symbol: %p, image name: " + "%s, exact match: %d\n", symbolFound, image->name, exactMatch)); + + if (_baseAddress) { + if (symbolFound) + *_baseAddress = symbolFound->st_value + textRegion->delta; + else + *_baseAddress = textRegion->vmstart; + } + + if (_imageName) + *_imageName = image->name; + + if (_symbolName) + *_symbolName = symbolName; // remote address + + if (_exactMatch) + *_exactMatch = exactMatch; + + return B_OK; +} + +// _FindImageAtAddress +const image_t * +SymbolLookup::_FindImageAtAddress(addr_t address) +{ + TRACE(("SymbolLookup::_FindImageAtAddress(%p)\n", (void*)address)); + + // iterate through the images + for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head)); + image; + image = &Read(*image->next)) { + if (image->regions[0].vmstart <= address + && address < image->regions[0].vmstart + image->regions[0].size) { + return image; + } + } + + return NULL; +} + diff --git a/src/kits/debug/SymbolLookup.h b/src/kits/debug/SymbolLookup.h new file mode 100644 index 0000000000..f64a35b9c2 --- /dev/null +++ b/src/kits/debug/SymbolLookup.h @@ -0,0 +1,135 @@ +/* + * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +#ifndef _SYMBOL_LOOKUP_H +#define _SYMBOL_LOOKUP_H + +#include + +#include + +#include + +//#define TRACE_DEBUG_SYMBOL_LOOKUP +#ifdef TRACE_DEBUG_SYMBOL_LOOKUP +# define TRACE(x) printf x +#else +# define TRACE(x) ; +#endif + +struct image_t; +struct runtime_loader_debug_area; + +namespace BPrivate { + +// Exception +class Exception { +public: + Exception(status_t error) + : fError(error) + { + } + + Exception(const Exception &other) + : fError(other.fError) + { + } + + status_t Error() const { return fError; } + +private: + status_t fError; +}; + +// Area +class Area : public DoublyLinkedListLinkImpl { +public: + Area(area_id id, const void *address, int32 size) + : fRemoteID(id), + fLocalID(-1), + fRemoteAddress(address), + fLocalAddress(NULL), + fSize(size) + { + } + + ~Area() + { + if (fLocalID >= 0) + delete_area(fLocalID); + } + + const void* RemoteAddress() const { return fRemoteAddress; } + const void* LocalAddress() const { return fLocalAddress; } + int32 Size() const { return fSize; } + + bool ContainsAddress(const void *address, int32 size) const + { + return ((addr_t)fRemoteAddress <= (addr_t)address + && (addr_t)address + size <= (addr_t)fRemoteAddress + fSize); + } + + const void *PrepareAddress(const void *address); + +private: + area_id fRemoteID; + area_id fLocalID; + const void *fRemoteAddress; + void *fLocalAddress; + int32 fSize; +}; + +// RemoteMemoryAccessor +class RemoteMemoryAccessor { +public: + RemoteMemoryAccessor(team_id team); + ~RemoteMemoryAccessor(); + + status_t Init(); + + const void *PrepareAddress(const void *remoteAddress, int32 size); + + template inline const Type &Read(const Type &remoteData) + { + const void *remoteAddress = &remoteData; + const void *localAddress = PrepareAddress(remoteAddress, + sizeof(remoteData)); + return *(const Type*)localAddress; + } + +private: + Area &_FindArea(const void *address, int32 size); + + typedef DoublyLinkedList AreaList; + +protected: + team_id fTeam; + +private: + AreaList fAreas; +}; + +// SymbolLookup +class SymbolLookup : private RemoteMemoryAccessor { +public: + SymbolLookup(team_id team); + ~SymbolLookup(); + + status_t Init(); + + status_t LookupSymbolAddress(addr_t address, addr_t *_baseAddress, + const char **_symbolName, const char **_imageName, bool *_exactMatch); + +private: + const image_t *_FindImageAtAddress(addr_t address); + + const runtime_loader_debug_area *fDebugArea; +}; + +} // namespace BPrivate + +using BPrivate::SymbolLookup; + +#endif // _SYMBOL_LOOKUP_H diff --git a/src/kits/debug/debug_support.cpp b/src/kits/debug/debug_support.cpp index 2406f0ff4f..7525073b4e 100644 --- a/src/kits/debug/debug_support.cpp +++ b/src/kits/debug/debug_support.cpp @@ -3,21 +3,30 @@ * Distributed under the terms of the MIT License. */ +#include #include +#include #include #include "arch_debug_support.h" +#include "SymbolLookup.h" + +struct debug_symbol_lookup_context { + debug_context context; + SymbolLookup *lookup; +}; // init_debug_context status_t -init_debug_context(debug_context *context, port_id nubPort) +init_debug_context(debug_context *context, team_id team, port_id nubPort) { - if (!context || nubPort < 0) + if (!context || team < 0 || nubPort < 0) return B_BAD_VALUE; + context->team = team; context->nub_port = nubPort; // create the reply port @@ -183,7 +192,7 @@ debug_read_string(debug_context *context, const void *_address, char *buffer, return sumRead; } - +// debug_get_cpu_state status_t debug_get_cpu_state(debug_context *context, thread_id thread, debug_debugger_message *messageCode, debug_cpu_state *cpuState) @@ -214,6 +223,9 @@ debug_get_cpu_state(debug_context *context, thread_id thread, } +// #pragma mark - + +// debug_get_instruction_pointer status_t debug_get_instruction_pointer(debug_context *context, thread_id thread, void **ip, void **stackFrameAddress) @@ -225,7 +237,7 @@ debug_get_instruction_pointer(debug_context *context, thread_id thread, stackFrameAddress); } - +// debug_get_stack_frame status_t debug_get_stack_frame(debug_context *context, void *stackFrameAddress, debug_stack_frame_info *stackFrameInfo) @@ -236,3 +248,100 @@ debug_get_stack_frame(debug_context *context, void *stackFrameAddress, return arch_debug_get_stack_frame(context, stackFrameAddress, stackFrameInfo); } + + +// #pragma mark - + +// debug_create_symbol_lookup_context +status_t +debug_create_symbol_lookup_context(debug_context *debugContext, + debug_symbol_lookup_context **_lookupContext) +{ + if (!debugContext || !_lookupContext) + return B_BAD_VALUE; + + // create the lookup context + debug_symbol_lookup_context *lookupContext + = new(nothrow) debug_symbol_lookup_context; + lookupContext->context = *debugContext; + ObjectDeleter contextDeleter(lookupContext); + + // create and init symbol lookup + SymbolLookup *lookup = new(nothrow) SymbolLookup(debugContext->team); + if (!lookup) + return B_NO_MEMORY; + + status_t error = lookup->Init(); + if (error != B_OK) { + delete lookup; + return error; + } + + // everything went fine: return the result + lookupContext->lookup = lookup; + *_lookupContext = lookupContext; + contextDeleter.Detach(); + + return B_OK; +} + +// debug_delete_symbol_lookup_context +void +debug_delete_symbol_lookup_context(debug_symbol_lookup_context *lookupContext) +{ + if (lookupContext) { + delete lookupContext->lookup; + delete lookupContext; + } +} + +// debug_lookup_symbol_address +status_t +debug_lookup_symbol_address(debug_symbol_lookup_context *lookupContext, + const void *address, void **baseAddress, char *symbolName, + int32 symbolNameSize, char *imageName, int32 imageNameSize, + bool *exactMatch) +{ + if (!lookupContext || !lookupContext->lookup) + return B_BAD_VALUE; + SymbolLookup *lookup = lookupContext->lookup; + + // find the symbol + addr_t _baseAddress; + const char *_symbolName; + const char *_imageName; + try { + status_t error = lookup->LookupSymbolAddress((addr_t)address, &_baseAddress, + &_symbolName, &_imageName, exactMatch); + if (error != B_OK) + return error; + } catch (BPrivate::Exception exception) { + return exception.Error(); + } + + // translate/copy the results + if (baseAddress) + *baseAddress = (void*)_baseAddress; + + if (symbolName && symbolNameSize > 0) { + // _symbolName is a remote address: We read the string from the + // remote memory. The reason for not using the cloned area is that + // we don't trust that the data therein is valid (i.e. null-terminated) + // and thus strlcpy() could segfault when hitting the cloned area end. + if (_symbolName) { + ssize_t sizeRead = debug_read_string(&lookupContext->context, + _symbolName, symbolName, symbolNameSize); + if (sizeRead < 0) + return sizeRead; + } else + symbolName[0] = '\0'; + } + + if (imageName) { + if (imageNameSize > B_OS_NAME_LENGTH) + imageNameSize = B_OS_NAME_LENGTH; + strlcpy(imageName, _imageName, imageNameSize); + } + + return B_OK; +} diff --git a/src/servers/debug/DebugServer.cpp b/src/servers/debug/DebugServer.cpp index 5f0e0599a6..3f48a227b3 100644 --- a/src/servers/debug/DebugServer.cpp +++ b/src/servers/debug/DebugServer.cpp @@ -107,6 +107,10 @@ private: bool _HandleMessage(DebugMessage *message); + void _LookupSymbolAddress(debug_symbol_lookup_context *lookupContext, + const void *address, char *buffer, int32 bufferSize); + void _PrintStackTrace(thread_id thread); + status_t _InitGUI(); static status_t _HandlerThreadEntry(void *data); @@ -333,7 +337,7 @@ TeamDebugHandler::Init(port_id nubPort) } // init a debug context for the handler - error = init_debug_context(&fDebugContext, nubPort); + error = init_debug_context(&fDebugContext, fTeam, nubPort); if (error != B_OK) { printf("debug_server: TeamDebugHandler::Init(): Failed to init " "debug context for team %ld, port %ld: %s\n", fTeam, nubPort, @@ -536,53 +540,7 @@ TeamDebugHandler::_HandleMessage(DebugMessage *message) printf("debug_server: Thread %ld entered the debugger: %s\n", thread, buffer); -// TODO: Temporary solution. Remove when attaching gdb is working. -#if 1 - // print a stacktrace - void *ip = NULL; - void *stackFrameAddress = NULL; - status_t error = debug_get_instruction_pointer(&fDebugContext, thread, &ip, - &stackFrameAddress); - - if (error == B_OK) { - printf("stack trace, current PC %p:\n", ip); - - for (int32 i = 0; i < 50; i++) { - debug_stack_frame_info stackFrameInfo; - - error = debug_get_stack_frame(&fDebugContext, stackFrameAddress, - &stackFrameInfo); - if (error < B_OK || stackFrameInfo.parent_frame == NULL) - break; - - // find area containing the IP - team_id team = fTeam; - bool useAreaInfo = false; - area_info info; - int32 cookie = 0; - while (get_next_area_info(team, &cookie, &info) == B_OK) { - if ((addr_t)info.address - <= (addr_t)stackFrameInfo.return_address - && (addr_t)info.address + info.size - > (addr_t)stackFrameInfo.return_address) { - useAreaInfo = true; - break; - } - } - - printf(" (%p) %p", stackFrameInfo.frame, - stackFrameInfo.return_address); - if (useAreaInfo) { - printf(" (%s + %#lx)\n", info.name, - (addr_t)stackFrameInfo.return_address - - (addr_t)info.address); - } else - putchar('\n'); - - stackFrameAddress = stackFrameInfo.parent_frame; - } - } -#endif + _PrintStackTrace(thread); bool kill = true; @@ -615,6 +573,111 @@ TeamDebugHandler::_HandleMessage(DebugMessage *message) return kill; } +// _LookupSymbolAddress +void +TeamDebugHandler::_LookupSymbolAddress( + debug_symbol_lookup_context *lookupContext, const void *address, + char *buffer, int32 bufferSize) +{ + // lookup the symbol + void *baseAddress; + char symbolName[1024]; + char imageName[B_OS_NAME_LENGTH]; + bool exactMatch; + bool lookupSucceeded = false; + if (lookupContext) { + status_t error = debug_lookup_symbol_address(lookupContext, address, + &baseAddress, symbolName, sizeof(symbolName), imageName, + sizeof(imageName), &exactMatch); + lookupSucceeded = (error == B_OK); + } + + if (lookupSucceeded) { + // we were able to look something up + if (strlen(symbolName) > 0) { + // we even got a symbol + snprintf(buffer, bufferSize, "%s + %#lx%s", symbolName, + (addr_t)address - (addr_t)baseAddress, + (exactMatch ? "" : " (closest symbol)")); + + } else { + // no symbol: image relative address + snprintf(buffer, bufferSize, "(%s + %#lx)", symbolName, + (addr_t)address - (addr_t)baseAddress); + } + + } else { + // lookup failed: find area containing the IP + bool useAreaInfo = false; + area_info info; + int32 cookie = 0; + while (get_next_area_info(fTeam, &cookie, &info) == B_OK) { + if ((addr_t)info.address <= (addr_t)address + && (addr_t)info.address + info.size > (addr_t)address) { + useAreaInfo = true; + break; + } + } + + if (useAreaInfo) { + snprintf(buffer, bufferSize, "(%s + %#lx)", info.name, + (addr_t)address - (addr_t)info.address); + } else if (bufferSize > 0) + buffer[0] = '\0'; + } +} + +// _PrintStackTrace +void +TeamDebugHandler::_PrintStackTrace(thread_id thread) +{ + // print a stacktrace + void *ip = NULL; + void *stackFrameAddress = NULL; + status_t error = debug_get_instruction_pointer(&fDebugContext, thread, &ip, + &stackFrameAddress); + + if (error == B_OK) { + // create a symbol lookup context + debug_symbol_lookup_context *lookupContext = NULL; + error = debug_create_symbol_lookup_context(&fDebugContext, + &lookupContext); + if (error != B_OK) { + printf("debug_server: Failed to create symbol lookup context: %s\n", + strerror(error)); + } + + // lookup the IP + char symbolBuffer[2048]; + _LookupSymbolAddress(lookupContext, ip, symbolBuffer, + sizeof(symbolBuffer) - 1); + + printf("stack trace, current PC %p %s:\n", ip, symbolBuffer); + + for (int32 i = 0; i < 50; i++) { + debug_stack_frame_info stackFrameInfo; + + error = debug_get_stack_frame(&fDebugContext, stackFrameAddress, + &stackFrameInfo); + if (error < B_OK || stackFrameInfo.parent_frame == NULL) + break; + + // lookup the return address + _LookupSymbolAddress(lookupContext, stackFrameInfo.return_address, + symbolBuffer, sizeof(symbolBuffer) - 1); + + printf(" (%p) %p %s\n", stackFrameInfo.frame, + stackFrameInfo.return_address, symbolBuffer); + + stackFrameAddress = stackFrameInfo.parent_frame; + } + + // delete the symbol lookup context + if (lookupContext) + debug_delete_symbol_lookup_context(lookupContext); + } +} + // _InitGUI status_t TeamDebugHandler::_InitGUI()