XcodeDebug---远程bug报告和分析
来源:互联网 发布:海贼王883知乎 编辑:程序博客网 时间:2024/05/28 23:22
参考
http://blog.csdn.net/daiyelang/article/details/17020211
http://blog.csdn.net/totogo2010/article/details/39892467
http://answerhuang.duapp.com/index.php/2014/07/06/dsym_tool/
在真机中使用app中我们经常会遇到闪退,这类闪退我们是无法看到bug报告的,那我们应该如何处理呢
iOS开发中我们会遇到程序抛出异常退出的情况,如果是在调试的过程中,异常的信息是一目了然,但是如果是在已经发布的程序中,获取异常的信息有时候是比较困难的。
iOS提供了异常发生的处理API,我们在程序启动的时候可以添加这样的Handler,这样的程序发生异常的时候就可以对这一部分的信息进行必要的处理,适时的反馈给开发者。
不足的地方是,并不是所有的程序崩溃都是由于发生可以捕捉的异常的,有些时候是因为内存等一些其他的错误导致程序的崩溃,这样的信息是不在这里体现的。
我做了一个简单的类,进行很基本的操作,可以添加和获取Handler,捕获到异常后将信息写入到app的Documens下的Exception.txt中。
其实还有很多的处理的办法。
1、可以在程序下一次起来的时候读取这个异常文件发生到服务端,或者直接就是在处理代码中用openurl的方式(mailto:)调用发送邮件的方式,将异常信息直接变成邮件发送到指定地址。
2、可以用一些第三方来搜集比如说友盟统计分析
第一种:发到自己的服务端
#pragma mark -#pragma mark Application lifecycle - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [window makeKeyAndVisible]; [NdUncaughtExceptionHandler setDefaultHandler]; NSArray *array = [NSArray arrayWithObject:@"there is only one objective in this arary,call index one, app will crash and throw an exception!"]; NSLog(@"%@", [array objectAtIndex:1]); return YES;} 基本接口展示: #import <Foundation/Foundation.h> @interface NdUncaughtExceptionHandler : NSObject { } + (void)setDefaultHandler;+ (NSUncaughtExceptionHandler*)getHandler; @end//还可以选择设置自定义的handler,让用户取选择 接口实现展示#import "NdUncaughtExceptionHandler.h" NSString *applicationDocumentsDirectory() { return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];} void UncaughtExceptionHandler(NSException *exception) { NSArray *arr = [exception callStackSymbols]; NSString *reason = [exception reason]; NSString *name = [exception name]; NSString *url = [NSString stringWithFormat:@"=============异常崩溃报告=============\nname:\n%@\nreason:\n%@\ncallStackSymbols:\n%@", name,reason,[arr componentsJoinedByString:@"\n"]]; NSString *path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"]; [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil]; //除了可以选择写到应用下的某个文件,通过后续处理将信息发送到服务器等 //还可以选择调用发送邮件的的程序,发送信息到指定的邮件地址 //或者调用某个处理程序来处理这个信息} @implementation NdUncaughtExceptionHandler -(NSString *)applicationDocumentsDirectory { return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];} + (void)setDefaultHandler{ NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);} + (NSUncaughtExceptionHandler*)getHandler{ return NSGetUncaughtExceptionHandler();} @end
异常崩溃报告:=============异常崩溃报告=============name:NSRangeExceptionreason:*** -[NSArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]callStackSymbols:0 CoreFoundation 0x02393919 __exceptionPreprocess + 1851 libobjc.A.dylib 0x024e15de objc_exception_throw + 472 CoreFoundation 0x0238958c -[__NSArrayI objectAtIndex:] + 2363 UncaughtE 0x000022e8 -[UncaughtEAppDelegate application:didFinishLaunchingWithOptions:] + 1574 UIKit 0x002b8543 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 11635 UIKit 0x002ba9a1 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 3466 UIKit 0x002c4452 -[UIApplication handleEvent:withNewEvent:] + 19587 UIKit 0x002bd074 -[UIApplication sendEvent:] + 718 UIKit 0x002c1ac4 _UIApplicationHandleEvent + 74959 GraphicsServices 0x02bf9afa PurpleEventCallback + 157810 CoreFoundation 0x02374dc4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 5211 CoreFoundation 0x022d5737 __CFRunLoopDoSource1 + 21512 CoreFoundation 0x022d29c3 __CFRunLoopRun + 97913 CoreFoundation 0x022d2280 CFRunLoopRunSpecific + 20814 CoreFoundation 0x022d21a1 CFRunLoopRunInMode + 9715 UIKit 0x002ba226 -[UIApplication _run] + 62516 UIKit 0x002c5b58 UIApplicationMain + 116017 UncaughtE 0x00002228 main + 10218 UncaughtE 0x000021b9 start + 53不足的地方是,并不是所有的程序崩溃都是由于发生可以捕捉的异常的,有些时候引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。首先定义一个UncaughtExceptionHandler类,.h头文件的代码如下:
UncaughtExceptionHandler类,.h头文件的代码如下:@interface UncaughtExceptionHandler : NSObject{BOOL dismissed;}@end1void InstallUncaughtExceptionHandler();然后在.mm文件实现InstallUncaughtExceptionHandler(),如下:void InstallUncaughtExceptionHandler(){signal(SIGABRT, MySignalHandler);signal(SIGILL, MySignalHandler);signal(SIGSEGV, MySignalHandler);signal(SIGFPE, MySignalHandler);signal(SIGBUS, MySignalHandler);signal(SIGPIPE, MySignalHandler);}这样,当应用发生错误而产生上述Signal后,就将会进入我们自定义的回调函数MySignalHandler。为了得到崩溃时的现场信息,还可以加入一些获取CallTrace及设备信息的代码,.mm文件的完整代码如下:#import "UncaughtExceptionHandler.h"#include #include NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName"; NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey"; NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey"; volatile int32_t UncaughtExceptionCount = 0; const int32_t UncaughtExceptionMaximum = 10; const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4; const NSInteger UncaughtExceptionHandlerReportAddressCount = 5; @implementation UncaughtExceptionHandler + (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 = UncaughtExceptionHandlerSkipAddressCount; i < UncaughtExceptionHandlerSkipAddressCount + UncaughtExceptionHandlerReportAddressCount; i++) { [backtrace addObject:[NSString stringWithUTF8String:strs[i]]]; } free(strs); return backtrace; } - (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex { if (anIndex == 0) { dismissed = YES; } } - (void)handleException:(NSException *)exception { 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%@", nil), [exception reason], [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]] delegate:self cancelButtonTitle:NSLocalizedString(@"Quit", nil) otherButtonTitles:NSLocalizedString(@"Continue", nil), nil] autorelease]; [alert show]; 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:UncaughtExceptionHandlerSignalExceptionName]) { <span style="white-space:pre"></span>kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]); } else { [exception raise]; }}@end NSString* getAppInfo() { NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\nUDID : %@\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"], [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"], [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"], [UIDevice currentDevice].model, [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion, [UIDevice currentDevice].uniqueIdentifier]; NSLog(@"Crash!!!! %@", appInfo); return appInfo; } void MySignalHandler(int signal) { int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); if (exceptionCount > UncaughtExceptionMaximum){return;} NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey]; NSArray *callStack = [UncaughtExceptionHandler backtrace]; [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey]; [[[[UncaughtExceptionHandler alloc] init] autorelease] performSelectorOnMainThread:@selector(handleException:) withObject: [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason: [NSString stringWithFormat: NSLocalizedString(@"Signal %d was raised.\n" @"%@", nil), signal, getAppInfo()] userInfo: [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey]] waitUntilDone:YES]; } void InstallUncaughtExceptionHandler() { signal(SIGABRT, MySignalHandler); signal(SIGILL, MySignalHandler); signal(SIGSEGV, MySignalHandler); signal(SIGFPE, MySignalHandler); signal(SIGBUS, MySignalHandler); signal(SIGPIPE, MySignalHandler); }在应用自身的 didFinishLaunchingWithOptions 前,加入一个函数:- (void)installUncaughtExceptionHandler{InstallUncaughtExceptionHandler();}最后,在 didFinishLaunchingWithOptions 中加入这一句代码就行了:1[self InstallUncaughtExceptionHandler];
这样在崩溃时还能从容地弹出对话框,比起闪退来,用户也不会觉得那么不爽。然后在下次启动时还可以通过邮件来发送Crash文件到邮箱,这就看各个应用的需求了。
第二种:使用第三方 并分析 bug文件
程序中集成了友盟的统计分析
要分析崩溃日志,首先需要保留发布时的编译出来的.xcarchive文件。这个文件包含了.DSYM文件。
我一般的做法是,发布成功后,把这个文件.xcarchive直接提交到代码版本库对应的版本分支里,这样就不会搞丢了。
这个文件在哪呢?打开XCode->菜单Window->Organizer,在编译成功的文件上右键,就能打开了。
两种比较麻烦的方法。
第一种方法:
使用dwarfdump命令
dwarfdump --uuid xx.app.dSYM 用来得到app的UUID。
dwarfdump --lookup 0x12b45d -arch armv7 xx.app.dSYM 使错误的日志能看懂,把相应的内存地址对应到正确的地方。
如果一开始dwarfdump命令不能用的话,要先装Command Line Tools,这个在设置里面能下载(cmd+“,”打开设置)。另外还必须在进入.DSYM所在文件夹。
使用dwarfdump需要安装Command Line Tools,XCode里设置下载。而且需要进入.DSYM所在文件夹里进行操作。
第二种方法:
使用xcrun atos命令
atos -o YourApp.app.dSYM/Contents/Resources/DWARF/YourApp 0x00062867
下面重点推荐下这个方法,方便快捷
第三方法:可视化工具
下面这是我的项目里通过友盟统计到的崩溃日志,如果光看这些日志报告的话,是不会知道是哪行代码引起的。
使用方法是把对应版本的.xcarchive文件拖到工具。对比UUID和友盟里日志是否一致,一致就把错误的地址信息拷贝到箭头处。点击分析。
即可得出具体代码崩溃位置。很简单吧。
dSYM 文件分析工具 http://answerhuang.duapp.com/index.php/2014/07/06/dsym_tool/
这是这位博主answer-huang开发了一个工具,专门用来快速定位崩溃日志的代码。感谢这位仁兄的提供这么方便的工具。
工具代码还是开源的:https://github.com/answer-huang/dSYMTools
工具下载地址:http://pan.baidu.com/s/1bnkxPvT
或者这里http://download.csdn.net/detail/totogo2010/8012367- XcodeDebug---远程bug报告和分析
- XcodeDebug---Instrument
- “出错了”和报告Bug的艺术
- 报告 Bug
- 报告 Bug
- (翻译)编写优秀Bug报告的艺术及案例分析
- (翻译)编写优秀Bug报告的艺术及案例分析
- 编写优秀Bug报告的艺术及案例分析
- 编写优秀Bug报告的艺术及案例分析
- 编写优秀Bug报告的艺术及案例分析
- 编写优秀Bug报告的艺术及案例分析
- 编写优秀Bug报告的艺术及案例分析
- 编写优秀Bug报告的艺术及案例分析
- 技术文章 | CVE-2017-12615/CVE-2017-12616:Tomcat信息泄漏和远程代码执行漏洞分析报告
- 和通讯录产品分析报告
- xcodedebug和release模式下打包IPA体积差距过大解决
- Struts2 S2 – 032远程代码执行漏洞分析报告
- Struts2 S2 – 032远程代码执行漏洞分析报告 .
- 在centos下编译openJDK---编译环境配置
- hdu 5108 高效筛素数法模板题
- UVA 10347 || Medians(中线组成的三角形面积是原三角形的3/4
- javaBean与Map<String,Object>互转
- JavaBean 和 Map 之间互相转换
- XcodeDebug---远程bug报告和分析
- netfilter(1)
- 一个私有云选型方案
- php高手进阶:实现强大的翻页跳转功能
- android自定义键盘(解决弹出提示的字体颜色问题)
- HTTP协议第二篇
- 先介绍一下在翻页中用到的数据库语法
- AOP 的利器:ASM 3.0 介绍
- 【GoLang笔记】A Tour of Go - Exercise: Web Crawler