iOS - 符号化堆栈跟踪符号

Posted

技术标签:

【中文标题】iOS - 符号化堆栈跟踪符号【英文标题】:iOS - Symbolicate Stack trace symbols 【发布时间】:2015-06-16 02:59:45 【问题描述】:

我想对使用 [NSThread callStackSymbols] 记录的堆栈跟踪符号进行符号化,以识别完整的堆栈回溯调用。但是,这并没有给出象征性的痕迹。

0   TestApp                               0x0029616f TestApp + 1823087
    1   TestApp                               0x003ef18d TestApp + 3236237
    2   UIKit                               0x2ab7bb1f <redacted> + 438
    3   UIKit                               0x2ac0bea3 <redacted> + 306
    4   UIKit                               0x2ab7bb1f <redacted> + 438
    5   CoreFoundation                      0x2757546d <redacted> + 48
    6   CoreFoundation                      0x2756e4c3 <redacted> + 234
    7   UIKit                               0x2ab7bc9b <redacted> + 818
    8   UIKit                               0x2ae32799 <redacted> + 584
    9   UIKit                               0x2abdfbd9 <redacted> + 308
    10  UIKit                               0x2ab5bdd7 <redacted> + 458
    11  CoreFoundation                      0x2760fffd <redacted> + 20
    12  CoreFoundation                      0x2760d6bb <redacted> + 278
    13  CoreFoundation                      0x2760dac3 <redacted> + 914
    14  CoreFoundation                      0x2755b3b1 CFRunLoopRunSpecific + 476
    15  CoreFoundation                      0x2755b1c3 CFRunLoopRunInMode + 106
    16  GraphicsServices                    0x2eb88201 GSEventRunModal + 136
    17  UIKit                               0x2abc543d UIApplicationMain + 1440
    18  TestApp                               0x0031581b TestApp + 2344987
    19  libdyld.dylib                       0x35a6baaf <redacted> + 2 

我在开发和企业构建中都试过这个。都没有解决。反正有什么象征意义吗?我搜索了很多论坛,所有论坛都要求使用下面的 atos 命令执行此操作

atos -arch armv7 -o 'app name.app'/'app name' 0x000000000

但是,我不确定上述命令必须使用的内存地址是什么以及如何获取它。

任何帮助将不胜感激,谢谢。

【问题讨论】:

这个答案可能会对您有所帮助:***.com/a/26461123/4557505 【参考方案1】:

你在这里跳过了一些片段。 atos 中的内存地址应该引用加载地址和堆栈地址,并且您还需要 dSYM 文件。

实际上有一篇关于 apteligent 的非常好的文章,名为 Symbolicating an ios Crash Report,关于手动执行此操作。我建议彻底阅读它以了解符号的工作原理。

【讨论】:

链接好像坏了【参考方案2】:

这是一个如何在运行时获取二进制图像信息的示例。

以下代码的输出如下所示:

YourApp 0x00000001adb1e000 - arm64e - E9B05479-3D07-390C-BD36-73EEDB2B1F75
CoreGraphics 0x00000001a92dd000 - arm64e - 2F7F6EE8-635C-332A-BAC3-EFDA4894C7E2
CoreImage 0x00000001afc00000 - arm64e - CF56BCB1-9EE3-392D-8922-C8894C9F94C7

然后,您将要符号化其帧的二进制图像的内存地址用作您提到的atos 命令中的参数。

代码:

import Foundation
import MachO

public struct BinaryImagesInspector 

    #if arch(x86_64) || arch(arm64)
    typealias MachHeader = mach_header_64
    #else
    typealias MachHeader = mach_header
    #endif

    /// Provides binary infos that are then used with the atos command to symbolicate stack traces
    /// - Parameter imageNamesToLog: an optional array of binary image names to restrict the infos to
    /// - Returns: An array of strings containing info on loaded binary name, its load address, architecture
    /// - Note: Example:
    ///
    /// atos -arch arm64 -o [YOUR-DSYM-ID].dSYM/Contents/Resources/DWARF/[YOUR APP] -l 0x0000000000000000 0x0000000000000000
    public static func getBinaryImagesInfo(imageNamesToLog: [String]? = nil) -> [String] 
        let count = _dyld_image_count()

        var stringsToLog = [String]()

        for i in 0..<count 

            guard let dyld = _dyld_get_image_name(i) else  continue 

            let dyldStr = String(cString: dyld)
            let subStrings = dyldStr.split(separator: "/")
            guard let imageName = subStrings.last else  continue 

            if let imageNamesToLog = imageNamesToLog 
                guard imageNamesToLog.contains(String(imageName)) else  continue 
            

            guard let uncastHeader = _dyld_get_image_header(i) else  continue 
            let machHeader = uncastHeader.withMemoryRebound(to: MachHeader.self, capacity: MemoryLayout<MachHeader>.size)  $0 
            guard let info = NXGetArchInfoFromCpuType(machHeader.pointee.cputype, machHeader.pointee.cpusubtype) else  continue 
            guard let archName = info.pointee.name else  continue 
            let uuid = getBinaryImageUUID(machHeader: machHeader)
            let logStr = "\(imageName) \(machHeader.debugDescription) - \(String(cString: archName)) - \(uuid ?? "uuid not found")"
            stringsToLog.append(logStr)
        

        return stringsToLog
    

    private static func getBinaryImageUUID(machHeader: UnsafePointer<MachHeader>) -> String? 

        guard var header_ptr = UnsafePointer<UInt8>.init(bitPattern: UInt(bitPattern: machHeader)) else 
            return nil
        

        header_ptr += MemoryLayout<MachHeader>.size

        guard var command = UnsafePointer<load_command>.init(bitPattern: UInt(bitPattern: header_ptr)) else 
            return nil
        

        for _ in 0..<machHeader.pointee.ncmds 

            if command.pointee.cmd == LC_UUID 
                guard let ucmd_ptr = UnsafePointer<uuid_command>.init(bitPattern: UInt(bitPattern: header_ptr)) else  continue 
                let ucmd = ucmd_ptr.pointee

                let cuuidBytes = CFUUIDBytes(byte0: ucmd.uuid.0,
                                             byte1: ucmd.uuid.1,
                                             byte2: ucmd.uuid.2,
                                             byte3: ucmd.uuid.3,
                                             byte4: ucmd.uuid.4,
                                             byte5: ucmd.uuid.5,
                                             byte6: ucmd.uuid.6,
                                             byte7: ucmd.uuid.7,
                                             byte8: ucmd.uuid.8,
                                             byte9: ucmd.uuid.9,
                                             byte10: ucmd.uuid.10,
                                             byte11: ucmd.uuid.11,
                                             byte12: ucmd.uuid.12,
                                             byte13: ucmd.uuid.13,
                                             byte14: ucmd.uuid.14,
                                             byte15: ucmd.uuid.15)
                guard let cuuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, cuuidBytes) else 
                    return nil
                
                let suuid = CFUUIDCreateString(kCFAllocatorDefault, cuuid)
                let encoding = CFStringGetFastestEncoding(suuid)
                guard let cstr = CFStringGetCStringPtr(suuid, encoding) else 
                    return nil
                
                let str = String(cString: cstr)

                return str
            

            header_ptr += Int(command.pointee.cmdsize)
            guard let newCommand = UnsafePointer<load_command>.init(bitPattern: UInt(bitPattern: header_ptr)) else  continue 
            command = newCommand
        

        return nil
    

进一步阅读:

How to determine binary image architecture at runtime? https://github.com/apple/swift/blob/master/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift https://lowlevelbits.org/parsing-mach-o-files/ https://developer.apple.com/library/archive/technotes/tn2151/_index.html

也可以在GitHub 上以 swift 包的形式提供。

【讨论】:

以上是关于iOS - 符号化堆栈跟踪符号的主要内容,如果未能解决你的问题,请参考以下文章

符号化 iOS 崩溃地址返回不适当的结果

IOS静态调试符号含义[关闭]

Unity 之 安卓堆栈跟踪和日志工具 (Android Logcat | 符号表解析Bugly捕获)

Unity 之 安卓堆栈跟踪和日志工具 (Android Logcat | 符号表解析Bugly捕获)

减小 dSYM 文件的大小以进行符号化

iOS系统符号符号服务