本笔记基于陈皓大神的笔记
启动GDB
调试程序
gdb <program>
调试core
gdb <program> core
调试运行中的进程
gdb <program> <PID>
gdb attach <PID>
启动时常用参数
-s/-symbols <file>
:指定符号表-se file
:指定文件中读取符号表信息-directory\-d <directory>
:加入一个源文件的搜索路径,默认搜索路径是环境变量中PATH所定义的路径。-core/-c <file>
:调试core dump的core文件
启动时常用指令
程序运行参数
set args #可指定运行时参数。(如:set args 10 20 30 40 50)
show args #命令可以查看设置好的运行参数。
设置运行环境
path <dir> #可设定程序的运行路径。
show paths #查看程序的运行路径。
set environment varname [=value] #设置环境变量。如:set env USER=hchen
show environment [varname] #查看环境变量。
设置工作目录
cd <dir> #相当于shell的cd命令。
pwd #显示当前的所在目录。
程序的输入输出
info terminal #显示你程序用到的终端的模式。
run > outfile #使用重定向控制程序输出
运行shell命令
shell <command string>
可以在gdb中直接make,rebuild 程序
make <make-args>
= shell make <make-args>
暂停/恢复
设置断点
-
函数
break <function>/<filename:function>
c++中可以使用class::function或function(type,type)格式来指定函数名。
-
行号
break <linenum>/<filename:linenum>
-
内存:
break *address
在程序运行的内存地址处停住
-
条件
break ... if <condition>
…可以是上述的参数,condition表示条件
-
打印断点
info breakpoints/break [n]
设置观察点
观察某个表达式(变量也是一种表达式)的值是否有变化了
-
expr值有变化时,马上停住程序
watch <expr>
-
expr被读时,停住程序
rwatch <expr>
-
expr被读或被写时,停住程序
awatch <expr>
-
打印观察点
info watchpoints
设置捕获事件
捕捉程序运行时的一些事件
-
格式
catch <event>
tcatch <event>
:设置捕捉点,当停住后自动删除。
-
事件
- throw :C++抛出的异常
- catch:C++捕捉到的异常
- exec/fork/vfork:exec/fork/vfork被调用
- load/unload:载入/卸载动态库
维护停止点与停止条件
clear
:清除所有的停止点clear <function>/<filename:function>
: 清除该函数上所有的停止点clear <linenum>/<filename:linenum>
:清除该行上所有的停止点delete/disable/enable [breakpoints] [range...]
:删除/禁用/使能指定断点condition <bnum> <expression>
:修改断点停止条件,bnum–断点号condition <bnum>
:删除断点停止条件ignore <bnum> <count>
:忽略断点的停止条件count次
恢复运行与单步调试
-
继续:
continue/fg [ignore-count]
ignore-count:跳过断点次数
-
逐语句:
step <count>
-
逐过程:
next <count>
-
step-mode模式:
set step-mode on/off
单步跟踪时,程序不会因为没有debug信息而不停住
-
离开当前函数:
finish
-
离开当前循环体:
until/u
-
单步一条机器指令:
stepi/si/nexti/ni
-
运行:
run/r
信号
-
指令
handle <signal> <keywords...>
-
keywords
- nostop:不停止运行,但会打印信息
- stop:停止运行。
- print:打印信息
- noprint:不打印信息
- pass/noignore:信号下发给程序
- nopass/ignore:信号不下发给程序
-
查看有哪些信号在被GDB检测
info signals/handle
多线程调试
当你的程序被GDB停住时,所有的运行线程都会被停住
-
查看线程
info threads
-
切换线程
thread <ID>
-
特定线程打断点
break <linespec> thread <threadno> [if ...]
-
控制其他线程
set scheduler-locking off|on|step
- off:不锁定任何线程,也就是所有线程都执行,这是默认值。
- on:只有当前被调试程序会执行。
- step:在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行。
查看信息
查看栈信息
- 栈的所有信息
bt
:调用栈的所有信息bt <n>
:打印栈顶上n层的栈信息bt <-n>
:打印栈底下n层的栈信息
- 切换当前栈
- 默认当前层:栈顶层
f <n>
:切换至第n层up/down <n>
:向上/下面移动n层,可以不打n,表示向上/下移动一层
- 信息打印
info f
:当前栈层的详情info args
:入参及其值info locals
:所有局部变量及其值。
查看源程序
显示源代码
list <linenum>/<function>/<first>, <last>/ , <last>/<+|-offset>
:显示周围代码/函数代码/行区域代码/向前|后便宜set listsize <count>
:设置一次显示源代码的行数
搜索源代码
- 向前搜索:
forward-search/search <regexp>
,regexp为正则表达式 - 全部搜索:
reverse-search <regexp>
源文件路径
-
设置路径
directory <dirname ... >
dir <dirname ... >
-
清除路径
directory
-
显示路径
show directories
源代码的内存
-
查看源码在内存中的地址
info line <function>/<filename:function>/<linenum>/<filename:linenum>
-
查看函数的汇编代码
disassemble <function>/<filename:function>
查看变量
指令格式
-
命令
print <expr>
print /<f> <expr>
:f 输出格式 -
操作符
@
:是一个和数组有关的操作符::
:指定一个在文件或是一个函数中的变量。{<type>} <addr>
:表示一个指向内存地址<addr>的类型为type的一个对象。
数组
p *array@len
输出格式
- x:按十六进制格式显示变量
- d:按十进制格式显示变量
- u:按十六进制格式显示无符号整型
- o:按八进制格式显示变量
- t:按二进制格式显示变量
- a:按十六进制格式显示变量
- c:按字符格式显示变量
- f:按浮点数格式显示变量
查看内存
-
指令
examine 简写 x
x/<n/f/u> <addr>
例子:
x/3uh 0x54320
-
参数
- n:表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容
- f:表示显示的格式,参上
- u:表示数据宽度,默认是4B。u参数可以用下面的字符来代替
- b表示单字节
- h表示双字节
- w表示四字节
- g表示八字节
自动显示
设置自动显示的变量,当程序停住时,或单步时,这些变量会自动显示
display <expr>
/display/<fmt> <expr>
/display/<fmt> <addr>
undisplay/delete <dnums...>
:删除自动显示disable/enable display <dnums...>
:禁用/使能自动显示info display
:展示自动显示表
打印的历史记录
每一个print都会被GDB记录下来。GDB会以$1, $2, $3 …这样的方式为你每一个print命令编上号。于是,你可以使用这个编号访问以前的表达式,如$1
定义环境变量
和shell一样使用$
- 定义:
set $i = 0
- 显示:
show convenience
- 使用:
$i
显示选项
略,个人觉得默认的挺好用的
查看寄存器
略,目前从事服务器开发,不大可能接触。
改变程序的执行
修改变量值
print x=4
跳转执行
-
jump <linespec>
<linespce>:文件的行号,可以是file:line格式,可以是+num这种偏移量格式
-
jump <address>
<address>:代码行的内存地址
-
注意事项:jump不会改变当前的程序栈中的内容,所以,当你从一个函数跳到另一个函数时,当函数运行完返回时进行弹栈时必然会发生错误,所以最好是同一个函数中进行跳转。
产生信号量
-
signal <singal>
-
<singal>:UNIX的系统信号量通常从1到15。所以<singal>取值也在这个范围
-
single命令和shell的kill命令不同,系统的kill命令发信号给被调试程序时,是由GDB截获的,而single命令所发出一信号则是直接发给被调试程序的
强制函数返回
return
return <expression>
强制调用函数
call <expr>
设置生成dump
Windows
SetUnhandledExceptionFilter()
:崩溃时触发的回调函数- 使用dbghelp.lib的
MiniDumpWriteDump()
,生成dump文件 - 使用在VS工程中,打开dmp文件(仅限于本机生成的)
#include "stdafx.h"
#include <windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
static char* g_szFilePath;
//我们的回调函数
LONG __stdcall ExceptCallBack( EXCEPTION_POINTERS *pExcPointer)
{
MessageBox(NULL,"程序崩溃!",NULL,MB_OK);
//创建dump文件
HANDLE hFile = CreateFile(g_szFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL ,NULL);
//向文件写下当前程序崩溃相关信息
MINIDUMP_EXCEPTION_INFORMATION loExceptionInfo;
loExceptionInfo.ExceptionPointers = pExcPointer;
loExceptionInfo.ThreadId = GetCurrentThreadId();
loExceptionInfo.ClientPointers = TRUE;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),hFile, MiniDumpNormal, &loExceptionInfo, NULL, NULL);
CloseHandle(hFile);
return EXCEPTION_EXECUTE_HANDLER;
}
void Dump_Init(char* szFilePath){
g_szFilePath = szFilePath;
SetUnhandledExceptionFilter(ExceptCallBack);
}
/*
int _tmain(int argc, _TCHAR* argv[])
{
//设置崩溃回调函数
Dump_Init("C:/Users/liliangwei/Desktop/dump.dmp");
int * p = NULL;
*p = 1;
return 0;
} */
Linux
如果无目录名,则core生成在运行目录
查看
cat /proc/sys/kernel/core_pattern
暂时
-
打开core:
ulimit -c unlimited
-
设置目录与文件名:
echo '%e.core.%p' > /proc/sys/kernel/core_pattern
永久
- /etc/profile中最后添加
ulimit -c unlimited
/sbin/sysctl -w kernel.core_pattern=%e.core.%p
GDB 指令缩写
缩写 | 原义 | 意义 |
---|---|---|
b | break | 打断点 |
c | continue | 继续 |
d | delete | 删除断点 |
f | frame | 栈层跳转 |
i | info | 查看信息 |
j | jump | 程序跳转 |
l | list | 展示源码 |
n | next | 单步-过程 |
p | 打印与设置变量 | |
r | run | 运行 |
s | step | 单步-语句 |
u | until | 离开循环体 |
ni | nexti | 机器级单步 |
si | stepi | 机器级单步 |
bt | backtrace | 所有栈信息 |
wa | watch | 设置观察点 |
ig | ignore | 忽略断点 |
fin | finish | 离开函数 |