其他程序员

iOS崩溃日志分析

2018-09-28  本文已影响293人  4335151e8554

前言

我们一般使用友盟等第三方平台来收集程序崩溃信息,但是一般情况下收集到的崩溃信息是我们无法看懂的,都是一些16进制的内存地址,无法准确定位到出错的代码。通常像这样的情况是由于我们没有上传符号文件到第三方平台,所以无法解析出具体的代码。

.dSYM为后缀的文件就是符号文件,里面存储着程序的符号表,也就是函数和内存地址的对应关系,有了这个符号文件,我们就可以通过崩溃信息中的内存地址解析出具体的出错代码,每次打包App生成的符号文件都可能不一样,所以说崩溃发生在哪个App版本就使用哪个版本的符号文件解析崩溃代码。

因为打包好之后的App安装包和符号文件是分开来的(出于安装包大小的考量),所以App包中没有符号文件,因此线上版本的App产生崩溃时只能得到崩溃地址,具体的解析工作还需拿到对应的dSYM符号文件之后才能进行。本地开发时符号文件就在本地,所以出现崩溃时Xcode打印出来的崩溃信息能看到具体的崩溃代码。

Xcode -> window -> Organizer -> 选择对应的包然后show in finder -> 显示包内容 -> dSYMs文件夹里存放的文件就是符号文件

选择崩溃产生的对应的App版本

通过自己收集崩溃信息,我们能够理解更多问题

1.首先在AppDelegate文件的didFinishLaunchingWithOptions方法中设置崩溃回调函数,程序出现崩溃时会调用该函数,我们在该函数里面收集崩溃信息
2.实现该函数handleException()
3.获取程序slide偏移值 (现在的App在运行到内存以后,会在前面随机空出一部分内存,而slide值就是这个随机空白段的长度,由于我们打包生成的符号文件是不加这个空白段的,所以我们收集到的崩溃地址需要减去slide值,才能在符号文件中解析出正确的信息)

slide演示

崩溃的收集代码如下

#import "AppDelegate.h"
#import <mach-o/dyld.h>     //需要导入这个头文件 使用获取偏移值的api

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    //1、设置异常回调函数
    NSSetUncaughtExceptionHandler(handleException);
    return YES;
}

//重要,用于获取偏移值
long calculate(void) {
    long s = 0;
    for (uint32_t i = 0; i < _dyld_image_count(); i++) {
        if (_dyld_get_image_header(i)->filetype == MH_EXECUTE) {
            long slide = _dyld_get_image_vmaddr_slide(i);
            s = slide;
            break;
        }
    }
    return s;
}

/**
 *  2、异常回调函数实现
 */
void handleException(NSException *exception){
    //2.设备型号

    //3.系统版本
    NSString *OSVersion = [NSString stringWithFormat:@"iOS %@",[UIDevice currentDevice].systemVersion];
    //4.崩溃名
    NSString *exceptionName = [exception name];
    //5.崩溃原因
    NSString *reason = [exception reason];
    //6.函数调用栈信息(重要)
    NSArray *array = [exception callStackSymbols]; //调用栈信息 拼接成字符串
    NSString *callStackStr = @"";
    for (NSString *str in array) {
        callStackStr = [NSString stringWithFormat:@"%@\n%@",callStackStr,str];
    }
    //7.崩溃前画面app画面截图(可选,没有太多参考价值)
    //8.崩溃时间
    //9.app版本
    NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
    NSString *AppVersion = [infoDict objectForKey:@"CFBundleShortVersionString"];

    //10.偏移值(重要)
    NSString *slide = [NSString stringWithFormat:@"%ld",calculate()];

    //11.上传崩溃信息到自己的后台
    ...
    
    exit(0);// 需要手动杀死程序  
}
QQ20190305-154428.png

上面就是我获得的函数调用栈callStackSymbols信息,第一列为调用顺序,第二列就是对应的库,第三列是对应方法函数对应的内存地址。从第二列可以看出,大部分都是系统库调用,这是我们无法修改的,所以不用管。我们只关注项目名所对应的那几行,那才是我们自己编写的代码,所以只需要关注5、6、22行的信息。由于栈的调用顺序,我们只需要分析最根层的方法即可,因为那才是崩溃发生的根源,也就是说只需要分析第5行。

由上面的崩溃信息可以看到第5行的内存地址是0x00000001012f9d90,我们可以通过命令行去dSYM符号文件中寻找该内存地址所对应的方法,这样就可以分析发生崩溃的方法了。

但是现在的iOS系统增加了一些安全措施,仅仅通过这个地址我们还无法从dSYM文件中解析出有用信息,因为每次运行程序时,系统都会在程序内存前面加上一段随机的空白段,所以我们需要用当前得到的地址0x00000001012f9d90减去随机空白段的长度才能得到最终的地址,只有这个地址才可以从dSYM文件中解析出正确的方法信息。这个偏移值我们可以通过系统的api获取,在上面的崩溃收集方法里已经收集了slide偏移值。

将第5行需要解析的地址减去偏移值slide得到最终地址(地址是16进制表示的,而偏移值是十进制的,注意转换)

1️⃣使用命令行解析

$atos -o 你的项目名.app.dSYM/Contents/Resources/DWARF/你的项目名 最终地址

2️⃣使用DSYMTools工具解析

参考文章
没有dsym符号文件如何解析崩溃
用命令和.dSYM 文件查找错误日志
偏移值的获取和符号地址的计算
dSYMTools
iOS自己捕获异常定位错误代码

上一篇下一篇

猜你喜欢

热点阅读