ilmanzo / ilmanzo/BinaryCoverage

cancel, lp, lpoptions, nfsrahead report 0% coverage despite being executed

Open
#158 3 comments 0 reactions 1 assignee View on GitHub

@ilmanzo is already working on this.

Since Aug 31, 2026.

Dominant language
C
Stars
9
Forks
6
Avg merge
1h 32m
Merged PRs (30d)
11

Description

> **Edited 2026-08-31.** The original body proposed two debuginfo-packaging causes. Both were **wrong** and have been retracted — see [this comment](https://github.com/ilmanzo/BinaryCoverage/issues/158#issuecomment-5475990530) for the retraction, a reproducer script, and a verified patch. This body now describes the actual findings.

## Observation

openQA job https://openqa.opensuse.org/tests/6194777 reports **0% function coverage** for binaries that definitely execute during the test:

- `cancel`, `lp`, `lpoptions` — console/cups (`coverage_targets` key `cups-client`)
- `nfsrahead` — console/rpcbind, console/postgresql_server (key `nfs-client`)

Schedule: `cups_rpcbind_coverage.yaml`. The `coverage_targets` mapping is correct — binaries are keyed under their real owning packages, so `coverage_setup.pm` does request `cups-client-debuginfo` and `nfs-client-debuginfo`, and both exist and are complete. **Debuginfo packaging is not the problem.**

Two independent root causes, both reproduced on an openSUSE Tumbleweed VM.

## A — `lp` / `cancel` / `lpoptions`: the only instrumented function is the error path

Not a tracing failure. CUPS 2.4.19 `systemv/lp.c` defines four functions: `main` (on funkoverage's `funcBlacklist`), `restart_job` and `set_job_attrs` (static, inlined away, absent from `.symtab`), and `usage` (`_CUPS_NORETURN`, reached only from `--help` or an argument error — 17 call sites, all error paths).

So the entire instrumented set is `usage`, and a successful run can never reach it:

```
$ funkoverage enumerate --no-libs /usr/bin/lp
/usr/bin/lp usage
Total: 1 functions across 1 image(s)

normal run (lp -d nonexistent /etc/hostname) : 0 functions called -> 0%
help run (lp --help) : 1 function called -> 100%
```

The traceable-function count predicts the reported set exactly — including the binaries that were **not** reported:

| binary | traceable functions | reported 0%? |
|---|---|---|
| `lp`, `cancel` | 1 (`usage`) | ✅ |
| `lpoptions` | 2 | ✅ |
| `lpadmin` | 3 | ❌ |
| `lpstat` | 10 | ❌ |

The 0% is arithmetically correct but indistinguishable from "never executed".

## B — `nfsrahead`: the shim aborts under udev, so the binary never runs at all

`nfsrahead` is a udev callout (`99-nfs.rules`: `SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="/usr/libexec/nfsrahead %k"`), not a shell command.

- Direct invocation from a root shell → 10 functions traced, fine.
- Invocation by udev → **0**, every `_called.log` 0 bytes.

```
11:0: '/usr/libexec/nfsrahead 11:0'(err) 'funkoverage-shim: helper: tracer start: \
tracer: attach fork tracepoint: ope...'
11:0: Process '/usr/libexec/nfsrahead 11:0' failed with exit code 1.
```

`Tracer.Start()` aborts at `link.Tracepoint("sched","sched_process_fork")` — the only attach needing `perf_event_open(2)` rather than `bpf(2)`. systemd-udevd's seccomp allow-list has `bpf` but **not** `perf_event_open` (capabilities are fine: its bounding set includes `cap_bpf`, `cap_perfmon`, `cap_sys_admin`). Allowing just that syscall makes coverage appear immediately.

**This is worse than lost coverage:** the error propagates to `os.Exit(1)` *before* `syscall.Exec(realBin)`, so while shimmed, udev's callout is a stub that always fails — `nfsrahead` never runs and NFS readahead tuning silently breaks. Any shimmed binary invoked from a seccomp-sandboxed unit hits this.

## Proposed fix

Implemented and verified (see the [comment](https://github.com/ilmanzo/BinaryCoverage/issues/158#issuecomment-5475990530) for the diff and reproducer):

1. **Remove `main` from `funcBlacklist`** — it is the one entry point that is real user code, and the liveness signal that separates "ran" from "never ran".
2. **Make the fork tracepoint non-fatal** — it only extends tracing to children; degrade instead of aborting.
3. **Fail open in `runWithTracing`** — if the tracer can't attach, warn and exec the real binary anyway. Never replace the user's program with a failing stub.

| case | before | after |
|---|---|---|
| `lp -d nonexistent file` | 0/1 → 0% | 1/2 → 50% |
| `nfsrahead` via udev | 0/16 → 0%, binary never ran | 10/16 → 62.5%, binary runs |

Environment: openSUSE Tumbleweed, `cups-2.4.19-2.3`, `nfs-client-2.9.2-54.1`, BinaryCoverage @ `5907733`.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.