转自:http://www.tuicool.com/articles/qQNfUfe

背景介绍

  1. 本文主要内容: 利用android的crash log来对c++开发的android应用进行错误定位.

  2. 容易稳定复现的BUG, 一般可以通过断点调试来解决. 如果测试人员也无法稳定复现, log就成了程序吊定位问题的救命稻草 .

  3. 通用操作系统都有自己的日志系统, android也不例外. 救命稻草已经给你了~ ()

  4. 但是, android的系统日志在c++代码崩溃时, 打印的都是内存地址和寄存器. 比如, 这样:

- ::35.331   I DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- ::35.331 I DEBUG : Build fingerprint: 'google/razorg/deb:4.4.2/KOT49H/937116:user/release-keys'
- ::35.331 I DEBUG : Revision: ''
- ::35.331 I DEBUG : pid: , tid: , name: Thread- >>> com.guangyou.ddgame <<<
- ::35.331 I DEBUG : signal (SIGSEGV), code (SEGV_MAPERR), fault addr
- ::35.431 D audio_hw_primary: out_set_parameters: enter: usecase(: deep-buffer-playback) kvpairs: routing=
- ::35.511 I DEBUG : r0 76d94458 r1 r2 r3
- ::35.511 I DEBUG : r4 760c1a48 r5 751e2440 r6 r7 760c1a48
- ::35.511 I DEBUG : r8 r9 76c96f3c sl 76c861c0 fp 76d94444
- ::35.511 I DEBUG : ip sp 76d94430 lr 75a81bd8 pc 75a81bdc cpsr 600f0010
- ::35.511 I DEBUG : d0 746968775f327865 d1 6a6e6169642f675f
- ::35.511 I DEBUG : d2 5f6f616978757169 d3 676e702e6e776f6d
- ::35.511 I DEBUG : d4 d5
- ::35.511 I DEBUG : d6 d7

这密密麻麻的都是些神马, 是人看的么?

饿. 这个麻… 谁让你当程序猿! 让你当! 活该要看天书!

硬着头皮也要来, 我们就来讲讲怎么消化天书吧~

怎样获取android的系统日志

假设你已经安装了 Android Develop Tools, 可以成功调用 adb . 并打开android开发用机的调试模式, 连接到电脑.

打开命令行, 在命令行输入: adb logcat . 就可以看到满屏幕的日志啦.

输入 adb logcat --help 可以看到 logcat 的用法提示.

这里有两个参数特别提醒一下, 比较常用:

1. -v XXXX : 用来选择log输出样式, 一般建议 threadtime , 更加详细.

2. -d : 让log一次性输出后马上完毕. 如果没有此命令, logcat 工具会一直输出, 即使更新在界面上.

如果需要保存log到文件, 方便以后查看. 可输入命令:

adb logcat -v threadtime -d > log.txt

理解NDK的crash log

如果你用c++开发的android应用在运行过程中, c++代码发生错误导致程序崩溃, 系统就会记录 crash log到上述的系统日志中.

下面是我正在开发的游戏一次崩溃后, 截取的日志

- ::35.331   I DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- ::35.331 I DEBUG : Build fingerprint: 'google/razorg/deb:4.4.2/KOT49H/937116:user/release-keys'
- ::35.331 I DEBUG : Revision: ''
- ::35.331 I DEBUG : pid: , tid: , name: Thread- >>> com.guangyou.ddgame <<<
- ::35.331 I DEBUG : signal (SIGSEGV), code (SEGV_MAPERR), fault addr
- ::35.431 D audio_hw_primary: out_set_parameters: enter: usecase(: deep-buffer-playback) kvpairs: routing=
- ::35.511 I DEBUG : r0 76d94458 r1 r2 r3
- ::35.511 I DEBUG : r4 760c1a48 r5 751e2440 r6 r7 760c1a48
- ::35.511 I DEBUG : r8 r9 76c96f3c sl 76c861c0 fp 76d94444
- ::35.511 I DEBUG : ip sp 76d94430 lr 75a81bd8 pc 75a81bdc cpsr 600f0010
- ::35.511 I DEBUG : d0 746968775f327865 d1 6a6e6169642f675f
- ::35.511 I DEBUG : d2 5f6f616978757169 d3 676e702e6e776f6d
- ::35.511 I DEBUG : d4 d5
- ::35.511 I DEBUG : d6 d7
- ::35.511 I DEBUG : d8 d9
- ::35.511 I DEBUG : d10 d11
- ::35.511 I DEBUG : d12 d13
- ::35.511 I DEBUG : d14 d15
- ::35.511 I DEBUG : d16 c3c3c3c3c3c3c3c3 d17 c3c3c3c3c3c3c3c3
- ::35.511 I DEBUG : d18 41c7ddc227000000 d19 3ff0000000000000
- ::35.511 I DEBUG : d20 3f811110896efbb2 d21 3fd7096611460fdb
- ::35.511 I DEBUG : d22 c0176a8ee0000000 d23 bfc5230c760b0605
- ::35.511 I DEBUG : d24 d25 3fc7922925a107e2
- ::35.511 I DEBUG : d26 3fdaa0f8fab43e33 d27 3fb43ad076b251ab
- ::35.511 I DEBUG : d28 3fa15cb6bdc3c156 d29 3ec6cd878c3b46a7
- ::35.511 I DEBUG : d30 3f65f3b6b9b97e01 d31 3ef99342e0ee5069
- ::35.511 I DEBUG : scr
- ::35.511 I DEBUG :
- ::35.511 I DEBUG : backtrace:
- ::35.511 I DEBUG : # pc 0089cbdc /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocos2d::Texture2D::getContentSize() const+)
- ::35.511 I DEBUG : # pc 0088f8dc /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocos2d::Sprite::setTexture(std::string const&)+)
- ::35.511 I DEBUG : # pc 007863dc /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocos2d::ui::Button::loadTextureDisabled(std::string const&, cocos2d::ui::Widget::TextureResType)+)
- ::35.511 I DEBUG :
- ::35.511 I DEBUG : stack:
- ::35.511 I DEBUG : 76d943f0
- ::35.511 I DEBUG : 76d943f4 4006bc0d /system/lib/libc.so (free+)
- ::35.511 I DEBUG : 76d943f8 76a72c54
- ::35.511 I DEBUG : 76d943fc 75eca614 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so
- ::35.511 I DEBUG : 76d94400 751c23c8 [anon:libc_malloc]
- ::35.511 I DEBUG : 76d94404 751c23c8 [anon:libc_malloc]
- ::35.511 I DEBUG : 76d94408 751c23c8 [anon:libc_malloc]
- ::35.511 I DEBUG : 76d9440c 75a749b4 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocos2d::Sprite::setTexture(cocos2d::Texture2D*)+)
- ::35.511 I DEBUG : 76d94410 0000003d
- ::35.511 I DEBUG : 76d94414 00e8efc8
- ::35.511 I DEBUG : 76d94418
- ::35.511 I DEBUG : 76d9441c
- ::35.511 I DEBUG : 76d94420
- ::35.511 I DEBUG : 76d94424 76d94458 [stack:]
- ::35.511 I DEBUG : 76d94428
- ::35.511 I DEBUG : 76d9442c 76d94444 [stack:]
- ::35.511 I DEBUG : # 76d94430
- ::35.511 I DEBUG : 76d94434 76d94458 [stack:]
- ::35.511 I DEBUG : 76d94438 76a66184
- ::35.511 I DEBUG : 76d9443c 760c1a48 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so
- ::35.511 I DEBUG : 76d94440 76d9447c [stack:]
- ::35.511 I DEBUG : 76d94444 75a748e0 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocos2d::Sprite::setTexture(std::string const&)+)
- ::35.511 I DEBUG : # 76d94448 76d944ec [stack:]
- ::35.511 I DEBUG : 76d9444c 793ff0e8 [anon:libc_malloc]
- ::35.511 I DEBUG : 76d94450 76a72c54
- ::35.511 I DEBUG : 76d94454
- ::35.511 I DEBUG : 76d94458
- ::35.511 I DEBUG : 76d9445c
- ::35.511 I DEBUG : 76d94460
- ::35.511 I DEBUG : 76d94464
- ::35.511 I DEBUG : 76d94468
- ::35.511 I DEBUG : 76d9446c
- ::35.521 I DEBUG : 76d94470 7b91dcf8 [anon:libc_malloc]
- ::35.521 I DEBUG : 76d94474 78ce6c50 [anon:libc_malloc]
- ::35.521 I DEBUG : 76d94478 76d944b4 [stack:]
- ::35.521 I DEBUG : 76d9447c 7596b3e0 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocos2d::ui::Button::loadTextureDisabled(std::string const&, cocos2d::ui::Widget::TextureResType)+)
- ::35.521 I DEBUG : # 76d94480
- ::35.521 I DEBUG : 76d94484
- ::35.521 I DEBUG : 76d94488 76d944ec [stack:]
- ::35.521 I DEBUG : 76d9448c 793fe780 [anon:libc_malloc]
- ::35.521 I DEBUG : 76d94490 76d944f0 [stack:]
- ::35.521 I DEBUG : 76d94494 793ff0e8 [anon:libc_malloc]
- ::35.521 I DEBUG : 76d94498
- ::35.521 I DEBUG : 76d9449c 4006bc0d /system/lib/libc.so (free+)
- ::35.521 I DEBUG : 76d944a0 76a72c54
- ::35.521 I DEBUG : 76d944a4 75eca614 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so
- ::35.521 I DEBUG : 76d944a8 78ce6c50 [anon:libc_malloc]
- ::35.521 I DEBUG : 76d944ac 78ce6c50 [anon:libc_malloc]
- ::35.521 I DEBUG : 76d944b0 76d9455c [stack:]
- ::35.521 I DEBUG : 76d944b4 75924e54 /data/app-lib/com.guangyou.ddgame-/libcocos2dcpp.so (cocostudio::ButtonReader::setPropsFromJsonDictionary(cocos2d::ui::Widget*, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> > const&)+)
- ::35.521 I DEBUG : 76d944b8
- ::35.521 I DEBUG : 76d944bc 78ce6c50 [anon:libc_malloc]
- ::35.521 I DEBUG :
- ::35.521 I DEBUG : memory near r0:
- ::35.521 I DEBUG : 76d94438 76a66184 760c1a48 76d9447c 75a748e0
- ::35.521 I DEBUG : 76d94448 76d944ec 793ff0e8 76a72c54
...
- ::35.521 I DEBUG :
- ::35.521 I DEBUG : memory near r4:
- ::35.521 I DEBUG : 760c1a28 760811c8 75ee318c 75ee3194 75ee319c
- ::35.521 I DEBUG : 760c1a38 4006d091 75f9a1f4 75f4ee5c 75e8ea0c
...

下面来逐行解读:

1. ndk crash log以 *** *** *** *** *** 开始.

2. 第一行 Build fingerprint: 'google/razorg/deb:4.4.2/KOT49H/937116:user/release-keys' 指明了运行的Android版本, 如果您有多份crash dump的话这个信息就比较有用了.

3. 接着一行显示的是当前的线程id(pid)和进程id(tid). 如果当前崩溃的线程是主线程的话, pid和tid会是一样的~

4. 第四行, 显示的是unix信号. 这里的 signal 11 , 即 SIGSEGV , 表示段错误, 是最常见的信号.( 什么是unix信号 , 什么是 SIGSEGV)

5. 接下来的部分是系统寄存器的dump信息.

  • rX( X=[0~9] ): 代表整数寄存器
  • dX( X=[0~31]): 是浮点指针寄存器
  • fp (or r11) : 指向当前正在执行的函数的堆栈底.
  • ip (or r12) : 一个寄存器, 我也没弄明白是干啥的.
  • sp (or r13) : 当前正在执行的函数的堆栈顶.(跟fp相对应)
  • lr (or r14) : link register . 简单来说, 当当前指令执行完了, 就会从这个寄存器获取地址, 来知道需要返回到哪里继续执行.
  • pc (or r15) : program counter. 存放下一条指令的地址.
  • cpsr : Current Program Status Register. 表示当前运行环境和状态的一些字节位.

    1. Crash dump还包含PC之前和之后的一些内存字段.
    2. 最后, 是崩溃时的调用堆栈. 如果你执行的是debug版本, 还能还原一些c++代码.

利用ndk-stack定位崩溃代码

上面的一些信息能简单的帮你定位以下问题. 如果信息量还不够大的话, 那就还有最后一招: 还原历史.

Android NDK 自从版本R6开始, 提供了一个工具 ndk-stack ( 在目录 {ndk_root}/ 中 ). 这个工具能自动分析dump下来的crash log, 将崩溃时的调用内存地址和c++代码一行一行对应起来.

我们先看一下用法, 执行命令 ndk-stack --help

Usage:
ndk-stack -sym <path> [-dump <path>] -sym Contains full path to the root directory for symbols.
-dump Contains full path to the file containing the crash dump.
This is an optional parameter. If ommited, ndk-stack will
read input data from stdin
  • -dump参数很容易理解, 即dump下来的log文本文件. ndk-stack 会分析此文件.
  • -sym参数就是你android项目下,编译成功之后, obj 目录下的文件.

下面我们就来示范一下:

$ adb logcat | ndk-stack -sym ./obj/local/armeabi
********** Crash dump: **********
Build fingerprint: 'htc_wwe/htc_bravo/bravo:2.3.3/
GRI40/96875.1:user/release-keys'
pid: , tid: >>> com.packtpub.droidblaster <<<
signal (SIGSEGV), code (SEGV_MAPERR), fault addr 0000000c
Stack frame # pc 00010a2c /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine update in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/TimeService.cpp:
Stack frame # pc 00009fcc /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine onStep in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/DroidBlaster.cpp:
Stack frame # pc 0000a348 /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine run in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/EventLoop.cpp:
Stack frame # pc 0000f994 /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine android_main in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/Main.cpp:
...

熟悉的代码出现啦~~

利用NDK崩溃日志查找BUG的更多相关文章

  1. [cocos2dx]利用NDK崩溃日志查找BUG

    摘要: 在android上开发c++应用, crash日志都是汇编码, 很难对应到c++代码中去. 通过此文, 你可以定位到程序崩溃时的C++代码, 精确查找问题. 博客: http://www.cn ...

  2. iOS开发-应用崩溃日志揭秘(一)

    作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果 ...

  3. 【转】iOS应用崩溃日志分析

    作为一名应用开发者,你是否有过如下经历?   为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! ...

  4. iOS应用崩溃日志分析

    转自raywenderlich   作为一名应用开发者,你是否有过如下经历?   为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应 ...

  5. 【转】iOS应用崩溃日志揭秘

    这篇文章还可以在这里找到 英语 If you're new here, you may want to subscribe to my RSS feed or follow me on Twitter ...

  6. iOS应用崩溃日志揭秘

    这篇文章还可以在这里找到 英语 Learn how to make sense of crash logs! 本文作者是 Soheil Moayedi Azarpour, 他是一名独立iOS开发者. ...

  7. iOS应用崩溃日志分析 iOS应用崩溃日志揭秘

    转自:http://www.raywenderlich.com/zh-hans/30818/ios%E5%BA%94%E7%94%A8%E5%B4%A9%E6%BA%83%E6%97%A5%E5%BF ...

  8. iOS崩溃日志 如何看

    日志主要分为六个部分:进程信息.基本信息.异常信息.线程回溯.线程状态和二进制映像. 我们在进行iPhone应用测试时必然会在"隐私"中找到不少应用的崩溃日志,但是不会阅读对于很多 ...

  9. iOS开发--应用崩溃日志揭秘(二)

    场景 4: 吃棒棒糖时闪退! 用户邮件说, “当rage master吃棒棒糖时应用就闪退…” 另一用户说, “我让rage master 吃棒棒糖,没几次应用就闪退了!”崩溃日志如下: Incide ...

随机推荐

  1. C#如何遍历数组?

    // 一维数组 int[] arr = { 1, 2, 3, 4, 5 }; foreach (int i in arr) { Console.WriteLine(i.ToString() + &qu ...

  2. 深入Asyncio(十一)优雅地开始与结束

    Startup and Shutdown Graceful 大部分基于asyncio的程序都是需要长期运行.基于网络的应用,处理这种应用的正确开启与关闭存在惊人的复杂性. 开启相对来说更简单点,常规做 ...

  3. Vue中表单校验

    1.安装校验插件vee-validate npm install vee-validate --save 2.在main.js中引用插件 // 表单校验 import VeeValidate, { V ...

  4. 九度OJ 1013:开门人和关门人 (排序)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5052 解决:2563 题目描述:     每天第一个到机房的人要把门打开,最后一个离开的人要把门关好.现有一堆杂乱的机房签到.签离记录,请 ...

  5. 【转载】基于注解的SpringMVC简单介绍

    SpringMVC是一个基于DispatcherServlet的MVC框架,每一个请求最先访问的都是DispatcherServlet,DispatcherServlet负责转发每一个Request请 ...

  6. springboot 默认tomcat配置

    1. Spring Boot 能支持的最大并发量主要看其对Tomcat的设置,可以在配置文件中对其进行更改.当在配置文件中敲出max后提示值就是它的默认值. 我们可以看到默认设置中,Tomcat的最大 ...

  7. 重新实践c++primer上面的代码

    又重新敲了敲c++primer上面的代码,觉得很有意思,讲的很细,c++真牛逼啊 #include <iostream> #include <string> #include ...

  8. LightOJ - 1079 Just another Robbery —— 概率、背包

    题目链接:https://vjudge.net/problem/LightOJ-1079 1079 - Just another Robbery    PDF (English) Statistics ...

  9. Mockito @BeforeClass @BeforeMethod @BeforeTest 的生命周期

    @BeforeClass---@AfterClass 类实例化前, 被执行, 主要用于设置环境变量等, 与SpringTestContext结合用的时候要注意, 这种情况下@autowire的bean ...

  10. tkinter之button

    Button按钮,直接上代码: from tkinter import * def gs(): global read s=Label(read,text='昨夜西风凋敝树,堵上高楼,望尽天涯路!', ...