gdb-2-技巧

 

打印美化

set print pretty on

使用layout图形化界面

set print object on打印派生对象

 

-函数

 

向上或向下切换函数堆栈帧

用gdb调试程序时,当程序暂停后,可以用“ up n ”或“ down n ”命令向上或向下选择函数堆栈帧,其

中 n 是层数。以上面程序为例:

 

可以看到程序断住后,先执行“ frame 2 ”命令,切换到 fun3 函数。接着执行“ up 1 ”命令,此时会切 换到 main 函数,也就是会往外层的堆栈帧移动一层。反之,当执行“ down 2 ”命令后,又会向内层堆 栈帧移动二层。如果不指定 n ,则 n 默认为 1 .

还有“ up-silently n ”和“ down-silently n ”这两个命令,与“ up n ”和“ down n ”命令区别在于,切换

堆栈帧后,不会打印信息,仍以上面程序为例:

 

 

-断点

 

在程序入口处打断点

如果不知道main在何处,那么可以在程序入口处打断点。先通过 readelf 获得入口地址,然后:

Entry point address:

(gdb) b *0x400440 (gdb) r

 

-打印

在gdb中,如果要打印C++ STL容器的内容,缺省的显示结果可读性很差:

gdb 7.0之后,可以使用gcc提供的python脚本,来改善显示结果:

1. 获得最新的python脚本

svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python

2. 将如下代码添加到.gdbinit文件中(假设python脚本位于 /home/maude/gdb_printers/ 下)

python

import sys

sys.path.insert(0, '/home/maude/gdb_printers/python') from libstdcxx.v6.printers import register_libstdcxx_printers register_libstdcxx_printers (None)

end

vscode 也有一个visualizerFile,是基于visual studio的,做一个xml把数据对应起来

 

打印大数组中的内容

在gdb中,如果要打印大数组的内容,缺省最多会显示200个元素:

(gdb) set print elements number-of-elements

也可以使用如下命令,设置为没有限制:

(gdb) set print elements 0

 

打印数组中任意连续元素值

在gdb中,如果要打印数组中任意连续元素的值,可以使用“ p array[index]@num ”命令 ( p 是 print 命令的缩写)。其中 index 是数组索引(从0开始计数), num 是连续多少个元素。以 上面代码为例:

如果要打印从数组开头连续元素的值,也可使用这个命令:“ p *array@num ”

 

打印数组的索引下标

(gdb) set print array-indexes on

 

打印进程内存信息

i proc mappings

 

打印静态变量的值

你可以显式地指定文件名(上下文):

(gdb) p 'static-1.c'::var $1 = 1

(gdb) p 'static-2.c'::var $2 = 2

 

打印变量的类型和所在文件

在gdb中,可以使用如下命令查看变量的类型:

(gdb) whatis he

type = struct child

如果想查看详细的类型信息:

(gdb) ptype he

type = struct child { char name[10]; enum {boy, girl} gender;

}

如果想查看定义该变量的文件:

(gdb) i variables he

 

打印内存的值

gdb中使用“ x ”命令来打印内存的值,格式为“ x/nfu addr ”。含义为以 f 格式打印从 addr 开始 n 个长度单元为 u 的内存值。参数具体含义如下:

an:输出单元的个数。

bf:是输出格式。比如 x 是以16进制形式输出, o 是以8进制形式输出,等等。 cu:标明一个单元的长度。 b 是一个 byte h 是两个 byte halfword), w 是四 byte word), g 是八个 byte giant word)。

以上面程序为例:

(1) 以16进制格式打印数组前 a 16个byte的值:

(gdb) x/16xb a

0x7fffffffe4a0: 0x00 0x7fffffffe4a8: 0x08

0x01 0x09

0x02 0x0a

0x03 0x0b

0x04 0x0c

(2) 以无符号10进制格式打印数组 a 前16个byte的值:

(gdb) x/16ub a

0x7fffffffe4a0: 0 0x7fffffffe4a8: 8

0x05 0x0d

0x06 0x0e

0x07 0x0f

(2) 以无符号10进制格式打印数组 a 前16个byte的值:

(gdb) x/16ub a

 

打印源代码行

如上所示,在gdb中可以使用 list (简写为l)命令来显示源代码以及行号。 list 命令可以指定行 号,函数:

(gdb) l 24

(gdb) l main

还可以指定向前或向后打印:

(gdb) l - (gdb) l +

还可以指定范围:

(gdb) l 1,10

 

每行打印一个结构体成员

可以执行“set print pretty on”命令,这样每行只会显示结构体的一名成员,而且还会根据成员的定义层 次进行缩进:

 

按照派生类型打印对象

在gdb中,当打印一个对象时,缺省是按照声明的类型进行打印:

set print object on

(gdb) whatis p type = Shape & (gdb) ptype p type = class Shape { public: virtual void draw(void);

} &

(gdb) set print object on (gdb) whatis p type = /* real type = Circle & */ Shape & (gdb) ptype p type = /* real type = Circle & */ class Shape { public: virtual void draw(void);

} &

 

指定程序的输入输出设备

在gdb中,缺省情况下程序的输入输出是和gdb使用同一个终端。你也可以为程序指定一个单独的输入 输出终端。

首先,打开一个新终端,使用如下命令获得设备文件名:

$ tty

/dev/pts/2

然后,通过命令行选项指定程序的输入输出设备:

$ gdb -tty /dev/pts/2 ./a.out (gdb) r

或者,在gdb中,使用命令进行设置:

(gdb) tty /dev/pts/2

 

使用“$_”和“$__”变量

" x "命令会把最后检查的内存地址值存在“ $_ ”这个“convenience variable”中,并且会把这个地址中的 内容放在“ $__ ”这个“convenience variable”,以上面程序为例:

(gdb) p $__ $2 = 15

 

打印程序动态分配内存的信息

用gdb调试程序时,可以用下面的自定义命令,打印程序动态分配内存的信息:

define mallocinfo

set $__f = fopen("/dev/tty", "w") 

call malloc_info(0, $__f) 

call fclose($__f)

end

 

(gdb) mallocinfo

打印调用栈帧中变量的值

也可以不进行切换,直接打印:

(gdb) p func2::b $1 = 2

同样,对于C++的函数名,需要使用单引号括起来,比如:

(gdb) p '(anonymous namespace)::SSAA::handleStore'::n->pi->inst->dump()

 

-多进程多线程

十、线程(Thread Stops

 

如果你程序是多线程的话,你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB很容易帮你完成这一工作。

 

break <linespec> thread <threadno>

break <linespec> thread <threadno> if ...

linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个IDGDB分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定thread <threadno>则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如:

 

(gdb) break frik.c:13 thread 28 if bartab > lim

调试已经运行的进程

调试已经运行的进程有两种方法:一种是gdb启动时,指定进程的ID:gdb program processID(也可 以用-p或者--pid指定进程ID,例如:gdb program -p=10210)。以上面代码为例,用“ps”命令已经获 得进程ID为10210:

bash-3.2# gdb -q a 10210

另一种是先启动gdb,然后用“attach”命令“附着”在进程上:

bash-3.2# gdb -q a

Reading symbols from /data/nan/a...done. (gdb) attach 10210

 

调试子进程

在调试多进程程序时,gdb默认会追踪父进程。例如:

(gdb) set follow-fork-mode child (gdb) start

 

同时调试父进程和子进程

在调试多进程程序时,gdb默认只会追踪父进程的运行,而子进程会独立运行,gdb不会控制。以上面 程序为例:

可以看到当单步执行到第8行时,程序打印出“Child” ,证明子进程已经开始独立运行。

如果要同时调试父进程和子进程,可以使用“ set detach-on-fork off ”(默认 detach-on-

fork on )命令,这样gdb就能同时调试父子进程,并且在调试一个进程时,另外一个进程处于挂起

在使用“ set detach-on-fork off ”命令后,用“ i inferiors ”( i 是 info 命令缩写)查看进程状

态,可以看到父子进程都在被gdb调试的状态,前面显示“*”是正在调试的进程。当父进程退出后,

用“ inferior infno ”切换到子进程去调试。

 

查看线程信息

(gdb) i threads

(gdb) i threads 1 2

 

只允许一个线程运行

用gdb调试多线程程序时,一旦程序断住,所有的线程都处于暂停状态。此时当你调试其中一个线程时 (比如执行“ step ”,“ next ”命令),所有的线程都会同时执行。以上面程序为例:

如果想在调试一个线程时,让其它线程暂停执行,可以使用“ set scheduler-locking on ”命令:

此外,“ set scheduler-locking ”命令除了支持 off 和 on 模式外(默认是 off ),还有一个 step 模 式。含义是:当用" step "命令调试线程时,其它线程不会执行,但是用其它命令(比如" next ")调试 线程时,其它线程也许会执行。

 

使用“$_thread”变量

gdb7.2版本引入了 $_thread 这个“ convenience variable ”,用来保存当前正在调试的线程号。这

个变量在写断点命令或是命令脚本时会很有用。以上面程序为例:

 

一个gdb会话中同时调试多个程序

gdb支持在一个会话中同时调试多个程序。以上面程序为例,首先调试 a 程序:

接着使用“ add-inferior [ -copies n ] [ -exec executable ] ”命令加载可执行文件 b 。其中 n 默

认为1:

 

打印程序进程空间信息

使用gdb调试多个进程时,可以使用“ maint info program-spaces ”打印当前所有被调试的进程信息。

以上面程序为例:

(gdb) maint info program-spaces Id

/home/nan/b

Bound inferiors: ID 4 (process 0)

/home/nan/b

Bound inferiors: ID 3 (process 0)

/home/nan/b

Bound inferiors: ID 2 (process 15902)

/home/nan/a

Bound inferiors: ID 1 (process 15753)

 

使用“$_exitcode”变量

当被调试的程序正常退出时,gdb会使用 $_exitcode 这个“ convenience variable ”记录程序退出时 的“ exit code ”。

 

产生coredump文件

(gdb) gcore

Saved corefile core.13256

 

加载可执行程序和core dump文件

gdb  coredump应用程序  coredump文件     调试coredump文件 

 

gdb 调试coredump文件过程:

第一步:首先需要一个进程的coredump文件,怎么搞出coredump文件呢?

1 ps -fax|grep                 进程名称 找到进程的pid

2gdb -p pid                     调试进程

3gcore coredump名称        则生成core文件

第二步:找出coredump文件的应用程序

1gdb -c corefile   使用gdb调试core文件

2info auxv          索引31对应的是core文件的应用程序

第三部:gdb使用应用程序调试coredump文件

gdb  coredump应用程序  coredump文件     调试coredump文件 

 

通过以上三步就可以调试coredump文件了

通过以下命令调试coredump文件

info threads 显示所有线程

bt 显示线程堆栈信息

thread thread_num   切换线程

frame num  切换栈

info r 显示当前帧的寄存器信息 (每一帧的寄存器信息都是不相同的)

 

readelf应用coredump

readelf -h 读取coredump文件头

readelf -wl 读取应用程序debug_line

readelf -wf 读取应用程序fdecie信息

 

-改变程序的执行

跳转到指定位置执行

看起来是在15行,调用fun的时候出错了。常见的办法是在15行设置个断点,然后从头 run 一次。

如果你的环境支持反向执行,那么更好了。

如果不支持,你也可以直接 jump 到15行,再执行一次:

(gdb) b 15

Breakpoint 2 at 0x40056a: file jump.c, line 15. (gdb) j 15

Continuing at 0x40056a.

Breakpoint 2, main () at jump.c:15

需要注意的是:

1. jump 命令只改变pc的值,所以改变程序执行可能会出现不同的结果,比如变量i的值 2. 通过(临时)断点的配合,可以让你的程序跳到指定的位置,并停下来

 

修改被调试程序的二进制文件

gdb不仅可以用来调试程序,还可以修改程序的二进制代码。

缺省情况下,gdb是以只读方式加载程序的。可以通过命令行选项指定为可写:

$ gcc -write ./a.out

(gdb) show write

Writing into executable and core files is on.

修改二进制代码(注意大小端和指令长度):

(gdb) set variable *(short*)0x400651=0x0ceb (gdb) disassemble /mr drawing

 

修改pc的值

(gdb) n 6

a++;

(gdb) p $pc

$3 = (void (*)()) 0x8050944 <main+35> (gdb) set var $pc=0x08050949

 

- 信号

查看信号处理信息

用gdb调试程序时,可以用“ i signals ”命令(或者“ i handle ”命令, i 是 info 命令缩写)查看gdb

如何处理进程收到的信号:

第一项( Signal ):标示每个信号。

第二项( Stop ):表示被调试的程序有对应的信号发生时,gdb是否会暂停程序。 第三项( Print ):表示被调试的程序有对应的信号发生时,gdb是否会打印相关信息。 第四项( Pass to program ):gdb是否会把这个信号发给被调试的程序。 第五项( Description ):信号的描述信息。

 

信号发生时是否打印信号信息

用gdb调试程序时,可以用“ handle signal print/noprint ”命令设置当信号发生时,是否打印信号信

息,以上面程序为例:

接下来用“ handle SIGHUP noprint ”命令设置当 SIGHUP 信号发生时,gdb不打印信号信息,执行如

下:

信号发生时是否把信号丢给程序处理

用gdb调试程序时,可以用“ handle signal pass(noignore)/nopass(ignore) ”命令设置当信号发生

时,是否把信号丢给程序处理.其中 pass 和 noignore 含义相同, nopass 和 ignore 含义相同。以上

给程序发送信号

用gdb调试程序的过程中,当被调试程序停止后,可以用“ signal signal_name ”命令让程序继续运

行,但会立即给程序发送信号。以上面程序为例:

(gdb) signal SIGHUP

使用“$_siginfo”变量

在某些平台上(比如Linux)使用gdb调试程序,当有信号发生时,gdb在把信号丢给程序之前,可以通 过 $_siginfo 变量读取一些额外的有关当前信号的信息,这些信息是 kernel 传给信号处理函数的。 以上面程序为例:

(gdb) p $_siginfo._sifields._sigfault.si_addr

$1 = (void *) 0x850e

 

-共享库

使用" info sharedlibrary regex "命令可以显示程序加载的共享链接库信息,其中 regex 可以是正则 表达式,意为显示名字符合 regex 的共享链接库。如果没有 regex ,则列出所有的库。以上面程序为 例:

(gdb) i sharedlibrary hiredi*

 

-脚本

配置gdb init文件

当gdb启动时,会读取HOME目录和当前目录下的的配置文件,执行里面的命令。这个文件通常 为“.gdbinit”。

# 打印STL容器中的内容

python

import sys

sys.path.insert(0, "/home/xmj/project/gcc-trunk/libstdc++-v3/python") from libstdcxx.v6.printers import register_libstdcxx_printers register_libstdcxx_printers (None)

end

# 保存历史命令

set history filename ~/.gdb_history set history save on

# 退出时不显示提示信息 set confirm off

# 按照派生类型打印对象 set print object on

# 打印数组的索引下标

set print array-indexes on

# 每行打印一个结构体成员 set print pretty on

 

-源文件

设置源文件查找路径

有时gdb不能准确地定位到源文件的位置(比如文件被移走了,等等),此时可以用 directory 命令 设置查找源文件的路径。以上面程序为例:

(gdb) directory ../ki/

 

有时调试程序时,源代码文件可能已经移到其它的文件夹了。此时可以用 set substitute-path from

to 命令设置新的文件夹( to )目录替换旧的( from )。以上面程序为例:

 

进入和退出图形化调试界面

启动gdb时指定“ -tui ”参数(例如: gdb -tui program ),或者运行gdb过程中使用“ Crtl+X+A ”组

合键,都可以进入图形化调试界面。以调试上面程序为例:

 

显示汇编代码窗口

使用gdb图形化调试界面时,可以使用“ layout asm ”命令显示汇编代码窗口。以调试上面程序为例:

 

显示寄存器窗口

使用gdb图形化调试界面时,可以使用“ layout regs ”命令显示寄存器窗口。以调试上面程序为例:

“ tui reg system ”命令显示系统寄存器:

 

---其他

log 日志

用gdb调试程序时,可以使用“ set logging on ”命令把执行gdb的过程记录下来,方便以后自己参考或 是别人帮忙分析。默认的日志文件是“ gdb.txt ”,也可以用“ set logging file file ”改成别的名字。

以上面程序为例:

 

支持预处理器宏信息

使用 gcc -g 编译生成的程序,是不包含预处理器宏信息的:

(gdb) p NAME

No symbol "NAME" in current context.

如果想在gdb中查看宏信息,可以使用 gcc -g3 进行编译:

(gdb) p NAME $1 = "Joe"

 

设置被调试程序的参数

可以在gdb启动时,通过选项指定被调试程序的参数,例如:

$ gdb -args ./a.out a b c

也可以在gdb中,通过命令来设置,例如:

(gdb) set args a b c

(gdb) show args

Argument list to give program being debugged when it is started is "a b c".

 

设置被调试程序的环境变量

set env LD_PRELOAD=/lib/x86_64-linux-gnu/libpthread.so.0


原文地址:https://www.cnblogs.com/lizhensheng/p/11117232.html