gdb

##############################################################################

1. 抓取进程当前调用堆栈

##############################################################################

gstack $(pidof proc_name)

##############################################################################

2. gdb调试某进程

##############################################################################

gdb proc_name $(pidof proc_name)
##############################################################################
# 启动 GDB
##############################################################################

gdb object                # 正常启动,加载可执行
gdb object core           # 对可执行 + core 文件进行调试
gdb object pid            # 对正在执行的进程进行调试
gdb                       # 正常启动,启动后需要 file 命令手动加载
gdb -tui                  # 启用 gdb 的文本界面


##############################################################################
# 帮助信息
##############################################################################

help                      # 列出命令分类
help running              # 查看某个类别的帮助信息
help run                  # 查看命令 run 的帮助
help info                 # 列出查看程序运行状态相关的命令
help info line            # 列出具体的一个运行状态命令的帮助
help show                 # 列出 GDB 状态相关的命令
help show commands        # 列出 show 命令的帮助


##############################################################################
# 断点
##############################################################################

break main                # 对函数 main 设置一个断点,可简写为 b main
break 101                 # 对源代码的行号设置断点,可简写为 b 101
break basic.c:101         # 对源代码和行号设置断点
break basic.c:foo         # 对源代码和函数名设置断点
break *0x00400448         # 对内存地址 0x00400448 设置断点
info breakpoints          # 列出当前的所有断点信息,可简写为 info break
delete 1                  # 按编号删除一个断点
delete                    # 删除所有断点
clear                     # 删除在当前行的断点
clear function            # 删除函数断点
clear line                # 删除行号断点
clear basic.c:101         # 删除文件名和行号的断点
clear basic.c:main        # 删除文件名和函数名的断点
clear *0x00400448         # 删除内存地址的断点
disable 2                 # 禁用某断点,但是部删除
enable 2                  # 允许某个之前被禁用的断点,让它生效
tbreak function|line      # 临时断点
ignore {id} {count}       # 忽略某断点 N-1 次
condition {id} {expr}     # 条件断点,只有在条件生效时才发生
condition 2 i == 20       # 2号断点只有在 i == 20 条件为真时才生效
watch {expr}              # 对变量设置监视点
info watchpoints          # 显示所有观察点


##############################################################################
# 运行程序
##############################################################################

run                       # 运行程序
run {args}                # 以某参数运行程序
run < file                # 以某文件为标准输入运行程序
run < <(cmd)              # 以某命令的输出作为标准输入运行程序
run <<< $(cmd)            # 以某命令的输出作为标准输入运行程序
set args {args} ...       # 设置运行的参数
show args                 # 显示当前的运行参数
cont                      # 继续运行,可简写为 c
step                      # 单步进入,碰到函数会进去
step {count}              # 单步多少次
next                      # 单步跳过,碰到函数不会进入
next {count}              # 单步多少次
CTRL+C                    # 发送 SIGINT 信号,中止当前运行的程序
attach {process-id}       # 链接上当前正在运行的进程,开始调试
detach                    # 断开进程链接
finish                    # 结束当前函数的运行
until                     # 持续执行直到代码行号大于当前行号(跳出循环)
until {line}              # 持续执行直到执行到某行
kill                      # 杀死当前运行的函数


##############################################################################
# 栈帧
##############################################################################

bt                        # 打印 backtrace 
frame                     # 显示当前运行的栈帧
up                        # 向上移动栈帧(向着 main 函数)
down                      # 向下移动栈帧(远离 main 函数)
info locals               # 打印帧内的相关变量
info args                 # 打印函数的参数


##############################################################################
# 代码浏览
##############################################################################

list 101                  # 显示第 101 行周围 10行代码
list 1,10                 # 显示 1 到 10 行代码
list main                 # 显示函数周围代码
list basic.c:main         # 显示另外一个源代码文件的函数周围代码
list -                    # 重复之前 10 行代码
list *0x22e4              # 显示特定地址的代码
cd dir                    # 切换当前目录
pwd                       # 显示当前目录
search {regexpr}          # 向前进行正则搜索
reverse-search {regexp}   # 向后进行正则搜索
dir {dirname}             # 增加源代码搜索路径
dir                       # 复位源代码搜索路径(清空)
show directories          # 显示源代码路径


##############################################################################
# 浏览数据
##############################################################################

print {expression}        # 打印表达式,并且增加到打印历史
print /x {expression}     # 十六进制输出,print 可以简写为 p
print array[i]@count      # 打印数组范围
print $                   # 打印之前的变量
print *$->next            # 打印 list
print $1                  # 输出打印历史里第一条
print ::gx                # 将变量可视范围(scope)设置为全局
print 'basic.c'::gx       # 打印某源代码里的全局变量,(gdb 4.6)
print /x &main            # 打印函数地址
x *0x11223344             # 显示给定地址的内存数据
x /nfu {address}          # 打印内存数据,n是多少个,f是格式,u是单位大小
x /10xb *0x11223344       # 按十六进制打印内存地址 0x11223344 处的十个字节
x/x &gx                   # 按十六进制打印变量 gx,x和斜杆后参数可以连写
x/4wx &main               # 按十六进制打印位于 main 函数开头的四个 long 
x/gf &gd1                 # 打印 double 类型
help x                    # 查看关于 x 命令的帮助
info locals               # 打印本地局部变量
info functions {regexp}   # 打印函数名称
info variables {regexp}   # 打印全局变量名称
ptype name                # 查看类型定义,比如 ptype FILE,查看 FILE 结构体定义
whatis {expression}       # 查看表达式的类型
set var = {expression}    # 变量赋值
display {expression}      # 在单步指令后查看某表达式的值
undisplay                 # 删除单步后对某些值的监控
info display              # 显示监视的表达式
show values               # 查看记录到打印历史中的变量的值 (gdb 4.0)
info history              # 查看打印历史的帮助 (gdb 3.5)


##############################################################################
# 目标文件操作
##############################################################################

file {object}             # 加载新的可执行文件供调试
file                      # 放弃可执行和符号表信息
symbol-file {object}      # 仅加载符号表
exec-file {object}        # 指定用于调试的可执行文件(非符号表)
core-file {core}          # 加载 core 用于分析


##############################################################################
# 信号控制
##############################################################################

info signals              # 打印信号设置
handle {signo} {actions}  # 设置信号的调试行为
handle INT print          # 信号发生时打印信息
handle INT noprint        # 信号发生时不打印信息
handle INT stop           # 信号发生时中止被调试程序
handle INT nostop         # 信号发生时不中止被调试程序
handle INT pass           # 调试器接获信号,不让程序知道
handle INT nopass         # 调试起不接获信号
signal signo              # 继续并将信号转移给程序
signal 0                  # 继续但不把信号给程序


##############################################################################
# 线程调试
##############################################################################

info threads              # 查看当前线程和 id
thread {id}               # 切换当前调试线程为指定 id 的线程
break {line} thread all   # 所有线程在指定行号处设置断点
thread apply {id..} cmd   # 指定多个线程共同执行 gdb 命令
thread apply all cmd      # 所有线程共同执行 gdb 命令
set schedule-locking ?    # 调试一个线程时,其他线程是否执行,off|on|step
set non-stop on/off       # 调试一个线程时,其他线程是否运行
set pagination on/off     # 调试一个线程时,分页是否停止
set target-async on/off   # 同步或者异步调试,是否等待线程中止的信息


##############################################################################
# 进程调试
##############################################################################

info inferiors                       # 查看当前进程和 id
inferior {id}                        # 切换某个进程
kill inferior {id...}                # 杀死某个进程
set detach-on-fork on/off            # 设置当进程调用fork时gdb是否同时调试父子进程
set follow-fork-mode parent/child    # 设置当进程调用fork时是否进入子进程


##############################################################################
# 汇编调试
##############################################################################

info registers            # 打印普通寄存器
info all-registers        # 打印所有寄存器
print/x $pc               # 打印单个寄存器
stepi                     # 指令级别单步进入,可以简写为 si
nexti                     # 指令级别单步跳过,可以简写为 ni
display/i $pc             # 监控寄存器(每条单步完以后会自动打印值)
x/x &gx                   # 十六进制打印变量
info line 22              # 打印行号为 22 的内存地址信息
info line *0x2c4e         # 打印给定内存地址对应的源代码和行号信息
disassemble {addr}        # 对地址进行反汇编,比如 disassemble 0x2c4e


##############################################################################
# 历史信息
##############################################################################

show commands             # 显示历史命令 (gdb 4.0)
info editing              # 显示历史命令 (gdb 3.5)
ESC-CTRL-J                # 切换到 Vi 命令行编辑模式
set history expansion on  # 允许类 c-shell 的历史
break class::member       # 在类成员处设置断点
list class:member         # 显示类成员代码
ptype class               # 查看类包含的成员
print *this               # 查看 this 指针
rbreak {regexpr}          # 匹配正则的函数前断点,如 ex_* 将断点 ex_ 开头的函数


##############################################################################
# 其他命令
##############################################################################

define command ... end    # 定义用户命令
<return>                  # 直接按回车执行上一条指令
shell {command} [args]    # 执行 shell 命令
source {file}             # 从文件加载 gdb 命令
quit                      # 退出 gdb


##############################################################################
# GDB 前端
##############################################################################

gdb-tui                   使用 gdb -tui 启动
cgdb                      http://cgdb.github.io/
emacs                     http://gnu.org/software/emacs
gdbgui                    https://github.com/cs01/gdbgui

GDB 图形化前端评测        http://www.skywind.me/blog/archives/2036


##############################################################################
# GDB 函数堆栈乱掉
##############################################################################

info reg
rbp
x/128ag address

info reg
ebp
x/127aw ebp

程序core掉,要去debug,但是函数堆栈乱掉了,很恶心.....经过Google/wiki一番,找到两种解决办法.

1. 手动还原backtrace

  手动还原其实就是看栈里面的数据,自己还原函数栈,听起来很复杂其实也比较简单.手头上没有比较好的例子,所以大家就去看

  http://devpit.org/wiki/x86ManualBacktrace 上面的例子.那个例子很好,是x86下面的,amd64下面也是类似.

  amd64下面,无非就是寄存器变成rbp,字长增加了一倍.当然这边选择了手动寻找函数返回地址,然后info symbol打印出函数名,其实还可以通过gdb格式化来直接打印函数名:

  gdb>x/128ag rbp内的内容

  所以手动还原的办法就变得很简单:

  gdb>info reg rbp            *x86换成info reg ebp

  gdb>x/128ag  rbp内的内容        *x86换成 x/128aw ebp的内容

  这样就能看到函数栈.如果你想解析参数是啥,也是可以的,只是比较麻烦,苦力活儿....想解析参数,就要知道栈的布局,可以参考这篇文章:

  http://blog.csdn.net/liigo/archive/2006/12/23/1456938.aspx

  这个办法比较简单,很容易实践,但是有一个前提,如果栈的内容被冲刷干净了,你连毛都看不到(事实就是这样).所以你需要开始栈保护...至少你还能找到栈顶的函数...

  gcc有参数: -fstack-protector 和 -fstack-protector-all,强烈建议开启....

2. 手动记录backtrace

  开启了栈保护,这样至少会看到一个函数栈....如果想要知道更多的信息,对不起,没的...后来看公司内部的wiki,外加上google,得知很多人通过trace的办法来debug.:-D

  简单的说,在gcc2时代,提供了两个接口函数:

  void __cyg_profile_func_enter (void *this_fn, void *call_site)
  void __cyg_profile_func_exit  (void *this_fn, void *call_site)

  方便大家伙做profile,然后很多人用这俩函数来调试代码.:-D

  函数功能很简单,第一个就是函数入栈,第二就是函数出栈.所以你只要自己维护一个栈,然后在他入栈的时候你也入栈(只记录函数地址),出栈的时候你也出栈.等程序挂了,你去看你自己维护的栈,这样就能搞到第二手的函数栈(第一手的可能被破坏了).然后在去info symbol或者x/num ag格式化打印也可以的.

  需要注意的是,编译需要加上参数-finstrumnet-function,而且这里函数的声明需要加上__attribute__ ((no_instrument_function))宏,否则他会无限递归调用下去,:-)

  如果是单线程,就搞一个栈就行了,如果多个线程,一个线程一个栈~~~