Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -42,10 +42,11 @@ # import "OFPlainMutex.h" /* For OFSpinlock */ #endif #import "OFString.h" #import "OFThread.h" #import "OFTimer.h" +#import "OFValue.h" #import "OFAllocFailedException.h" #import "OFEnumerationMutationException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" @@ -250,23 +251,52 @@ #if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__) static void uncaughtExceptionHandler(id exception) { OFString *description = [exception description]; - OFArray *backtrace = nil; + OFArray OF_GENERIC(OFValue *) *stackTraceAddresses = nil; + OFArray OF_GENERIC(OFString *) *stackTraceSymbols = nil; OFStringEncoding encoding = [OFLocale encoding]; fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n", [description cStringWithEncoding: encoding]); - if ([exception respondsToSelector: @selector(backtrace)]) - backtrace = [exception backtrace]; + if ([exception respondsToSelector: @selector(stackTraceAddresses)]) + stackTraceAddresses = [exception stackTraceAddresses]; - if (backtrace != nil) { - OFString *s = [backtrace componentsJoinedByString: @"\n "]; - fprintf(stderr, "\nBacktrace:\n %s\n\n", - [s cStringWithEncoding: encoding]); + if (stackTraceAddresses != nil) { + size_t count = stackTraceAddresses.count; + + if ([exception respondsToSelector: + @selector(stackTraceSymbols)]) + stackTraceSymbols = [exception stackTraceSymbols]; + + if (stackTraceSymbols.count != count) + stackTraceSymbols = nil; + + fputs("\nBacktrace:\n", stderr); + + if (stackTraceSymbols != nil) { + for (size_t i = 0; i < count; i++) { + void *address = [[stackTraceAddresses + objectAtIndex: i] pointerValue]; + const char *symbol = [[stackTraceSymbols + objectAtIndex: i] + cStringWithEncoding: encoding]; + + fprintf(stderr, " %p %s\n", address, symbol); + } + } else { + for (size_t i = 0; i < count; i++) { + void *address = [[stackTraceAddresses + objectAtIndex: i] pointerValue]; + + fprintf(stderr, " %p\n", address); + } + } + + fputs("\n", stderr); } abort(); } #endif Index: src/exceptions/OFException.h ================================================================== --- src/exceptions/OFException.h +++ src/exceptions/OFException.h @@ -20,14 +20,14 @@ #endif OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjectType); -@class OFMutableArray OF_GENERIC(ObjectType); @class OFString; +@class OFValue; -#define OFBacktraceSize 16 +#define OFStackTraceSize 16 #if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS) # ifndef EADDRINUSE # define EADDRINUSE WSAEADDRINUSE # endif @@ -147,11 +147,11 @@ * The OFException class is the base class for all exceptions in ObjFW, except * the OFAllocFailedException. */ @interface OFException: OFObject { - void *_backtrace[OFBacktraceSize]; + void *_stackTrace[OFStackTraceSize]; OF_RESERVE_IVARS(OFException, 4) } /** * @brief Creates a new, autoreleased exception. @@ -166,16 +166,25 @@ * @return A description of the exception */ - (OFString *)description; /** - * @brief Returns a backtrace of when the exception was created or nil if no - * backtrace is available. + * @brief Returns a stack trace of when the exception was created or `nil` if + * no stack trace is available. The returned array contains OFValues + * with @ref OFValue#pointerValue set to the address. + * + * @return The stack trace as array of addresses + */ +- (nullable OFArray OF_GENERIC(OFValue *) *)stackTraceAddresses; + +/** + * @brief Returns a stack trace of when the exception was created or `nil` if + * no stack trace symbols are available. * - * @return A backtrace of when the exception was created + * @return The stack trace as array of symbols */ -- (nullable OFArray OF_GENERIC(OFString *) *)backtrace; +- (nullable OFArray OF_GENERIC(OFString *) *)stackTraceSymbols; @end #ifdef __cplusplus extern "C" { #endif Index: src/exceptions/OFException.m ================================================================== --- src/exceptions/OFException.m +++ src/exceptions/OFException.m @@ -30,10 +30,11 @@ #ifdef OF_HAVE_THREADS # import "OFPlainMutex.h" #endif #import "OFString.h" #import "OFSystemInfo.h" +#import "OFValue.h" #import "OFInitializationFailedException.h" #import "OFLockFailedException.h" #import "OFUnlockFailedException.h" @@ -256,11 +257,11 @@ static _Unwind_Reason_Code backtraceCallback(struct _Unwind_Context *ctx, void *data) { struct BacktraceCtx *bt = data; - if (bt->i < OFBacktraceSize) { + if (bt->i < OFStackTraceSize) { # ifndef HAVE_ARM_EHABI_EXCEPTIONS bt->backtrace[bt->i++] = (void *)_Unwind_GetIP(ctx); # else uintptr_t ip; @@ -285,11 +286,11 @@ { struct BacktraceCtx ctx; self = [super init]; - ctx.backtrace = _backtrace; + ctx.backtrace = _stackTrace; ctx.i = 0; _Unwind_Backtrace(backtraceCallback, &ctx); return self; } @@ -299,49 +300,73 @@ { return [OFString stringWithFormat: @"An exception of type %@ occurred!", self.class]; } -- (OFArray OF_GENERIC(OFString *) *)backtrace +- (OFArray OF_GENERIC(OFValue *) *)stackTraceAddresses { #ifdef HAVE__UNWIND_BACKTRACE - OFMutableArray OF_GENERIC(OFString *) *backtrace = + OFMutableArray OF_GENERIC(OFValue *) *stackTrace = + [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + + for (uint_fast8_t i = 0; i < OFStackTraceSize && + _stackTrace[i] != NULL; i++) + [stackTrace addObject: + [OFValue valueWithPointer: _stackTrace[i]]]; + + objc_autoreleasePoolPop(pool); + + [stackTrace makeImmutable]; + + return stackTrace; +#else + return nil; +#endif +} + +- (OFArray OF_GENERIC(OFString *) *)stackTraceSymbols +{ +#if defined(HAVE__UNWIND_BACKTRACE) && defined(HAVE_DLADDR) + OFMutableArray OF_GENERIC(OFString *) *stackTrace = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); - for (uint8_t i = 0; i < OFBacktraceSize && _backtrace[i] != NULL; i++) { -# ifdef HAVE_DLADDR + for (uint_fast8_t i = 0; i < OFStackTraceSize && + _stackTrace[i] != NULL; i++) { Dl_info info; - if (dladdr(_backtrace[i], &info)) { + if (dladdr(_stackTrace[i], &info)) { + ptrdiff_t offset = (char *)_stackTrace[i] - + (char *)info.dli_saddr; OFString *frame; - if (info.dli_sname != NULL) { - ptrdiff_t offset = (char *)_backtrace[i] - - (char *)info.dli_saddr; - - frame = [OFString stringWithFormat: - @"%p <%s+%td> at %s", - _backtrace[i], info.dli_sname, offset, - info.dli_fname]; - } else - frame = [OFString stringWithFormat: - @"%p at %s", - _backtrace[i], info.dli_fname]; - - [backtrace addObject: frame]; - } else -# endif - [backtrace addObject: - [OFString stringWithFormat: @"%p", _backtrace[i]]]; + if (info.dli_fname != NULL && info.dli_sname != NULL) + frame = [OFString stringWithFormat: + @"%s`%s+%td", + info.dli_fname, info.dli_sname, offset]; + else if (info.dli_sname != NULL) + frame = [OFString stringWithFormat: + @"%s+%td", info.dli_sname, offset]; + else if (info.dli_fname != NULL) + frame = [OFString stringWithFormat: + @"%s`%p", info.dli_fname, _stackTrace[i]]; + else + frame = [OFString stringWithFormat: + @"%p", _stackTrace[i]]; + + [stackTrace addObject: frame]; + } else + [stackTrace addObject: + [OFString stringWithFormat: @"%p", _stackTrace[i]]]; } objc_autoreleasePoolPop(pool); - [backtrace makeImmutable]; + [stackTrace makeImmutable]; - return backtrace; + return stackTrace; #else return nil; #endif } @end