GDB调试指南-源码查看
相关内容
前言
我们在调试过程中难免要对照源码进行查看,如果已经开始了调试,而查看源码或者编辑源码却要另外打开一个窗口,那未免显得太麻烦。文本将会介绍如何在GDB调试模式下查看源码或对源码进行编辑。
准备工作
为了说明后面的内容,我们先准备一些源码,分别是main.c:
//main.c
int main(void)
{
printf("it will print from 5 to 1\n");
printNum(5);
printf("print end\n");
printf("it will print 1 to 5\n");
printNum1(5);
printf("print end\n");
return 0;
}
头文件test.h:
void printNum(int n);
void printNum1(int n);
以及test.c:
void printNum(int n)
{
if( n < 0)
return;
while(n > 0)
{
printf("%d\n",n);
n--;
}
}
void printNum1(int n)
{
if( n < 0)
return;
int i = 1;
while(i <= n)
{
printf("%d\n",i);
i++;
}
}
编译运行:
$ gcc -g -o main main.c test.c
$ chmod +x main
$ ./main
it will print from 5 to 1
5
4
3
2
1
print end
it will print 1 to 5
1
2
3
4
5
print end
程序功能比较简单,用来打印5到1的数以及1到5的数,这里也就不多做解释。
列出源码
首先要介绍的就是list命令(可简写为l),它用来打印源码。
直接打印源码
例如:
$ gdb main
(gdb) l
1 //main.c
2 #include<stdio.h>
3 #include"test.h"
4 int main(void)
5 {
6 printf("it will print from 5 to 1\n");
7 printNum(5);
8 printf("print end\n");
9
10 printf("it will print 1 to 5\n");
(gdb)
直接输入l可从第一行开始显示源码,继续输入l,可列出后面的源码。后面也可以跟上+或者-,分别表示要列出上一次列出源码的后面部分或者前面部分。
列出指定行附近源码
l后面可以跟行号,表明要列出附近的源码:
(gdb) l 9
4 int main(void)
5 {
6 printf("it will print from 5 to 1\n");
7 printNum(5);
8 printf("print end\n");
9
10 printf("it will print 1 to 5\n");
11 printNum1(5);
12 printf("print end\n");
13 return 0;
在这里,l后面跟上9,表明要列出第9行附近的源码。
列出指定函数附近的源码
这个很容易理解,而使用也很简单,l后面跟函数名即可,例如:
(gdb) l printNum
1
2 void printNum(int n)
3 {
4 if( n < 0)
5 return;
6 while(n > 0)
7 {
8 printf("%d\n",n);
9 n--;
10 }
在这里,l后面跟上函数名printNum,它便列出了printNum函数附近的源码。
设置源码一次列出行数
不知道你有没有发现,在列出函数源码的时候,它并没有列全,因为l每次只显示10行,那么有没有方法每次列出更多呢?
我们可以通过listsize属性来设置,例如设置每次列出20行:
(gdb) set listsize 20
(gdb) show listsize
Number of source lines gdb will list by default is 20.
这样每次就会列出20行,当然也可以设置为0或者unlimited,这样设置之后,列出就没有限制了,但源码如果较长,查看将会不便。
列出指定行之间的源码
list first,last
例如,要列出3到15行之间的源码:
(gdb) l 3,15
3 {
4 if( n < 0)
5 return;
6 while(n > 0)
7 {
8 printf("%d\n",n);
9 n--;
10 }
11 }
12
13 void printNum1(int n)
14 {
15 if( n < 0)
启始行和结束行号之间用逗号隔开。两者之一也可以省略,例如:
(gdb) list 3,
3 {
4 if( n < 0)
5 return;
6 while(n > 0)
7 {
8 printf("%d\n",n);
9 n--;
10 }
11 }
12
省略结束行的时候,它列出从开始行开始,到指定大小行结束,而省略开始行的时候,到结束行结束,列出设置的大小行,例如默认设置为10行,则到结束行为止,总共列出10行。前面我们也介绍了修改和查看默认列出源码行数的方法。
列出指定文件的源码
前面执行l命令时,默认列出main.c的源码,如果想要看指定文件的源码呢?可以
l location
其中location可以是文件名加行号或函数名,因此可以使用:
(gdb) l test.c:1
1
2 void printNum(int n)
3 {
4 if( n < 0)
5 return;
6 while(n > 0)
7 {
8 printf("%d\n",n);
9 n--;
10 }
(gdb)
来查看指定文件指定行,或者指定文件指定函数:
(gdb) l test.c:printNum1
9 n--;
10 }
11 }
12
13 void printNum1(int n)
14 {
15 if( n < 0)
16 return;
17 int i = 1;
18 while(i <= n)
(gdb)
或者指定文件指定行之间:
(gdb) l test.c:1,test.c:3
1
2 void printNum(int n)
3 {
(gdb)
指定源码路径
在查看源码之前,首先要确保我们的程序能够关联到源码,一般来说,我们在自己的机器上加上-g参数编译完之后,使用gdb都能查看到源码,但是如果出现下面的情况呢?
源码被移走
例如,我现在将main.c移动到当前的temp目录下,再执行l命令:
(gdb) l
1 main.c: No such file or directory.
(gdb)
它就会提示找不到源码文件了,那么怎么办呢?
我们可以使用dir命名指定源码路径,例如:
(gdb) dir ./temp
Source directories searched: /home/hyb/workspaces/gdb/sourceCode/./temp:$cdir:$cwd
这个时候它就能找到源码路径了。我这里使用的是相对路径,保险起见,你也可以使用绝对路径。
更换源码目录
例如,你编译好的程序文件,放到了另外一台机器上进行调试,或者你的源码文件全都移动到了另外一个目录,怎么办呢?当然你还可以使用前面的方法添加源码搜索路径,也可以使用set substitute-path from to将原来的路径替换为新的路径,那么我们如何知道原来的源码路径是什么呢?借助readelf命令可以知道:
$ readelf main -p .debug_str
[ ] long unsigned int
[ ] short int
[ ] /home/hyb/workspaces/gdb/sourceCode
[ ] main.c
(显示部分内容)
main为你将要调试的程序名,这里我们可以看到原来的路径,那么我们现在替换掉它:
(gdb) set substitute-path /home/hyb/workspaces/gdb/sourceCode /home/hyb/workspaces/gdb/sourceCode/temp
(gdb) show substitute-path
List of all source path substitution rules:
`/home/hyb/workspaces/gdb/sourceCode' -> `/home/hyb/workspaces/gdb/sourceCode/temp'.
(gdb)
设置完成后,可以通过show substitute-path来查看设置结果。这样它也能在正确的路径查找源码啦。
需要注意的是,这里对路径做了字符串替换,那么如果你有多个路径,可以做多个替换。甚至可以对指定文件路径进行替换。
最后你也可以通过unset substitute-path [path]取消替换。
编辑源码
为了避免已经启动了调试之后,需要编辑源码,又不想退出,可以直接在gdb模式下编辑源码,它默认使用的编辑器是/bin/ex,但是你的机器上可能没有这个编辑器,或者你想使用自己熟悉的编辑器,那么可以通过下面的方式进行设置:
EDITOR=/usr/bin/vim
export EDITOR
/usr/bin/vim可以替换为你熟悉的编辑器的路径,如果你不知道你的编辑器在什么位置,可借助whereis命令或者witch命令查看:
whereis vim
vim: /usr/bin/vim /usr/bin/vim.tiny /usr/bin/vim.basic /usr/bin/vim.gnome /etc/vim /usr/share/vim /usr/share/man/man1/vim.1.gz
which vim
/usr/bin/vim
设置之后,就可以在gdb调试模式下进行编辑源码了,使用命令edit location,例如:
(gdb)edit 3 #编辑第三行
(gdb)edit printNum #编辑printNum函数
(gdb)edit test.c:5 #编辑test.c第五行
可自行尝试,这里的location和前面介绍的一样,可以跟指定文件的特定行或指定文件的指定函数。
编辑完保存后,别忘了重新编译程序:
(gdb)shell gcc -g -o main main.c test.c
这里要注意,为了在gdb调试模式下执行shell命令,需要在命令之前加上shell,表明这是一条shell命令。这样就能在不用退出GDB调试模式的情况下编译程序了。
另外一种模式
启动时,带上tui(Text User Interface)参数,会有意想不到的效果,它会将调试在多个文本窗口呈现:
gdb main -tui
但是本文不作介绍,有兴趣的可以探索一下。
总结
本文介绍了GDB调试中的源码查看,源码编辑以及如何在GDB调试模式下执行shell命令。
思考
关注公众号【编程珠玑】,获取更多Linux/C/C++/Python/Go/算法/工具等原创技术文章。后台免费获取经典电子书和视频资源