GDB:从单线程调试到多线程调试(MFiX单步调试)
GDB:从单线程调试到多线程调试
1. 裸跑GDB
1.1 安装GDB
sudo apt-get install gdb
1.2 编译程序
由于需要调试,因此编译的时候需要添加-g
编译参数:
1.3 GDB调试运行
1.4 常用调试参数
进入上面那个界面以后,说明正常启动GDB了,目前只是GDB启动了,程序还没有跑起来,输入run
可以让程序跑起来,但是这样程序就直接执行结束了,没有被逐行调试,没什么意义,因此通常需要先打断点,再启动程序。下面先列出常用的命令:
命令 | 简写形式 | 说明 |
---|---|---|
list |
l |
查看源码(后面可以接数字,表示查看该行附近的源码) |
next |
n |
(执行完该行后)跳到下一行,遇到函数直接执行完成 |
step |
s |
(执行完该行后)跳到下一行,遇到函数会进入 |
finish |
运行到函数结束 | |
continue |
c |
继续运行(如果后面没有断点,则一直执行到函数结束) |
break |
b |
打断点(d 行号,或d 函数名) |
info breakpoints |
显示断点信息(比如目前有几个断点,是否被启用等等) | |
delete |
d |
删除断点(delete 断点编号 :删除某一断点,delete :删除所有断点) |
print |
p |
打印变量值(p 代码中变量名 :打印代码中变量的值) |
info locals |
打印当前所在函数局部变量的值 | |
run |
r |
启动程序(设置完断点后用该命令启动程序) |
until |
u |
执行到指定行 |
info |
i |
显示信息 |
help |
h |
显示帮助信息(如:help list ) |
quit |
q |
退出GDB |
!(shell命令) |
在GDB里执行shell命令,第4.2节会用到 | |
shell (shell命令) |
同上 |
1.5 简单示例
用GDB运行程序:
查看源码:
打断点后运行:
然后就可以配合n
、s
和p
来一步步调试源码了。
2. GDB增强实现CGDB
2.1 安装
原生的GDB由于没有独立显示代码的窗口,调试比较麻烦,需要不断l
来显示代码,CGDB是GDB的加强版,下载链接为:http://cgdb.github.io/
按照教程安装即可,注意configure的时候添加参数:
CXXFLAGS='-std=c++11' ./configure --prefix=/usr/local
因为新版CGDB是用C++11编写的,不添加前面的参数可能会报错,其他的按照官网步骤即可。
2.2 配置
使用规则和GDB基本一致,不同的是有两个窗口,不过默认的是上下分屏,不太好看,这里先配置一下:[谷歌接口报错]:
1.网络错误或者文本过长。
2.谷歌接口可能对于某些网络不能用,具体不清楚。可以尝试挂VPN试试。
3.这个问题我没办法修复,请右键菜单更换百度、腾讯翻译接口。
vim ~/.cgdb/cgdbrc
默认没有这个文件,自己用vim填写一下:
:set ignorecase
:set ts=4
:set wso=vertical
:set hls
map <F11> :until<cr>
代表的含义依次是:
大小写不敏感;tab对应4个空格;分屏为左右垂直分屏;搜索高亮;F11快捷键,用于跳出循环(默认没有这个快捷键)
2.3 使用
在命令行输入
cgdb bugging
后进入如下界面:
左侧为源码,右侧为CGDB调试界面,命令基本和GDB一致。
按键盘esc
可以进入源码界面,用vim的操作方式控制方向;按键盘i
又重新回到gdb的调试界面。
2.4 快捷键
可以先按esc
进入源码界面,然后用快捷键控制单步运行,这样操作方便很多。有如下常用快捷键:
- F5 - Send a run command to GDB.(相当于
r
) - F6 - Send a continue command to GDB.(相当于
c
) - F7 - Send a finish command to GDB.(相当于
f
) - F8 - Send a next command to GDB.(相当于
n
) - F11 - 跳出循环(自己配置的快捷键)
- F10 - Send a step command to GDB.(相当于
s
)
3. CGDB单线程调试运行MFiX(gdb命令行参数)
可以用CGDB来单步调试mfix代码。这里使用的是mfix-19.1,常规的运行命令为:
./mfixsolver -f mfix.dat
如果用CGDB调试,首先:
cgdb mfixsolver
进入GDB界面:
由于Fortran代码没有提供高亮,因此左侧代码没有花花绿绿的效果,先凑合用。
然后打断点,运行mfix。注意需要添加命令行参数:run -f mfix.dat
注意,如果要打印module里的变量,需要带上module名称,例如 p module_name::var.
参考:https://stackoverflow.com/questions/10264329/fortran-module-variables-not-accessible-in-debuggers
然后可以用之前设置的快捷键单步调试mfix程序。注意主循环逻辑都在RUN_MFIX
函数里,因此要进入到这个函数里才能进一步单步调试循环过程。最后通过单步调试可以找到循环迭代的位置,在RUN_MFIX
这个subroutine里:
4. CGDB调试多线程简单案例
多线程(MPI)调试调试一直都是个很麻烦的事情,虽然有专业的多线程调试软件,但是一般都是收费的。这里提供一种在GDB下的方法。主要是先在函数里人为添加一个死循环,然后用GDB attach
到这些进程,然后通过set
环境变量的方式使其退出循环,并接管程序,进行单步调试。
4.1 添加死循环
首先有下面一段程序:
#include <stdio.h>
#include <string.h>
#include <mpi.h>
const int MAX_STRING = 100;
int main(void){
char greeting[MAX_STRING];
int comm_sz;
int my_rank;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
#ifdef MPI_DEBUG
int gdb_break = 1;
while(gdb_break) {};
#endif
if(my_rank != 0){
sprintf(greeting, "Greetings from process %d of %d!", my_rank, comm_sz);
/*
int MPI_Send(
void* msg_buf_p, //发送消息的缓冲区(内存首地址)
int msg_size, //发送消息的缓冲区大小(数据个数)
MPI_Datatype msg_type, //发送数据类型
int dest, //数据目的地,这里是0号进程
int tag, //标签,非负int。比如同样的数据类型,有的用来打印有的用来计算,只通过前面四个
参数无法区分
MPI_Comm communicator //通讯子,指定通讯范围
);
*/
MPI_Send(greeting, strlen(greeting)+1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}else{
printf("Greetings from process %d of %d!\n", my_rank, comm_sz);
for(int q = 1; q < comm_sz; q++){
/*
int MPI_Recv(
void* msg_buf_p, //接收消息的缓冲区
int buf_size, //接收消息的缓冲区大小
MPI_Datatype buf_type, //接收消息的类型
int source, //消息从哪里发过来
int tag, //标签,与发送过来的标签一致
MPI_Comm communicator, //通讯子,指定通讯范围
MPI_Status* status_p //通常赋值MPI_STATUS_IGNORE即可
);
*/
MPI_Recv(greeting, MAX_STRING, \
MPI_CHAR, q, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
printf("%s\n", greeting);
}
}
MPI_Finalize();
return 0;
}
我在代码的前面加了这样一句:
#ifdef MPI_DEBUG
int gdb_break = 1;
while(gdb_break) {};
#endif
这样,我在编译的时候添加参数-DMPI_DEBUG
,运行的时候就能停在这个循环处:
4.2 运行程序
现在我们启动这个程序:
现在的程序由于进入死循环,因此停在这里。下面我们另外再打开两个terminal,并进入GDB。
为了方便说明,上面的运行程序的terminal编号#1,另外打开的两个terminal分别编号#2, #3。
4.3 启动CGDB
现在#2和#3都以管理员身份进入CGDB:
sudo cgdb
4.4 找到进程编号
在GDB里调用shell,查看a.out的进程号:
(gdb) !ps aux | grep a.out
ps aux
是显示所有进程信息,|
是管道符,把结果传给grep
,筛选出含有a.out
内容的部分。
可以看到,进程号971
和972
对应的是a.out
的两个进程,通常进程号小的是0号进程。
4.5 GDB attach到进程
找到进程号,就可以让GDB attach过去,然后接管程序的运行。
分别让#2和#3 attach到971进程和972进程。
4.6 跳出死循环
可以看到,现在这两个进程都停在了19行while
这个地方,现在让程序跳出循环:
(gdb) set gdb_break = 0
4.7 GDB调试
现在GDB已经接管程序了,可以用n
往下执行代码了:
这是#2(也即0号进程)的执行结果,已经跳出循环。下面多执行几步:
可以看到进入0号进程的逻辑,并打印输出。继续执行:
这里程序进入等待状态,等待#2发送消息。当#2发送完消息:
#1则收到#2发来的消息,并退出等待,可以继续往下运行:
继续执行#1:
#1也即0号进程,打印出#2发来的数据,并再次进入for
循环,如果此时还有别的进程发来消息则会继续,但是这里一共只有两个进程,因此只会收到1号进程发来的消息。
当两个进程都全部执行完后,整个程序执行结束:
5. 参考
[1] 实验楼《GDB 简明教程》
[2] CSDN 《cgdb的介绍和使用》
[3] GitHub (CGDB编译报错解决)
[4] CSDN 《gdb调试 -带有命令行参数》
[5] 知乎《终端调试哪家强?》
[6] CGDB中文手册《CGDB配置命令》
[7] segmentfault《MPI并行程序的调试技巧》
GDB:从单线程调试到多线程调试(MFiX单步调试)的更多相关文章
- vs调试qt代码,无法单步调试
在使用vs调试qt代码时,可以编译但无法单步调试QT源码.报错缺少qmain_win.cpp或者其他q******.cpp文件. 1.因为安装qt时没有安装qt源码库,重新下载一个src源码就可以了. ...
- 支持源码单步调试QT库编译笔记
支持源码单步调试QT库编译笔记 编译环境:windows 10 编译工具:mingw_4_4_0 Qt源码版本:qt-everywhere-opensource-src-4.8.5(下载地址:http ...
- Oracle存储过程单步调试方法
oracle存储过程单步调试的方法 1.在要调试的过程上单击test,如下图所示: 2.出现如下界面时单击最左上方的按钮:,如下图所示: 3.单击后呈现如下画面: 其中: 表示要停止test; 表示要 ...
- gdb常用命令及使用gdb调试多进程多线程程序
一.常用普通调试命令 1.简单介绍GDB 介绍: gdb是Linux环境下的代码调试⼯具.使⽤:需要在源代码⽣成的时候加上 -g 选项.开始使⽤: gdb binFile退出: ctrl + d 或 ...
- Linux下面 多线程死锁问题的调试
最近写服务,经常是单进程,多线程的,加了各种锁,很担心出现死锁问题,专门学习了一下死锁问题的诊断. 死锁 (deallocks): 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种 ...
- GDB 单步调试汇编
本文同时发表在 https://github.com/zhangyachen/zhangyachen.github.io/issues/134 之前在看汇编的时候一直是肉眼看GCC -S的结果,缺点是 ...
- 手把手教你使用eclipse+qemu+gdb来单步调试ARM内核【学习笔记】
平台信息:linux4.0 平台:qemu 作者:庄泽彬 说明:笨叔叔的Linux视频的笔记 一.编译linux源码 export CROSS_COMPILE=arm-linux-gnueabi- e ...
- 一起talk GDB吧(第二回:GDB单步调试)
各位看官们,大家好.我们在上一回中说简单地介绍了GDB.这一回中,我们介绍GDB的调试功能:单步 调试. 闲话休提,言归正转. 让我们一起talk GDB吧! 看官们,我们先说一下什么是单步调试.大家 ...
- LINUX上使用GDB单步调试Chromium Android C++代码。
###动机###在LINUX使用GDB单步调试Chromium Android C++代码. [1]编译android平台Chromium, 修改GN文件中编译选项:-g -O0 使得编译优化更少,便 ...
随机推荐
- git 查询某人的提交记录
git log --author=liubo --name-only
- cmake get_filename_component
get_filename_component Get a specific component of a full filename. get_filename_component(<VAR&g ...
- Ckeditor 中粘贴图片
我们在ckeditor 中有上传图片,但是实际使用中这种手动上传图片方式并不是很方便,而是复制或者截图粘贴图片. 这里我们实现主要是获取对应的粘贴事件. CKEDITOR.instances[&quo ...
- [Java] 通过XPath获取XML中某个节点的属性
/** * Get PA Url * @author jzhang6 * @return url */ public String getPAUrl(){ String PAUrl = "& ...
- etl业务验证方法1
/* Copyright (c) 2015 Xiamen Weixin Software Co., Ltd. All rights reserved * * Create by huanglc@ho ...
- 修改数据库中的内容报错:PropertyAccessException:Null value was assinged to a property of primitive type setter of
错误原因:totalTime的类型为int,数据库中为NULL,int 类型不能赋值为NULL,只能为0,所以报此异常. 解决方案:将totalTime的类型改为Integer,或者初始化为0
- mvc api odata查询选项之 $inlinecount $format 选项(转)
出处:http://www.it165.net/pro/html/201505/40236.html 网上百度“odata 语法”会出来很多结果,其中有一项是比较一致的,那就是odata支持一下几种语 ...
- 打开窗口进行选择文件(txt文件),打开所选文件,读入文件
用mfc编写项目的时候往往需要调用窗口,允许用户通过窗口进行选择文件操作 TCHAR szBuffer[MAX_PATH] = { 0 }; OPENFILENAME ofn = { 0 }; ofn ...
- Layout布局源码浅析之——FrameLayout
一直想研究下安卓各种布局控件,FrameLayout是安卓最简单的界面布局,所以就从FrameLayout讲起. 1.属性.frameLayout继承ViewGroup,除了拥有ViewGroup的属 ...
- Lucene的数值索引以及范围查询
对文本搜索引擎的倒排索引(数据结构和算法).评分系统.分词系统都清楚掌握之后,本人对数值索引和搜索一直有很大的兴趣,最近对Lucene对数值索引和范围搜索做了些学习,并将主要内容整理如下: 1. Lu ...