https://github.com/ysmood/gotrace
A lib for monitoring runtime goroutine stack
https://github.com/ysmood/gotrace
goroutine leak leak-detection test
Last synced: 3 months ago
JSON representation
A lib for monitoring runtime goroutine stack
- Host: GitHub
- URL: https://github.com/ysmood/gotrace
- Owner: ysmood
- License: mit
- Created: 2020-09-28T20:35:28.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-04-01T09:45:19.000Z (almost 4 years ago)
- Last Synced: 2025-05-12T13:56:18.542Z (8 months ago)
- Topics: goroutine, leak, leak-detection, test
- Language: Go
- Homepage: https://pkg.go.dev/github.com/ysmood/gotrace
- Size: 30.3 KB
- Stars: 39
- Watchers: 2
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
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
Check the [examples](examples_test.go) and [godoc](https://pkg.go.dev/github.com/ysmood/gotrace) for detailed usage.
Let's get started with a typical async producer-consumer model, can you find the leaking issue of the code below?
If we visit the port 3000 again and again the service will leak memory.
```go
package main
import (
"fmt"
"net/http"
)
func main() {
_ = http.ListenAndServe(":3000", http.HandlerFunc(handle))
}
func handle(_ http.ResponseWriter, _ *http.Request) {
c := make(chan int)
go produce(c)
go consume(c)
}
func produce(c chan int) {
for i := range "..." {
c <- i
}
}
func consume(c chan int) {
for i := range c {
fmt.Println(i)
}
}
```
We can use gotrace to locate the leak point, let's create a `main_test.go` file:
```go
package main
import (
"testing"
"github.com/ysmood/gotrace"
)
func TestHandle(t *testing.T) {
// Just add one line before the standard test case
gotrace.CheckLeak(t, 0)
handle(nil, nil)
}
```
Run `GODEBUG="tracebackancestors=10" go test`, wait for 3 seconds you will see the below output:
```txt
--- FAIL: TestHandle (3.00s)
main_test.go:10: leaking goroutines: goroutine 6 [chan receive]:
play.consume(0x0)
/Users/ys/repos/play/default/main.go:25 +0x74
```
The `range` on line 25 get stuck, the reason is `chan receive`, it keeps waiting for new messages from `c`, but the `produce` function has already exited, it will not send messages to `c` anymore.
We have several ways to fix it, such as change the `produce` to:
```go
func produce(c chan int) {
for i := range "..." {
c <- i
}
close(c)
}
```
Run `go test` again, the test should exit immediately without errors.