This guide explains how to attach a debugger to CMake's unit testing framework. We'll focus on using GDB on Linux for both command-line and IDE debugging. See documentation on CMake Development for more information.
On Linux, the GNU Debugger (GDB) is the standard tool for debugging the
CMake test suite. The core process involves launching the cmake executable
from within GDB with a specific set of arguments that configure and run the
desired test.
For effective debugging, GDB must be configured to handle child processes
correctly, which CMake tests often create. A good practice is to use a local
.gdbinit file in your build directory. This keeps CMake-specific settings
separate from your global configuration.
1. Enable Local .gdbinit Files (One-Time Setup)
To allow GDB to automatically load configuration from your build directory,
add the following line to your global GDB initialization file at
$HOME/.gdbinit. This is a one-time setup that makes future projects easier
to manage.
set auto-load local-gdbinit on
2. Create a Project-Specific .gdbinit
Next, create a .gdbinit file inside your CMake build directory.
This file will contain settings specific to debugging CMake.
To make this easier, you can symlink the template file provided in the CMake
source tree:
# Navigate to your build directory
cd /path/to/your/cmake/build
# Create a symlink to the template
ln -s $cmake_srcdir/Utilities/gdb/gdbinit-template .gdbinitThe template contains the essential settings for debugging CMake tests:
# Allows GDB to follow child processes
set follow-fork-mode child
# Allows the parent process continue in parallel
set non-stop onTo start debugging, first cd to the build directory. Then, launch the
cmake executable using gdb --args, which passes the necessary test
configuration arguments directly to CMake.
Note
To get the launch command, run ctest -R "RunCMake.$TESTNAME" -VV -N
The following example runs the InstallPackageInfo test.
# Define paths to your CMake source and build directories
CMAKE_SOURCE_DIR="$HOME/cmake"
CMAKE_BUILD_DIR="$CMAKE_SOURCE_DIR/build"
# Define the specific test to run
TEST_NAME="InstallPackageInfo"
# Navigate to the build directory
cd "$CMAKE_BUILD_DIR"
# Launch GDB with the appropriate arguments for the test
gdb --args ./bin/cmake \
"-DCMAKE_MODULE_PATH=$CMAKE_SOURCE_DIR/Tests/RunCMake" \
"-DRunCMake_GENERATOR=Ninja" \
"-DRunCMake_SOURCE_DIR=$CMAKE_SOURCE_DIR/Tests/RunCMake/$TEST_NAME" \
"-DRunCMake_BINARY_DIR=$CMAKE_BUILD_DIR/Tests/RunCMake/$TEST_NAME" \
"-P" "$CMAKE_SOURCE_DIR/Tests/RunCMake/RunCMakeTest.cmake"Once GDB loads, you may set breakpoints (e.g., b cmInstallCommand) and
then start the test by typing run.
Some test suites contain multiple sub-tests. To run only a specific one,
you can use the RunCMake_TEST_FILTER environment variable.
For example, to run only the "Metadata" test within the InstallPackageInfo
suite, you can set the variable before launching GDB:
RunCMake_TEST_FILTER="Metadata" gdb --args ...Alternatively, you can set the environment variable from within the GDB session before running the test:
(gdb) set environment RunCMake_TEST_FILTER Metadata
(gdb) run
You can also debug CMake tests directly from your IDE.
If you have configured GDB to auto-load local .gdbinit files as described
above, CLion will automatically pick up the necessary settings.
A simple way to debug a test is to modify its CTest run configuration:
- Select the Test: In the "Run/Debug Configurations" dialog, find the
CTestentry for your test (e.g.,RunCMake.InstallPackageInfo). - Add CTest Arguments: In the "CTest arguments" field, add
--extra-verbose. This is helpful for debugging because it prints the exact commandCTestuses to run the test. - Set Working Directory: Ensure the "Working Directory" field is set to
$CMakeCurrentLocalGenerationDir$.
You can now set breakpoints in your code and debug this configuration.
Create a launch.json file in the .vscode directory of your
CMake source folder with the following configuration. This configuration
hardcodes the necessary GDB settings, so it does not depend on an external
.gdbinit file.
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug CMake Test",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/cmake",
"args": [
"-DCMAKE_MODULE_PATH=${workspaceFolder}/Tests/RunCMake",
"-DRunCMake_GENERATOR=Ninja",
"-DRunCMake_SOURCE_DIR=${workspaceFolder}/Tests/RunCMake/InstallPackageInfo",
"-DRunCMake_BINARY_DIR=${workspaceFolder}/build/Tests/RunCMake/InstallPackageInfo",
"-P",
"${workspaceFolder}/Tests/RunCMake/RunCMakeTest.cmake"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build",
"environment": [],
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Follow child processes",
"text": "set follow-fork-mode child",
"ignoreFailures": true
},
{
"description": "Don't stop the parent process",
"text": "set non-stop on",
"ignoreFailures": true
}
]
}
]
}Note
Remember to change the test name (InstallPackageInfo) in the "args" section to the specific test you want to debug.