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 ¶
const GeneratorUnknown = "unknown"
GeneratorUnknown is reported for a file whose marker is present but carries no usable tool name.
Variables ¶
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 ¶
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 ¶
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.
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 ¶
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.