iOS Crash原理及日志收集
2020-04-23 本文已影响0人
哦小小树
0x01 常见的Crash类型
KVO
NSNotification
数组越界
内存爆炸
野指针
后台任务超时
主线程卡顿超出阈值
死锁
CPU消耗过大
0x02 系统提供的异常捕获
NSUncaughtExceptionHandler
示例代码
- (void)crashMonitor {
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler); // 添加异常监听句柄
}
void UncaughtExceptionHandler(NSException *exception) {
NSString *name = [exception name];
NSString *reason = [exception reason];
NSArray *stackTrace = [exception callStackSymbols];
NSString *crashInfo = [NSString stringWithFormat:@"\n%@\n%@\n%@\n",name,reason, stackTrace];
[[CrashManager shareInstance] saveCrashToFile:crashInfo]; // 存储crash文件
}
Signal捕获
操作逻辑
- iOS系统出现crash的时候会发出signal
- 注册signal及回调配置【句柄】
// 注册句柄, SIG_Flag为要监听的signal, handleCallBack为回调函数
signal(SIG_Flag, handleCallBack)
示例代码
// signal.h中包含所有指令
signal(SIGABRT, handleSignal); // 调用abort
signal(SIGILL, handleSignal); // 非法指令
signal(SIGSEGV, handleSignal); // 无效内存引用
signal(SIGPIPE, handleSignal); // 端口消息发送失败
如果需要测试,需要再接收信号前通过lldb
配置:
pro hand -p true -s false SIGABRT
void handleSignal(int signal) {
NSLog(@"中断信信号:%@",signal);
// 打印函数调用栈
}
这内部处理信号的逻辑涉及到各种内核,线程状态操作。后续研究再来完善。
0x03 日志记录
- 写入本地文件
// 日志目录路径
- (NSString *)logDirectory {
NSString *directory = [NSHomeDirectory() stringByAppendingPathComponent:@"log"];
if (![[NSFileManager defaultManager] fileExistsAtPath:directory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:nil];
}
return directory;
}
// 日志文件信息
- (NSString *)crashFilePath {
return [[self logDirectory] stringByAppendingPathComponent:@"crash.log"];
}
// 保存日志信息
- (void)saveCrashToFile:(NSString *)crashInfo {
NSFileManager *manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:[self crashFilePath]]) {
[manager createFileAtPath:[self crashFilePath] contents:nil attributes:nil];
}
NSFileHandle *handler = [NSFileHandle fileHandleForWritingAtPath:[self crashFilePath]];
[handler seekToEndOfFile]; // 拼接形式操作
[handler writeData:[crashInfo dataUsingEncoding:NSUTF8StringEncoding]];
}
- 压缩上传
先获取需要压缩的日志文件
- (NSArray *)sortedCrashFiles {
// 目录中所有日志文件
NSFileManager *mana = [NSFileManager defaultManager];
if (![mana fileExistsAtPath:[self logDirectory]]) return nil;
// 返回文件名列表
NSArray *filesArr = [mana contentsOfDirectoryAtPath:[self logDirectory] error:nil];
// 如果需要对文件做些筛选可以在此处处理
NSArray *sortArr = [filesArr sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
// 配置全路径,找到文件,以文件创建时间进行排序
// NSString *filePath1 = [[self logDirectory] stringByAppendingPathComponent:obj1];
// NSString *filePath2 = [[self logDirectory] stringByAppendingPathComponent:obj2];
//
// NSDictionary *file1Dcit = [mana attributesOfItemAtPath:filePath1 error:nil];
// NSDictionary *file2Dict = [mana attributesOfItemAtPath:filePath2 error:nil];
//
// NSDate *file1Date = [file1Dcit objectForKey:NSFileCreationDate];
// NSDate *file2Date = [file2Dict objectForKey:NSFileCreationDate];
// return [file1Date compare:file2Date];
return [obj1 compare:obj2]; // 直接按照文件名进行排序
}];
NSMutableArray *tmpArr = @[].mutableCopy;
for (NSString *fileName in sortArr) { // 拼接为全路径
[tmpArr addObject:[[self logDirectory] stringByAppendingPathComponent:fileName]];
}
return tmpArr;
}
使用SSZipArchive
进行资源压缩
- (NSString *)zipFileForAllCrash {
// 压缩所有的crash文件, 返回压缩好的文件路径
NSArray *sortArray = [self sortedCrashFiles];
if (sortArray.count <= 0) return nil;
// 创建一个压缩文件路径
NSString *crashZipPath = [[self logDirectory] stringByAppendingPathComponent:[[self timeString] stringByAppendingString:@".zip"]];
// 根据路径压缩所有文件信息
BOOL ret = [SSZipArchive createZipFileAtPath:crashZipPath withFilesAtPaths:sortArray];
if (!ret) { // 压缩失败
crashZipPath = nil;
NSLog(@"创建压缩文件失败");
}
return crashZipPath;
}
获取到压缩后的路径可以执行上传操作,当提交成功后就可以删除本地日志信息
- (void)clearAllCacheLogFile {
NSFileManager *mana = [NSFileManager defaultManager];
// 删除所有文件
[mana removeItemAtPath:[self logDirectory] error:nil];
}
示例代码
https://github.com/linfengwenyou/CrashCollectTest
0x03 主流的崩溃日志收集框架
-
成熟的开源项目:
KSCrash:https://github.com/kstenerud/KSCrash
plcrashreporter:https://github.com/microsoft/plcrashreporter
[停更]CrashKit:https://github.com/kaler/CrashKit -
对于保密性要求不高的可以考虑下:
Crashlytics:国外的,可能对于国内不是很稳定
友盟、Bugly
参考资料:
https://blog.csdn.net/weixin_36139431/article/details/95358484
https://www.it610.com/article/1191455498289913856.htm