Flutter Crash Analytics(iOS)


Flutter App crash日志搜集包括三部分,一部分來自於Dart code引起的異常,可以在flutter framework的main函數進行全局捕獲,此外還需對Native端iOSAndroid的異常進行捕獲.

iOS異常搜集與分析

  • 開啟DWARF文件搜集

  • 獲取Mach異常和Unix信號( ),用於捕獲系統內核的異常,在 http://opensource.apple.com 可以看到內核的完整代碼;

    void InstallUncaughtExceptionHandler()
    {
    //根據需要選擇性的配置
    signal(SIGABRT, CustomSignalHandler);
    signal(SIGILL, CustomSignalHandler);
    signal(SIGSEGV, CustomSignalHandler);
    signal(SIGFPE, CustomSignalHandler);
    signal(SIGBUS, CustomSignalHandler);
    signal(SIGPIPE, CustomSignalHandler);
    ...
    }
  • 注冊NSSetUncaughtExceptionHandler捕獲應用異常事件

  • 通過三方庫搜集,KSCrash,plcrashreporter,CrashKit,Crashlytics,Hockeyapp,友盟,Bugly,App Dynamic

  • 獲取Crash符號日志

    • 通過手機鏈接上Xcode直接獲取
    • 通過應用內置的異常獲框架獲取crash日志並上傳到服務器.

Crash日志解析

  • 隱藏符號替換: 根據官方文檔的說明通過AppStore發布之后下載的dysm文件會有部分敏感符號被隱藏,本地生成的則不會替換。所以在使用時需要先將其替換

      dsymutil -symbol-map <PathToXcodeArchive>/MyGreatApp.xcarchive/BCSymbolMaps <PathToDownloadedDSYMs>/<UUID>.dSYM
    
  • 檢測Crash日志的uuid和ipa的執行文件uuid是否相同

    dwarfdump --uuid <PathToDSYMFile>/Contents/Resources/DWARF/<BinaryName>
    dwarfdump --uuid <PathToBinary>
    
  • 當crash日志和對應的ipa執行文件uuid對應之后,利用xcode的Symbolicate工具進行符號化,注意這里的-l后面的參數,分別指定了

    atos -arch <BinaryArchitecture> -o <PathToDSYMFile>/Contents/Resources/DWARF/<BinaryName>  -l <LoadAddress> <AddressesToSymbolicate>
    atos -arch arm64 -o TouchCanvas.app.dSYM/Contents/Resources/DWARF/TouchCanvas -l 0x1022c0000 0x00000001022df754
    ViewController.touchesEstimatedPropertiesUpdated(_:) (in TouchCanvas) + 304
  • 使用內置的工具解析

    export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer​
    ./symbolicatecrash /Users/yourUserName/Desktop/CrashSignifying/crashFileName.crash /Users/UserName/Desktop/CrashSignifying/dSYMFileName.dSYM > crashFileName.crash
    

Crash日志介紹,

  • 一份crash日志包含了以下幾個部分

  • Header:

    Incident Identifier: 6156848E-344E-4D9E-84E0-87AFD0D0AE7B
    CrashReporter Key:   76f2fb60060d6a7f814973377cbdc866fffd521f
    Hardware Model: iPhone8,1
    Process: TouchCanvas [1052]
    Path: /private/var/containers/Bundle/Application/51346174-37EF-4F60-B72D-8DE5F01035F5/TouchCanvas.app/TouchCanvas
    Identifier: com.example.apple-samplecode.TouchCanvas
    Version: 1 (3.0)
    Code Type: ARM-64 (Native)
    Role: Foreground
    Parent Process: launchd [1]
    Coalition: com.example.apple-samplecode.TouchCanvas [1806]
    Date/Time: 2020-03-27 18:06:51.4969 -0700
    Launch Time: 2020-03-27 18:06:31.7593 -0700
    OS Version: iPhone OS 13.3.1 (17D50)
  • Exception Information:

    Exception Type:  EXC_BREAKPOINT (SIGTRAP) 異常類型
    Exception Subtype: 異常類型可讀取的描述信息
    Exception Message: 對Exception Type的解釋
    Exception Codes: 0x0000000000000001, 0x0000000102afb3d0 一個64bit的異常code
    Exception Note:
    不屬於一個特定的異常類型。如果此字段包含EXC_corpost_NOTIFY,則崩潰並非源自硬件陷阱,原因可能是該進程已被操作系統或名為abort()的進程顯式終止。如果此字段包含SIMULATED(這不是崩潰),則進程沒有崩潰,但操作系統可能隨后請求終止進程。如果此字段包含非致命條件(這不是崩潰),則進程不會終止,因為創建崩潰報告的問題不是致命的。
    Termination Reason: 應用程序終止的具體原因
    Triggered by Thread or Crashed Thread: 觸發崩潰或者崩潰的線程
  • Diagnostic Messages: 系統當前對app的診斷信息

    Termination Description: SPRINGBOARD, 
    scene-create watchdog transgression: application<com.example.MyCoolApp>:667
    exhausted real (wall clock) time allowance of 19.97 seconds

常見的異常處理方案

  1. WatchDog timeout: 這種異常通常是由於主線程阻塞造成,同步的網絡請求,處理大量的model轉換,如json解析,3d models的解析,同步的處理大量的coredata數據,應用計算機視覺算法對輸入圖像和視頻執行各種任務。

    • 下面2張圖時是改善前后的方案

    • 改進之后

    • scence-create超時

       Termination Description: SPRINGBOARD, 
      scene-create watchdog transgression: application<com.example.MyCoolApp>:667
      exhausted real (wall clock) time allowance of 19.97 seconds
      | ProcessVisibility: Foreground
      | ProcessState: Running
      | WatchdogEvent: scene-create
      | WatchdogVisibility: Foreground
      | WatchdogCPUStatistics: (
      | "Elapsed total CPU time (seconds): 15.290 (user 15.290, system 0.000), 28% CPU",
      | "Elapsed application CPU time (seconds): 0.367, 1% CPU"
      | )
    • 后台任務執行超時

      Termination Reason: CAROUSEL, WatchConnectivity watchdog transgression. 
      Exhausted wall time allowance of 15.00 seconds.
      Termination Description: SPRINGBOARD,
      CSLHandleBackgroundWCSessionAction watchdog transgression: xpcservice<com.example.MyCoolApp.watchkitapp.watchextension>:220:220
      exhausted real (wall clock) time allowance of 15.00 seconds
      | <FBExtensionProcess: 0x16df02a0; xpcservice<com.example.MyCoolApp.watchkitapp.watchextension>:220:220; typeID: com.apple.watchkit>
      Elapsed total CPU time (seconds): 24.040 (user 24.040, system 0.000), 81% CPU
      | Elapsed application CPU time (seconds): 1.223, 6% CPU, lastUpdate 2020-01-20 11:56:01 +0000
  2. Memory Access Crash: 內存訪問crash

    • 取消引用指向無效的地址空間,往只讀的空間寫入數據,訪問無效地址的指令,通常會出現EXC_BAD_ACCESS(SIGSEGV),或者是EXC_BAD_ACCESS(SIGBUS)異常
      text
      Exception Type: EXC_BAD_ACCESS (SIGSEGV)
      Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
    • 壞的內存訪問有時候不會生成EXC_BAD_ACCESS,直接拋出一個signal異常,例如:SIGSEGV, SEGV_MAPERR, or SEGV_NOOP text Exception Type: SIGSEGV
      Exception Codes: SEGV_MAPERR at 0x41e0af0c5ab8
    • 內存訪問異常分析

      • Address Sanitizer(gcc ... -fsanitize=address): 內存錯誤檢查,在Xcode運行面板中可以開啟此項檢查。
        • 比如使用一個釋放的對象
        • 數組越界,棧緩存溢出
      • Undefined Behavior Sanitizer: 不安全的操作指令
        • 緩沖區溢出
        • 使用未初始化的變量
        • 使用釋放后的變量
        • 重復釋放
        • 多線程數據競爭
      • Thread Sanitizer: 線程安全檢查
        • 記錄內存訪問信息,判斷是否有不同線程訪問修改其值
      • Profile Analytics: 靜態分析檢查
        • 在運行時檢查,未初始化變量,泄漏,未使用變量
      • Enabling the Malloc Debugging Features: 開啟僵屍檢查模式,可以給釋放的對象發送消息,來檢測異常
    • 異常子類型檢查

       Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
       Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
      ...
      Exception Type: EXC_BAD_ACCESS (SIGBUS)
      Exception Codes: KERN_MEMORY_ERROR at 0x00000001098c1000
      KERN_INVALID_ADDRESS: 無效的內存地址
      KERN_PROTECTION_FAILURE: 使用受保護的地址空間,不屬於當前線程的空間
      KERN_MEMORY_ERROR: 訪問的地址無法提供數據,如分頁缺失
      EXC_ARM_DA_ALIGN: 訪問的內存地址未對齊
    • 虛擬內存引用錯誤,應用了其它的app的vm region

      Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
      Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
      VM Region Info: 0 is not in any region. Bytes before following region: 4307009536
      REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
      UNUSED SPACE AT START
      --->
      __TEXT 0000000100b7c000-0000000100b84000 [ 32K] r-x/r-x SM=COW ...pp/MyGreatApp
      • 如果objc_msgSend, objc_retain, or objc_release在堆棧信息的最上方,考慮開啟Zombie Objects檢查 .
      • 如果gpus_ReturnNotPermittedKillClient出現,則說明試圖在后台使用OpenGL ES進行渲染,需要將OpenGL ES代碼移植到Metal,移植參考Migrating OpenGL Code to Metal.
  3. EXC_BREAKPOINT

    • EXC_BREAKPOINT (SIGTRAP) and EXC_BAD_INSTRUCTION (SIGILL): 跟中陷阱終斷,或者不合法的指令(illegal), 模擬 __builtin_trap
  4. EXC_CRASH (SIGABRT): 程序執行了abort()函數,如算數邏輯錯誤保護,程序啟動的必選參數強制保護.擴展初始化占用時間過長被系統執行了abort()

  5. EXC_CRASH (SIGKILL): kill,

    • 0x8badf00d(bad food,watch dog issue);
    • 0xc00010ff(cool off),系統由於熱事件終止應用程序, 運行環境異常,電量使用過大造成,可以參考iOS Performance and Power Optimization with Instruments;
    • 0xbaadca11(bad call),操作系統終止了應用程序,因為未能報告對PushKit通知的CallKit調用。
    • 0xbad22222,系統終止了VoIP應用程序,因為它恢復得太頻繁,(提示,2重復了很多次);
    • 0xc51bad01(backgroud),watchOS終止了應用程序,因為它在執行后台任務時占用了太多的CPU時間。(自身的問題)
    • 0xc51bad02(backgroud), watchOS終止了應用程序,因為它未能在分配的時間內完成后台任務。(自身的問題)
    • 0xc51bad03(backgroud), watchOS終止了應用程序,因為它未能在分配的時間內完成后台任務,但系統總體上非常繁忙,應用程序可能沒有收到太多CPU時間來執行后台任務。(系統的問題)
    • EXC_CRASH (SIGQUIT): 進程在另一個進程的請求下終止(具備管理這個進程的生命周期)。
    • EXC_GUARD: 違反了保護資源的原則。,但大多數受保護的資源崩潰都來自受保護的文件描述符,這些描述符在異常子類型字段中具有GUARD_TYPE_FD(guard type file descriptor)值。操作系統將文件描述符標記為受保護的,這樣普通的文件描述符API就無法修改它們, CLOSE,DUP(duplicate),NOCLOEXEC,SOCKET_IPC,FILEPORT,WRITE
    • EXC_RESOURCE: 進程超出了資源消耗限制。CPU and CPU_FATAL,MEMORY,IO(disk task),WAKEUPS(每秒醒來次數太多,這會消耗電池壽命), such as perform(_:on:with:waitUntilDone:), async(execute:), or dispatch_async(_:_:),執行過於頻繁,GCD負荷過重,參考Modernizing Grand Central Dispatch Usage, 隊列控制,計划執行,並發數控制
    • EXC_ARITHMETIC: 邏輯運算錯誤,除0或浮點運算錯誤
  6. App內存不足被強制殺掉: 虛擬內存占用過高導致被殺掉

    • app在啟動后以pageFault的方式,將物理空間上的內存以分頁的形式映射到內存中
    • crash demo: 通常它不會作為crash日志搜集,在系統設置統計分析中可以找到這些日志.
      text
      "crashReporterKey" : "b9aa251a63bd9e743afbb906f43eb7ea5f206292",
      "product" : "iPad8,2",
      "incident" : "32B05E3C-CB45-40F8-BA66-5668779740E1",
      "date" : "2019-10-10 23:30:39.48 -0700",
      "build" : "iPhone OS 13.1.2 (17A860)",
      "memoryStatus" : {
      "pageSize" : 16384,
      },
      "largestProcess" : "OneCoolApp", //占用內存最大進程,因某種原因被系統殺掉

    • 特別注意: 一個jetsam event報告包括一個數組進程,如果你的app崩潰了,但是被掛掉的進程不是你的app,就需通過其它的診斷途徑來查看問題,確認手機內是否有其它的crash日志
    • reason: per-process-limit,進程超過了系統限定的程序內存駐留最大限制,超過此大小,將極大可能被系統殺掉,應用程序的擴展進程也被考慮在內,並且他們的內存占用限制更少
      text
      {
      "uuid" : "a02fb850-9725-4051-817a-8a5dc0950872",
      "states" : [
      "frontmost"
      ],
      "lifetimeMax" : 92802,
      "purgeable" : 0,
      "coalition" : 68,
      "rpages" : 92802,
      "reason" : "per-process-limit",
      "name" : "MyCoolApp"
      }
      • reason: vm-pageshortage,系統內存壓力過重,殺掉后台應用
      • reason: vnode-limit,系統打開了太多的文件,內核有限制vnode數量,會放棄部分后台進程,保證前台應用存活
      • reason: highwater,系統守護進程超出了最高內存占用
      • reason: fc-thrashing, 當內存映射文件的非連續部分讀寫過於頻繁時,就會發生這種情況。為了避免終止最前端的應用程序,系統可能會在后台終止您的應用程序,以釋放文件緩存中的空間,即使您的應用程序沒有破壞文件緩存。
      • reason: jettisoned,系統殺掉了應用程序因為一些其它的原因
      • pageSize默認為16KB
      • pageSize優化: 方法重排,常用方法集中到一個頁,避免頻繁切換,合理利用數據結構,減少不必要的開銷,字節對齊,進一步優化數據占用空間,
      • 可以通過vm_stat查看進程的vm空間 text Mach Virtual Memory Statistics: (page size of 4096 bytes)
        Pages free: 3194.
        Pages active: 34594.
        Pages inactive: 17870.
        Pages wired down: 9878.
        "Translation faults": 6333197.
        Pages copy-on-write: 81385.
        Pages zero filled: 3180051.
        Pages reactivated: 343961.
        Pageins: 33043.
        Pageouts: 78496.
        Object cache: 66227 hits of 96952 lookups (68% hit rate)
  7. Diagnosing Memory, Thread, and Crash Issues Early

    UBSan check Compiler flag
    Misaligned Pointer -fsanitize=alignment
    Invalid Boolean -fsanitize=bool
    Out-of-Bounds Array Access -fsanitize=bounds
    Invalid Enumeration Value -fsanitize=enum
    Dynamic Type Violation -fsanitize=vptr
    Invalid Float Cast -fsanitize=float-cast-overflow
    Division by Zero -fsanitize=integer-divide-by-zero,-fsanitize=float-divide-by-zero
    Nonnull Argument Violation -fsanitize=nonnull-attribute, -fsanitize=nullability-arg
    Nonnull Return Value Violation -fsanitize=returns-nonnull-attribute, -fsanitize-nullability-return
    Nonnull Variable Assignment Violation -fsanitize=nullability-assign
    Null Reference Creation and Null Pointer Dereference -fsanitize=null
    Invalid Object Size -fsanitize=object-size
    Invalid Shift -fsanitize=shift
    Integer Overflow -fsanitize=signed-integer-overflow
    Reaching of Unreachable Point -fsanitize=unreachable
    Invalid Variable-Length Array -fsanitize=vla-bound

參考地址

Understanding the Exception Types in a Crash Report
iOS Performance and Power Optimization with Instruments
Adding Identifiable Symbol Names to a Crash Report
Diagnosing Memory, Thread, and Crash Issues Early
Improving Your App's Performance
Examining the Fields in a Crash Report
Viewing Virtual Memory Usage
Writing ARM64 Code for Apple Platforms
Investigating Memory Access Crashes
Apple官方文檔WatchDog處理方案
Identifying High-Memory Use with Jetsam Event Reports


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM