Skip to content

Valgrind

Adds Ceedling tasks to run test executables under Valgrind memory error detection to help find memory leaks, invalid memory accesses, and use-after-free bugs in your C code.

The Valgrind plugin creates a duplicate test build with valgrind: command line plugin tasks. This is intentional and needed.

Note

A valgrind: task is a delta build. This plugin maintains its own dependency cache. A test executable will be rebuilt the first time you run a valgrind: build.


Plugin overview

Linux only

Valgrind runs on Linux and other Unix-like systems. It is not available on Windows or macOS (ARM).

Enabling this plugin on a platform without Valgrind does not affect your other builds. ceedling test:all and every non-valgrind: task run normally. Only a valgrind: task requires the executable, and it reports the platform constraint when it cannot find one.

The Valgrind plugin integrates Valgrind dynamic analysis into Ceedling, building and then running test executables under Valgrind’s Memcheck tool.

The plugin provides two invocation modes mirroring standard test: tasks.

  • Whole test suite: By running ceedling valgrind:all, all test executables are built and then each is run under Valgrind. A separate log file is produced for each test executable in build/artifacts/valgrind/.
  • Single test file: By running ceedling valgrind:<filename>, a single test executable is built and run under Valgrind, producing one log file.

The plugin triggers standard test task builds but hands each test fixture executable off to Valgrind along with configurable arguments.

By default the build runs all test binaries to completion and is marked as failed if any Valgrind memory errors are found. The optional :fail_build: setting can be set to false to log errors without failing the build.

Installation & set up

Installation

You have at least 3 options for getting the valgrind tool working on your system, including one readymade option.

Linux package manager

Install Valgrind from your Linux distribution’s package manager:

sudo apt-get install valgrind   # Debian / Ubuntu
sudo dnf install valgrind       # Fedora / RHEL

Build from source

Or build from source from valgrind.org/downloads/.

MadScienceLab Docker images

Fully packaged MadScienceLab Docker images containing Ruby, Ceedling, the GCC toolchain, and more are available. The -plugins variants of the images come with all of Ceedling’s plugin tools preinstalled.

Enable the plugin

Enable the plugin by adding valgrind to the enabled plugins list in your project configuration:

:plugins:
  :enabled:
    - valgrind

Sanitizer flags

A test executable built with sanitizers (-fsanitize) cannot run under Valgrind. A valgrind: build whose flags carry -fsanitize stops with a message naming them.

Configuration

The plugin ships with sensible defaults that enable full leak checking. By default the build runs all test binaries to completion and is marked as failed if any memory errors are found. Set :fail_build: false to log errors without failing the build.

You can change the Valgrind configuration in your project configuration under an optional :valgrind section.

Default configuration

In the default configuration, the Valgrind plugin will run the entire test suite, collecting Valgrind reports for each test fixture executable.

By default, the plugin runs with these command line arguments for valgrind:

  • --leak-check=full
  • --show-leak-kinds=all
  • --track-origins=yes
  • --errors-for-leak-kinds=all

:arguments:

The list of flags passed to Valgrind for every test binary. To override the defaults, redefine the argument list in your project configuration:

:valgrind:
  :arguments:
    # A subset of the default arguments
    - "--leak-check=full"
    - "--show-leak-kinds=all"

:fail_build:

When true (the default), any memory errors are logged immediately after running a test executable. After all test binaries complete execution, if any memory errors were found across the run, the build is marked as failed with a total count of memory errors and test files processed.

When false, memory errors are still logged per test binary but the build is not marked as failed.

:valgrind:
  :fail_build: false

Detection uses report parsing, not Valgrind’s exit code

Valgrind’s --error-exitcode flag is not used because its exit code would overlap with Unity’s own exit code, which equals the number of failing test cases. Instead, the plugin counts errors from the report it wrote.

With :xml_report: disabled, it sums every ERROR SUMMARY: N errors line in the text log. Valgrind writes one such line per traced process, so a test that forks reports more than one.

With :xml_report: enabled, it counts <error> elements in the XML instead. Valgrind writes no error summary to the text log once XML output is active.

:suppressions:

A list of Valgrind suppression files. Each becomes one --suppressions= argument, placed ahead of your own :arguments: entries.

:valgrind:
  :suppressions:
    - suppressions/third_party.supp

Default: []

A malformed suppression file aborts the run

Valgrind refuses to start when it cannot parse a suppression file, which surfaces as a test executable that produced no output. Each suppression needs at least one location line that is not ....

:xml_report:

When true, writes a machine-readable XML report per test binary alongside the text log. Useful for CI systems that consume Valgrind output directly.

:valgrind:
  :xml_report: true

Default: false

Usage

The Valgrind plugin follows the conventions of test: tasks.

Run Valgrind for all tests

Build all test executables and run each under Valgrind:

$ ceedling valgrind:all

Run Valgrind for a single test file

Build and run Valgrind for one test file (pass the filename, no path):

$ ceedling valgrind:test_my_module.c

You can also pass just the source filename; Ceedling will locate the corresponding test file automatically:

$ ceedling valgrind:my_module.c

Artifacts

Each test executable produces a Valgrind log file at:

build/artifacts/valgrind/<test_name>.log

With :xml_report: enabled, a machine-readable report is written beside it:

build/artifacts/valgrind/<test_name>.xml

For example, running ceedling valgrind:test_my_module.c produces:

build/artifacts/valgrind/test_my_module.log

These log files contain the full Valgrind Memcheck output, including any detected memory errors, leak summaries, and (with --track-origins=yes) the origin of uninitialized values.

The log files are included in ceedling clean targets.

Console output

These excerpts come from real runs. Build progress lines are left out.

A clean run adds one notice after the test summary:

VALGRIND: ✅ OVERALL TEST SUMMARY
TESTED:  2
PASSED:  1
FAILED:  0
IGNORED: 1

ℹ️ NOTICE: Wrote 1 Valgrind report to build/artifacts/valgrind/

A test that a memory error ends gets a notice naming the first error and its report. It replaces Ceedling’s generic crash notice:

☠️ ERROR: Test executable `test_nosanitize_crash_sigsegv.out` terminated with Valgrind memory errors.
> Invalid read of size 4 at test_nosanitize_crash_sigsegv.c:32
> Boom log: build/artifacts/valgrind/test_nosanitize_crash_sigsegv.log

VALGRIND: FAILED TEST SUMMARY
[test/test_nosanitize_crash_sigsegv.c]
  Test: test_add_numbers_will_fail
  At line (28): "Test case crashed >> Segmentation fault"

VALGRIND: ❌ OVERALL TEST SUMMARY
TESTED:  2
PASSED:  1
FAILED:  1
IGNORED: 0

ℹ️ NOTICE: Wrote 1 Valgrind report to build/artifacts/valgrind/

❌ BUILD FAILURE SUMMARY
 • [VALGRIND] Unit test failures
 • [VALGRIND] Valgrind detected 2 memory errors across 1 test

With :fail_build: set to false, the same run omits the last line. Only the test failure fails the build.

A test that completes despite memory errors logs one line as it runs:

🪲 ERROR: Valgrind detected 2 memory errors in test_example ➡️ see build/artifacts/valgrind/test_example.log

A test whose report is missing logs a warning, since its memory error status is unknown:

⚠️ WARNING: Valgrind wrote no report for test_example at build/artifacts/valgrind/test_example.log. Memory error status is unknown.

Interpreting results

To review the log for a specific test:

cat build/artifacts/valgrind/test_my_module.log

Common Valgrind findings include:

Finding Description
definitely lost Memory was allocated but never freed — a confirmed leak
indirectly lost Memory reachable only through another leaked block
Invalid read/write Access outside allocated memory bounds
Use of uninitialised value Reading memory before it was written

See Valgrind documentation for more details.