rules_go icon indicating copy to clipboard operation
rules_go copied to clipboard

Add support for GOSSAFUNC/-ssafunc SSA debug flags in rules_go

Open lollllcat opened this issue 4 months ago • 6 comments

Description

Background: The Go compiler’s SSA backend can emit an interactive HTML dump of the SSA IR for a given function when you set the GOSSAFUNC environment variable:

GOSSAFUNC=MyFunc go build

This produces an ssa.html file showing all compilation phases, inlining decisions, and optimizations for MyFunc, which is invaluable when investigating performance regressions, validating correctness, or understanding the compiler’s IR transformations.

Description

This produces an ssa.html file showing all compilation phases, inlining decisions, and optimizations for MyFunc, which is invaluable when investigating performance regressions, validating correctness, or understanding the compiler’s IR transformations.

Problem

Currently, rules_go offers no way to request SSA debug output from within a Bazel build:

  • Passing --action_env=GOSSAFUNC=… has no effect, because Bazel’s sandbox strips most Go toolchain environment variables.
  • There is no dedicated ssafunc attribute (or extension of gc_goopts) on go_binary, go_library, or go_test.
  • Teams must build outside Bazel (via plain go build) to inspect SSA dumps, breaking hermeticity and reproducibility.

Proposal

  1. Add a new ssafunc attribute to go_binary, go_library, and go_test:

    go_binary(
        name       = "myapp",
        srcs       = ["main.go"],
        ssafunc    = ["main", "(*MyType).Compute"],
        importpath = "github.com/example/myapp",
    )
    
  2. Wire ssafunc into the compile action by either:

    • Setting GOSSAFUNC=<fn> in the sandboxed environment, or
    • Passing -ssafunc=<fn> to go tool compile once available upstream.
  3. Emit the SSA HTML into the build output directory:

    bazel build //:myapp
    open bazel-bin/myapp.ssa.html
    
  4. Document the feature in the rules_go README alongside gc_goopts and gc_linkopts.

Example

load("@io_bazel_rules_go//go:def.bzl", "go_binary")

go_binary(
    name       = "server",
    srcs       = ["server.go"],
    ssafunc    = ["(*Server).handleRequest", "helperFunc"],
    importpath = "github.com/example/server",
)

Building this target will produce bazel-bin/server.ssa.html, allowing developers to inspect SSA IR directly from a Bazel build.

lollllcat avatar Jul 29 '25 03:07 lollllcat

Can --ssafunc be passed in via gc_goopts? In that case we could just check whether it's been passed and if so add the emitted file to an ssa output group (via OutputGroupInfo) that users can request.

I would prefer that over adding a dedicated attribute. We have many already and there appears to be a way to get by with freeform flags that matches what you would do with regular Go.

Would you be interested in working on a PR for this?

fmeum avatar Jul 30 '25 06:07 fmeum

Hi Fabian, thanks for replying. I think the issue is currently Go only supports GOSSAFUNC as an environment variable, and there is no --ssafunc command line flag yet. Let me know if passing GOSSAFUNC to gc_goopts will work in this case. If so, I'm happy to work on a PR for this. Meanwhile, I will also check with the Go community to see if they can support --ssafunc.

lollllcat avatar Aug 01 '25 20:08 lollllcat

@jayconrod What do you think of having Go actions inherit --action_env? It's been "marketed" as a best practice for some time in the past, but it also leaks the (by default non-hermetic) PATH into otherwise perfectly hermetic Go actions. It's also frequently abused in ways that hurt build and in particular cache performance.

I'm looking for a way to cover the many env variables go cares about without introducing too many attributes.

fmeum avatar Aug 01 '25 20:08 fmeum

I'd rather not inherit --action_env if there's any other way to do it. I'm especially concerned about cache busting. Isn't this basically why we're recompiling protoc all the time?

I also don't think this should be controlled by attribute on go_binary: it's a property of the configuration, not a property of the target.

Could be controlled with a custom build setting maybe? It would need to be something the rules are aware of because we'd need to declare an additional output file.

jayconrod avatar Aug 01 '25 21:08 jayconrod

Is it clear that one wouldn't want to enable this for particular targets?

In the C++ world, this would probably be solved with features, which you set both per-target and globally, as well as a custom output group that exports the files. A build setting would be more in line with the rest of the config in rules_go, but can't be controlled per target.

fmeum avatar Aug 01 '25 21:08 fmeum

I guess I'm curious about how it would be used. @lollllcat could you say more about that? I've never actually used this feature, but I assume since the output is HTML it's meant to be human-readable. Would you read that file directly in a browser, or would it be consumed by some other rule?

jayconrod avatar Aug 01 '25 21:08 jayconrod