https://github.com/berkaroad/detectlock-go
运行时检测golang中的死锁
https://github.com/berkaroad/detectlock-go
deadlock go mutex rwmutex
Last synced: 6 months ago
JSON representation
运行时检测golang中的死锁
- Host: GitHub
- URL: https://github.com/berkaroad/detectlock-go
- Owner: berkaroad
- License: mit
- Created: 2021-10-31T14:58:47.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2023-09-15T19:06:17.000Z (about 2 years ago)
- Last Synced: 2024-07-23T07:56:53.450Z (about 1 year ago)
- Topics: deadlock, go, mutex, rwmutex
- Language: Go
- Homepage:
- Size: 17.6 KB
- Stars: 8
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# DetectLock-go
用于检测运行时死锁。获得产生死锁的goroutine编号后,结合go-pprof的堆栈信息,即可找到代码相关位置。
goroutine的锁信息记录,采用分片锁,降低了多线程并发竞争的性能影响。
- 支持 `sync.Mutex`、`sync.RWMutex`
- 支持检测 `锁重入`、`多把锁顺序不一致导致的互锁`
- 支持 启用 / 禁用 调试功能。
## 用法
```go
import (
"github.com/berkaroad/detectlock-go"
)// 应用启动时,设置启用调试
detectlock.EnableDebug()// 声明 sync.Mutex、sync.RWMutex 替换为 detectlock.Mutex、detectlock.RWMutex
var locker1 *detectlock.Mutex = &detectlock.Mutex{}
var locker2 *detectlock.RWMutex = &detectlock.RWMutex{}// 异步检测死锁
items := detectlock.Items()
fmt.Println(detectlock.DetectAcquired(items)) // 检测获得锁的goroutine列表
fmt.Println(detectlock.DetectReentry(items)) // 检测锁重入的goroutine列表
fmt.Println(detectlock.DetectLockedEachOther(items)) // 检测互锁的goroutine列表// 关闭调试,并清理锁使用信息
detectlock.DisableDebug()
```## 数据格式
`goroutine <协程ID>: [(<锁标识>, <锁状态>, <调用者函数>(file: <源码文件名>:<源码行号>)), ...]`
- 锁标识:相同标识即为同一把锁。
- 锁状态,共4种:
- acquired
获得Mutex锁,或RWMutex写锁。
- wait
等待Mutex锁,或等待RWMutex写锁。
- r-acquired
获得RWMutex读锁。
- r-wait
等待RWMutex读锁。
- 调用者函数
调用了加锁操作的调用者的函数名,包含所属的包名。
- 源码文件名
调用了加锁操作的调用者的函数所在的源文件名。
- 源码行号
调用了加锁操作的调用者的函数所在的源文件中的行号。
## 检测到死锁的示例
- 检测 sync.Mutex 多把锁顺序不一致导致的互锁
```plain
--- DetectAcquired ---
goroutine 29: [(0xc0000b4008, acquired, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:30)), (0xc0000b4000, wait, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:33))]
goroutine 30: [(0xc0000b4000, acquired, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:20)), (0xc0000b4008, wait, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:23))]--- DetectLockedEachOther ---
goroutine 29: [(0xc0000b4008, acquired, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:30)), (0xc0000b4000, wait, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:33))]
goroutine 30: [(0xc0000b4000, acquired, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:20)), (0xc0000b4008, wait, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:23))]```
- 检测 sync.Mutex 锁重入
```plain
--- DetectAcquired ---
goroutine 53: [(0xc0000160b8, acquired, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:19)), (0xc0000160b8, wait, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:22))]--- DetectReentry ---
goroutine 53: [(0xc0000160b8, acquired, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:19)), (0xc0000160b8, wait, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:22))]```
- 检测 sync.Mutex、sync.RWMutex 多把锁顺序不一致导致的互锁
```plain
--- DetectAcquired ---
goroutine 8: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 10: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 13: [(0xc0000160b8, acquired, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:30)), (0xc0000180c0, wait, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:33))]
goroutine 14: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 16: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 50: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 52: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 54: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 56: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 58: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]--- DetectLockedEachOther ---
goroutine 8: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 10: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 13: [(0xc0000160b8, acquired, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:30)), (0xc0000180c0, wait, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:33))]
goroutine 14: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 16: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 50: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 52: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 54: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 56: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 58: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]```
## Benchmark
通过对 `启用调试` 、`禁用调试` 、 `原生` 的锁操作的性能对比,`启用调试` 模式下性能比较差,因此不适合用于生产环境。而对于 `禁用调试` 、 `原生` 的对比,性能损耗相差无几。
```sh
make go-bench
``````plain
GO111MODULE=on GOPROXY=https://goproxy.cn,direct go test -run=none -count=1 -benchtime=10000x -benchmem -bench=. ./... | grep Benchmark
BenchmarkMutex_Lock/EnableDebug-4 10000 97118 ns/op 5928 B/op 75 allocs/op
BenchmarkMutex_Lock/DisableDebug-4 10000 462.6 ns/op 160 B/op 10 allocs/op
BenchmarkMutex_Lock/sync.Mutex-4 10000 401.7 ns/op 160 B/op 10 allocs/op
BenchmarkMutex_TryLock/EnableDebug-4 10000 103766 ns/op 5928 B/op 75 allocs/op
BenchmarkMutex_TryLock/DisableDebug-4 10000 447.7 ns/op 160 B/op 10 allocs/op
BenchmarkMutex_TryLock/sync.Mutex-4 10000 391.4 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_RLock/EnableDebug-4 10000 102587 ns/op 5928 B/op 75 allocs/op
BenchmarkRWMutex_RLock/DisableDebug-4 10000 603.0 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_RLock/sync.RWMutex-4 10000 415.0 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_TryRLock/EnableDebug-4 10000 413464 ns/op 6090 B/op 70 allocs/op
BenchmarkRWMutex_TryRLock/DisableDebug-4 10000 407.6 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_TryRLock/sync.RWMutex-4 10000 428.3 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_Lock/EnableDebug-4 10000 101591 ns/op 5928 B/op 75 allocs/op
BenchmarkRWMutex_Lock/DisableDebug-4 10000 544.3 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_Lock/sync.RWMutex-4 10000 875.6 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_TryLock/EnableDebug-4 10000 102170 ns/op 5928 B/op 75 allocs/op
BenchmarkRWMutex_TryLock/DisableDebug-4 10000 471.0 ns/op 160 B/op 10 allocs/op
BenchmarkRWMutex_TryLock/sync.RWMutex-4 10000 456.1 ns/op 160 B/op 10 allocs/op```
## 发布版本
### v1.0.1 (2023-09-16)
- 加锁时,额外收集调用者函数栈
用于问题诊断时,可以看到调用者的函数完整名、代码行。
- 补全 `sync.Mutex`、`sync.RWMutex`
补充缺失的函数: `TryLock()`、`TryRLock()`。
- 优化了未开启调试场景下,默认不占用10Mb内存
### v1.0 (2021-10-31)
- 支持 `sync.Mutex`、`sync.RWMutex`;
- 支持检测 `锁重入`、`多把锁顺序不一致导致的互锁`;
- 支持 启用 / 禁用 调试功能。