Home

Awesome

failpoint

LICENSE Language Go Report Card Build Status Coverage Status Mentioned in Awesome Go

An implementation of failpoints for Golang. Fail points are used to add code points where errors may be injected in a user controlled fashion. Fail point is a code snippet that is only executed when the corresponding failpoint is active.

Quick Start (use failpoint-ctl)

  1. Build failpoint-ctl from source

    git clone https://github.com/pingcap/failpoint.git
    cd failpoint
    make
    ls bin/failpoint-ctl
    
  2. Inject failpoints to your program, eg:

    package main
    
    import "github.com/pingcap/failpoint"
    
    func main() {
        failpoint.Inject("testPanic", func() {
            panic("failpoint triggerd")
        })
    }
    
  3. Transfrom your code with failpoint-ctl enable

  4. Build with go build

  5. Enable failpoints with GO_FAILPOINTS environment variable

    GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
    

    Note: GO_FAILPOINTS does not work with InjectCall type of marker.

  6. If you use go run to run the test, don't forget to add the generated binding__failpoint_binding__.go in your command, like:

    GO_FAILPOINTS="main/testPanic=return(true)" go run your-program.go binding__failpoint_binding__.go
    

Quick Start (use failpoint-toolexec)

  1. Build failpoint-toolexec from source

    git clone https://github.com/pingcap/failpoint.git
    cd failpoint
    make
    ls bin/failpoint-toolexec
    
  2. Inject failpoints to your program, eg:

    package main
    
    import "github.com/pingcap/failpoint"
    
    func main() {
        failpoint.Inject("testPanic", func() {
            panic("failpoint triggerd")
        })
    }
    
  3. Use a separate build cache to avoid mixing caches without failpoint-toolexec, and build

    GOCACHE=/tmp/failpoint-cache go build -toolexec path/to/failpoint-toolexec

  4. Enable failpoints with GO_FAILPOINTS environment variable

    GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
    
  5. You can also use go run or go test, like:

    GOCACHE=/tmp/failpoint-cache GO_FAILPOINTS="main/testPanic=return(true)" go run -toolexec path/to/failpoint-toolexec your-program.go
    

Design principles

Key concepts

How to inject a failpoint to your program

Some complicated failpoints demo

Failpoint name best practice

As you see above, _curpkg_ will automatically wrap the original failpoint name in failpoint.Eval call. You can think of _curpkg_ as a macro that automatically prepends the current package path to the failpoint name. For example,

package ddl // which parent package is `github.com/pingcap/tidb`

func demo() {
	// _curpkg_("the-original-failpoint-name") will be expanded as `github.com/pingcap/tidb/ddl/the-original-failpoint-name`
	if val, ok := failpoint.Eval(_curpkg_("the-original-failpoint-name")); ok {...}
}

You do not need to care about _curpkg_ in your application. It is automatically generated after running failpoint-ctl enable and is deleted with failpoint-ctl disable.

Because all failpoints in a package share the same namespace, we need to be careful to avoid name conflict. There are some recommended naming rules to improve this situation.

Implementation details

  1. Define a group of marker functions
  2. Parse imports and prune a source file which does not import a failpoint
  3. Traverse AST to find marker function calls
  4. Marker function calls will be rewritten with an IF statement, which calls failpoint.Eval to determine whether a failpoint is active and executes failpoint code if the failpoint is enabled

rewrite-demo

Acknowledgments