【打印调用栈】
(gdb) bt
#0 epoll_wait () at bionic/libc/arch-arm/syscalls/epoll_wait.S:10
#1 0x401275ea in android::Looper::pollInner (this=this@entry=0x747fd3f0, timeoutMillis=<optimized out>, timeoutMillis@entry=85688516) at system/core/libutils/Looper.cpp:223
#2 0x40127814 in android::Looper::pollOnce (this=0x747fd3f0, timeoutMillis=85688516, outFd=outFd@entry=0x0, outEvents=outEvents@entry=0x0, outData=outData@entry=0x0) at system/core/libutils/Looper.cpp:191
#3 0x401d13dc in pollOnce (timeoutMillis=<optimized out>, this=<optimized out>) at system/core/include/utils/Looper.h:176
#4 android::NativeMessageQueue::pollOnce (this=0x747fef58, env=0x4151dfa8, timeoutMillis=<optimized out>) at frameworks/base/core/jni/android_os_MessageQueue.cpp:97
#5 0x4153d310 in dvmPlatformInvoke () at dalvik/vm/arch/arm/CallEABI.S:258
#6 0x4156d8de in dvmCallJNIMethod (args=0x6d5a5e18, pResult=0x4151f568, method=0x6d60e2d8, self=0x4151f558) at dalvik/vm/Jni.cpp:1159
#7 0x41546724 in dalvik_mterp () at dalvik/vm/mterp/out/InterpAsm-armv7-a-neon.S:16240
【切换到调用栈的第n层】
(gdb) f 11
#11 0x41587ff6 in Dalvik_java_lang_reflect_Method_invokeNative (args=<optimized out>, pResult=0x4151f568) at dalvik/vm/native/java_lang_reflect_Method.cpp:101
101 noAccessCheck);
【显示汇编代码】
(gdb) disassemble
Dump of assembler code for function Dalvik_java_lang_reflect_Method_invokeNative(u4 const*, JValue*):
0x41587f7c <+0>: stmdb sp!, {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, lr}
0x41587f80 <+4>: add.w r4, r0, #12
0x41587f84 <+8>: ldmia.w r4, {r4, r8, r9}
0x41587f88 <+12>: mov r5, r0
0x41587f8a <+14>: mov r11, r1
0x41587f8c <+16>: ldr r6, [r0, #4]
0x41587f8e <+18>: ldr r7, [r0, #8]
0x41587f90 <+20>: ldr.w r10, [r0, #28]
0x41587f94 <+24>: ldr r1, [r5, #24]
0x41587f96 <+26>: mov r0, r4
0x41587f98 <+28>: bl 0x4158e828 <dvmSlotToMethod(ClassObject*, int)>
0x41587f9c <+32>: ldr r3, [r0, #4]
...
(gdb) disassemble 0x401b406c
Dump of assembler code for function _JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...):
0x401b4058 <+0>: push {r2, r3}
0x401b405a <+2>: push {r0, r1, r4, lr}
0x401b405c <+4>: add r3, sp, #16
0x401b405e <+6>: ldr r4, [r0, #0]
0x401b4060 <+8>: ldr.w r2, [r3], #4
0x401b4064 <+12>: ldr.w r4, [r4, #568] ; 0x238
0x401b4068 <+16>: str r3, [sp, #4]
0x401b406a <+18>: blx r4
0x401b406c <+20>: ldmia.w sp!, {r2, r3, r4, lr}
0x401b4070 <+24>: add sp, #8
0x401b4072 <+26>: bx lr
End of assembler dump.
【查看当前的寄存器值】
(gdb) info reg
r0 0x0 0
r1 0x4151dfa8 1095884712
r2 0x10 16
r3 0x0 0
r4 0x4175c138 1098236216
r5 0x6d68eeb8 1835593400
r6 0x0 0
r7 0x42dc4350 1121731408
r8 0x42dc4278 1121731192
r9 0x416f12a8 1097798312
r10 0x0 0
r11 0x4151f568 1095890280
r12 0xbe88e2d8 3196642008
sp 0xbe88e6c0 0xbe88e6c0
lr 0x41587ff7 1096318967
pc 0x41587ff6 0x41587ff6 <Dalvik_java_lang_reflect_Method_invokeNative(u4 const*, JValue*)+122>
cpsr 0x200f0030 537854000
【显示当前进程的所有线程】
(gdb) info thread
Id Target Id Frame
15 LWP 1992 epoll_wait () at bionic/libc/arch-arm/syscalls/epoll_wait.S:10
14 LWP 2076 epoll_wait () at bionic/libc/arch-arm/syscalls/epoll_wait.S:10
13 LWP 1815 __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
12 LWP 1814 recvmsg () at bionic/libc/arch-arm/syscalls/recvmsg.S:9
11 LWP 1808 __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
10 LWP 1817 __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
9 LWP 1813 __rt_sigtimedwait () at bionic/libc/arch-arm/syscalls/__rt_sigtimedwait.S:10
8 LWP 1819 __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
7 LWP 2062 __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
6 LWP 1818 __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
5 LWP 1826 __ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:9
4 LWP 2320 __ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:9
3 LWP 2210 epoll_wait () at bionic/libc/arch-arm/syscalls/epoll_wait.S:10
2 LWP 1824 __ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:9
* 1 LWP 1804 epoll_wait () at bionic/libc/arch-arm/syscalls/epoll_wait.S:10
【切换线程】
(gdb) t 9
[Switching to thread 9 (LWP 1813)]
#0 __rt_sigtimedwait () at bionic/libc/arch-arm/syscalls/__rt_sigtimedwait.S:10
10 mov r7, ip
(gdb) bt
#0 __rt_sigtimedwait () at bionic/libc/arch-arm/syscalls/__rt_sigtimedwait.S:10
#1 0x400b039c in sigwait (set=<optimized out>, sig=0x7194ad48) at bionic/libc/bionic/sigwait.cpp:43
#2 0x415716ca in signalCatcherThreadStart (arg=<optimized out>) at dalvik/vm/SignalCatcher.cpp:287
#3 0x41574176 in internalThreadStart (arg=0x747fd9d8) at dalvik/vm/Thread.cpp:1746
...
【查看内存值】
(gdb) x /32wx 0x7194ad48
0x7194ad48: 0x00000004 0x6fbf3830 0x415dabd8 0x41573619
0x7194ad58: 0x41700880 0x42dc0768 0x00000005 0x00000001
0x7194ad68: 0x00000001 0x00000000 0x6fbf3830 0x747fd9d8
0x7194ad78: 0x415dabd8 0xbe88e598 0x747fd9d8 0x41574129
0x7194ad88: 0x7184d000 0x415db07c 0x400e92ec 0x41574177
0x7194ad98: 0x747fd9d8 0x00010002 0x747fd9f8 0x41700880
0x7194ada8: 0x7194add0 0x747f85a8 0x41574129 0x400aa1d4
0x7194adb8: 0x747fd9d8 0x747f85a8 0x7194add0 0x00000001
(gdb) x /20c 0xbe88eb48
0xbe88eb48: 47 '/' 115 's' 98 'b' 105 'i' 110 'n' 58 ':' 47 '/' 118 'v'
0xbe88eb50: 101 'e' 110 'n' 100 'd' 111 'o' 114 'r' 47 '/' 98 'b' 105 'i'
0xbe88eb58: 110 'n' 58 ':' 47 '/' 115 's'
【显示符号】
(gdb) p *(Method*)0x6d682328
$1 = {clazz = 0x41755dc0, accessFlags = 9, methodIndex = 0, registersSize = 6, outsSize = 3, insSize = 1, name = 0x6f8c1862 <Address 0x6f8c1862 out of bounds>, prototype = {dexFile = 0x6d5aac48, protoIdx = 3750}, shorty =
0x6f88be67 <Address 0x6f88be67 out of bounds>, insns = 0x6f745d98, jniArgInfo = 0, nativeFunc = 0x0, fastJni = false, noRef = false, shouldTrace = false, registerMap = 0x71a557d8, inProfile = false}
【若干配置】
set print pretty on :结构体显示的漂亮一些
set print union :设置显示结构体时,是否显式其内的联合体数据。
set print vtbl :当此选项打开时,GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。
例如:
(gdb) set print pretty on
(gdb) p *(Method*)0x6d682328
$2 = {
clazz = 0x41755dc0,
accessFlags = 9,
methodIndex = 0,
registersSize = 6,
outsSize = 3,
insSize = 1,
name = 0x6f8c1862 <Address 0x6f8c1862 out of bounds>,
prototype = {
dexFile = 0x6d5aac48,
protoIdx = 3750
},
shorty = 0x6f88be67 <Address 0x6f88be67 out of bounds>,
insns = 0x6f745d98,
jniArgInfo = 0,
nativeFunc = 0x0,
fastJni = false,
noRef = false,
shouldTrace = false,
registerMap = 0x71a557d8,
inProfile = false
}
【C++中命名空间显示错误问题】
(gdb) p *(art::ScopedObjectAccess *) 0xbe81e618
A syntax error in expression, near `) 0xbe81e618'.
解决方案:用单引号''将类型名扩起来,如:
(gdb) p *('art::ScopedObjectAccess' *) 0xbe81e618
$19 = {
<art::ScopedObjectAccessUnchecked> = {
<art::ScopedObjectAccessAlreadyRunnable> = {
self_ = 0xb4f07800,
env_ = 0xb4f512b0,
vm_ = 0xb4f5c280
},
members of art::ScopedObjectAccessUnchecked:
tsc_ = {
self_ = 0xb4f07800,
thread_state_ = art::kRunnable,
old_thread_state_ = art::kNative,\
expected_has_no_thread_ = false
}
}, <No data fields>}
【如何获取类成员在类中的偏移】
(gdb) p &(('art::ScopedObjectAccess' *)0)->tsc_.expected_has_no_thread_
$26 = (const bool *) 0x18
(gdb) p &(('art::ScopedObjectAccess' *)0)->env_
$27 = (art::JNIEnvExt * const *) 0x4
(gdb) p &((struct task_struct *)0)->prio
$1 = (int *) 0x30
【如何获取类的大小】
(gdb) p (('art::ScopedObjectAccess' *)0)+1
$33 = (art::ScopedObjectAccess *) 0x1c
【设置代码搜索路径】
(gdb) directory ~/disk/android/
Source directories searched: /home/disk/android:$cdir:$cwd
(gdb) list
67 }
68
69 // Uncompress an encoded reference from its bit representation.
70 MirrorType* UnCompress() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
71 uintptr_t as_bits = kPoisonReferences ? -reference_ : reference_;
72 return reinterpret_cast<MirrorType*>(as_bits);
73 }
74
75 friend class Object;
76
【查找内存值】
(gdb) find /w /20 0x41f7f0, 0x41f8f0, 0x3f800000
0x41f7f0~0x41f8f0范围内的内存中,查询0x3f800000,步长为word,最多查找20个。
【打印数组】
(gdb) p je_arenas
$0 = (arena_t **) 0x7f93f0a280
(gdb) p * (arena_t **) 0x7f93f0a280@10
$1 = {0x7f93e02200, 0x7f93f12280, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
编译程序时需要加上-g,之后才能用gdb进行调试:gcc -g main.c -o main
gdb中命令:
回车键:重复上一命令
(gdb)help:查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h
(gdb)run:重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件),简写r
(gdb)start:单步执行,运行程序,停在第一执行语句
(gdb)list:查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数),简写l
(gdb)set:设置变量的值
(gdb)next:单步调试(逐过程,函数直接执行),简写n
(gdb)step:单步调试(逐语句:跳入自定义函数内部执行),简写s
(gdb)backtrace:查看函数的调用的栈帧和层级关系,简写bt
(gdb)frame:切换函数的栈帧,简写f
(gdb)info:查看函数内部局部变量的数值,简写i
(gdb)finish:结束当前函数,返回到函数调用点
(gdb)continue:继续运行,简写c
(gdb)print:打印值及地址,简写p
(gdb)quit:退出gdb,简写q
(gdb)break+num:在第num行设置断点,简写b
(gdb)info breakpoints:查看当前设置的所有断点
(gdb)delete breakpoints num:删除第num个断点,简写d
(gdb)display:追踪查看具体变量值
(gdb)undisplay:取消追踪观察变量
(gdb)watch:被设置观察点的变量发生修改时,打印显示
(gdb)i watch:显示观察点
(gdb)enable breakpoints:启用断点
(gdb)disable breakpoints:禁用断点
(gdb)x:查看内存x/20xw 显示20个单元,16进制,4字节每单元
(gdb)run argv[1] argv[2]:调试时命令行传参
(gdb)set follow-fork-mode child#Makefile项目管理:选择跟踪父子进程(fork())
core文件:先用$ ulimit -c 1024 开启core,当程序出错会自动生成core文件。调试时 gdb a.out core
使用gdb添加断点的几种方式
设置断点有很多方式。下面我们举例说明下常用的几种方式。
通过行号设置断点
格式:
break [行号]
break 行号,断点设置在该行开始处,注意:该行代码未被执行
如果你的程序是用c或者c++写的,那么你可以使用“文件名:行号”的形式设置断点。示例如下:
//test.c
#include <stdio.h>
void judge_sd(int num){
if ((num & 1) == 0){
printf("%d is even\n",num);
return;
}else{
printf("%d is odd\n",num);
return;
}
}
int main(int argc, char const *argv[]){
judge_sd(0);
judge_sd(1);
judge_sd(4);
return 0;
}
编译:
gcc -g test.c -o test
- 1
gdb test
break 文件名 : 行号,适用于有多个源文件的情况。
示例中的(gdb) b test.c:18是设置了断点。断点的位置是test.c文件的18行。使用r命令执行脚本时,当运行到18行时就会暂停。注意:该行代码未被执行
通过函数设置断点
格式:
break [函数名]
break 函数名,断点设置在该函数的开始处,断点所在行未被执行:
同样可以将断点设置在函数处:
b judge_sd
设置条件断点
如果按上面的方法设置断点后,每次执行到断点位置都会暂停。有时候非常讨厌。我们只想在指定条件下才暂停。这时候根据条件设置断点就有了用武之地。设置条件断点的形式,就是在设置断点的基本形式后面增加 if条件。示例如下:
break test.c:6 if num>0
- 1
当在num>0时,程序将会在第6行断住。
查看断点
语法:
info breakpoints
可以使用info breakpoints查看断点的情况。包含都设置了那些断点,断点被命中的次数等信息。示例如下:
它将会列出所有已设置的断点,每一个断点都有一个标号,用来代表这个断点。
删除断点
语法:
delete breakpoint
对于无用的断点我们可以删除。删除的命令格式为 delete breakpoint 断点编号。info breakpoint命令显示结果中的num列就是编号。删除断点的示例如下:
查看源码
断点设置完后,当程序运行到断点处就会暂停。暂停的时候,我们可以查看断点附近的代码。查看代码的子命令是list,缩写形式为l。
指定行号查看代码
语法:
list first,last
例如,要列出6到21行之间的源码:
列出指定文件的源码
前面执行l命令时,默认列出test.c的源码,如果想要看指定文件的源码呢?可以
list 【文件名加行号或函数名】
总结
本文介绍了GDB调试中的断点设置、源码查看。断点设置可以便于我们后期观察变量,堆栈等信息,为进一步的定位与调试做准备。源码查看可以通过指定行号或者方法名来查看相关代码。
1. 普通断点
根据代码行数设置断点是最常见的一种方式,在debug程序运行前就可以进行断点的配置。如:
(gdb) b src/main.cpp:127
当程序执行到main.cpp
文件的第127行时就会出发断点。
2. 条件断点
顾名思义,这种断点是当满足一定条件时才会触发,比较适合进行异常排查。设置方式(gdb)break line-or-function if (condition)
, 如:
(gdb) b src/main.cpp:127 if cnt==10
3. 数据断点
就是根据地址来进行设置断点,只能是在debug程序运行之后设置,因为只有运行后,你才能很方便地获知变量的地址。当该地址上的内容发生改变时就会触发断点。
设置数据断点有两种方式,一种是直接指出地址值,如:
(gdb) b *0x400522
注意必须加*号。而获取地址值的方法是,先设置普通断点,在断点处print &变量名
就能获取该变量的地址。
另一种当然就是直接设置变量名了,如:
(gdb) b &变量名
4. 函数断点
这种断点是当程序执行到某个程序时就会触发断点。设置方式如:
(gdb) b funcName
但是函数断点并不是对所有函数都有效,比如优化后的静态函数和inline函数等,可能就无法触发断点。
5. 监视
设置监视也必须是在程序运行后才行。如:
(gdb) watch *地址 # 当地址所指内容发送变化时断点
(gdb) watch var #当var值变化时,断点
(gdb) watch (condition) #当条件符合时,断点
监视也被称为硬件断点。可以监测栈变量和堆变量值的变化,当被监测变量值发生变化时,程序被停住。