-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeadlock_debug.go
95 lines (79 loc) · 1.96 KB
/
deadlock_debug.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//go:build deadlockdebug
// +build deadlockdebug
package gorex
import (
"fmt"
"reflect"
"runtime"
"sync"
"github.com/huandu/go-tls"
)
type debuggerLockerKey struct {
LockerPtr uintptr
IsWrite bool
}
type debuggerPcs struct {
pcs *[]uintptr
n int
}
type debuggerGStorage struct {
PCS map[debuggerLockerKey]debuggerPcs
}
var (
debuggerMap = &sync.Map{}
exitC chan struct{}
)
func getOrStoreDebuggerGStorage() *debuggerGStorage {
stor, _ := debuggerMap.LoadOrStore(GetG(), &debuggerGStorage{})
return stor.(*debuggerGStorage)
}
func getDebuggerLockerKey(lockPtr sync.Locker, isWrite bool) debuggerLockerKey {
return debuggerLockerKey{
LockerPtr: reflect.ValueOf(lockPtr).Elem().UnsafeAddr(),
IsWrite: isWrite,
}
}
func goroutineCheckOnExit() {
stor := getOrStoreDebuggerGStorage()
for lKey, pcs := range stor.PCS {
frames := runtime.CallersFrames((*pcs.pcs)[:pcs.n])
fmt.Fprintf(debugPanicOut, "an opened lock %+v which was never released (and the goroutine already exited):\n",
lKey)
printFrames(frames)
}
debuggerMap.Delete(GetG())
if exitC != nil {
close(exitC)
exitC = nil
}
}
var pcsPool = sync.Pool{New: func() interface{} {
pcs := make([]uintptr, 128)
return &pcs
}}
func goroutineOpenedLock(lockPtr sync.Locker, isWrite bool) {
pcs := pcsPool.Get().(*[]uintptr)
n := runtime.Callers(2, *pcs)
stor, lKey := getOrStoreDebuggerGStorage(), getDebuggerLockerKey(lockPtr, isWrite)
if _, found := stor.PCS[lKey]; found {
panic("should not happen")
}
if stor.PCS == nil {
stor.PCS = map[debuggerLockerKey]debuggerPcs{}
}
stor.PCS[lKey] = debuggerPcs{pcs: pcs, n: n}
_, ok := tls.Get("gorex-debugger")
if ok {
return
}
tls.AtExit(goroutineCheckOnExit)
tls.Set("gorex-debugger", nil)
}
func goroutineClosedLock(lockPtr sync.Locker, isWrite bool) {
stor, lKey := getOrStoreDebuggerGStorage(), getDebuggerLockerKey(lockPtr, isWrite)
pcs := stor.PCS[lKey]
if pcs.pcs != nil {
pcsPool.Put(pcs.pcs)
}
delete(stor.PCS, lKey)
}