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

- Published: 2026-10-23
- Author: Rene Windegger
- Canonical: https://covdbg.com/announcing-covdbg-1-5-0/

---

**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](#windows-on-arm-and-32-bit-programs) · [Cobertura XML](#cobertura-xml) · [Exact counts](#exact-counts-in-persistent-mode) · [No secret for public repositories](#no-secret-for-public-repositories) · [More in 1.5.0](#more-in-150) · [Upgrading](#moving-to-150)

## 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](/docs/getting-started/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:

```powershell
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:

```yaml
- 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](/docs/guides/report-formats/#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](/docs/reference/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:

```yaml
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](/docs/integrations/github-actions/) 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](/docs/reference/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](/docs/changelog/).

## Try it with your project

[Download covdbg](/download/) and follow the [quick-start guide](/docs/getting-started/quick-start/). If you have questions about moving to ARM64, 32-bit programs or Cobertura, email [our support team](/company/contact/#email-addresses).
