Skip to content

pkg/coverage

LLVM source-based code coverage collection, HTML report generation, and per-file summary statistics.

import "github.com/professor-moody/crucible/pkg/coverage"

Types

FileCoverage

type FileCoverage struct {
    Filename  string
    Lines     int
    LineHits  int
    LinePct   float64
    Funcs     int
    FuncHits  int
    FuncPct   float64
    Branches  int
    BranchHit int
    BranchPct float64
}

Coverage statistics for a single source file, parsed from llvm-cov export --summary-only JSON output.


ReportSummary

type ReportSummary struct {
    Files      []FileCoverage
    TotalLines int
    HitLines   int
    LinePct    float64
    ProfileDir string
}

Aggregate coverage data across all source files. Files is sorted by LinePct ascending (worst-covered files first).


Functions

CollectAndMerge

func CollectAndMerge(harness, corpusDir, tmpDir string) (string, error)

Replays each file in corpusDir through the harness binary, collecting raw .profraw files via the LLVM_PROFILE_FILE environment variable. Merges all profiles into a single .profdata file using llvm-profdata merge -sparse.

Returns the path to the merged profdata file within tmpDir. The caller is responsible for cleaning up tmpDir.

The harness must be built with -fprofile-instr-generate -fcoverage-mapping (and not -fsanitize=fuzzer, which would enter the fuzzing loop). Hidden files (.-prefixed) and subdirectories in corpusDir are skipped.

Returns an error if no profile data is collected from any corpus entry.

GenerateHTML

func GenerateHTML(harness, profdata, sourceDir, outputDir string) error

Runs llvm-cov show to produce an HTML coverage report directory. The report includes annotated source files with line-by-line execution counts and region highlighting.

When sourceDir is non-empty, it is passed as -path-equivalence=/,<sourceDir> to remap source paths.

GenerateSummary

func GenerateSummary(harness, profdata string) (*ReportSummary, error)

Runs llvm-cov export --summary-only and parses the JSON result into per-file coverage statistics. Returns a ReportSummary with files sorted by line coverage percentage (worst first).

FormatSummary

func FormatSummary(s *ReportSummary) string

Returns a human-readable table of per-file coverage stats. Includes columns for file name, lines (total/hit/percent), and functions (total/hit/percent). Long file paths are shortened for display.


Usage

tmpDir, _ := os.MkdirTemp("", "crucible-cov-*")
defer os.RemoveAll(tmpDir)

// Collect coverage from corpus replay
profdata, err := coverage.CollectAndMerge("./crucible-cov", "./corpus", tmpDir)
if err != nil {
    log.Fatal(err)
}

// Generate HTML report
coverage.GenerateHTML("./crucible-cov", profdata, "/path/to/llama.cpp", "./coverage-report")

// Print per-file summary
summary, _ := coverage.GenerateSummary("./crucible-cov", profdata)
fmt.Println(coverage.FormatSummary(summary))

Prerequisites

The harness binary must be built with LLVM coverage instrumentation:

clang -g -O1 -fprofile-instr-generate -fcoverage-mapping \
  -DGGML_USE_CPU \
  ggml/src/ggml.c ggml/src/gguf.c \
  harness/libfuzzer/harness.c \
  -o crucible-cov \
  -lstdc++ -lm

llvm-profdata and llvm-cov must be available in PATH.