Skip to content

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

Description

@ilmanzo

Edited 2026-08-31. The original body proposed two debuginfo-packaging causes. Both were wrong and have been retracted — see this comment 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 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions