codevol

package
v0.0.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package codevol measures how much code a Go program is made of, and how much of it is somebody else's (ADR-0173).

Three acquisition tiers live here, separated by what they need rather than by what they answer. That separation is the point: the cheap tiers work on a deploy target with no toolchain, no source tree and no module cache, where the expensive one cannot run at all.

  • Modules reads the module list out of the running binary via runtime/debug. Costs microseconds, needs nothing.
  • ReadSelfSymbols reads the binary's own symbol table to report what the linker actually kept after dead-code elimination. Tens of milliseconds, needs only an unstripped ELF binary.
  • CountFiles classifies source lines with go/scanner. Needs the source tree, so the caller supplies the file paths — this package deliberately does not import golang.org/x/tools, which is what lets it be registered into the static provider set beside the other two.

Nothing here imports a UI package or the introspect registry; the providers in keelson/runtime/introspect/providers wrap these into tables.

Index

Constants

View Source
const GeneratorUnknown = "unknown"

GeneratorUnknown is reported for a file whose marker is present but carries no usable tool name.

Variables

View Source
var PackageProps = packageprops.Props{
	WASMWASI:         packageprops.WASMBlocked,
	WASMJS:           packageprops.WASMBlocked,
	WASMFreestanding: packageprops.WASMBlocked,
}

PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it.

Blocked on every WASM target: ReadSelfSymbols reads the running executable's own ELF symbol table, which none of the WASM targets provide. The other two tiers would compile.

Functions

func GeneratorOf

func GeneratorOf(line string) (tool string, ok bool)

GeneratorOf extracts the tool from a generated-code marker line, reporting ok only when the line is such a marker.

The tool field is free-form prose that nothing validates — values in this repository run from an import path through a shell invocation to "copy paste" (and, in four files, "copy pase") — so this trims and normalises rather than parsing, and never rejects a marker it matched: there is always some string to group by.

func PackageOfSymbol

func PackageOfSymbol(name string) (pkg string)

PackageOfSymbol derives an import path from a Go linker symbol name.

The rule is "everything up to the first '.' after the last '/'", which is exact for ordinary function and method symbols and approximate for the rest: generic instantiations and some synthesised symbols yield keys that are not real import paths. Module attribution does not depend on this precision (a longest-prefix module match tolerates a too-long key), which is why the party split stays exact while the package grain does not.

Returns "" when no package can be derived.

Types

type ModuleIndex

type ModuleIndex struct {
	// contains filtered or unexported fields
}

ModuleIndex resolves an import path to the module that owns it. It is built from the in-binary module list, so the resolution is exact for every module the linker recorded — no toolchain, no guessing.

func NewModuleIndex

func NewModuleIndex(mods []ModuleInfo) (idx *ModuleIndex)

NewModuleIndex builds an index over mods.

func (*ModuleIndex) Lookup

func (idx *ModuleIndex) Lookup(importPath string) (modulePath string, party Party)

Lookup returns the module owning importPath and its party. A path matching no module is standard library: the stdlib is the one body of code in a Go binary that has no module, so "unmatched" and "stdlib" are the same statement rather than a failure to resolve.

type ModuleInfo

type ModuleInfo struct {
	Path string
	// Version is the module version, or "(devel)" for a main module built
	// outside a tagged checkout.
	Version string
	// Sum is the go.sum checksum. Empty for the main module and for any
	// module resolved through a replace directive.
	Sum string
	// ReplacedBy is "path@version" when a replace directive redirected this
	// module, empty otherwise. A replaced module's code is not what its
	// Path/Version claims, so a supply-chain reading must not ignore it.
	ReplacedBy string
	IsMain     bool
	Party      Party
}

ModuleInfo is one Go module linked into the running binary, as recorded by the toolchain at link time.

func Modules

func Modules() (mods []ModuleInfo, ok bool)

Modules reports every module linked into the running binary.

This is the cheapest and most portable evidence available about a Go program's composition: it needs no toolchain, no source and no module cache, because the linker recorded it in the binary. ok is false only when the binary carries no build information at all, which the toolchain omits for some non-module builds.

One caveat worth knowing before trusting a small answer: a `go test` binary carries the main module but an **empty dependency list**, so under `go test` this returns one row. Only a `go build` binary carries the full list. The integration lane covers the built-binary case for that reason.

type PackageSymbols

type PackageSymbols struct {
	PkgPath    string
	ModulePath string
	Party      Party
	NumSymbols int
	// TextBytes is machine code. DataBytes is everything else the symbol
	// table sizes — read-only data, initialised data, BSS.
	//
	// They are separate because conflating them is actively misleading: in
	// the boxer binary crypto/internal/fips140/drbg.memory is a single 32 MiB
	// zero-filled buffer, 42% of all sized bytes and not code at all. Summing
	// the two makes the standard library look like half the program.
	TextBytes uint64
	DataBytes uint64
}

PackageSymbols is one package's contribution to the linked binary, as the linker left it after dead-code elimination.

type Party

type Party string

Party classifies a module relative to the program being measured. It is the axis the "our code vs third-party code" question is actually asked on.

const (
	// PartyFirst is the main module — the code this repository owns.
	PartyFirst Party = "first"
	// PartyThird is any other module in the build.
	PartyThird Party = "third"
	// PartyStdlib is the standard library, which carries no module.
	PartyStdlib Party = "stdlib"
)

type SymbolReport

type SymbolReport struct {
	Packages  []PackageSymbols
	TotalText uint64
	TotalData uint64
	// Unattributed counts sized symbols that carry no derivable package —
	// linker-synthesised itabs and type descriptors, mostly.
	Unattributed int
	// ModuleExact records whether module attribution came from the in-binary
	// module list (true) or was left unresolved (false). Package attribution
	// is always heuristic; see [PackageOfSymbol].
	ModuleExact bool
}

SymbolReport is one reading of the binary's own symbol table.

func ReadSelfSymbols

func ReadSelfSymbols(idx *ModuleIndex) (rep SymbolReport, err error)

ReadSelfSymbols reads the running executable's symbol table and rolls it up per package.

This is the "what actually shipped" lens, and unlike a call-graph analysis it is a fact rather than an approximation: the linker already decided, and this reads the decision. On the boxer binary it costs about 30 ms and 38 MB for 122k symbols, and its text total agrees with `go tool nm` to within 0.01%.

idx may be nil, in which case module attribution is skipped and every row reports the stdlib party.

type Volume

type Volume struct {
	Files int
	// CodeLines counts lines bearing at least one non-comment token,
	// CommentLines lines bearing only comment tokens, BlankLines the rest.
	// A line with both code and a trailing comment counts as code only, so
	// the three always sum to the file's line count.
	CodeLines    int
	CommentLines int
	BlankLines   int
	// GeneratedFiles counts files carrying the conventional marker. It
	// matters more than it looks: 40% of this repository's own compiled
	// lines are generated, and a first-party total that hides that overstates
	// what anyone wrote.
	GeneratedFiles int
	GeneratedCode  int
	// OtherLangLines counts the C, C++, assembly and header files compiled
	// with a cgo package — invisible to any Go-only count, and 377k lines in
	// this module's dependency closure.
	OtherLangLines int
	// Generators names the tools that wrote the generated files, sorted and
	// deduplicated (ADR-0173 §SD10). A slice rather than one string because a
	// quarter of this repository's generated packages carry more than one
	// tool, and the leeway packages carry three.
	Generators []string
}

Volume is a line tally over a set of source files.

func CountFiles

func CountFiles(goPaths []string, otherPaths []string) (v Volume)

CountFiles tallies goPaths (classified with go/scanner) and otherPaths (counted as raw lines).

Callers pass file paths rather than a loader's package type on purpose: this keeps the package free of golang.org/x/tools, so it can be linked into builds that must not carry a Go toolchain's worth of analysis code.

Unreadable files are skipped rather than failed on — a volume tally is best-effort evidence, and one unreadable file should not empty a table.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL