问题 堆栈跟踪在到达我的代码之前停止(在使用NDK的Android上)


我正在使用NDK r5b在Android 2.3.x上进行开发。偶尔我的代码会崩溃,我想知道在哪里。当我有一个指针(即来自Android的堆栈跟踪)时,我已经知道如何在我的应用程序中获取相应的行。

但是,我经常看到像这样无用的堆栈跟踪(完整堆栈跟踪):

     #00  pc 0006561a  /system/lib/egl/libGLESv2_adreno200.so
     #01  pc 0006b900  /system/lib/egl/libGLESv2_adreno200.so
     #02  pc 0005aac8  /system/lib/egl/libGLESv2_adreno200.so
     #03  pc 0001687a  /system/lib/egl/libGLESv1_CM_adreno200.so
     #04  pc 000096ce  /system/lib/egl/libGLESv1_CM_adreno200.so

或这个:

(gdb) bt
#0  0xafd0c51c in epoll_wait () from /Volumes/SecureCode/webos/rta/android/obj/local/armeabi/libc.so
#1  0xa81216a6 in ?? ()

甚至根本没有提到我的代码。

有没有办法获得比这更好的堆栈跟踪?为什么某些库函数“不透明”,因为它们不允许回溯“透视”到调用函数,导致堆栈跟踪中的停止?

据我所知,调试这样的问题的唯一方法是在程序中的每个点使用日志记录和/或使用gdb逐步执行每一行。

是否有ROM可用于这些Android库的调试版本而不是运行时库,这会有帮助吗? (我只使用一部手机进行开发,所以我不担心保持全部功能。)(其实,我注意到了这条路 libc.so 在上面 gdb 堆栈跟踪在我的应用程序目录中。我可以用不同的方法打包它(调试) libc.so,那会有帮助吗?)

最后一件事可能有所帮助:在logcat(第一个)的上述堆栈跟踪中,我的库在原始堆栈转储中被提及:

stack:
  ...
  ...
  4471cb88  00000028  
  4471cb8c  afd4649c  
  4471cb90  80b4eb71  /data/data/com.audia.dev.rta/lib/librta.so
  4471cb94  00299180  
  ...
  ...

但那不是函数指针。可能是什么,在应用程序崩溃后会有任何帮助吗?我猜它可能不是,如果它是一个堆指针或类似的东西。


4792
2018-06-15 06:19


起源



答案:


有没有办法获得比这更好的堆栈跟踪?

据我所知,您必须自己构建和编写Android映像。它使您可以拥有Android(可执行文件和共享库)的完整符号,但专有共享库除外。

它还提供使用gdb使用符号。

$ adb shell setprop debug.db.uid 32767
$ adb forward tcp:5039 tcp:5039

/*
 program terminated and debuggerd caught exception like the following.
 Use the PID number for gdbclient 3rd parameter.
 I/DEBUG   ( 2154): ******************************************************** 
 I/DEBUG   ( 2154): * Process 2508 has been suspended while crashing.  To
 I/DEBUG   ( 2154): * attach gdbserver for a gdb connection on port 5039:
 I/DEBUG   ( 2154): *
 I/DEBUG   ( 2154): *     adb shell gdbserver :5039 --attach 2508 &
 I/DEBUG   ( 2154): *
 I/DEBUG   ( 2154): * Press HOME key to let the process continue crashing.
 I/DEBUG   ( 2154): ********************************************************)
*/

$ gdbclient "" "" 2508

编辑:

您仍然可以使用ndk-gdb而不是gdbclient命令。请指定共享库的符号文件。

(gdb) set solib-search-path (ANDROID_SOURCE_PATH)/out/target/product/(PRODUCT_NAME)/symbols/system/lib

编辑2:

如果您不需要Android系统共享库的符号,只需adb pull共享库并将sollib-search-path设置为它。

$ adb pull /system/lib lib

$ ndk-gdb
...
(gdb) set solib-search-path lib

6
2018-06-27 06:07



听起来很有希望。数字32767和2508有什么意义? - tmandry
“setprop debug.db.uid 32767”表示当进程uid低于指定的数量(UID)时,debuggerd等待用户操作。 'gdbclient“”“2508”表示gdbserver将进程ID作为调试器的消息附加到进程ID为2508(PID)的进程。 - Kazuki Sakamoto
谢谢。所以大部分都可以在不自己编译Android的情况下完成,但一个好处是你可以在启用调试的情况下编译Android库并获取符号吗? - tmandry
更新了我的答案,只是跟踪没有符号的堆栈。 - Kazuki Sakamoto
好的,这是有道理的。我可以看到这非常有帮助。注意:在任一用例中,您还应该添加包含项目库的目录 solib-search-path (即根据 obj/local/armeabi),这是使用时自动发生的事情 ndk-gdb。 - tmandry


答案:


有没有办法获得比这更好的堆栈跟踪?

据我所知,您必须自己构建和编写Android映像。它使您可以拥有Android(可执行文件和共享库)的完整符号,但专有共享库除外。

它还提供使用gdb使用符号。

$ adb shell setprop debug.db.uid 32767
$ adb forward tcp:5039 tcp:5039

/*
 program terminated and debuggerd caught exception like the following.
 Use the PID number for gdbclient 3rd parameter.
 I/DEBUG   ( 2154): ******************************************************** 
 I/DEBUG   ( 2154): * Process 2508 has been suspended while crashing.  To
 I/DEBUG   ( 2154): * attach gdbserver for a gdb connection on port 5039:
 I/DEBUG   ( 2154): *
 I/DEBUG   ( 2154): *     adb shell gdbserver :5039 --attach 2508 &
 I/DEBUG   ( 2154): *
 I/DEBUG   ( 2154): * Press HOME key to let the process continue crashing.
 I/DEBUG   ( 2154): ********************************************************)
*/

$ gdbclient "" "" 2508

编辑:

您仍然可以使用ndk-gdb而不是gdbclient命令。请指定共享库的符号文件。

(gdb) set solib-search-path (ANDROID_SOURCE_PATH)/out/target/product/(PRODUCT_NAME)/symbols/system/lib

编辑2:

如果您不需要Android系统共享库的符号,只需adb pull共享库并将sollib-search-path设置为它。

$ adb pull /system/lib lib

$ ndk-gdb
...
(gdb) set solib-search-path lib

6
2018-06-27 06:07



听起来很有希望。数字32767和2508有什么意义? - tmandry
“setprop debug.db.uid 32767”表示当进程uid低于指定的数量(UID)时,debuggerd等待用户操作。 'gdbclient“”“2508”表示gdbserver将进程ID作为调试器的消息附加到进程ID为2508(PID)的进程。 - Kazuki Sakamoto
谢谢。所以大部分都可以在不自己编译Android的情况下完成,但一个好处是你可以在启用调试的情况下编译Android库并获取符号吗? - tmandry
更新了我的答案,只是跟踪没有符号的堆栈。 - Kazuki Sakamoto
好的,这是有道理的。我可以看到这非常有帮助。注意:在任一用例中,您还应该添加包含项目库的目录 solib-search-path (即根据 obj/local/armeabi),这是使用时自动发生的事情 ndk-gdb。 - tmandry


几个笔记:

  • 在某些情况下,堆栈跟踪可能会被损坏,因为您的堆栈已被部分删除。不太可能。
  • 你用的是什么操作系统?姜饼(Android 2.3)是 许多 在堆栈跟踪方面更好。如果您没有运行Android 2.3,请在某处找到适合您手机的Android 2.3 ROM,或者购买一台运行2.3的廉价开发手机。
  • 你见过吗? Onur的剧本?它对我来说非常好用,即使在Android 2.2手机上也是如此。
  • 希望fadden正在读这个,我相信他的答案比我的更有帮助。

4
2018-06-15 06:33



我正在运行姜饼,是的。我会看一下这个剧本,但除了用它打开库之外,它还可以做些什么 gdb 并运行 list *0xabcd1234 能够? (这可能更方便,是的,但我真正想要的是获取我目前可以获得的更多信息的方法。) - tmandry
出于好奇:你有没有尝试过上述建议?我使用类似的方法(更少脚本,但仍使用addr2line)从这些堆栈跟踪中获取函数名称,模块名称和行号。 - jimkberry
哦,以及最初对我来说不明显的事情:如果您使用的是NDK构建脚本,请不要在lib / armeabi中对.so文件运行addr2line,因为它们被“install”命令剥离,并将它们放在那里所以无法告诉你任何事情。请使用obj / local / armeabi中的文件。 - jimkberry
addr2line的问题是它需要一个地址,而一些堆栈跟踪(如上所述)只是不在代码中给出一个地址。对于堆栈跟踪,我通常没有问题得到我需要的。 - tmandry


看看以下问题: 当我的gcc C ++应用程序崩溃时如何生成堆栈跟踪

我们对我们的Android应用程序做了同样的事情:我们编写了自己的信号处理程序,处理信号7(sigbus)和11(sigsegv)并从处理程序打印出堆栈tace。 我们没有使用backtrace()函数,但手动展开堆栈...

结合前两个答案,您应该能够编写自己的信号处理程序来转储堆栈跟踪。本文也可以帮助您: http://www.ibm.com/developerworks/power/library/l-sigdebug/index.html。 请记住,提取寄存器内容取决于体系结构,因此您必须将上述代码中使用的结构替换为android上的结构(取决于ARM处理器)。例如,我不得不深入研究'struct ucontext'的Android源代码。

如果有堆栈跟踪,请在输出上运行脚本,该脚本将使用addr2line和未提取的可执行文件解析符号。


1
2018-06-27 14:14



这有助于在应用程序投入生产时检索和发送堆栈跟踪,但它实际上是否提供了比默认Android堆栈跟踪更丰富的堆栈跟踪? - tmandry
是的,我相信了。您将获得应用程序的完整本机堆栈跟踪。默认的Android堆栈跟踪包含Java堆栈,并在本机堆栈跟踪的第一个条目处停止(至少我们确实经历过)。您在问题中提到的堆栈跟踪看起来像各种系统范围的跟踪 - bxantus
不,大多数堆栈跟踪都会进入应用程序本身,因为错误发生在本机端(这是大多数应用程序所在的位置。)它们总是由应用程序引起,但堆栈跟踪通常会在它到达之前停止我的.so,因此问题。 - tmandry


很抱歉用自己的问题充斥自己的问题,但我确实发现了整合 谷歌Breakpad 获得良好的堆栈跟踪/崩溃报告是一种很好的方法。编写一个调用Breakpad的信号处理程序很容易,它可以处理所有事情;我们只需要将报告上传到我们的服务器。我们还整合了呼叫过程 stackwalk.sh 进入我们的构建系统。它需要一些工作,但总而言之,它非常适合在Android上获得良好的本机崩溃报告。

这个答案 有关于编写信号处理程序的一些细节;您需要的其余代码位于维基下的Google Breakpad网站上。


1
2017-11-18 02:05





我确实得到了这个问题的答案 一些 堆栈跟踪。 (从这个问题的外观可能是我得到的全部。)这些是以一个终止的 lr (链接寄存器)地址。看我的 其他问题/答案


0
2018-06-27 01:05