Home > Software design >  Getting Thread.callStackSymbols from release version give me weird stack trace
Getting Thread.callStackSymbols from release version give me weird stack trace

Time:02-04

In my project, I send logs to my server when an error happens (not a crash, only an error). These logs are an error description and the stack trace that should give me information about where and when the error happens. I'm getting the stack trace using

Thread.callStackSymbols

And it works very well when I'm using it on my device from Xcode, but I'm receiving stack traces on my server from release versions of my app that don't make sense.

I tested it forcing an error in a class named X and sending the stack trace of this error to my server. Running locally directly from the Xcode, the stack trace shows class X as expected. But when I built my release version to use as my users, the stack trace that I received in my server doesn't mention class X (where the error happens).

I'm using the dSYM files generated and all the necessary things to symbolicate my stack trace, but it still does not work. I also saw my DEBUG_INFORMATION_FORMAT and it is DWARF with dSYM file which seems right to me.

What am I missing? I read somewhere that maybe Thread.callStackSymbols don't be reliable on the device side in the release version. Is there another way to get the stack trace to send to my server?

CodePudding user response:

I think I understand the problem:

Thread.callStackSymbols symbolicates the address automatically on the device, but it probably does not work because symbols are removed from release versions. To fix this problem, I based my code on the NSProgrammer's answer

Now, I'm sending Thread.callStackReturnAddresses (you need to translate these addresses to hex values) and the load address to my server. I got the load address using _dyld_get_image_header. It's important to notice that, if you want to get the load address of a specific framework, you need to check like this

    for i in 0..<_dyld_image_count() {
      guard let address = _dyld_get_image_header(i),
            let name = String(validatingUTF8: _dyld_get_image_name(i)) else {
        continue
      }
      
      if name.contains(FRAMEWORK_NAME) {
        // send address to the server
      }
    }

With the stack address and the load address in your server, you can get the dSYM file and use atos like

atos -arch arm64 -o <PathToDSYMFile>/Contents/Resources/DWARF/<BinaryName>  -l <LoadAddress> <AddressesToSymbolicate>

Some addresses probably won't be symbolicated because they refer to other libs like system libs, but the correct addresses will work fine!

  • Related