Skip to content

Caliper v1.5.0

Choose a tag to compare

@trendvidia trendvidia released this 11 Sep 23:20
· 13 commits to main since this release

Analysis-engine release: a load mode that takes dependency types from export data, three correctness fixes in how packages and findings are classified, and a refusal to run under a toolchain newer than the binary.

Fixed

  • A load refuses a newer toolchain instead of answering wrongly (#35). go/packages asks for complete types for
    every direct import of a package it type-checks from source, and fills
    them from export data; when that read fails it calls log.Fatalf from
    inside its importer, which no caller can recover — an MCP server dies
    with it. A go command from a newer release than the Go this program was
    built with is exactly such a failure. The load now stops before
    starting, with an error naming both versions and what to do about it.
    Measured on steward with a caliper built by go1.26.7 against go1.27.1's
    go list: before, the process exited 1 with internal error: package "math/rand/v2" without types was imported from "google.golang.org/grpc/internal/transport"; now it returns the
    refusal. The default load is refused too, for a different reason: it
    completes under skew, but the older type checker cannot process the
    newer toolchain's sources, so the same run left 15 of steward's 117
    packages in the SSA program and reported no error — a call graph
    missing 87% of the module. Unskewed behaviour is unchanged, 117 of 117
    packages before and after, in both modes. Export data unreadable for
    another reason, a truncated build cache say, can still reach the same
    fatal; only the predictable cause is guarded.

  • context_misuse no longer flags a detached context in a deferred
    cleanup
    (#10). A deferred Rollback, Close or Unlock runs when
    the function returns, including because ctx was cancelled or timed
    out, so passing ctx there skips the cleanup exactly when it is needed
    most — pgx's Rollback returns at once on a done context and leaves
    the connection in a transaction. context.Background() inside a
    defer, whether deferred directly, inside a deferred closure, or as
    the parent of a WithTimeout there, is the recommended idiom and is
    not reported. context.TODO() in a defer still is. Measured on the
    repositories the issue was filed from: gameflow goes from 19 findings
    to 1, and all 9 removed sites are deferred cleanups (four
    tx.Rollback in the trader store, a migration Rollback, a
    conn.Close, an advisory-unlock Exec, two deferred metric
    decrements); govm's 5 and voya's 0 are unchanged.

  • A deferred call is reported once, not twice. The CFG lists a defer
    statement in the block that registers it and again in a BlockDefer
    block where it runs, which is how a path query sees a deferred
    Unlock or Close on every exit; rules that scan nodes to report on
    them saw both copies. On govm, unchecked_error with include_defers
    gave 162 findings of which 8 were exact duplicates; on gameflow,
    context_misuse gave 19 of which 9 were. Both are now 0, with the
    same unique findings (154 and 1 respectively). Affects
    context_misuse, unchecked_error, goroutine_leak and
    sql_injection; lock_violation is unchanged, since its loop feeds
    path queries that need both copies.

  • A dependency with a type error inside a function body no longer
    drops its importers from the SSA program
    (#30). The program was
    built with ssautil.AllPackages, which skips every package go/packages
    marks IllTyped — and IllTyped propagates, so one dependency with
    any error removed every loaded package importing it, transitively,
    from the call graph, silently: the loaded package's own type-check was
    clean and it stayed in Engine.Packages(). On steward, grpc's
    internal/transport fails on go-plus 1.27.1 with undefined: http2.TrailerPrefix inside a function body, and 36 of 117 packages
    had no SSA package and no call-graph nodes. Now a package is skipped
    only for an error outside a function body — a parse error, a type
    error in a declaration, export data that could not be read — or for
    importing such a package; one whose errors are all inside function
    bodies is created from its types alone, so its declarations are there
    for its importers and its unfinished bodies never reach the SSA
    builder. steward: 117 of 117 packages in the SSA program under the
    default load, with the same 13,119 within-module edges as under
    DepsFromExportData, edge for edge. Pinned on the fixture by a
    dependency with a body-only error (importer analysed, calls its clean
    function; the dependency's functions have no bodies) and one with a
    declaration error (importer dropped, as before), under both loads.
    An error is placed by its source position, so //line directives in
    generated code cannot move a declaration error into a body; and the
    notice go/packages adds when caliper was built with an older Go than
    the go command it runs does not count as an error.
    Default-load timing on steward is unchanged (load 1.6–1.7 s, call
    graph 3.8–4.0 s over the now-complete 117 packages; one run of three
    hit 11.4 s on a busy machine).

Added

  • EngineOptions.DepsFromExportData type-checks the loaded packages
    from source and takes every dependency's types from the compiler's
    export data (go list -export) instead of parsing and type-checking
    its source. It drops packages.NeedDeps from the load; under it the
    SSA program is built with the loaded packages from syntax and every
    dependency from types alone (#28, for trendvidia/alidade). Default
    false, everything unchanged.

    Measured on steward (117 packages, 1,293 reached with dependencies,
    warm build cache, three interleaved runs each, median and range):

    Load SSA + CHA call graph
    default 1.93 s (1.4–2.5) 3.46 s (2.5–5.1)
    DepsFromExportData 0.99 s (0.98–1.3) 0.36 s (0.29–0.50)

    The first split load on a tree whose dependencies are not yet in the
    build cache pays their compilation once (3.4 s on steward, where they
    were).

    Limitation: no interprocedural analysis through dependency code. A
    dependency's SSA functions have no bodies, so no call-graph edge leaves
    one (steward: 8,150,734 such edges by default, 0 under the option) and
    no taint flows through one. Calls from a loaded package into a
    dependency are still edges to that function; dynamic calls resolve
    among fewer candidates, since a package reached only through a
    dependency carries just the declarations its importers reference
    (steward: 176,011 edges into dependencies by default, 22,519 under the
    option). A dependency that does not compile on the host's toolchain has
    no export data and is type-checked from source with its bodies skipped;
    it is still bodiless in the SSA program, and the loaded packages that
    import it are analysed. Pinned by TestDepsFromExportData_* on a
    fixture with a third-party dependency (github.com/google/uuid) and a
    local one that type-checks but does not compile: the call-graph edges
    among the loaded packages are identical under both modes, edge for
    edge.

    On steward the within-module graph is not identical but a strict
    superset: 13,119 edges over all 117 packages against 6,366 over 81,
    because grpc's internal/transport has a type error inside a function
    body on this toolchain (undefined: http2.TrailerPrefix), the default
    load sees it and drops the 36 packages importing it from the SSA
    program, and the split load never type-checks that body. The 6,366 are
    all present under the option and every extra edge touches one of the
    36. The default load's drop is #30.