GDB support for republishing ROS messages in an rr replay
Be sure to have installed the newest build of rr and GDB compiled with Python 2 by following the instructions from here.
-
Clone this repository into e.g. your home folder:
git clone https://github.com/larics/gdb_ros_publisher
-
Add the following to
.gdbinit
in your home directory. If you cloned the repository elsewhere, adjust the paths accordingly.define ros_publisher_setup source ~/gdb_ros_publisher/GdbPublisherNode.py source ~/gdb_ros_publisher/RunGdbPublisherNode.py source ~/gdb_ros_publisher/gdb_ros_publisher_setup end
-
If necessary, customize the
RunGdbPublisherNode.py
file.-
Additional topics and message types can be set up by adding additional instances of
GdbPublisher
to theGdbPublisherDictionary
instance. The class is constructed as follows:GdbPublisher(topic, msgtype, queue_size = 1)
The arguments of the constructor are:
topic
, string – topic name, without the leading slashmsgtype
– a ROS message class typequeue_size
, integer – ROS publisher queue size, optional (default: 1)
-
Build the helper library for intercepting message publication:
g++ -shared -fPIC -o gdb_ros_publisher_helper.so -I /opt/ros/$ROS_DISTRO/include -g gdb_ros_publisher_helper.cpp
Tell rr to inject this library into your node:
rr record --env=LD_PRELOAD=/home/user/gdb_ros_publisher/gdb_ros_publisher_helper.so <node and arguments>...
-
- Run a
roscore
server and start a gdb debugging session (rr replay
). Useful options are-M
(mark event numbers) and-k
(keeps listening, useful for using with an IDE). - Call the
ros_publisher_setup
command in the gdb shell.
Afterwards, you can use the gdb commands enable ros_publisher
and disable ros_publisher
to enable/disable publishing (as with all gdb commands, you can use abbreviations, for example ena ros
and dis ros
).
http://larics.fer.hr/laricswiki/doku.php?id=software:debugging#reversible_debugging_with_rr
The included breakpoint in gdb_ros_publisher_helper.cpp
should be enough to catch all published messages. To add a new message publishing breakpoint, add a new GdbMessageBreakpoint
instance to the breakpoints
list. The constructor has the following prototype:
GdbMessageBreakpoint(location, context_extractor = lambda: {})
The arguments of the constructor are:
location
, string – location in the source code from where the message will be extracted (it has to be completely initialized and ready for publishing at this point in the source code)context_extractor
, lambda function which returns a dictionary – this function is executed every time the breakpoint is reached, and computes a dictionary with extracted message variables. The following keys can be specified (all are optional, and default behavior is adapted for a breakpointvoid ros::Publication::publish(const M &message)
):serialized_variable
,gdb.Value
– the variable with the serialized messagetopic
, string – the topic on which the message is published; it has to match the topic in the appropriateGdbPublisher
. The default behavior is to look up the topic name from thethis->name_
string, wherethis
is theros::Publication
instance from invocation ofros::Publisher::publish()
latch
, boolean - whether the topic is latched, by default obtained fromthis->latch_
in ros::Publication
You can use the methods in the gdb
module when writing variable extractors. Don't forget that they need to be inside a lambda function when instancing a publishing breakpoint, because they are executed every time the breakpoint is hit. Here are some examples:
-
Get a variable named
message
:gdb.selected_frame().read_var('message')
-
Get a variable named
message
, which is a shared pointer in this particular case, and dereference it (in libstdc++, the pointer is stored in thepx
field of a shared pointer object; for a unique pointer, the pointer is located in_M_t._M_head_impl
):gdb.selected_frame().read_var('message')['px'].dereference()
For accessing structure members, the
[]
Python operator is used on thegdb.Value
object that represents a C++ structure. -
Get the string in
this->impl_->topic_
, whereimpl_
is a shared pointer andtopic_
is a C++ string:str(gdb.selected_frame().read_var('this').dereference() ['impl_']['px'].dereference()['topic_']['_M_dataplus']['_M_p'].string())
The
string()
method of agdb.Value
(whose C++ type ischar *
) returns a Unicode string, sostr()
is finally used to convert it in a regular Python string. -
You can also use
gdb.parse_and_eval('c++ expression')
to evaluate C++ expressions (such as function/method calls)