精华内容
下载资源
问答
  • 错误分析是基于设备中的crash log 与 编译文件时生成的dsYM文件相结合得出的结果,crash log 中包含应用的 crash trace stack,dsym中包含应用的符号表信息,符号表信息用于匹配程序中的文件,函数,代码,行号。...

    https://developer.apple.com/library/ios/technotes/tn2151/_index.html

    错误分析是基于设备中的crash log 与 编译文件时生成的dsYM文件相结合得出的结果,crash log 中包含应用的 crash trace stack,dsym中包含应用的符号表信息,符号表信息用于匹配程序中的文件,函数,代码,行号。

    一、可用crash trace 追踪的crash

    二、low memery 是追踪不到的因为没有对应以下的这类型的符号

    1)EXC_BAD_ACCESS

    SIGSEGV: 通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。

    SIGABRT:  收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。
    SEGV:(Segmentation  Violation),代表无效内存地址,比如空指针,未初始化指针,栈溢出等;

    SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)

    SIGILL:尝试执行非法的指令,可能不被识别或者没有权限
    2)EXC_BAD_INSTRUCTION

    3)EXC_ARITHMETIC

    不用第三方工具,如何分析?

    首先,确保在release(Ad Hoc或者App Store)一个版本时,保存了对应的xxx.app和xxx.dSYM文件。
    其次,验证xxx.crash、xxx.app和xxx.dSYM三者的uuid是否一致。
    验证方法:
    1)xxx.app。dwarfdump --uuid mobileguard.app/mobileguard
    2)xxx.dSYM。dwarfdump --uuid mobileguard.app.dSYM/Contents/Resources/DWARF/mobileguard
    3)xxx.crash。
    确保三者uuid一致以后,用symbolicatecrash工具生成易读的日志信息。

    先准备环境:
    1)链接symbollicatecrash到/usr/bin/中,就可以直接使用sybollicatecrash命令。ln -s /Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash /usr/bin/symbolicatecrash
    2)设置xcode DEVELOPER_DIR。export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer”
    然后执行命令:symbolicatecrash m.crash mobileguard.app.dSYM > n.crash

    当然是有 分析工具可以用的:

    Crashlystics (twitter,使用自动集成工具,通过上传dsym文件形式,强烈推荐使用,简单,好用,定位问题快,准)

    Bugly (腾讯,用相应的jar 将dsym分析后上传至腾讯服务器)

    MoClick (友盟,需下载crash log 并用相应的工具解析)

    Hockeyapp (微软,需上传dsym文件,收费,有自动集成工具)

     

    转载于:https://www.cnblogs.com/codetime/p/6124197.html

    展开全文
  • iOS开发~Crash优化

    2020-09-11 14:31:46
    iOS App开发中和上线后,都会遇到程序异常退出的情况,导致崩溃的原因有很多,如果是在调试的过程中,可以通过设施断点或者打印关键信息的方式来进行调试,如果对于一些复杂模块非必现的异常崩溃,这种方式有时难以...

    一、概要

    iOS App开发中和上线后,都会遇到程序异常退出的情况,导致崩溃的原因有很多,如果是在调试的过程中,可以通过设施断点或者打印关键信息的方式来进行调试,如果对于一些复杂模块非必现的异常崩溃,这种方式有时难以定位问题,而且对于已经发布上线的应用,这种方式更是无能为力。下面详细讨论崩溃产生的原因以及如何处理。

     

    二、Crash的捕获

    1、Crash产生原因

    1、应用中有Bug。 2、Watchdog 超时机制 3、用户强制退出 4、低内存终止 5、其他违反系统规则的操作,大部分是内存问题 发生崩溃,系统会生成一份崩溃日志在本地,或者上传 ITC

     

    2、Crash的类型(异常、信号错误)

    通常我们见到的Crash分为两种,异常和信号错误。

    异常:系统内存错误,触发EXC_BAD_ACCESS引起的,程序运行过程中访问了错误的内存地址。

    NSRangeException等 NSException类

    信号错误:出现了不能被处理的signal异常,导致程序向自身发送了SIGABRT信号而崩溃。

    信号中断(SGIABRT)、非法指令信号(SIGILL)、总线错误信号(SIGBUS)、段错误信号(SIGSEGV)、访问一个已经释放的对象(EXC_BAD_ACCESS)

    3、捕获异常崩溃信息

    只能捕获一些异常崩溃,如 unrecognized selector、NSRangeException beyond bounds越界等Exception属错误

    Appdelegate

    在Appdelegate 的 didFinishLaunchingWithOptions 中 添加
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    
    方法实现如下
    void UncaughtExceptionHandler(NSException *exception) {
        /**
         *  获取异常崩溃信息
         */
        NSArray *callStack = [exception callStackSymbols];
        NSString *reason = [exception reason];
        NSString *name = [exception name];
        NSString *content = [NSString stringWithFormat:@"========异常错误报告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[callStack componentsJoinedByString:@"\n"]];
        
        //将崩溃信息持久化在本地,下次程序启动时、或者后台,将崩溃信息作为日志发送给开发者。
        [[NSUserDefaults standardUserDefaults] setObject:content forKey:@"ExceptionContent"];
    }

    测试

    数组越界错误 NSMutableArray *array = [NSMutableArray array]; NSLog(@"%@",array[1]);

    4、捕获信号错误崩溃信息

    信号类型崩溃捕获,测试的时候如果测试Signal类型的崩溃,不要在xcode下的debug模式进行测试。因为系统的debug会优先去拦截。应该build好应用之后直接点击运行app进行测试。

    什么是信号

    在计算机科学中,信号(英语:Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。


    在iOS中就是未被捕获的Objective-C异常(NSException),导致程序向自身发送了SIGABRT信号而崩溃。

    SIGABRT–程序中止命令中止信号
    SIGALRM–程序超时信号
    SIGFPE–程序浮点异常信号
    SIGILL–程序非法指令信号
    SIGHUP–程序终端中止信号
    SIGINT–程序键盘中断信号
    SIGKILL–程序结束接收中止信号
    SIGTERM–程序kill中止信号
    SIGSTOP–程序键盘中止信号
    SIGSEGV–程序无效内存中止信号
    SIGBUS–程序内存字节未对齐中止信号
    SIGPIPE–程序Socket发送失败中止信号

    捕获方法

    Appdelegate 的 didFinishLaunchingWithOptions 中 添加
    
    signal(SIGHUP, SignalHandler);
    signal(SIGINT, SignalHandler);
    signal(SIGQUIT, SignalHandler);
    
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
    
    方法实现如下
    
    void SignalHandler(int signal){
        
        int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
        if (exceptionCount > UncaughtExceptionMaximum)
        {
            return;
        }
        
        void* callstack[128];
        int frames = backtrace(callstack, 128);
        char **strs = backtrace_symbols(callstack, frames);
        
        int i;
        NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
        for (
             i = UncaughtExceptionHandlerSkipAddressCount;
             i < UncaughtExceptionHandlerSkipAddressCount +
             UncaughtExceptionHandlerReportAddressCount;
             i++)
        {
            [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
        }
        free(strs);
        NSLog(@"%@",backtrace);
        NSLog(@"%@", [NSString stringWithFormat:
                      NSLocalizedString(@"Signal %d was raised.", nil),
                      signal]);
    }
    
    

    测试

    UIView *tempView = [[UIView alloc]init];
    [tempView release];
    
    //对象已经被释放,内存不合法,此块内存地址又没被覆盖,所以此内存内垃圾内存,所以调用方法的时候会导致SIGSEGV的错误
    [tempView setNeedsDisplay];
    
    或者说 我在堆内存中找栈内存地址
    
    id x_id = [self performSelector:@selector(createNum)];
    
    - (int)createNum {
        return 10;
    }
    
    这种情况也是会导致SIGSEGV的错误的
    
    
    如果在内存中释放不存在的空间,就会导致SIGABRT错误
    
    Test * test = {1, 2};
    
    free(test);
    
    
    内存地址不对齐会导致SIGBUS错误
    
    char *s = "hello world";
    *s = 'H';
    
    

    问题

    信号捕获后 app卡死了
    
    大部分这类型的错误会报错 EXC_BAD_ACCESS ,而这种错误都是发生在内存问题,例如
    
    1、访问数据为空、数据类型不对
    
    2、操作了不该操作的对象,野指针
    

    EXC_BAD_ACCESS错误的调试

    1、xcode可以用僵尸模式打印出对象 然后通过对象查找对应的代码位置

    1、Edit Scheme - Diagnositics - Memory Management 勾选 Zombie Objects 和 Malloc Stack
    
    2、会打印出 
    cyuyan[7756:17601127] *** -[UIViewController respondsToSelector:]: message sent to deallocated instance 0x7fe71240d390
    
    这句开启僵尸模式后打出来的输出,包含了我们需要的 进程pid、崩溃地址,终端通过下面命令查看堆栈日志来找到崩溃代码
    
    3、查找日志
    sudo malloc_history 7756 0x7fe71240d390
    
    

    2、覆写一个object的respondsToSelector方法

    在 other c flags中加入-D FOR_DEBUG(记住请只在Debug Configuration下加入此标记)。这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。重写一个object的respondsToSelector方法,打印报错前的
    
    #ifdef _FOR_DEBUG_  
    -(BOOL) respondsToSelector:(SEL)aSelector {  
        printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);  
        return [super respondsToSelector:aSelector];  
    }  
    #endif

    3、通过instruments的Zombies

    引申:怎么定位到野指针的地方。如果还没定位到,这个对象被提前释放了,怎么知道该对象在什么地方释放的

    一种是多线程,一种是野指针。这两种Crash都带随机性,我们要让随机crash变成不随机 把这一随机的过程变成不随机的过程。对象释放后在内存上填上不可访问的数据,其实这种技术其实一直都有,xcode的Enable Scribble就是这个作用。 1、Edit Scheme - Diagnositics - Memory Management 勾选 Malloc Scribble 但是此方法测试后暂时未解决

    可能崩溃的一些场景

    1、野指针

    1、对象释放后内存没被改动过,原来的内存保存完好,可能不Crash或者出现逻辑错误(随机Crash)。 2、对象释放后内存没被改动过,但是它自己析构的时候已经删掉某些必要的东西,可能不Crash、Crash在访问依赖的对象比如类成员上、出现逻辑错误(随机Crash)。 3、对象释放后内存被改动过,写上了不可访问的数据,直接就出错了很可能Crash在objc_msgSend上面(必现Crash,常见)。 4、对象释放后内存被改动过,写上了可以访问的数据,可能不Crash、出现逻辑错误、间接访问到不可访问的数据(随机Crash)。 5、对象释放后内存被改动过,写上了可以访问的数据,但是再次访问的时候执行的代码把别的数据写坏了,遇到这种Crash只能哭了(随机Crash,难度大,概率低)!! 6、对象释放后再次release(几乎是必现Crash,但也有例外,很常见)。

    2、内存处理不当、内存泄露

    3、主线程UI长时间卡死,系统强制销毁app

    死锁?

    4、多线程切换访问引起的crash

    多线程抢写数据库?

    5、和服务端约定的数据结构变更导致操作数据类型问题、或者操作空

     

    三、Crash日志获取

    1、iOS设备可以直接查看

    路径:
    ios 10之后:设置 -> 隐私 -> 分析 -> 数据分析
    ios 10之前:设置 -> 隐私 -> 诊断与用量

    2、链接设备到电脑 Itunes同步后,日志会保存在电脑上

    mac路径:~/Library/Logs/CrashReporter/MobileDevice/
    可以看到所有和该电脑同步过的设备的崩溃日志(.crash文件)
    
    为什么有部分crash无法收集到?

    3、xcode获取

    xcode查看设备日志并导出日志  
    
    Window - Devices - 选择设备 - 点击View Device Logs -> All logs可以看到所有的崩溃日志。

    4、线上的崩溃,没有设备

    1、三方:bugly、crashlytics
    
    2、后台、异步线程将上面描述的捕获到的崩溃上传服务器
    
    3、线上的ITC上可能会有部分日志,可以通过Xcode同步下来崩溃与能耗日志
    
    Xcode Window - Organizer - Crashes 

    四、崩溃日志的解析

    1、崩溃日志的实例

    下面是一份测试过程产生的崩溃日志

    //进程信息
    Incident Identifier: 3C3F8BF8-3099-4E82-92E1-8690212E8FF9
    CrashReporter Key:   bb5f9839ae661ab755f25eff65fee8fd41369628
    Hardware Model:      iPod5,1
    Process:             demo [973]
    Path:                /private/var/containers/Bundle/Application/0D3657DE-DE1E-4FF0-A0F7-C09EBC002763/demo.app/demo
    Identifier:          com.yanghuang.demo
    Version:             17 (1.1.9)
    Code Type:           ARM (Native)
    Parent Process:      launchd [1]
    //基本信息
    Date/Time:           2017-08-22 16:11:49.49 +0800
    Launch Time:         2017-08-22 16:11:40.40 +0800
    OS Version:          iOS 9.3.5 (13G36)
    Report Version:      104
    //异常
    Exception Type:  EXC_BREAKPOINT (SIGTRAP)
    Exception Codes: 0x0000000000000001, 0x00000000e7ffdefe
    Triggered by Thread:  0
    
    Filtered syslog:
    None found
    //线程回溯
    Thread 0 name:  Dispatch queue: com.apple.main-thread
    Thread 0 Crashed:
    0   libswiftCore.dylib              0x0033788c 0x1ac000 + 1620108
    1   ...wiftSwiftOnoneSupport.dylib  0x009b4830 0x9ac000 + 34864
    2   demo                            0x00029288 0x24000 + 21128
    3   demo                            0x00029414 0x24000 + 21524
    4   UIKit                           0x25cd2754 0x25c87000 + 309076
    5   UIKit                           0x25cd26e0 0x25c87000 + 308960
    6   UIKit                           0x25cba6d2 0x25c87000 + 210642
    7   UIKit                           0x25cd2004 0x25c87000 + 307204
    8   UIKit                           0x25cd1c7e 0x25c87000 + 306302
    9   UIKit                           0x25cca68e 0x25c87000 + 276110
    10  UIKit                           0x25c9b124 0x25c87000 + 82212
    11  UIKit                           0x25c996d2 0x25c87000 + 75474
    12  CoreFoundation                  0x216e1dfe 0x21626000 + 769534
    13  CoreFoundation                  0x216e19ec 0x21626000 + 768492
    14  CoreFoundation                  0x216dfd5a 0x21626000 + 761178
    15  CoreFoundation                  0x2162f228 0x21626000 + 37416
    16  CoreFoundation                  0x2162f014 0x21626000 + 36884
    17  GraphicsServices                0x22c1fac8 0x22c16000 + 39624
    18  UIKit                           0x25d03188 0x25c87000 + 508296
    19  demo                            0x0002ff48 0x24000 + 48968
    20  libdyld.dylib                   0x212d7872 0x212d5000 + 10354
    
    Thread 1 name:  Dispatch queue: com.apple.libdispatch-manager
    Thread 1:
    0   libsystem_kernel.dylib          0x213ac2f8 0x21396000 + 90872
    1   libdispatch.dylib               0x212a1d60 0x2128b000 + 93536
    2   libdispatch.dylib               0x212a1abe 0x2128b000 + 92862
    
    ... 省略部分内容
    
    //二进制映像
    Binary Images
    0x24000 - 0x33fff demo armv7  <aa31c8c1f8cb333596dbfe056b120673> /var/containers/Bundle/Application/0D3657DE-DE1E-4FF0-A0F7-C09EBC002763/demo.app/demo
    0x140000 - 0x15bfff Masonry armv7  <9615e97c54d335f7821568396c65d324> /var/containers/Bundle/Application/0D3657DE-DE1E-4FF0-A0F7-C09EBC002763/demo.app/Frameworks/Masonry.framework/Masonry

    1.进程信息

    第一部分是闪退进程的相关信息。 Incident Identifier 是崩溃报告的唯一标识符。 CrashReporter Key 是与设备标识相对应的唯一键值。虽然它不是真正的设备标识符,但也是一个非常有用的情报:如果你看到100个崩溃日志的CrashReporter Key值都是相同的,或者只有少数几个不同的CrashReport值,说明这不是一个普遍的问题,只发生在一个或少数几个设备上。 Hardware Model 标识设备类型。 如果很多崩溃日志都是来自相同的设备类型,说明应用只在某特定类型的设备上有问题。上面的日志里,崩溃日志产生的设备是iPhone 4s。 Process 是应用名称。中括号里面的数字是闪退时应用的进程ID。


    2.基本信息

    这部分给出了一些基本信息,包括闪退发生的日期和时间,设备的iOS版本。如果有很多崩溃日志都来自iOS 6.0,说明问题只发生在iOS 6.0上。
    
    Version: 崩溃进程的版本号. 这个值包含在 CFBundleVersion and CFBundleVersionString中.
    
    Code Type: 崩溃日志所在设备的架构. 会是ARM-64, ARM, x86-64, or x86中的一个.
    
    OS Version: 崩溃发生时的系统版本
    
    

    3.异常信息

    异常信息会列出异常的类型、位置。
    
    在这部分,你可以看到闪退发生时抛出的异常类型。还能看到异常编码和抛出异常的线程。根据崩溃报告类型的不同,在这部分你还能看到一些另外的信息。
    
    Exception Codes: 处理器的具体信息有关的异常编码成一个或多个64位进制数。通常情况下,这个区域不会被呈现,因为将异常代码解析成人们可以看懂的描述是在其它区域进行的。
    
    Exception Subtype: 供人们可读的异常代码的名字
    
    Exception Message: 从异常代码中提取的额外的可供人们阅读的信息.
    
    Exception Note: 不是特定于一个异常类型的额外信息.如果这个区域包含SIMULATED (这不是一个崩溃)然后进程没有崩溃,但是被watchdog杀掉了
    
    Termination Reason: 当一个进程被终止的时的原因。
    
    Triggered by Thread: 异常所在的线程.
    
    

    4.线程回溯

    这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单。看下面这行日志: 2 demo 0x00029288 0x24000 + 21128 它包括四列: 帧编号—— 此处是2。 二进制库的名称 ——此处是 demo. 调用方法的地址 ——此处是 0x00029288. 第四列分为两个子列,一个基本地址和一个偏移量。此处是0×0x24000 + 21128, 第一个数字指向文件,第二个数字指向文件中的代码行。

    5.二进制映像

    这部分列出了闪退时已经加载的二进制文件。

    2、符号化Symbolication

    第一次看到崩溃日志上的回溯时,你或许会觉得它没什么意义。我们习惯使用方法名和行数,而非像这样的神秘位置:

    2   demo     0x00029288 0x24000 + 21128

    将这些十六进制地址转化成方法名称和行数的过程称之为符号化。

    从Xcode的Organizer窗口获取崩溃日志后过几秒钟,崩溃日志将被自动符号化。上面那行被符号化后的版本如下 :

    2   demo     0x00029288 ViewController.crashAction(Any) -> () (ViewController.swift:36)
    

    Xcode符号化崩溃日志时,需要访问与App Store上对应的应用二进制文件以及生成二进制文件时产生的 .dSYM 文件。必需完全匹配才行。否则,日志将无法被完全符号化。

    所以,保留每个分发给用户的编译版本非常重要。提交应用前进行归档时,Xcode将保存应用的二进制文件。可以在Xcode Organizer的Archives标签栏下找到所有已归档的应用文件。

    在发现崩溃日志时,如果有相匹配的.dSYM文件和应用二进制文件,Xcode会自动对崩溃日志进行符号化。如果你换到别的电脑或创建新的账户,务必将所有二进制文件移动到正确的位置,使Xcode能找到它们。

    注意:

    1、你必需同时保留应用二进制文件和.dSYM文件才能将崩溃日志完整符号化。每次提交到iTunes Connect的构建都必需归档。 .dSYM文件和二进制文件是特定绑定于每一次构建和后续构建的,即使来自相同的源代码文件,每一次构建也与其他构建不同,不能相互替换。如果你使用Build 和 Archive 命令,这些文件会自动放在适当位置。 如果不是使用Build 和 Archive命令,放在Spotlight能够搜索到的位置(比如Home目录)即可。

    2、xcode debug方式打包默认没有DSYM文件,只需要修改对应的build options即可

    build settings -> build options
    把debug 项改成 DWARF with dSYM File 即可

    如何通过.crash文件反编译得到明文的crash文件

    需要文件:

    1、demo.app
    
    2、demo.app.dSYM
    
    3、demo.crash (已获得)
    
    4、symbolicatecrash
    
    符号化前先检查一下三者的uuid是不是一致的,只有是一致的才能符号化成功。
    
    查看xx.app文件的uuid的方法:
    
    dwarfdump --uuid xxx.app/xxx (xxx工程名)
    
    查看xx.app.dSYM文件的uuid的方法令:
    
    dwarfdump --uuid xxx.app.dSYM (xxx工程名)
    
    而.crash的uuid位于,crash日志中的Binary Images:中的第一行尖括号内。如:armv7<8bdeaf1a0b233ac199728c2a0ebb4165>

    步骤如下:

    1、首先我们找到Archives目录(/Users/用户名/Library/Developer/Xcode/Archives/2017-08-22/demo)

    2、找到对应app目录、对应的Archives文件、显示包内容打开。在dSYMs文件夹中找到demo.app.dSYM
    在Products->Applications文件夹中找到 demo.app

    3、找到symbolicatecrash

    find /Applications/Xcode.app -name symbolicatecrash -type f
    //终端输入上面命令、得到一个路径,这个路径就是symbolicatecrash的路径
    拷贝到和上面文件同一目录

    4: 在终端中输入以下命令

    ./symbolicatecrash -v demo.crash demo.app.dSYM
    
    如果出现Error: "DEVELOPER_DIR" is not defined 再执行下面一句后再次执行
    
    export DEVELOPER_DIR="/Applications/XCode.app/Contents/Developer"
    
    

    然后用控制台打开你的demo.crash文件, 你就会看到编译后的crash文件, 同Xcode看到的崩溃日志一致。通过查看崩溃日志,可以轻易的找到崩溃原因并修正。

    Thread 0 name:  Dispatch queue: com.apple.main-thread
    Thread 0 Crashed:
    0   libswiftCore.dylib              0x0033788c 0x1ac000 + 1620108
    1   ...wiftSwiftOnoneSupport.dylib  0x009b4830 0x9ac000 + 34864
    2   demo                            0x00029288 ViewController.crashAction(Any) -> () (ViewController.swift:36)
    3   demo                            0x00029414 @objc ViewController.crashAction(Any) -> () (ViewController.swift:0)
    4   UIKit                           0x25cd2754 -[UIApplication sendAction:to:from:forEvent:] + 80
    5   UIKit                           0x25cd26e0 -[UIControl sendAction:to:forEvent:] + 64
    6   UIKit                           0x25cba6d2 -[UIControl _sendActionsForEvents:withEvent:] + 466
    7   UIKit                           0x25cd2004 -[UIControl touchesEnded:withEvent:] + 604
    8   UIKit                           0x25cd1c7e -[UIWindow _sendTouchesForEvent:] + 646
    9   UIKit                           0x25cca68e -[UIWindow sendEvent:] + 642
    10  UIKit                           0x25c9b124 -[UIApplication sendEvent:] + 204
    11  UIKit                           0x25c996d2 _UIApplicationHandleEventQueue + 5010
    12  CoreFoundation                  0x216e1dfe __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
    13  CoreFoundation                  0x216e19ec __CFRunLoopDoSources0 + 452
    14  CoreFoundation                  0x216dfd5a __CFRunLoopRun + 794
    15  CoreFoundation                  0x2162f228 CFRunLoopRunSpecific + 520
    16  CoreFoundation                  0x2162f014 CFRunLoopRunInMode + 108
    17  GraphicsServices                0x22c1fac8 GSEventRunModal + 160
    18  UIKit                           0x25d03188 UIApplicationMain + 144
    19  demo                            0x0002ff48 main (AppDelegate.swift:13)
    20  libdyld.dylib                   0x212d7872 start + 2
    
    

    3、低内存闪退

    因为低内存崩溃日志与普通崩溃日志略有不同。

    iOS设备检测到低内存时,虚拟内存系统发出通知请求应用释放内存。这些通知发送到所有正在运行的应用和进程,试图收回一些内存。

    如果内存使用依然居高不下,系统将会终止后台线程以缓解内存压力。如果可用内存足够,应用将能够继续运行而不会产生崩溃报告。否则,应用将被iOS终止,并产生低内存崩溃报告。

    低内存崩溃日志上没有应用线程的堆栈回溯。相反,上面显示的是以内存页数为单位的各进程内存使用量。

    被iOS因释放内存页终止的进程名称后面你会看到jettisoned 字样。如果看到它出现在你的应用名称后面,说明你的应用因使用太多内存而被终止了。

    低内存崩溃日志看起来像这样:

    4、异常编码

    通常,异常编码以一些文字开头,紧接着是一个或多个十六进制值,此数值正是说明闪退根本性质的所在。 从这些编码中,可以区分出闪退是因为程序错误、非法内存访问或者是其他原因。

    下面是一些常见的异常编码:

    0x8badf00d: 该编码表示应用是因为发生watchdog超时而被iOS终止的。  通常是应用花费太多时间而无法启动、终止或响应用系统事件。
    
    0xbad22222: 该编码表示 VoIP 应用因为过于频繁重启而被终止。
    
    0xdead10cc: 该代码表明应用因为在后台运行时占用系统资源,如通讯录数据库不释放而被终止 。
    
    0xdeadfa11: 该代码表示应用是被用户强制退出的。根据苹果文档, 强制退出发生在用户长按开关按钮直到出现 “滑动来关机”, 然后长按 Home按钮。强制退出将产生 包含0xdeadfa11 异常编码的崩溃日志, 因为大多数是强制退出是因为应用阻塞了界面。
    
    EXC_CRASH // SIGABRT: 进程异常退出。该异常类型崩溃最常见的原因是未捕获的Objective-C和C++异常和调用abort()。如果他们需要太多的时间来初始化,程序将被终止,因为触发了看门狗。如果是因为启动的时候被挂起,所产生的崩溃报告异常类型(Exception Subtype)将是launch_hang。
    
    EXC_BREAKPOINT // SIGTRAP:进程试图执行非法或未定义指令。这个进程可能试图通过一个配置错误的函数指针,跳到一个无效的地址。
    
    SIGSEGV、SIGBUS 这些在前面捕获异常的内容有描述
    
    

    注意: 在后台任务列表中关闭已挂起的应用不会产生崩溃日志。 一旦应用被挂起,它何时被终止都是合理的。所以不会产生崩溃日志。

     

     

     

    展开全文
  • 代码bug一、如何获得crash日志当一个iOS应用程序崩溃时,系统会创建一份crash日志保存在设备上。这份crash日志记录着应用程序崩溃时的信息,通常包含着每个执行线程的栈调用信息(低内存闪退日志例外),对于开发...
     
     
    Outline
    如何获得crash日志
    如何解析crash日志
    如何分析crash日志
         1. iOS策略相关
         2. 常见错误标识
         3. 代码bug


    一、如何获得crash日志

    当一个iOS应用程序崩溃时,系统会创建一份crash日志保存在设备上。这份crash日志记录着应用程序崩溃时的信息,通常包含着每个执行线程的栈调用信息(低内存闪退日志例外),对于开发人员定位问题很有帮助。

    如果设备就在身边,可以连接设备,打开Xcode - Window - Organizer,在左侧面板中选择Device Logs(可以选择具体设备的Device Logs或者Library下所有设备的Device Logs),然后根据时间排序查看设备上的crash日志。这是开发、测试阶段最经常采用的方式。

    如果应用程序已经提交到App Store发布,用户已经安装使用了,那么开发者可以通过iTunes Connect(Manage Your Applications - View Details - Crash Reports)获取用户的crash日志。不过这并不是100%有效的,而且大多数开发者并不依赖于此,因为这需要用户设备同意上传相关信息,详情可参见iOS: Providing Apple with diagnostics and usage information摘要。

    考虑到并不是所有iPhone用户都允许自动发送诊断报告(crash日志),而且对于部分提交到Apple得crash日志,开发者还需要手动去拉取,然后找到对应的符号文件进行解析——这是一件很繁琐的事情。所以实际项目开发中,通常接入现有的crash收集工具(参考1参考2),或者自己编写一个进行自动化收集、解析和统计汇总。


    二、如何解析crash日志

    当获得一份crash日志时,我们需要将初始展示的十六进制地址等原始信息映射为源代码级别的方法名称和代码行数,使其对开发人员可读。这个过程称为符号化解析。要成功地符号化解析一份crash日志,我们需要有对应的应用程序二进制文件以及符号(.dSYM)文件。

    如果处于开发调试阶段,通常Xcode都能匹配到crash日志对应的二进制文件和符号文件,所以能够帮我们自动解析。

    如果处于测试阶段,测试人员已经安装了不同的版本(比如alpha、beta版本),那么需要保存好对应版本的二进制文件和符号文件,以便在应用程序崩溃时对crash日志进行解析。对于这种场景下产生的crash日志,只需要将.crash文件、.app文件和.dSYM文件三者放在同一个目录下,然后将.crash文件拖放到Xcode - Window - Organizer中左侧面板Library下的Device Logs中,即可进行解析。

    如果要提交发布,那么我们通常会先执行Clean,再Build,最后通过Product - Archive来打包。这样,Xcode会将二进制文件和符号文件归档在一起,可以通过Organizer中的Archives进行浏览。

    这里是一份关于如何解析crash日志的讨论:http://stackoverflow.com/questions/1460892/symbolicating-iphone-app-crash-reports 。


    三、如何分析crash日志

    在分析一份crash日志之前,如果开发人员对于常见的错误类型有所了解,那定是极好的。
    crash日志的产生来源于两种问题:违反iOS策略被干掉,以及自身的代码bug。

    1. iOS策略

    1.1 低内存闪退
    前面提到大多数crash日志都包含着执行线程的栈调用信息,但是低内存闪退日志除外,这里就先看看低内存闪退日志是什么样的。
    我们使用Xcode 5和iOS 7的设备模拟一次低内存闪退,然后通过Organizer查看产生的crash日志,可以发现Process和Type都为Unknown:



    而具体的日志内容如下:



    第一部分是崩溃信息,包括识别标识、软硬件信息和时间信息等。
    第二部分是内存页分配信息,以及当前占用内存最多的进程,上图中为crashTypeDemo。
    第三部分是具体的进程列表,描述着每个进程使用内存的情况以及当前状态。在较早的版本中可以在某些进程后面看到“jettisoned”字样,表明这些进程使用过多内存被终止了,而现在我们看到的是“vm-pageshortage”字样。

    当iOS检测到内存过低时,它(的VM系统)会发出低内存警告通知,尝试回收一些内存;如果情况没有得到足够的改善,iOS会终止后台应用以回收更多内存;最后,如果内存还是不足,那么正在运行的应用可能会被终止掉。
    所以,我们的应用应该合理地响应系统抛出来的低内存警告通知,对一些缓存数据和可重新创建的对象进行释放,同时要避免出现内存泄露等问题。

    低内存闪退是由iOS策略决定终止应用程序运行的,同样基于iOS策略的还有Watchdog超时和用户强制退出。

    1.2 Watchdog超时
    Apple的iOS Developer Library网站上,QA1693文档中描述了Watchdog机制,包括生效场景和表现。如果我们的应用程序对一些特定的UI事件(比如启动、挂起、恢复、结束)响应不及时,Watchdog会把我们的应用程序干掉,并生成一份响应的crash报告。

    这份crash报告的有趣之处在于异常代码:“0x8badf00d”,即“ate bad food”。
    如果说特定的UI事件比较抽象,那么用代码来直接描述的话,对应的就是(创建一个工程时Xcode自动生成的)UIApplicationDelegate的几个方法:



    所以当遇到Watchdog日志时,可以检查下上图几个方法是否有比较重的阻塞UI的动作。

    QA1693举的例子是在主线程进行同步网络请求。如果我们是在公司的Wifi环境下使用则一切顺利,但当应用程序发布出去面向很大范围的用户,在各种网络环境下运行,则不可避免地会出现一片Watchdog超时报告。
    另一种可能出现问题的场景就是数据量比较大的情况下进行的数据库版本迁移(同样是在主线程上),这也是促使我写这篇总结的一个直接因素。

    1.3 用户强制退出

    一看到“用户强制退出”,首先可能想到的双击Home键,然后关闭应用程序。不过这种场景是不会产生crash日志的,因为双击Home键后,所有的应用程序都处于后台状态,而iOS随时都有可能关闭后台进程,所以这种场景没有crash日志。

    另一种场景是用户同时按住电源键和Home键,让iPhone重启。这种场景会产生日志(仅验证过一次),但并不针对特定应用程序。

    这里指的“用户强制退出”场景,是稍微比较复杂点的操作:先按住电源键,直到出现“滑动关机”的界面时,再按住Home键,这时候当前应用程序会被终止掉,并且产生一份相应事件的crash日志。



    通常,用户应该是遇到应用程序卡死,并且影响到了iOS响应,才会进行这样的操作——不过感觉这操作好高级,所以这样的crash日志应该比较少见。

    2. 常见错误标识

    2.1 Exception codes
    上面“用户强制退出”的crash日志中的Exception Codes是“0xdeadfa11”,再上面“Watchdog超时”的crash日志中的Exception Codes是“0x8badf00d”,这些都是特有的Exception codes。
    根据官方文档描述,至少有以下几种特定异常代码:

    0x8badf00d错误码:Watchdog超时,意为“ate bad food”。

    0xdeadfa11错误码:用户强制退出,意为“dead fall”。

    0xbaaaaaad错误码:用户按住Home键和音量键,获取当前内存状态,不代表崩溃。



    0xbad22222错误码:VoIP应用(因为太频繁?)被iOS干掉。

    0xc00010ff错误码:因为太烫了被干掉,意为“cool off”。

    0xdead10cc错误码:因为在后台时仍然占据系统资源(比如通讯录)被干掉,意为“dead lock”。

    2.2 Exception types
    查看我们的crash分析报告邮件,会发现最经常遇到的错误类型是SEGV(Segmentation Violation,段违例),表明内存操作不当,比如访问一个没有权限的内存地址。
    当我们收到SIGSEGV信号时,可以往以下几个方面考虑:
    访问无效内存地址,比如访问Zombie对象;
    尝试往只读区域写数据;
    解引用空指针;
    使用未初始化的指针;
    栈溢出;
    此外,还有其它常见信号:
    SIGABRT:收到Abort信号,可能自身调用abort()或者收到外部发送过来的信号;
    SIGBUS:总线错误。与SIGSEGV不同的是,SIGSEGV访问的是无效地址(比如虚存映射不到物理内存),而SIGBUS访问的是有效地址,但总线访问异常(比如地址对齐问题);
    SIGILL:尝试执行非法的指令,可能不被识别或者没有权限;
    SIGFPE:Floating Point Error,数学计算相关问题(可能不限于浮点计算),比如除零操作;
    SIGPIPE:管道另一端没有进程接手数据;

    3. 代码bug
    此外,比较常见的崩溃基本都源于代码bug,比如数组越界、插空、多线程安全性、访问野指针、发送未实现的selector等。如果引入Core Data,则又有另外一些常见问题,不过这是另一个话题了。

    遇到这些bug时,都有比较清楚的错误原因说明,比如“index 0 beyond bounds for empty array”等。需要稍微注意点的是多线程问题,当一时找不到解决思路时,不妨往多线程方面考虑下。

    展开全文
  • 可以看到app崩溃主要集中在iOS9.2系统中,app版本3.2中。 接下来,就定位代码崩溃发生的行了。 Xcode下载崩溃日志 1、打开xcode 先要下载崩溃日志 Window -> Organizer 2、选择crashes 3、查看相应...
  • 若一个App没有加入Crashlytics或者Buggly这些崩溃日志监控,那么我们在App崩溃的时候如何获取崩溃信息呢? 此时我们可以通过symbolicatecrash工具对手机日志来进行分析定位,需要以下一些步骤: 一、获取crash日志...
  • 如何查看iOS已上架app崩溃分布、定位crash发生的行   在app上线后我们需要知道app运行的怎么样,下载的人多不多,这些呢就需要在iTunes connect中查看。对于开发人员来说,查看app的崩溃情况尤为重要。下面...
  • iOS-崩溃crash大解析

    千次阅读 2018-04-24 14:52:44
    前言iOS崩溃是让iOS开发人员比较头痛的事情,app崩溃了,说明代码写的有问题,这时如何快速定位到崩溃的地方很重要。调试阶段是比较容易找到出问题的地方的,但是已经上线的app并分析崩溃报告就比较麻烦了。之前我...
  • iOS应用Crash分析实践

    2021-03-25 12:56:40
    本套课程主要分为4部分: 如何统计和分析Crash 通过友盟平台做Crash的分析和报警 SDK工具的集成与使用 各种场景下产生的Crash问题的原因和解决方案   通过本套课程的学习,可以帮助初学者快速定位和解决产品的...
  • 如果已上架App出现奔溃(Crash)情况,对于开发人员来说如何查看Crash信息定位及对应的处理方式尤为重要。以下就是查看Crash信息定位的步骤和处理方式。 Crash的来源:分布情况(自发现或者用户发现) 1. 通过对应的...
  • iOS崩溃crash大解析

    2017-02-14 11:27:18
    iOS崩溃是让iOS开发人员比较头痛的事情,app崩溃了,说明代码写的有问题,这时如何快速定位到崩溃的地方很重要。调试阶段是比较容易找到出问题的地方的,但是已经上线的app并分析崩溃报告就比较麻烦了。 之前我...
  • iOS 崩溃Crash解析

    千次阅读 2017-03-07 15:43:05
    iOS崩溃是让iOS开发人员比较头痛的事情,app崩溃了,说明代码写的有问题,这时如何快速定位到崩溃的地方很重要。调试阶段是比较容易找到出问题的地方的,但是已经上线的app并分析崩溃报告就比较麻烦了。 之前我...
  • 友盟统计有一个很适用的功能是错误分析,通过它我们可以看到App的一些crash错误,对于定位问题有帮助,那么如何根据这些错误日志解决bug呢?今天在友盟的错误分析里面找到了一个这样的错误:...
  • 解析iOS Crash文件

    2017-03-07 18:38:23
    1、前提:再线下测试过程中,发生crash后都是十六进制,开发和QA都无法立马定位到对应的堆栈信息。使用symbolicatecrash工具可以实现crash文件解析,导出对应的堆栈log信息方便查看和定位bug出现的位置。 2、目的:...
  • iOS crash崩溃日志总结

    2020-06-08 16:27:24
    当游戏运行崩溃(也叫闪退)时,iOS系统生成对应的*.crash崩溃日志并保存在设备上。这份崩溃日志通常包含游戏闪退时当时运行的每个线程的堆栈调用信息,我们通过分析这些信息,定位游戏闪退发生的代码位置和异常类型...
  • iOS 如何追踪app

    千次阅读 2016-11-03 19:51:07
    crash日志上有很多有用的信息,比如每个正在执行线程的完整堆栈跟踪信息和内存映像,这样就能够通过解析这些信息进而定位crash发生时的代码逻辑,从而找到App闪退的原因。通常来说,crash产生来源于两种问题:违反...
  • 如何获取crash日志?  当一个iOS应用程序崩溃时,系统会创建一份crash日志保存在设备上。这份crash日志记录着应用程序崩溃时的信息,通常包含着每个执行线程的栈调用信息(低内存闪退日志例外),对于开发人员定位...
  • 如果已上架App出现奔溃(Crash)情况,对于开发人员来说如何查看Crash信息定位及对应的处理方式尤为重要。以下就是查看Crash信息定位的步骤和处理方式。 Crash的来源:分布情况(自发现或者用户发现) 1. 通过对应...
  • iOS Crash异常日志收集

    2019-09-27 17:28:51
    任何一款应用在使用过程中出现各种异常问题在所难免,如何能快速还原场景定位异常,对于能否及时修复问题非常重要.在apple的文档中我们发现了这样一张图,完整地描述了应用的整个流程: 编译器将源代码编译成机器...
  • 继上一篇如何定位Obj-C野指针随机Crash介绍了思路后,这次我们继续看,如何让非必现Crash变为必现。 陈其锋,腾讯SNG即通产品部音视频技术中心软件工程师,主要负责iOS平台音视频功能开发,热衷于移动开发,...
  • 是的,你没有看错,要说的就是提高iOScrash率! 欲让其灭亡先让其疯狂,我们当然不是人为制造crash,准确地说,是使隐藏的随机性crash暴露出来,提高测试时的crash率,从而降低版本发布后的crash率。 写c、c
  • http://www.cocoachina.com/bbs/read.php?tid-180736.html ... 发布app被拒,查看苹果返回的crash日志.(自己测试程序很多次, 无crash情况) 日志解析后定位到代码 下面建议如何用symbolicatecrash工

空空如也

空空如也

1 2 3 4 5
收藏数 94
精华内容 37
关键字:

ios如何定位crash