Skip to content

Commit 3246a5f

Browse files
committed
add event traces to follow shed.lock, with modified version of checkdead
1 parent ecf7e00 commit 3246a5f

File tree

10 files changed

+696
-3
lines changed

10 files changed

+696
-3
lines changed

src/runtime/debug.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ func GOMAXPROCS(n int) int {
1818
n = 1 // WebAssembly has no threads yet, so only one CPU is possible.
1919
}
2020

21+
pushEventTrace("GOMAXPROCS acquiring sched lock")
2122
lock(&sched.lock)
23+
pushEventTrace("GOMAXPROCS acquired sched lock")
2224
ret := int(gomaxprocs)
25+
pushEventTrace("GOMAXPROCS releasing sched lock")
2326
unlock(&sched.lock)
27+
pushEventTrace("GOMAXPROCS released sched lock")
28+
2429
if n <= 0 || n == ret {
2530
return ret
2631
}

src/runtime/debugcall.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,14 @@ func debugCallWrap1() {
222222
}
223223
casgstatus(gp, _Grunning, _Grunnable)
224224
dropg()
225+
226+
pushEventTrace("debugCallWrap1 acquiring sched lock")
225227
lock(&sched.lock)
228+
pushEventTrace("debugCallWrap1 acquired sched lock")
226229
globrunqput(gp)
230+
pushEventTrace("debugCallWrap1 releasing sched lock")
227231
unlock(&sched.lock)
232+
pushEventTrace("debugCallWrap1 released sched lock")
228233

229234
if trace.enabled {
230235
traceGoUnpark(callingG, 0)

src/runtime/eventtrace.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package runtime
2+
3+
import (
4+
"runtime/internal/circbuf"
5+
)
6+
7+
const maxEventLogSize = 128
8+
9+
type eventTraceElement struct {
10+
Time int64
11+
G uint64
12+
M int64
13+
log [maxEventLogSize]byte
14+
}
15+
16+
func (e *eventTraceElement) SetLog(log string) {
17+
var i int
18+
var c byte
19+
20+
for i, c = range []byte(log) {
21+
if i >= maxEventLogSize-1 {
22+
return
23+
}
24+
25+
e.log[i] = c
26+
}
27+
28+
for i++; i < maxEventLogSize; i++ {
29+
e.log[i] = 0
30+
}
31+
}
32+
33+
func (e *eventTraceElement) LogBytes() []byte {
34+
return e.log[:]
35+
}
36+
37+
func initEventTrace() {
38+
if debug.eventtrace <= 0 {
39+
return
40+
}
41+
42+
sched.eventTrace = circbuf.NewCircularBufferInit(int(debug.eventtrace),
43+
func() *eventTraceElement { return &eventTraceElement{} })
44+
}
45+
46+
func pushEventTrace(log string) {
47+
if sched.eventTrace == nil {
48+
return
49+
}
50+
51+
g := getg()
52+
53+
now := nanotime()
54+
ok, e := sched.eventTrace.PushLater()
55+
if !ok {
56+
return
57+
}
58+
59+
e.Time = now
60+
e.G = g.goid
61+
e.M = g.m.id
62+
e.SetLog(log)
63+
}
64+
65+
func printEventTrace() {
66+
if sched.eventTrace == nil {
67+
return
68+
}
69+
70+
sched.eventTrace.ForEachEvent(func(e *eventTraceElement) {
71+
print("eventtrace: ", "t=", e.Time, ", g=", e.G, " m=", e.M, ", ")
72+
gwrite(e.LogBytes())
73+
println()
74+
})
75+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package circbuf
2+
3+
import (
4+
"runtime/internal/atomic"
5+
)
6+
7+
type element[T any] struct {
8+
id uint64
9+
value T
10+
}
11+
12+
type CircularBuffer[T any] struct {
13+
reading uint32
14+
idx uint64
15+
buf []*element[T]
16+
}
17+
18+
func NewCircularBuffer[T any](size int) *CircularBuffer[T] {
19+
if size <= 0 {
20+
return nil
21+
}
22+
23+
return &CircularBuffer[T]{
24+
buf: make([]*element[T], size),
25+
}
26+
}
27+
28+
func NewCircularBufferInit[T any](size int, zeroT func() T) *CircularBuffer[T] {
29+
if size <= 0 {
30+
return nil
31+
}
32+
33+
buf := make([]*element[T], size)
34+
for i := 0; i < size; i++ {
35+
buf[i] = &element[T]{value: zeroT()}
36+
}
37+
38+
return &CircularBuffer[T]{
39+
buf: buf,
40+
}
41+
}
42+
43+
func (b *CircularBuffer[T]) reserveID() uint64 {
44+
return atomic.Xadd64(&b.idx, 1)
45+
}
46+
47+
func (b *CircularBuffer[T]) PushLater() (ok bool, ret T) {
48+
if v := atomic.Load(&b.reading); v == 1 {
49+
ok = false
50+
return
51+
}
52+
53+
id := b.reserveID()
54+
idx := int((id - 1) % uint64(cap(b.buf)))
55+
56+
b.buf[idx].id = id
57+
58+
return true, b.buf[idx].value
59+
}
60+
61+
func (b *CircularBuffer[T]) Push(value T) bool {
62+
if v := atomic.Load(&b.reading); v == 1 {
63+
return false
64+
}
65+
66+
id := b.reserveID()
67+
idx := int((id - 1) % uint64(cap(b.buf)))
68+
69+
if b.buf[idx] == nil || b.buf[idx].id == 0 {
70+
b.buf[idx] = &element[T]{}
71+
}
72+
73+
b.buf[idx].id = id
74+
b.buf[idx].value = value
75+
76+
return true
77+
}
78+
79+
func (b *CircularBuffer[T]) Read() []T {
80+
if !atomic.Cas(&b.reading, 0, 1) {
81+
return nil
82+
}
83+
defer atomic.Store(&b.reading, 0)
84+
85+
var out []T
86+
b.forEachEvent(func(e T) {
87+
out = append(out, e)
88+
})
89+
return out
90+
}
91+
92+
func (b *CircularBuffer[T]) ForEachEvent(fn func(e T)) {
93+
if fn == nil || !atomic.Cas(&b.reading, 0, 1) {
94+
return
95+
}
96+
defer atomic.Store(&b.reading, 0)
97+
98+
b.forEachEvent(fn)
99+
}
100+
101+
func (b *CircularBuffer[T]) forEachEvent(fn func(e T)) {
102+
h := b.FindHead()
103+
i := h
104+
for {
105+
e := b.buf[i]
106+
if e == nil || e.id == 0 {
107+
break
108+
}
109+
110+
fn(e.value)
111+
b.buf[i].id = 0
112+
113+
i = (i + 1) % cap(b.buf)
114+
115+
if i == h {
116+
break
117+
}
118+
}
119+
120+
b.idx = 0
121+
}
122+
123+
func (b *CircularBuffer[T]) FindHead() int {
124+
var smallId uint64 = 1<<64 - 1
125+
var smallIdx int
126+
127+
for idx, e := range b.buf {
128+
if e == nil || e.id == 0 {
129+
break
130+
}
131+
132+
if e.id < smallId {
133+
smallId = e.id
134+
smallIdx = idx
135+
}
136+
}
137+
138+
return smallIdx
139+
}

0 commit comments

Comments
 (0)