iOS 捕获全局异常,统一收集

参考博文:http://www.cnblogs.com/easonoutlook/archive/2012/12/27/2835979.html

开发程序的过程中不管我们已经如何小心,总是会在不经意间遇到程序闪退。流畅的操作被无情地Crash打断,当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们所说的Crash文件,当时如果是真机测试离开Xcode的时候Crash掉,我们是无法知道crash的具体位置的。现在做一个程序统一记录crash的位置。先科普一下crash的知识:

Crash文件结构

1、Process Information(进程信息)

Incident Idnetifier 崩溃报告的唯一标识符,不同的Crash
CrashReporter Key 设备标识相对应的唯一键值(并非真正的设备的UDID,苹果为了保护用户隐私iOS6以后已经无法获取)。通常同一个设备上同一版本的App发生Crash时,该值都是一样的。
Hardware Model 代表发生Crash的设备类型,上图中的“iPad4,4”代表iPad Air
Process 代表Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID
Path 可执行程序在手机上的存储位置,注意路径时到XXX.app/XXX,XXX.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的XXX,感兴趣的可以自己查一下相关资料,有机会我后面也会介绍到
Identifier 你的App的Indentifier,通常为“com.xxx.yyy”,xxx代表你们公司的域名,yyy代表某一个App
Version 当前App的版本号,由Info.plist中的两个字段组成,CFBundleShortVersionString and CFBundleVersion
Code Type 当前App的CPU架构
Parent Process 当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd

2、Basic Information

Date/Time Crash发生的时间,可读的字符串
OS Version 系统版本,()内的数字代表的时Bulid号
Report Version Crash日志的格式,目前基本上都是104,不同的version里面包含的字段可能有不同

3、Exception(非常重要)

Exception Type 异常类型
Exception Subtype: 异常子类型
Crashed Thread 发生异常的线程号

4、Thread Backtrace

发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。上图的信息表明本次Crash出现xxxViewController的323行,出错的函数调用为orderCountLoadFailed。

5、Thread State

Crash时发生时刻,线程的状态,通常我们根据Crash栈即可获取到相关信息,这部分一般不用关心。

6、Binary Images

Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行文件的包得uuid位c0f……cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。

废话说了那么多,到底怎么捕获整个系统的crash呢,入正题,其实也很简单:创建一个工具类  ExceptionHandler.h  和 ExceptionHandler.m(oc版本),swift版本的下面再讲.

ExceptionHandler.h

#import <UIKit/UIKit.h>

@interface ExceptionHandler : NSObject{

BOOL dismissed;

}

@end

void HandleException(NSException *exception);

void SignalHandler(int signal);

void InstallUncaughtExceptionHandler(void);

ExceptionHandler.m

#import "ExceptionHandler.h"

#include <libkern/OSAtomic.h>

#include <execinfo.h>

NSString * const ExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";

NSString * const ExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";

NSString * const ExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount = 0;

const int32_t UncaughtExceptionMaximum = 10;

const NSInteger ExceptionHandlerSkipAddressCount = 4;

const NSInteger ExceptionHandlerReportAddressCount = 5;

@implementation ExceptionHandler

+ (NSArray *)backtrace

{

void  * callstack[128];

int frames = backtrace(callstack, 128);

char **strs = backtrace_symbols(callstack, frames);

int i;

NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

for (

i = ExceptionHandlerSkipAddressCount;

i < ExceptionHandlerSkipAddressCount +

ExceptionHandlerReportAddressCount;

i++)

{

[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];

}

free(strs);

return backtrace;

}

- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex

{

if (anIndex == 0)

{

dismissed = YES;

}

}

- (void)validateAndSaveCriticalApplicationData

{

}

- (void)handleException:(NSException *)exception

{

[self validateAndSaveCriticalApplicationData];

UIAlertView *alert =

[[UIAlertView alloc]

initWithTitle:NSLocalizedString(@"Unhandled exception", nil)

message:[NSString stringWithFormat:NSLocalizedString(

@"You can try to continue but the application may be unstable.\n\n"

@"Debug details follow:\n%@\n%@", nil),

[exception reason],

[[exception userInfo] objectForKey:ExceptionHandlerAddressesKey]]

delegate:self

cancelButtonTitle:NSLocalizedString(@"Quit", nil)

otherButtonTitles:NSLocalizedString(@"Continue", nil), nil];

[alert show];

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

CFRunLoopRef runLoop = CFRunLoopGetCurrent();

CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

while (!dismissed)

{

for (NSString *mode in (NSArray *)allModes)

{

CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);

}

}

CFRelease(allModes);

NSSetUncaughtExceptionHandler(NULL);

signal(SIGABRT, SIG_DFL);

signal(SIGILL, SIG_DFL);

signal(SIGSEGV, SIG_DFL);

signal(SIGFPE, SIG_DFL);

signal(SIGBUS, SIG_DFL);

signal(SIGPIPE, SIG_DFL);

if ([[exception name] isEqual:ExceptionHandlerSignalExceptionName])

{

kill(getpid(), [[[exception userInfo] objectForKey:ExceptionHandlerSignalKey] intValue]);

}

else

{

[exception raise];

}

}

@end

void HandleException(NSException *exception)

{

int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

if (exceptionCount > UncaughtExceptionMaximum)

{

return;

}

NSArray *callStack = [ExceptionHandler backtrace];

NSMutableDictionary *userInfo =

[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];

[userInfo

setObject:callStack

forKey:ExceptionHandlerAddressesKey];

[[[ExceptionHandler alloc] init]

performSelectorOnMainThread:@selector(handleException:)

withObject:

[NSException

exceptionWithName:[exception name]

reason:[exception reason]

userInfo:userInfo]

waitUntilDone:YES];

}

void SignalHandler(int signal)

{

int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

if (exceptionCount > UncaughtExceptionMaximum)

{

return;

}

NSMutableDictionary *userInfo =

[NSMutableDictionary

dictionaryWithObject:[NSNumber numberWithInt:signal]

forKey:ExceptionHandlerSignalKey];

NSArray *callStack = [ExceptionHandler backtrace];

[userInfo

setObject:callStack

forKey:ExceptionHandlerAddressesKey];

[[[ExceptionHandler alloc] init]

performSelectorOnMainThread:@selector(handleException:)

withObject:

[NSException

exceptionWithName:ExceptionHandlerSignalExceptionName

reason:

[NSString stringWithFormat:

NSLocalizedString(@"Signal %d was raised.", nil),

signal]

userInfo:

[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:ExceptionHandlerSignalKey]]

waitUntilDone:YES];

}

void InstallUncaughtExceptionHandler(void)

{

NSSetUncaughtExceptionHandler(&HandleException);

signal(SIGABRT, SignalHandler);

signal(SIGILL, SignalHandler);

signal(SIGSEGV, SignalHandler);

signal(SIGFPE, SignalHandler);

signal(SIGBUS, SignalHandler);

signal(SIGPIPE, SignalHandler);

}

使用方式:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

InstallUncaughtExceptionHandler();

return YES;

}

大功告成

swift版本:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

//输出bug信息

print("系统bug日志记录---------------------\(NSUserDefaults.standardUserDefaults().valueForKey(ERROR_MESSAGE))")

NSSetUncaughtExceptionHandler { exception in

var message = exception.callStackSymbols

message.removeAll()

message.append("错误理由:\(exception.reason!)")

message.append("错误详细信息:\(exception.callStackSymbols)")

NSUserDefaults.standardUserDefaults().setObject(message, forKey: ERROR_MESSAGE)

NSUserDefaults.standardUserDefaults().synchronize()

print("系统bug日志记录---------------------\n\(message)")

}

return true

}

swift比oc简单很多,直接在didFinishLaunchingWithOptions加入上面的代码就可以。

测试代码:

NSMutableArray *array = [NSMutableArray array];

[array addObject:@"1"];

[array addObject:@"2"];

NSLog(@"%@",array[9]);

效果图  数组越界:








时间: 2024-12-25 05:37:18

iOS 捕获全局异常,统一收集的相关文章

Android应用捕获全局异常自定义处理

[2016-06-30]最新的全局异常处理DRCrashHandler已经集成在DR_support_lib库中 具体请看: https://coding.net/u/wrcold520/p/DR_support_lib/git/tree/master [2016-06-28] 1 增加log4j的支持[2016-06-28] 2 增加全局异常处理(可自定义程序崩溃提示消息,自定义发送错误报告到服务器)[2016-06-28] 3 增加两种应用退出方法:① appExit,结束掉所有Acitiv

捕获全局异常UncaughtExceptionHandler

标签:捕获全局异常,UncaughtExceptionHandler ========================网上相关博客======================== 参考:http://blog.csdn.net/jdsjlzx/article/details/7606423 Android自定义捕获Application全局异常 http://git.oschina.net/oschina/android-app 大家可以参考. 大家都知道,现在安装Android系统的手机版本和

Android应用源码之捕获全局异常

Android应用源码之捕获全局异常 本项目就是一个简单的全局异常捕捉例子,捕捉到异常以后可以把异常信息写入文件以供后来分析或者用友好的方式进行提示后再退出程序. 下载地址:http://www.devstore.cn/code/info/630.html

WebForm 在 Global.asax 中捕获全局异常

1 /// <summary> 2 /// 捕获全局异常 3 /// </summary> 4 /// <param name="sender">sender</param> 5 /// <param name="e">e</param> 6 protected void Application_Error(Object sender, EventArgs e) 7 { 8 Exception

Android -- 使用UncaughtExceptionHandler捕获全局异常

在集成了统计SDK(友盟统计,百度统计等)之后,有一个非常有利于测试的功能:错误分析!此功能能够将程序在运行中碰到的崩溃(runtimeException)问题反馈到服务器,帮助开发者改善产品,多适配机器. 然而在公司android开发中不集成这些SDK,那应该怎么实现这样的功能呢?下面让我们来看下如何使用UncaughtExceptionHandler来捕获异常. 首先实现创建一个类,实现UncaughtExceptionHandler接口.代码如下: public class CrashHa

在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常

毕竟人不是神,谁写的程序都会有bug,有了bug不可怕,可怕的是出错了,你却不知道错误在哪里.所以我们需要将应用程序中抛出的所有异常都记录起来,不然出了错,找问题就能要了你的命.下面我们主要讨论的是如何捕捉全局的异常.基本上在winform或web中捕获全局异常的思路都是一样的,在全局的应用程序对象中添加异常捕获的代码,并写入日志文件中. 一.在Winform程序中捕获全局异常 在winfrom中我们需要了解Application对象中的两个事件 ①Application.ThreadExcep

springboot捕获全局异常和配置多数据源

目录 配置多数据源 写两个数据源的配置类. @(springboot捕获全局异常和配置多数据源) 捕获全局异常是在项目运行期间如果调用的某一个方法出现了运行时异常,则会捕获,并且给出回馈. 首先需要建一个包,包里新建一个捕获异常类GlobalExceptionHandler.前提是springboot的启动类的扫描注解ComponentScan()要扫描到. /** * 用于捕获全局异常 */ @ControllerAdvice//控制器切面 public class GlobalExcepti

Android使用UncaughtExceptionHandler捕获全局异常

参考资料: http://blog.csdn.net/hehe9737/article/details/7662123 http://www.apkbus.com/android-128270-1-1.html http://www.cnblogs.com/freeliver54/archive/2011/10/17/2215423.html Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {//

C#捕获全局异常

using System; using System.Collections.Generic; using System.Windows.Forms; using System.IO; namespace GobalException {     static class Program     {         /// <summary>         /// 应用程序的主入口点.         /// </summary>         [STAThread]