Go instrumentor tool

The Go instrumentor is a command-line tool which transforms your source code to enable full SDK functionality when running in the Antithesis environment. The instrumentor provides assertion cataloging and coverage instrumentation.

  • Assertion cataloging: The instrumentor modifies your program so that at runtime it emits a catalog entry for every assertion you define (like Always(), Sometimes(), Unreachable(), etc.) Assertion cataloging is necessary for certain assertions to work.

  • Coverage instrumentation: The instrumentor adds coverage callbacks at every basic block in the program's control flow and writes the transformed source files to a separate directory. These callbacks enable the Antithesis platform to test your program more effectively.

The Go instrumentor manipulates Go language code via a source transformation. This means the tool must be invoked in your own build or CI system before the source is compiled into binaries, packaged into containers, and uploaded to Antithesis. We’re happy to work with you to determine the best way to integrate the Go instrumentor into your build system. Contact us if you need help with this.

The Go instrumentor is distributed as part of the Antithesis Go SDK.

Setup

Install the instrumentor as follows:

  1. Add the Antithesis SDK for Go to your Go module and install the instrumentor tool.
go get github.com/antithesishq/antithesis-sdk-go@latest
go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@latest
go mod tidy

These examples use the (generally recommended) version called latest. You might choose a specific version such as v0.2.7.

  1. Ensure you can run the instrumentor
`go env GOPATH`/bin/antithesis-go-instrumentor

Using the instrumentor

The instrumentor may be run in two modes: (1) assertion cataloging mode, where a command-line flag restricts the instrumentor to only cataloging assertions; and (2) coverage instrumentation and assertion cataloging mode, which is the default. The syntax and a short description of each mode follows. To see an overview of this tool, run antithesis-go-instrumentor --help.

Assertion cataloging mode

antithesis-go-instrumentor -assert_only [options] go_project_dir
Arguments

go_project_dir
The top-level directory of your Go project. Must contain a valid go.mod file

Options

See common options below.

Assertion cataloging behavior (Cataloging only)

You must rerun the instrumentor every time you add new assertions so that these assertions may be cataloged.

Coverage instrumentation and assertion cataloging mode

antithesis-go-instrumentor [options] go_project_dir target_dir
Arguments

go_project_dir
The top-level directory of your Go project.

target_dir
The directory to which the instrumentor will write its output. Must be an existing but empty directory. The instrumentor will generate three subdirectories: (1) customer, which contains the instrumented code and the assertion catalog, (2) symbols, which contains symbols information that must be copied to your configuration image, and (3) notifier, which contains an Antithesis runtime helper that will be compiled into your program.

Options

-prefix <string>
A string to prepend to the symbol table. Also see common options below.

Assertion cataloging behavior (Cataloging and instrumentation)

You must rerun the instrumentor every time you add new assertions so that these assertions may be cataloged.

Coverage instrumentation behavior

As stated above, the instrumentor will create 'target_dir', which will include three subdirectories as described above:

  1. customer
  2. symbols
  3. notifier

The instrumentor will perform the following steps:

Step 1: Code is instrumented

The instrumentor will first add coverage instrumentation to the code. It will instrument every .go file that is not excluded via exclude (see below). The original code is left in place and the instrumented code is written to customer.

Coverage instrumentation adds a function call to every basic block that is executed, for example:

func main() {
    __antithesis_instrumentation__.Notify(1)
    greet()
}

func greet() {
    __antithesis_instrumentation__.Notify(2)
    fmt.Println("Hello, world!")
}

You must rerun the instrumentor every time you add new basic blocks so that they may be instrumented.

Step 2: The symbol table is created

The symbol table information is generated in the symbols directory.
The name of this file is derived from a hash of its contents.

Step 3: Notifier module is generated

The notifier module is generated in the notifier directory. The notifier package name is determined using the hash from the symbols table filename, and prefixed with 'z' to conform to go package name requirements. The notifier.go file is generated with this package name.

The generated notifier module named antithesis.notifier/zXXXXXXXXXXXX is added as a dependency to the instumented go program in customer.

Step 4: Copy all non-instrumented files

Copies all non-instrumented files from the go_project_dir to the 'target_dir' customer directory. The hierarchy of the copied files is preserved.

The complete modified program, including both instrumented and intentionally uninstrumented files, has now been written to the customer directory.

The Antithesis Go instrumentor uses the same parser as the Go compiler, which was not designed with source transformations in mind. Our goal is that the output folder be an exact copy of the input folder, with as many Go files instrumented as possible while erring on the side of being buildable over that of being fully instrumented.

Unfortunately, the abstract syntax tree that the Go parser creates does not handle comments well. Comments may shift into the wrong place when a modified AST is serialized again as a .go file. This may result in invalid Go source. The instrumentor tries to prevent this by stripping most comments. Unfortunately, some comments in Go affect the compiler’s behavior, especially in relation to native libraries. The Antithesis instrumentor implements special handling for such comments. If it determines that it cannot correctly instrument a particular file, it simply copies that file to the output directory. In the worst case, the user can explicitly exclude a problematic file or directory from being instrumented. In every case, the instrumentor will print a warning about every file that was passed through uninstrumented.

Contact us if you need help with this.

The Antithesis instrumentor will output a file ending in .sym.tsv. This file should be copied into a directory named /symbols in the root of the appropriate container image. This file must be copied with the exact filename that is output by the instrumentor. For example, suppose that running the instrumentor on software called client produces the file client-go-df2451fe9b4e.sym.tsv. In this case, the resulting file should be copied to /symbols/client-go-df2451fe9b4e.sym.tsv.

Common instrumentation options

-V <int>
Verbosity level 0-3, where 3 is highest. The default is 0.

-catalog_dir <string>
The path to the directory where the assertion catalog will be generated. For example: /home/mydir. The default is the same directory that the go.mod file is located in.

-exclude <string>
The full path to a text file that lists the files and directories to exclude from being scanned by the Go instrumentor. Excluded files and directories will not be instrumented and their assertions will not be cataloged. By default all the .go files in a Go module will be cataloged, and the instrumentor ignores all directories beginning with a .

The exclusion file must contain paths to the files or directories to exclude – these paths are relative to the Go project directory. The entries must be newline separated. Lines beginning with a “#” are ignored, and so are all-whitespace lines. One exclusion file might be:

new_feature.go
# This line does nothing
mypack/newstuff

Every file or directory that you attempt to exclude must exist. If you attempt to exclude nonexistent files or directories, the instrumentor will fail.

-instrumentor_version <string>
The version of the SDK instrumentation package to require. The default is the version of the SDK for the Go instrumentor being run but you may also use another version of the SDK here e.g. “v0.2.10”.

-logfile <string>
The full path to a file where the instrumentor will log messages. The default is stderr.

-version
Outputs the current version of this application

-local_sdk_path
Path to a local copy of the Antithesis SDK. Used for running the instrumentor without an internet connection. Most users will not need to do this.

-skip_test_files=<bool>
Specify true to have the instrumentor ignore all files that end with _test.go

-skip_protobuf_files=<bool>
Specify true to have the instrumentor ignore all files that end with .pb.go