做开发的过程中,调试时遇到什么bug,首先就是要定位bug在哪。此时,如果可以重现这个bug,能把设备接到电脑上调试是最好不过了。
但是,但是能重现的bug一般一眼就能看出来是什么问题好么。最头疼的是,给测试设备上安装了app,然后去给策划或测试试玩,结果他们说程序闪退,但又不能重现出来。。。
现在有办法解决这个问题了。需要如下三个步骤即可:
1.取出或者查看设备上的崩溃日志。
2.分析崩溃日志,找到报错在哪里(定位到函数和代码行数)。
3.打开代码,改bug咯。
先从ios设备来看吧。这里引用一篇近乎完美的博客: iOS应用崩溃日志分析(感谢博主)文章中详细的讲了ios设备查看崩溃日志的过程,以及常见的问题。
总结起来,有三种方式可以查看到ios设备上的崩溃日志。
1.在可以获取到运行app的手机,或者用户配合导出日志的情况下。可以将设备连接到电脑,然后与电脑上的iTunes同步,崩溃日志会同步到电脑上的指定文件夹。(上面博客有指明)
2.在可以直接获取到运行app的手机的情况下。可以将设备连接到电脑,然后打开xcode软件,选择Window->Devices,然后可以在xcode中查看设备上的所有崩溃日志。
3.在app已经上线的情况下,可以通过iTunes Connect获取用户的崩溃日志。
获取崩溃日志之后,就可以进入第二步,日志分析了。这边我也拿一段崩溃日志来分析一下。
//崩溃日志基本信息
Incident Identifier: 6F309552-C289-42F2-B6BB-E54AFCF533C8
CrashReporter Key: df911cdd677cc29c3aa0ee4b050148d32de03fce
Hardware Model: iPad2,5
Process: MyApp-mobile [36063]
Path: /private/var/mobile/Containers/Bundle/Application/8F7E2D1B-E882-476C-A8D7-D1B47C6C66B5/MyApp-mobile.app/MyApp-mobile
Identifier: com.qfl.MyApp
Version: 1.0 (1.0.0)
Code Type: ARM (Native)
Parent Process: launchd [1]
//崩溃时间和系统版本号
Date/Time: 2016-04-06 15:11:24.24 +0800
Launch Time: 2016-04-06 15:11:16.16 +0800
OS Version: iOS 9.0.2 (13A452)
Report Version: 104
//导致崩溃的异常
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000050
Triggered by Thread: 0
Filtered syslog:
None found
//线程回溯
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 ??? 0x00000050 0 + 80
1 MyApp-mobile 0x000bb800 0x4000 + 751616
2 MyApp-mobile 0x004052fc 0x4000 + 4199164
3 MyApp-mobile 0x00509bdc 0x4000 + 5266396
4 MyApp-mobile 0x00509e7c 0x4000 + 5267068
5 MyApp-mobile 0x00507126 0x4000 + 5255462
6 MyApp-mobile 0x00508d24 0x4000 + 5262628
7 MyApp-mobile 0x00508b68 0x4000 + 5262184
8 MyApp-mobile 0x005948d6 0x4000 + 5834966
9 MyApp-mobile 0x00587792 0x4000 + 5781394
10 MyApp-mobile 0x005873a6 0x4000 + 5780390
11 MyApp-mobile 0x00587310 0x4000 + 5780240
12 MyApp-mobile 0x0058b00a 0x4000 + 5795850
13 MyApp-mobile 0x00581438 0x4000 + 5755960
14 MyApp-mobile 0x00581f5a 0x4000 + 5758810
15 MyApp-mobile 0x00581680 0x4000 + 5756544
16 MyApp-mobile 0x005c10fc 0x4000 + 6017276
17 MyApp-mobile 0x005c1296 0x4000 + 6017686
18 MyApp-mobile 0x0059fa98 0x4000 + 5880472
19 UIKit 0x29558cfe -[UIWindow _sendTouchesForEvent:] + 646
20 UIKit 0x29551e56 -[UIWindow sendEvent:] + 642
21 UIKit 0x295237e4 -[UIApplication sendEvent:] + 204
22 UIKit 0x29521fde _UIApplicationHandleEventQueue + 4934
23 CoreFoundation 0x25422c3e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
24 CoreFoundation 0x2542282c __CFRunLoopDoSources0 + 452
25 CoreFoundation 0x25420b9a __CFRunLoopRun + 794
26 CoreFoundation 0x25374248 CFRunLoopRunSpecific + 520
27 CoreFoundation 0x25374034 CFRunLoopRunInMode + 108
28 GraphicsServices 0x2e42bad0 GSEventRunModal + 160
29 UIKit 0x29589898 UIApplicationMain + 144
30 MyApp-mobile 0x000e6556 0x4000 + 927062
31 libdyld.dylib 0x37412872 start + 2
Thread 1:
0 libsystem_kernel.dylib 0x374e292c __workq_kernreturn + 8
1 libsystem_pthread.dylib 0x37582b50 _pthread_wqthread + 1036
2 libsystem_pthread.dylib 0x37582734 start_wqthread + 8
Thread 2 name: Dispatch queue: com.apple.libdispatch-manager
Thread 2:
0 libsystem_kernel.dylib 0x374e33c0 kevent_qos + 24
1 libdispatch.dylib 0x373dd1c8 _dispatch_mgr_invoke + 256
2 libdispatch.dylib 0x373dcf26 _dispatch_mgr_thread$VARIANT$mp + 38
//线程回溯最后面还有二进制信息
Binary Images:
虽然崩溃日志是看到了,通过异常信息也能知道是什么原因。但是并不能定位到我们自己的代码啊。
上面可以看到,关于我们自己的代码 MyApp开头的行,我们只能看到一些符号。并没有指定到某个函数,某一行。其实,这个崩溃日志是没有经过符号化的,所以我看不懂。
这里符号化是有两种方式的:
1.在xcode中查看崩溃日志,xcode会自动将日志符号化。
2.通过终端命令,手动符号化。
这两种方式都需要dSYM文件。这里需要查看一下项目的Build Settigns->Build Options->Debug information Format属性。只有该属性设置为DWARF with dSYM File时,编译才会生成dSYM文件。如果忘了设置,就重新设置一下,然后重新编译工程(Clean然后再Build)即可。dSYM文件和app包是对应的,如果不对应,可能无法符号化日志,所以切记在打包app提交测试时,要保存对应版本的dSYM文件。
如果已经设置好了可以在
/Users/qfl/Library/Developer/Xcode/DerivedData/MyApp-aerllxbslxqnjhgfeoiagutyemyz/Build/Products/Debug-iphoneos
目录下找到app和dSYM文件。
如果在xcode中查看崩溃日志,xcode会自动将日志符号化。所以这里只看第二种方式了,使用atos命令来符号化崩溃日志。
格式如下:
atos -o dysm 文件路径 -l 模块load地址(后面的一个16进制地址) -arch cpu指令集 调用方法的地址(前面一个16进制地址)
大家可以从这里看示例:iOS Crash 分析(文三)- 符号化崩溃日志 (感谢博主)
现在直接可以看到符号化后的内容,就可以到自己的代码中找到报错的地方了。
这边还有一个需要注意的地方。
例子1:
Date/Time: 2016-04-06 15:02:22.22 +0800
Launch Time: 2016-04-06 15:02:17.17 +0800
OS Version: iOS 9.0.2 (13A452)
Report Version: 104
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000050
Triggered by Thread: 0
Filtered syslog:
None found
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 ??? 0x00000050 0 + 80
1 MyApp-mobile 0x00102800 std::__1::__function::__func, void (cocos2d::Ref*)>::operator()(cocos2d::Ref*&&) (functional:1437)
2 MyApp-mobile 0x0044c2fc std::__1::function::operator()(cocos2d::Ref*) const (functional:1817)
3 MyApp-mobile 0x00550bdc cocos2d::MenuItem::activate() (CCMenuItem.cpp:105)
4 MyApp-mobile 0x00550e7c cocos2d::MenuItemLabel::activate() (CCMenuItem.cpp:241)
5 MyApp-mobile 0x0054e126 cocos2d::Menu::onTouchEnded(cocos2d::Touch*, cocos2d::Event*) (CCMenu.cpp:290)
6
从上面的信息看,指明了是MYTest类中的addUI()方法中报错了,但是没有指定到行。
我们可以从$_52看出,这里表示是第52个(从0开始)表达式中报错。
这个是我写的测试类,总共有52个表达式(一个屏蔽触摸,一个关闭测试页面,然后前面还有50个测试按钮。。。所以从0开始算,这里刚好是第52个表达式)
这是一个比较奇特的情况,一般写在函数中报错时,会看到很正常的某个函数(第n行)的报错。所以这里要注意一下。