关于ios:iOS-SIGKILL-信号量崩溃抓取以及优化实践

75次阅读

共计 4894 个字符,预计需要花费 13 分钟才能阅读完成。

一、什么是 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 之后,解体日志会在下次启动之后就返回,但教训证,可能有例外情况。

三、SIGKILL 日志中 Code 的含意解释

  • 0x8badf00d

发音 (ate bad food),意思就是吃了坏的食物。示意操作系统因为看门狗起因杀死了应用程序;具体能够参见苹果文档 Addressing Watchdog Terminations (https://developer.apple.com/d…)

  • 0xc00010ff

发音 (cool off)。示意操作系统因为过热杀死了应用程序,对于怎么样使你的程序更高效,更低消耗,能够观看:

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

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

  • 0xbaadca11

发音 (bad call)。示意 应用程序响应 PushKit 的音讯并且 CallKit 调用失败,从而应用程序被零碎杀死。

  • 0xbad22222

示意因为通过 VoIP 调起程序太频繁而被零碎杀死。

  • 0xc51bad01

watchOS 因为后台任务消耗太多 CPU 工夫而被杀死。这就须要优化和缩小后台任务的 CPU 工夫,进步 CPU 应用效率,或者在后盾的时候缩小大量工作。

  • 0xc51bad02

watchOS 因为后台任务不能在初始化工夫内实现而杀死 应用程序,缩小在后台任务的数量能够解决这个问题。

四、百度 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)  81 libdispatch.dylib  __dlock_wait  (in libdispatch.dylib)  562 libdispatch.dylib  __dispatch_thread_event_wait_slow  (in libdispatch.dylib)  563 libdispatch.dylib  ___DISPATCH_WAIT_FOR_QUEUE__  (in libdispatch.dylib)  3644 libdispatch.dylib  __dispatch_sync_f_slow  (in libdispatch.dylib)  144.........9 BaiduBoxApp  ___48+[xxxConfig sharedInstance]_block_invoke  (in BaiduBoxApp) 010 libdispatch.dylib  __dispatch_client_callout  (in libdispatch.dylib)  2011 libdispatch.dylib  __dispatch_once_callout  (in libdispatch.dylib)  3212 BaiduBoxApp  +[xxxConfig sharedInstance]  (in BaiduBoxApp)  20

——END ——

相干链接:

[1] Addressing Watchdog Terminations

https://github.com/alibaba/de…

[2] Understanding the Exception Types in a Crash Report

https://developer.apple.com/d…\_CRASH-

[3] MetricKit Framework

https://developer.apple.com/d…

[4] Examining the Fields in a Crash Report

https://developer.apple.com/d…

举荐浏览:

如何在几百万 qps 的网关服务中实现灵便调度策略

深入浅出 DDD 编程

百度 APP iOS 端内存优化实际 - 内存管控计划

Ernie-SimCSE 比照学习在内容反作弊上利用

品质评估模型助力危险决策程度晋升

合约广告平台架构演进实际

正文完
 0