ROS

ROS - GDB

GDB for ROS, Core Dump

Posted by Rico's Nerd Cluster on May 20, 2023

Introduction

GDB is EXTREMELY handy. I used to doing a lot of print statements, but now I use GDB a lot. The reason is simple: I can check multiple things at once without recompiling everytime, especially my compilation time is long.

Concepts

Debug Symbols in C or C++ are mappings from machine code to function names, variable names, line numbers, etc.

E.g., in a binary, i is just a memory location. When built with a Debug flag (see the Usage section), we know the variable’s name.

1
int i = 1;

Debugging symbols do not impact the binary execution logic. It’s purely informational and consumed by gdb. The binary filesize though would increase.

Compilation

  • In CMakeLists.txt
1
set(CMAKE_BUILD_TYPE Debug)
1
- Or alternatively, `catkin_make -DCMAKE_BUILD_TYPE=Debug`
  • In gcc:
1
gcc -g -o myprog myprogram.c

Run GDB

  • In a ROS Launch file:
1
<node pkg="rgbd_slam_rico" type="rgbd_rico_slam_mini" name="rgbd_rico_slam_mini" output="screen" launch-prefix="gdb -ex run --args"/>
  • Alternatively, you can run the binary using the command gdb:
1
gdb ./devel/lib/<PACKAGE_NAME>/binary

GDB Usage

Some common usages include:

  • b <FILE_NAME>:<LINE_NUM> set a breakpoint
  • r: run
  • c: continue
  • p <var_name>: print a variable name
  • info breakpoints: check which breakpoints are available
  • delete <BREAKPOINT_NUM>: to delete a break point’s number

Some cautions:

  • When debug printing an element in a cv::Mat, we need to specify its data type. To print it as a:
    • double (CV_64F), do (gdb) x/4g var. 4 means the first 4 values, g means double precision.
    • float (CV_32F): (gdb) x/4f var
    • int: (gdb) x/4d var

Core Dump

Assume we have multiple C++ nodes that crash after running a long time (so they are hard to reproduce). What would be a good way to start debugging?

  • Core dump

A “core dump” is a snapshot of a computer program’s memory state, typically taken when the program crashes or terminates abnormally, and it’s used to help diagnose the cause of the crash.

  • How to get it working:
    1. ulimit -c unlimited
    2. Adjust kernel settings to store it in /tmp:
      1
      
       cat /proc/sys/kernel/core_pattern
      
    3. To change it: sudo sysctl -w kernel.core_pattern=/tmp/core.%e.%p.%h.%t
    4. /etc/systemd/system.conf
      1
      
       DefaultLimitCORE=infinity
      
    5. sudo systemctl daemon-reload
    6. Create a crash program:
      1
      2
      3
      4
      5
      6
      7
      
       #include <stdio.h>
       int main() {
           // Intentionally dereference a NULL pointer to cause a segmentation fault.
           int *ptr = NULL;
           *ptr = 42;
           return 0;
       }
      
    7. Compile the executable with debugging symbols:
      1
      
       gcc -o0 crash_example crash_example.c
      
    8. Temporarily Enable Core Dumps: ulimit -c unlimited
    9. Verify that the current core dump file name pattern with: cat /proc/sys/kernel/core_pattern
    10. Run the program: ./crash_example
    11. Launch gdb: gdb ./crash_example /tmp/core.<executable_name>.<pid>.<hostname>.<timestamp>: and run bt at gdb. There will be backtrace saved