Search code examples
macosbacktracemach-odylddwarf

OSX equivalent of SymGetLineFromAddr or SymGetLineFromAddr64


I want to add source file and line numbers to my backtraces, on Windows there is a nice function SymGetLineFromAddr64 which provides such information if available. I can't find something similar on mac. dladdr outputs only symbol name without file information.


Solution

  • There is no public equivalent to SymGetLineFromAddr64 on OS X, but you can get source file and line number with the atos(1) developer tool.

    Here is some sample code to get a fully symbolized backtrace.

    #import <Foundation/Foundation.h>
    
    static NSArray * Backtrace(NSArray *callStackReturnAddresses)
    {
        NSMutableArray *backtrace = [NSMutableArray new];
        for (NSNumber *address in callStackReturnAddresses)
        {
            NSString *hexadecimalAddress = [NSString stringWithFormat:@"0x%0*lx", (int)sizeof(void*) * 2, address.unsignedIntegerValue];
            NSTask *task = [NSTask new];
            NSPipe *pipe = [NSPipe pipe];
            task.launchPath = @"/usr/bin/xcrun";
            task.arguments = @[ @"atos", @"-d", @"-p", @(getpid()).description, hexadecimalAddress ];
            task.standardOutput = pipe;
            NSString *symbol = @"???";
            @try
            {
                [task launch];
                NSData *data = [pipe.fileHandleForReading readDataToEndOfFile];
                symbol = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
                symbol = [symbol stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
                [task waitUntilExit];
            }
            @catch (NSException *exception) {}
            [backtrace addObject:[NSString stringWithFormat:@"%@ %@", hexadecimalAddress, symbol]];
        }
        return [backtrace copy];
    }
    
    static void test(void)
    {
        NSLog(@"%@", Backtrace([NSThread callStackReturnAddresses]));
    }
    
    int main(int argc, const char * argv[])
    {
        test();
        return 0;
    }
    

    Running this code will produce the following output:

    0x000000010000134e test (in a.out) (main.m:31)
    0x000000010000131b main (in a.out) (main.m:36)
    0x00007fff8c0935fd start (in libdyld.dylib) + 1