ROS外传:程序调试(c++)

选择调试器

写了ROS程序,如果调试呢?很长一段时间,我都是通过cout变量看哪儿除了问题进行调试的,大佬勿笑话.其实程序不大的话还是蛮方便,什么多余的设置都不用.然而面对大型的程序这就有些吃力了.一般大家通过IDE调试程序,ROS官方列出了许多可用的IDE
http://wiki.ros.org/IDEs
调试程序最常用的一般来说无非就是: 加断点,显示变量值,执行下一步等几个命令.
大家使用的IDE可能不一样.我使用的VScode,有的使用Qtcreator, Ecllipse, Clion,有的不使用IDE.要调试ROS的话,每个IDE都有不同的设置方法.VScode我目前为止都不知道怎么设置= =...在上面的网址下给出了VScode关于ROS的plugin,我安装了.这个plugin能给出的功能其实自身并不包含调试,而是使一些ros命令方便在VScode里实现.
我注意到qtCreator等几个IDE貌似添加了很好的ROS辅助功能,不过我查找了一番(并没有花太多时间,也许遗漏了),里面讲了怎么在IDE里设置ROS啊什么的,但是没有讲之后该怎么做.也就就是普通的在某一行程序旁添加断点??我也许以后才取试了,我想找一个比较universal的方法.跟什么IDE没有关系,那么这就非使用gdb莫属了.
gdb就相当于ubuntu自带的debugger吧.具体能调试哪些语言什么见官网.
https://www.gnu.org/software/gdb/
使用gdb调试,和程序猿理想中的调试方法有些出入.使用IDE习惯了的用户,设置断点会在程序旁某个位置点一下,然后运行程序调试.程序就会在断点处暂停.
gdb调试,一切都在terminal中使用命令行实现
https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf
上面pdf中的内容包含了gdb调试最常用的命令,我们根据它来调试程序.

普通的非ROS程序用gdb怎么调试呢?在编译好后(编译的方法和正常的程序一样,不过要在cmakelists前面添加)

set(CMAKE_BUILD_TYPE Debug)

之后直接使用
gdb 可执行程序 就可以进入gdb调试模式了.如果你的程序需要添加一些输入,比如说,你正常跑程序是下面这样

./可执行程序 arg1 arg2 arg3

那么你使用gdb的话就是

gdb 可执行程序
r arg1 arg2 arg3

r代表的是run
ROS里调试稍微有些不一样,不过大体相同.
我们先写一段ROS代码.再使用gdb尝试添加断点,查看变量等内容.使用的代码是我们在ROS从入门到放弃第二讲的内容中用到的pub数据类型Int8的代码.没看过那个的也没关系,和ROS官方tutorial发布一个string的代码几乎一样.下面列出代码

#include "ros/ros.h"
#include "std_msgs/Int8.h" //#include "std_msgs/String.h"

#include 

//publish int8 number
int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");

  ros::NodeHandle n;

  ros::Publisher chatter_pub = n.advertise("chatter", 1000); //ros::Publisher chatter_pub = n.advertise("chatter", 1000);

  ros::Rate loop_rate(10);

  int count = 0;
  while (ros::ok())
  {
    std_msgs::Int8 msg; //std_msgs::String msg;

    // std::stringstream ss;
    // ss << "hello world " << count;
    msg.data = count;// msg.data = ss.str();

    ROS_INFO("%d", msg.data); //ROS_INFO("%f", msg.data.c_str())

    chatter_pub.publish(msg); //line 28

    ros::spinOnce();

    loop_rate.sleep();
    ++count;
  }


  return 0;
}

把这段程序随意写入一个ros package当中,写入CMakeLists.txt进行编译.我们还需要在pakcage的CMakeLists.txt接近顶部添加

set(CMAKE_BUILD_TYPE Debug)

其实这个我没试过哈哈哈,但是应该能针对某一个package作用,自己我直接把所有ros package都设置成debug模式了.方法是使用catkin_make编译的时候,往后添加一点东西

catkin_make -DCMAKE_BUILD_TYPE=Debug 

这样编译之后,你的所有程序都可以使用gdb进行debug了.

开始调试

跑ros程序的两个方法一个是rosrun,一个是roslaunch.使用下面语句执行文件

rosrun --prefix 'gdb -ex run --args' [package_name] [node_name] 

执行完上面的语句,你会发现程序直接开始跑了,我还来不及设置断点什么的呢.问题应该出在run那个参数上,不过我暂时没找到替代的,得想想其他办法.
rosrun其实就相当于直接执行二进制文件./文件.那么我们直接找到catkin_make产生的二进制文件进行运行不就完了?每一个package产生的二进制文件存放的位置在

you_catkin_workspace/devel/lib/your_package_name

里.cd进去上面的地址,你应该能看到你在CMakeLists.txt里通过add_executable产生的那个二进制文件的名字(就是rosNode了).在terminal中输入

gdb node_name

就会发现进入gdb模式了,terminal的输出像下面这样

gdbDebug.png

这时候我们可以输入第三个链接pdf中的命令进行调试了.
设置断点
pdf中写的设置断点的方法为break ,这个where指的是程序的哪一行,由于的代码段貌似不能显示行数,我在上面的程序的28行注释了一下line 28.我们在terminal中输入

break 28

会看到一则消息关于断点设置的.然后我们就可以跑程序了.在terminal中输入

run

我们根据terminal的输出应该能很轻易的知道28行程序暂停了.断点已经设置成功了.
另外也可以

break 函数名

设置断点,这样进入函数的时候程序会暂停
查看变量值 
现在我们要查看某个变量的值呢?pdf中显示是print/format 或者display/format .what表示是什么变量.我们在terminal中输入

display count

便能看到程序中变量count的值了.
执行下一步
同样查看pdf得知,在terminal中输入next就可以了.
设置被包含的程序的断点
我们写大型程序的时候,一个rosnode肯定include了好多文件,上面的break 28默认在主函数中设置断点了,那么我们想在其他不在同一个cpp文件中的函数设置断点之类的呢?比如我们在CMakeLists.txt中编译的时候使用的下面语句

add_executable(main main.cpp b.cpp c.cpp)

之后使用

gdb main

进行调试,如果我们想在b.cpp中加一个断点,我们只需要在terminal中特殊指明是哪个cpp加上行数即可,比如

break b.cpp:line_number

其中line_number表示你想在b.cpp中的哪行代码添加breakpoint.
显示出错的位置
gdb调试出错了,比如出现segmentation fault,通常会自动显示出错位置,输入where可以显示出更详细的内容
退出gdb
程序正在运行,按下crtl+c,在输入q退出.
程序没有运行,直接输入q退出.
总之根据pdf我们慢慢尝试,把gdb调试运用熟练.上面的调试其实和ROS没有什么关系,普通的c++程序编译好后也是这么调试.使用roslaunch时则稍有不同.但是非常简单.比在需要调试的那个node之后添加一句话就可以了.比如上面的发布int8的程序,假设名字叫debugTest1.cpp,在test包里,由他编译出来的node的名字是debugTest1.跑这个nodelaunch文件名叫debugTest.launch,内容如下




那么为了debug,只需要再该node改成下面即可




之后仍旧如常地跑文件roslaunch test debugTest.launch
会出现下面的窗口

debugRoslaunch.png

你会在上方小的黑色窗口里输入gdb的相关命令进行调试.
roslaunch里添加的那一行内容也可以更换,见
http://wiki.ros.org/roslaunch/Tutorials/Roslaunch%20Nodes%20in%20Valgrind%20or%20GDB
我们选择的是第一个.你可以自己添加其他内容试试.

一些小的trick或者命令

print Eigen矩阵
如果x代表矩阵,那么命令这么书写

print *x.data()@length_x

其中length_x是矩阵的row*column.如果length_x小于矩阵的size的话,那么会由第一行第一个从左往右输出到第二行到第三行...由此到length_x长度.
如果直接print x则除了矩阵的内容之外还有一大堆多余的东西.
在load shared library的时候暂停

set stop-on-solib-events 1

使用了这行命令在每次有shared library载入的时候程序会暂停.这时候你可以针对该lib设置断点什么的.其实可以直接设置尚未载入的lib的source文件的断点,语法和设置被包含的程序的断点那部分一样.不过由于lib尚为被载入所以找不到程序,gdb会问你

No source file named System.cpp.
Make breakpoint pending on future shared library load? (y or [n]) 

选y就可以了.

你可能感兴趣的:(ROS外传:程序调试(c++))