Valgrind with ROS
Introduction
Valgrind is a programming tool used for memory debugging. In the ros wiki (accessed Aug 2023), the use of using roslaunch
with valgrind is described. This post will demonstrate the use of valgrind
with a rosnode
via roslaunch
.
The results of this post was obtained on Ubuntu 20.04 with ROS Noetic.
Setup
First, you need to have valgrind
installed. On Ubuntu, you can install valgrind
with:
sudo apt install valgrind
Roslaunch File
Using roslaunch
, one simply needs to include valgrind
in the launch-prefix
tag for the node to be launched with valgrind
. The segfault-example-node will be used as an example node to be launched.
<launch>
<node pkg="ros_segfault_example" type="segfault_example_node" name="segfault_example_node"
launch-prefix="valgrind" />
</launch>
Running
Launching the rosnode
via roslaunch
, in addition to the usual roslaunch
printout, the following also appears:
==7169== Memcheck, a memory error detector
==7169== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7169== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==7169== Command: /home/bronya/catkin_ws/devel/lib/ros_segfault_example/segfault_example_node __name:=segfault_example_node __log:=/home/bronya/.ros/log/94e98a06-41a1-11ee-a929-f3b6ab2164c3/segfault_example_node-1.log
==7169==
This indicates that valgrind is indeed running with the target executable.
Debugging
In my case, I will induce a segfault. After a segfault was induced, the following can be found near the top of the printout:
...
==7169== Invalid read of size 4
==7169== at 0x128959: channelCB1(boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&) (segfault_example_node.cpp:24)
==7169== by 0x12ED0B: boost::detail::function::void_function_invoker1<void (*)(boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&), void, boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&>::invoke(boost::detail::function::function_buffer&, boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&) (function_template.hpp:117)
==7169== by 0x130D6F: boost::function1<void, boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&>::operator()(boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&) const (function_template.hpp:763)
==7169== by 0x1303B5: boost::detail::function::void_function_obj_invoker1<boost::function<void (boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&)>, void, boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> >::invoke(boost::detail::function::function_buffer&, boost::shared_ptr<std_msgs::String_<std::allocator<void> > const>) (function_template.hpp:158)
==7169== by 0x132A50: boost::function1<void, boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> >::operator()(boost::shared_ptr<std_msgs::String_<std::allocator<void> > const>) const (function_template.hpp:763)
==7169== by 0x132290: ros::SubscriptionCallbackHelperT<boost::shared_ptr<std_msgs::String_<std::allocator<void> > const> const&, void>::call(ros::SubscriptionCallbackHelperCallParams&) (subscription_callback_helper.h:144)
==7169== by 0x49A2138: ros::SubscriptionQueue::call() (in /opt/ros/noetic/lib/libroscpp.so)
==7169== by 0x4950171: ros::CallbackQueue::callOneCB(ros::CallbackQueue::TLS*) (in /opt/ros/noetic/lib/libroscpp.so)
==7169== by 0x4951882: ros::CallbackQueue::callAvailable(ros::WallDuration) (in /opt/ros/noetic/lib/libroscpp.so)
==7169== by 0x49A4FCE: ros::SingleThreadedSpinner::spin(ros::CallbackQueue*) (in /opt/ros/noetic/lib/libroscpp.so)
==7169== by 0x498D21E: ros::spin() (in /opt/ros/noetic/lib/libroscpp.so)
==7169== by 0x128CDE: main (segfault_example_node.cpp:36)
...
Indeed, similar to the example in gdb with ros, it also points to the fact that line 24 was problematic. In particular, I was trying to access element 0 of a std::vector
of length 0 and capacity 0.
Conclusion
In this post, I’ve demonstrated the simple use of valgrind
with ROS, via integrating in via roslaunch
. Valgrind is much more powerful than the example I’ve shown here, and it is often using for debugging memory issues and profiling. Indeed, if you suspect memory leak, you can start profiling memory leak by adding arguments to the launch-prefix
like so:
<launch>
<node pkg="node_package" type="example_node" name="example_node_name"
launch-prefix="valgrind --tool=memcheck --leak-check=yes" />
</launch>
Comments or discussions may be posted here.