gotrace

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2022 License: MIT Imports: 11 Imported by: 12

README

Overview

A lib for monitoring runtime goroutine stack. Such as wait for goroutines to exit, leak detection, etc.

Features

  • context.Context first design
  • Concurrent leak detection
  • No dependencies and 100% test coverage
  • Provides handy low-level APIs to extend the lib

Guides

To get started check the examples.

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var Backoff = func(t time.Duration) time.Duration {
	const maxSleep = 300 * time.Millisecond
	if t == 0 {
		return time.Microsecond
	}

	t *= 2

	if t > maxSleep {
		return maxSleep
	}

	return t
}

Backoff is the default algorithm for sleep backoff

View Source
var Exit = os.Exit

Exit to os.Exit

View Source
var GetStack = func(all bool) string {
	for i := 1024 * 1024; ; i *= 2 {
		buf := make([]byte, i)
		if n := runtime.Stack(buf, all); n < i {
			return string(buf[:n-1])
		}
	}
}

GetStack of current runtime

View Source
var TraceAncestorsEnabled = regexp.MustCompile(`tracebackancestors=\d+`).MatchString(os.Getenv("GODEBUG"))

TraceAncestorsEnabled returns true if GODEBUG="tracebackancestors=N" is set

Functions

func Check added in v0.3.0

func Check(timeout time.Duration, ignores ...Ignore) error

Check if there's goroutine leak

func CheckLeak added in v0.5.1

func CheckLeak(t T, timeout time.Duration, ignores ...Ignore)

CheckLeak reports error if the test is leaking goroutine. Default timeout is 3s. Default ignore is gotrace.IgnoreNonChildren() .

func CheckMainLeak added in v0.5.1

func CheckMainLeak(m M, timeout time.Duration, ignores ...Ignore)

CheckMainLeak reports error if goroutines are leaking after all tests are done. Default timeout is 3s. It's powerful but less accurate than Check, if you only use CheckMainLeak it will be hard to tell which test is the cause of the leak.

func CheckWithContext added in v0.6.0

func CheckWithContext(ctx context.Context, ignores ...Ignore) error

CheckWithContext if there's goroutine leak

func Signal added in v0.1.1

func Signal(signals ...os.Signal) context.Context

Signal to cancel the returned context, default signal is CTRL+C .

Example
package main

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/ysmood/gotrace"
)

func main() {
	// Skip the test for Windows because it can't send signal programatically.
	if runtime.GOOS == "windows" {
		fmt.Println("true")
		return
	}

	go func() {
		traces := gotrace.Wait(gotrace.Signal())
		fmt.Println(strings.Contains(traces.String(), "gotrace_test.ExampleSignal"))
	}()

	time.Sleep(100 * time.Millisecond)

	p, _ := os.FindProcess(os.Getpid())
	_ = p.Signal(os.Interrupt)

	time.Sleep(100 * time.Millisecond)

}
Output:

true

func Timeout

func Timeout(d time.Duration) context.Context

Timeout shortcut for context.WithTimeout(context.Background(), d)

Types

type Ignore

type Ignore func(t *Trace) bool

Ignore returns true to ignore t

func CombineIgnores

func CombineIgnores(list ...Ignore) Ignore

CombineIgnores into one

Example
package main

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/ysmood/gotrace"
)

func main() {
	ignore := gotrace.CombineIgnores(
		gotrace.IgnoreCurrent(),
		func(t *gotrace.Trace) bool {
			return strings.Contains(t.Raw, "ExampleCombineIgnores.func2")
		},
	)

	go func() {
		time.Sleep(2 * time.Second)
	}()

	go func() {
		time.Sleep(time.Second)
	}()

	start := time.Now()
	gotrace.Wait(context.TODO(), ignore)
	end := time.Since(start)

	if time.Second < end && end < 2*time.Second {
		fmt.Println("only waits for the second goroutine")
	}

}
Output:

only waits for the second goroutine

func IgnoreCurrent

func IgnoreCurrent() Ignore

IgnoreCurrent running goroutines

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/ysmood/gotrace"
)

func main() {
	ignore := gotrace.IgnoreCurrent()

	go func() {
		time.Sleep(time.Second)
	}()

	start := time.Now()
	gotrace.Wait(context.TODO(), ignore)
	end := time.Since(start)

	if end > time.Second {
		fmt.Println("waited for 1 second")
	}

}
Output:

waited for 1 second

func IgnoreFuncs

func IgnoreFuncs(names ...string) Ignore

IgnoreFuncs ignores a Trace if it's first Stack's Func equals one of the names.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/ysmood/gotrace"
)

func main() {
	ignoreCurrent := gotrace.IgnoreCurrent()
	ignore := gotrace.IgnoreFuncs("internal/poll.runtime_pollWait")

	go func() {
		time.Sleep(time.Second)
	}()

	start := time.Now()
	gotrace.Wait(context.TODO(), ignore, ignoreCurrent)
	end := time.Since(start)

	if end > time.Second {
		fmt.Println("waited for 1 second")
	}

}
Output:

waited for 1 second

func IgnoreList

func IgnoreList(list Traces) Ignore

IgnoreList of traces

func IgnoreNonChildren added in v0.5.0

func IgnoreNonChildren() Ignore

IgnoreNonChildren goroutines

type M added in v0.3.0

type M interface {
	Run() int
}

M interface for testing.M

type Stack

type Stack struct {
	Func string
	Loc  string
}

Stack info

type T added in v0.3.0

type T interface {
	Helper()
	Fail()
	Failed() bool
	Cleanup(f func())
	Logf(format string, args ...interface{})
}

T interface for testing.T

type Trace

type Trace struct {
	Raw                  string
	GoroutineID          int64
	GoroutineAncestorIDs []int64 // Need GODEBUG="tracebackancestors=N" to be set
	WaitReason           string  // https://github.com/golang/go/blob/874b3132a84cf76da6a48978826c04c380a37a50/src/runtime/runtime2.go#L997
	Stacks               []Stack
	// contains filtered or unexported fields
}

Trace of one goroutine

func (Trace) HasParent added in v0.5.0

func (t Trace) HasParent(id int64) bool

HasParent goroutine id

func (Trace) String added in v0.1.0

func (t Trace) String() string

String interface for fmt

type Traces

type Traces []*Trace

Traces of goroutines

func Get

func Get(all bool) Traces

Get the Trace of the calling goroutine. If all is true, all other goroutines' Traces will be appended into the result too.

Example
package main

import (
	"fmt"

	"github.com/ysmood/gotrace"
)

func main() {
	list := gotrace.Get(true)

	fmt.Println("id of current:", list[0].GoroutineID)
	fmt.Println("caller of current:", list[0].Stacks[2].Func)

}
Output:


id of current: 1
caller of current: github.com/ysmood/gotrace_test.ExampleGet

func Wait

func Wait(ctx context.Context, ignores ...Ignore) (remain Traces)

Wait uses Backoff for WaitWithBackoff

func WaitWithBackoff added in v0.5.1

func WaitWithBackoff(ctx context.Context, backoff func(time.Duration) time.Duration, ignores ...Ignore) (remain Traces)

WaitWithBackoff algorithm. Wait for other goroutines that are not ignored to exit. It returns the ones that are still active. It keeps counting the active goroutines that are not ignored, if the number is zero return.

func (Traces) Any

func (list Traces) Any() bool

Any item exists in the list

func (Traces) Filter added in v0.5.0

func (list Traces) Filter(ignore Ignore) Traces

Filter returns the remain Traces that are ignored

func (Traces) String added in v0.1.0

func (list Traces) String() string

String interface for fmt. It will merge similar trace together and print counts.

Example
package main

import (
	"fmt"
	"strings"
	"time"

	"github.com/ysmood/gotrace"
)

func main() {
	go func() {
		time.Sleep(time.Second)
	}()

	traces := gotrace.Wait(gotrace.Timeout(0))

	str := fmt.Sprintf("%v %v", traces[0], traces)

	fmt.Println(strings.Contains(str, "gotrace_test.ExampleTraces_String"))

}
Output:

true

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL