covdbg 1.5.0: Windows on ARM, 32-bit programs, Cobertura and exact counts

covdbg 1.5.0 is available. It measures programs on more of the machines you build for: natively on Windows on ARM, and 32-bit programs with the x64 covdbg. It exports Cobertura XML, counts every run of a block exactly in PERSISTENT mode, and measures a public repository in GitHub Actions without a secret.

In this release: Windows on ARM and 32-bit programs · Cobertura XML · Exact counts · No secret for public repositories · More in 1.5.0 · Upgrading

Windows on ARM and 32-bit programs

covdbg now comes in a native ARM64 build, which measures ARM64 programs on Windows on ARM. Download the ARM64 installer or ZIP.

The x64 covdbg also measures 32-bit x86 programs, which Windows runs under WOW64. There is no separate 32-bit covdbg to install: you run the same covdbg.exe against a 32-bit test executable, and it uses libcovdbg32.dll, a 32-bit build of its agent, which ships in the x64 installer and ZIP. The ARM64 build measures ARM64 programs only, and refuses a 32-bit one before it starts.

covdbg build Measures
x64 x64 and 32-bit x86 programs
ARM64 ARM64 programs

Coverage from different builds of a program combines too. covdbg merge keeps two different builds at one path, such as the x64 and ARM64 builds of your tests, side by side, and combines the lines of the sources they share. It also merges databases recorded in different checkouts, matching source files by their path relative to each run's source_root.

See Installation.

Cobertura XML

Codecov and most CI coverage dashboards read Cobertura, and it is what Windows C++ projects that measure coverage with OpenCppCoverage produce today. covdbg 1.5.0 writes it:

covdbg convert -i coverage.covdb -f COBERTURA -o coverage.xml

Each source file is a class and each module a package, the way OpenCppCoverage grouped them, so a dashboard and the coverage history behind it carry over. When you switch, the collector is the only step that changes:

- name: Convert to Cobertura
  shell: pwsh
  run: covdbg convert -i tests.covdb -f COBERTURA -o coverage.xml

- name: Upload to Codecov
  uses: codecov/codecov-action@v5
  with:
    files: coverage.xml

See Cobertura Format.

Exact counts in PERSISTENT mode

--mode PERSISTENT counts how many times each block ran. Until now, covdbg took a block's probe out to run the instruction under it and put it back afterwards. While it was out, another thread could run the same block without being counted, so busy code shared by many threads was undercounted.

In 1.5.0 the probe stays in place. covdbg runs the instruction from a relocated copy and jumps back, so the counts are exact, even when many threads run the same code at once. A few blocks still take the probe out: those that start with an indirect call, a trap such as int3 or ud2, or an ARM64 exclusive load or store, and every block in a process that runs with hardware shadow stacks (CET). Their counts are a lower bound when several threads run them at the same time.

PERSISTENT mode also counts an instruction once when it faults and the program's exception handler runs it again, including when the handler itself faults first, up to four faults nested in one another's handlers on a thread.

See the CLI reference.

No secret for public repositories

Public repositories are free, but until now their CI still had to carry a project token. Every public project had to create one and store it as a secret before its coverage step could pass.

From 1.5.0, a job in GitHub Actions only needs permission to request GitHub's token for itself:

jobs:
  coverage:
    runs-on: windows-latest
    permissions:
      contents: read
      id-token: write      # lets covdbg prove which repository this run belongs to

Without COVDBG_PROJECT_TOKEN and without a stored sign-in, covdbg presents that token, which GitHub signs with the name of the repository the job runs in. For a public repository nothing else is needed. A private repository still needs a project token, and one that is set takes precedence.

GitHub issues no token to a pull request from a fork, for the same reason it withholds secrets there. Measure pushes and pull requests from your own repository, and skip fork pull requests. The GitHub Actions guide shows how.

More in 1.5.0

  • CTest under covdbg. With --follow-children, a launcher without debug information of its own, such as ctest.exe or cmd.exe, runs unmeasured, and covdbg measures the processes it starts. covdbg --follow-children ctest.exe --test-dir build measures the test binaries CTest runs. Every process of one binary now adds its hits to one coverage database, so runs that start many processes finish much sooner.
  • Programs that debug themselves. JITs, anti-debug checks and embedded debuggers get their own single steps and breakpoints passed on. Code after a resumable __debugbreak is measured, and if (x) __debugbreak(); no longer loops forever.
  • More precise analysis. covdbg follows switch jump tables to their cases, as MSVC and Clang lay them out, and keeps probes out of jump tables and ARM64 literal pools.
  • Writable code is optional. --optimization-mode FAVOR_STABILITY gives probed code pages their original protection back, for targets or security software that object to writable code. FAVOR_SPEED, the default, keeps them writable for the fastest run.
  • MCP queries have limits. The covdbg mcp query tool stops a statement at a time limit and bounds the size of its result, so a runaway query no longer blocks your agent's session. covdbg mcp --query-timeout changes the limit.
  • Upgrades without a reboot. Upgrading or uninstalling covdbg while covdbg mcp runs no longer needs a reboot. The running server stays on the old version until the client reconnects.

Moving to 1.5.0

  1. Install covdbg 1.5.0. In GitHub Actions, set version: '1.5.0' on liasoft/setup-covdbg.
  2. Record your coverage again. Coverage databases are now schema 8, and 1.5.0 refuses .covdb files written by earlier versions.
  3. Check patterns with a / in them. A files: or modules: pattern with a / at the start or in the middle is now anchored to the directory of the .covdbg.yaml that holds it. src/**/*.cpp matches src\app\main.cpp next to the config, but no longer lib\src\x.cpp. A pattern without a /, or one that starts with **/, matches at any depth as before. See Configuration.
  4. Keep covdbg's files together. covdbg looks for its agent DLLs only beside covdbg.exe. A run without them now stops with exit code 1, where it used to record nothing and exit 0.
  5. Check your option values. An invalid --mode, --symbol-engine, --format or --log-level, or log_level in .covdbg.yaml, is refused instead of falling back to the default. covdbg analyze fails with exit code 1 when it cannot load its config.
  6. On a public repository in GitHub Actions, you can grant id-token: write and remove the COVDBG_PROJECT_TOKEN secret.

The full list of changes is in the changelog.

Try it with your project

Download covdbg and follow the quick-start guide. If you have questions about moving to ARM64, 32-bit programs or Cobertura, email our support team.

We store your theme and cookie choices to remember your preferences. With your permission, Google Analytics and our self-hosted Matomo help us understand how this website is used.

You can change your choice using Cookie settings in the footer. Cookie policy · Privacy policy