ROSでGDBデバッグ後に処理復帰させる方法


普通に、GDBでBreakPointを設定して一度止めた後、処理を再開させて、次の解析を行う。
マルチプロセスなソフトだったり、ハードエンジンと並行して動かしたりする場合、
解析対象をBreakさせている間に、他の処理がどんどん進んでしまうので、
再開させると、グダグダになるのは、よくあるパターン。

ROSも、各ノードが独立しているので、1つのノードでデバッグしている間に、他のノードが進んでしまう。
いやいや、「ROS Time」があるんだから、
お作法良く、うまく作りこんでいるノードだったら、復帰できるよね?!って話

サンプル対象

以下のコードを拝借しました。

ROS講座08 pub & sub 遅延測定
https://qiita.com/srs/items/36511930b6ffae9bc87a
より、

  • basic_timing_talker.cpp:送信ノード
  • basic_timing_bridge.cpp:中継ノード
  • basic_timing_listener.cpp:受信ノード

準備

解析対象のノードを、Debugオプション付きでビルドしておく

$ catkin_make -DCMAKE_BUILD_TYPE=Debug 

送信ノードの出力TopicをRosbagで記録
ターミナル3つ開いて、以下を順に実行

$ roscore
$ rosbag record chatter1
$ roslaunch basic_lecture timing.launch

以降は、記録したBAGファイルの再生で動作確認を行う
ここでは、仮に2020-10-06-14-18-43.bagとして、以下、説明を記載

chatter1を出力するノードは、不要になるので、timing.launch内のname="basic_timing_talker1"をコメントアウトして、再ビルドしておく

<launch>
  <arg name="HZ_ALL" default="1"/>
<!-- 
  <node name="basic_timing_talker1" pkg="basic_lecture" type="basic_timing_talker"   output="screen">
    <remap from="chatter" to="chatter1"/>
    <param name="HZ" value="1"/>
  </node>
-->
  <node name="basic_timing_bridge1" pkg="basic_lecture" type="basic_timing_bridge"   output="screen">
    <param name="number" value="1"/>
    <remap from="chatter_in"  to="chatter1"/>
    <remap from="chatter_out" to="chatter2"/>
    <param name="HZ" value="$(arg HZ_ALL)"/>
  </node>
  <node name="basic_timing_bridge2" pkg="basic_lecture" type="basic_timing_bridge"   output="screen">
    <param name="number" value="2"/>
    <remap from="chatter_in"  to="chatter2"/>
    <remap from="chatter_out" to="chatter3"/>
    <param name="HZ" value="$(arg HZ_ALL)"/>
  </node>
  <node name="basic_timing_bridge3" pkg="basic_lecture" type="basic_timing_bridge"   output="screen">
    <param name="number" value="3"/>
    <remap from="chatter_in"  to="chatter3"/>
    <remap from="chatter_out" to="chatter4"/>
    <param name="HZ" value="$(arg HZ_ALL)"/>
  </node>
  <node name="basic_timing_bridge4" pkg="basic_lecture" type="basic_timing_bridge"   output="screen">
    <param name="number" value="4"/>
    <remap from="chatter_in"  to="chatter4"/>
    <remap from="chatter_out" to="chatter5"/>
    <param name="HZ" value="$(arg HZ_ALL)"/>
  </node>
  <node name="basic_timing_listener1" pkg="basic_lecture" type="basic_timing_listener" output="screen">
    <remap from="chatter" to="chatter5"/>
    <param name="HZ" value="$(arg HZ_ALL)"/>
  </node>
</launch>

実行

■解析対象全体を起動

ターミナル3つ開いて、以下を順に実行

$ roscore
$ rosbag play 2020-10-06-14-18-43.bag -r 0.1 --clock --pause
$ roslaunch basic_lecture timing.launch

注意:rosbag playは、pause、クロック生成で、レートを下げておく。レートが高いと、一連の動作が間に合わなくなる。

■解析対象ノードを特定

次に、新たにターミナルを開いて、プロセスIDを確認

$ ps -x

デバッグしたいノードのプロセスIDを見つける。
以下では、basic_timing_bridge1を解析対象とする。
解析対象のPIDは、4242とする。

■pause_playbackサービスを特定

rosbagのpause_playbackサービス名を確認

$ rosservice list

以下のような名前(数字の部分は、rosbagを実行する毎に変わる)
/play_1601994760080759400/pause_playback

■attach

GDBからデバッグしたいノードへattachする

$ gdb
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) attach 4242
[New LWP 4255]
[New LWP 4256]
[New LWP 4257]
[New LWP 4258]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f75684d0a30 in __GI___nanosleep (requested_time=0x7fffc69eda00, remaining=0x7fffc69eda10) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
28      ../sysdeps/unix/sysv/linux/nanosleep.c: No such file or directory.
(gdb)

■BreakPoint設定

止めたい場所に、breakpointを設定。breakpointを確認

(gdb) b basic_timing_bridge.cpp:11
Breakpoint 1 at 0x55608521f91e: file /root/lecture/src/basic_lecture/src/basic_timing_bridge.cpp, line 11.
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000055608521f91e in chatterCallback(boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&)
                                                   at /root/lecture/src/basic_lecture/src/basic_timing_bridge.cpp:11
(gdb)

■rosbag制御設定

該当のbreakpointでトリガーかかった場合に、外部コマンドとしてpause_playbackが実行されるように設定

(gdb) commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>shell rosservice call /play_1601994760080759400/pause_playback true
>end
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000055608521f91e in chatterCallback(boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&)
                                                   at /root/lecture/src/basic_lecture/src/basic_timing_bridge.cpp:11
        shell rosservice call /play_1601994760080759400/pause_playback true
(gdb)

■ROS処理を継続

  1. GDB側でcontを実行し、break状態を解除
  2. rosbag play側でspaceキーを押して、pauseを解除

■ROS処理停止

設定したbreakpointに到着すると、rosbag playもpauseされるので、ROS処理全体が停止。
breakpointで解析作業を終えたら、以下で継続
1. GDB側でcontを実行し、break状態を解除
2. rosbag play側でspaceキーを押して、pauseを解除

課題

  • rosbag playを停止させる反応が遅い。
  • VSCode内のデバッガからは、GDBのcommands命令が使えない(VSCodeが入力受け付けなくなる)

以上