扣丁書屋

iOS SIGKILL 信號量崩潰抓取以及優化實踐

一. 什么是SIGKILL崩潰

很多時候,當我們在崩潰日志中看到 SIGKILL 關鍵信息的時候,這就表示操作系統從上層殺死了我們的進程,也就是我們常說的 kill -9 命令。

Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: Namespace RUNNINGBOARD, Code 0xdead10cc

一般來說,Apple 崩潰日志里面通常都會包含應用程序被殺死的具體的原因。如上所示,Termination Reason 里面就包含了這個崩潰的錯誤代碼 0xdead10cc,就表示應用程序掛起的時候發生了文件和數據庫鎖操作而被操作系統殺死。

二. 怎么抓取 SIGKILL 崩潰

1 為什么SIGKILL不能被捕獲

和其他信號不同,SIGKILL 是不可被捕獲的。這是 Linux / Mach 內核的限制,這種限制就是為了讓操作系統在程序無法響應的時候,可以從上一層控制進程的生命周期。

2 使用 MetricKit 框架捕獲SIGKILL

2.1 Metrickit 是什么

MetricKit 框架是蘋果在 iOS13 系統開始引入的用來匯總和分析有關異常和崩潰診斷以及電源和性能指標的動態庫。

2.2 使用Metrickit 收集 SIGKILL信號量 的好處

  • 不需要注冊信號量捕獲回調函數
  • 不需要時刻監控,只需冷啟階段注冊獲取一次就行

2.3 怎么使用 Metrickit 獲取崩潰信息

2.3.1 添加 MetricKit 動態庫依賴

2.3.2 注冊 MetricKit 監聽者

if (@available(iOS 14.0, *)) {
    MXMetricManager *manager = [MXMetricManager sharedManager];
    if (self && manager && [manager respondsToSelector:@selector(addSubscriber:)]) {
        [manager addSubscriber:self];
    }
} 

2.3.3 監聽者實現MXMetricManagerSubscriber協議方法,payloadDic里面包含著上次本應用發生的崩潰日志堆棧和信息

 // 用戶如果有崩潰數據,注冊監聽之后就會回調
- (void)didReceiveDiagnosticPayloads:(NSArray<MXDiagnosticPayload *> * _Nonnull)payloads  API_AVAILABLE(ios(14.0)){
    if (@available(iOS 14.0, *)) {
        for (MXDiagnosticPayload *payload in payloads) {
                NSDictionary *payloadDic = [payload dictionaryRepresentation];
            });
        }
    }
}

2.3.4 當收到回調消息后,需要對關鍵信息做組裝,獲取崩潰堆棧和相關關鍵信息


NSArray *callStackRootFrames = [dicFrame  ArrayValueForKey:@"callStackRootFrames"];
if (callStackRootFrames.count <= 0) {
    continue;
}
NSDictionary *dicZero = [callStackRootFrames ObjectAtIndex:0];
int rootIndex = 0;
while (dicZero && dicZero.count > 0) {
  //獲取Image 的 UUID
    NSString *binaryUUID = [dicZero   stringValueForKey:@"binaryUUID"];
  //獲取Image 的 名稱
    NSString *binaryName = [dicZero   stringValueForKey:@"binaryName"];
  //獲取Image 的加載地址
    long long baseAdd = [[dicZero NumberValueForKey:@"offsetIntoBinaryTextSegment"] longLongValue];
     //獲取崩潰函數的地址
    long long address = [[dicZero  numberValueForKey:@"address"] longLongValue];
  //看上一層調用堆棧的
    NSArray *subFrames = [dicZero  arrayValueForKey:@"subFrames"];
    [strStack appendFormat:@"%d %@ 0x%llx 0x%llx + %lld\n", rootIndex, binaryName, baseAdd, address, address - model.baseAddress];
    rootIndex++;
    if (subFrames && subFrames.count >= 0) {
        dicZero = [subFrames  ObjectAtIndex:0];
    } else {
        dicZero = nil;
    }
}

2.3.5 使用 Metrickit 收集崩潰的不足

  1. 只支持 iOS14 以后的崩潰日志收集;PS:MetricKit是iOS13開始有的框架,但是崩潰日志的支持是iOS14開始支持的。
  2. 崩潰日志沒有返回具體的崩潰時間和啟動時間,崩潰場景信息除了堆棧外沒有其余信息,附加信息較少,需要另外的手段來收集
  3. 如果使用了段遷移編譯技術,主程序 Mach-O 的加載地址和 uuid MetricKit無法給出正確的值,需要例外處理??赏ㄟ^ Mach-O文件的LC-MAIN入口來獲取主程序main函數的地址,從而算出加載其起始地址。
  • iOS14 的崩潰日志是24小時會回調通知一次,時效性低;iOS15 之后,崩潰日志會在下次啟動之后就返回,但經驗證,可能有例外情況。

3 SIGKILL 日志中 Code 的含義解釋

  • 0x8badf00d:

發音(ate bad food),意思就是吃了壞的食物。表示操作系統因為看門狗原因殺死了應用程序;具體可以參見蘋果文檔Addressing Watchdog Terminations (https://developer.apple.com/documentation/xcode/addressing-watchdog-terminations)- 0xc00010ff:

發音 (cool off)。表示操作系統因為過熱殺死了應用程序, 關于怎么樣使你的程序更高效,更低消耗,可以觀看:

a. iOS Performance and Power Optimization with Instruments (https://developer.apple.com/videos/)

b. WWDC session (https://developer.apple.com/videos/)

  • 0xbaadca11:

發音(bad call)。表示 應用程序響應 PushKit 的消息并且CallKit調用失敗,從而應用程序被系統殺死。

  • 0xbad22222:

表示因為通過 VoIP 調起程序太頻繁而被系統殺死。

  • 0xc51bad01:

watchOS 因為后臺任務耗費太多 CPU 時間而被殺死。這就需要優化和減少后臺任務的 CPU 時間,提高 CPU 使用效率,或者在后臺的時候減少大量任務。

  • 0xc51bad02:

watchOS 因為后臺任務不能在初始化時間內完成而殺死 應用程序,減少在后臺任務的數量可以解決這個問題。

4 百度App常見SIGKILL問題

4.1 主線程執行耗時操作太久

當應用程序在阻塞主線程一段時間之后就會被看門狗殺死,一般的耗時事件可能有如下幾種情形:

  • 弱網下同步的網絡請求
  • 處理大量的數據的任務,比如大的JSON文件或者 3D 模型的加載和處理
  • 觸發大量的 Core Data 同步保存操作
  • 觸發大量的數據庫操作等
  • 主線程解碼大圖片,解壓文件等操作

通用解決方案:

將耗時任務的處理放在子線程處理,等處理完成之后回調給主線程, 類似如下操作,在單獨的隊列處理任務,處理之后回調 block


 - (void)getContentArray:(void (^)(NSArray *resultArray))completeBlock {
  dispatch_barrier_async(self.readWriteQueue, ^{
      if (completeBlock) {
          NSArray *resultArray = [NSArray arrayWithArray:self.array];
          completeBlock(resultArray);
      }
  });
}

****4.2 主線程和子線程死鎖,陷入互相等待的循環

舉個栗子:

主線程和子線程在單例初始化的時候陷入死鎖 [xxxConfig sharedInstance],見如下崩潰堆棧


Thread 0 Crashed:
0 libsystem_kernel.dylib  ___ulock_wait  (in libsystem_kernel.dylib)  8
1 libdispatch.dylib  __dlock_wait  (in libdispatch.dylib)  56
2 libdispatch.dylib  __dispatch_once_wait  (in libdispatch.dylib)  120
3 BaiduBoxApp  +[xxxConfig sharedInstance]  (in BaiduBoxApp)  20
4 BaiduBoxApp  +[xxxConfig updateABConfig]  (in BaiduBoxApp)  0
5 BaiduBoxApp  -[xxxManager startOnce]  (in BaiduBoxApp)  20

子線程堆棧

Thread 33 :
0 libsystem_kernel.dylib  ___ulock_wait  (in libsystem_kernel.dylib)  8
1 libdispatch.dylib  __dlock_wait  (in libdispatch.dylib)  56
2 libdispatch.dylib  __dispatch_thread_event_wait_slow  (in libdispatch.dylib)  56
3 libdispatch.dylib  ___DISPATCH_WAIT_FOR_QUEUE__  (in libdispatch.dylib)  364
4 libdispatch.dylib  __dispatch_sync_f_slow  (in libdispatch.dylib)  144
.........
9 BaiduBoxApp  ___48+[xxxConfig sharedInstance]_block_invoke  (in BaiduBoxApp) 0
10 libdispatch.dylib  __dispatch_client_callout  (in libdispatch.dylib)  20
11 libdispatch.dylib  __dispatch_once_callout  (in libdispatch.dylib)  32
12 BaiduBoxApp  +[xxxConfig sharedInstance]  (in BaiduBoxApp)  20

相關鏈接

[1] Addressing Watchdog Terminations

https://github.com/alibaba/dexposed

[2] Understanding the Exception Types in a Crash Report

https://developer.apple.com/documentation/xcode/understanding-the-exception-types-in-a-crash-report#EXC_CRASH-

[3] MetricKit Framework

https://developer.apple.com/documentation/metrickit?language=objc

[4] Examining the Fields in a Crash Report

https://developer.apple.com/documentation/xcode/examining-the-fields-in-a-crash-report


https://mp.weixin.qq.com/s/2S3XIpKXMnYNFayeBE_d2Q

最多閱讀

iOS 性能檢測新方式?——AnimationHitches 1年以前  |  22531次閱讀
快速配置 Sign In with Apple 3年以前  |  6305次閱讀
APP適配iOS11 4年以前  |  5059次閱讀
App Store 審核指南[2017年最新版本] 4年以前  |  4899次閱讀
所有iPhone設備尺寸匯總 4年以前  |  4796次閱讀
使用 GPUImage 實現一個簡單相機 3年以前  |  4561次閱讀
開篇 關于iOS越獄開發 4年以前  |  4186次閱讀
在越獄的iPhone設置上使用lldb調試 4年以前  |  4093次閱讀
給數組NSMutableArray排序 4年以前  |  4009次閱讀
使用ssh訪問越獄iPhone的兩種方式 4年以前  |  3708次閱讀
UITableViewCell高亮效果實現 4年以前  |  3705次閱讀
關于Xcode不能打印崩潰日志 4年以前  |  3632次閱讀
iOS虛擬定位原理與預防 1年以前  |  3629次閱讀
使用ssh 訪問越獄iPhone的兩種方式 4年以前  |  3447次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>