Skip to content

Commit 380a486

Browse files
committed
feat: add testsources for mcall/systemstack
1 parent c92c28e commit 380a486

2 files changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Program mcall exercises the runtime.mcall path by parking goroutines
5+
// through various blocking primitives (channels, mutexes, select, sleep)
6+
// and cooperative preemption on deep stacks. A coredump taken while it
7+
// runs is likely to contain threads mid-mcall.
8+
//
9+
// Build: go build -o mcall ./tools/coredump/testsources/go/mcall/
10+
// Run: ./mcall (prints PID, then blocks forever — use gcore to dump)
11+
package main
12+
13+
import (
14+
"fmt"
15+
"os"
16+
"runtime"
17+
"sync"
18+
"time"
19+
)
20+
21+
//go:noinline
22+
func deepStack(depth int) int {
23+
if depth <= 0 {
24+
runtime.Gosched()
25+
return 0
26+
}
27+
return deepStack(depth-1) + 1
28+
}
29+
30+
//go:noinline
31+
func chanBlocker(ch <-chan struct{}) {
32+
<-ch
33+
}
34+
35+
//go:noinline
36+
func mutexBlocker(mu *sync.Mutex, done <-chan struct{}) {
37+
for {
38+
mu.Lock()
39+
mu.Unlock()
40+
select {
41+
case <-done:
42+
return
43+
default:
44+
}
45+
}
46+
}
47+
48+
//go:noinline
49+
func sleeper() {
50+
time.Sleep(time.Hour)
51+
}
52+
53+
//go:noinline
54+
func selectBlocker(ch1, ch2 <-chan struct{}) {
55+
select {
56+
case <-ch1:
57+
case <-ch2:
58+
}
59+
}
60+
61+
func main() {
62+
fmt.Println("PID:", os.Getpid())
63+
fmt.Println("Waiting for gcore... (Ctrl+C to stop)")
64+
65+
done := make(chan struct{})
66+
neverClose := make(chan struct{})
67+
var mu sync.Mutex
68+
69+
// mcall via park_m: channel receive blocks.
70+
for i := 0; i < 4; i++ {
71+
go chanBlocker(neverClose)
72+
}
73+
74+
// mcall via park_m: select blocks.
75+
go selectBlocker(neverClose, neverClose)
76+
77+
// mcall via park_m: time.Sleep blocks.
78+
for i := 0; i < 2; i++ {
79+
go sleeper()
80+
}
81+
82+
// mcall via gopreempt_m: cooperative yields on deep stacks.
83+
for i := 0; i < 4; i++ {
84+
go func() {
85+
for {
86+
deepStack(64)
87+
select {
88+
case <-done:
89+
return
90+
default:
91+
}
92+
}
93+
}()
94+
}
95+
96+
// mcall via various paths: mutex contention.
97+
mu.Lock()
98+
for i := 0; i < 2; i++ {
99+
go mutexBlocker(&mu, done)
100+
}
101+
mu.Unlock()
102+
103+
// Block main forever (mcall via park_m).
104+
<-neverClose
105+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Program systemstack exercises the runtime.systemstack path by calling
5+
// runtime.Callers in a tight loop. A coredump taken while it runs is
6+
// likely to contain threads executing on the system stack.
7+
//
8+
// Build: go build -o systemstack ./tools/coredump/testsources/go/systemstack/
9+
// Run: ./systemstack (profiles for 30s, then exits)
10+
package main
11+
12+
import (
13+
"os"
14+
"runtime"
15+
"runtime/pprof"
16+
"time"
17+
)
18+
19+
func main() {
20+
f, err := os.Create("cpu.pprof")
21+
if err != nil {
22+
panic(err)
23+
}
24+
defer f.Close()
25+
26+
pprof.StartCPUProfile(f)
27+
defer pprof.StopCPUProfile()
28+
29+
pcs := make([]uintptr, 32)
30+
deadline := time.After(30 * time.Second)
31+
for {
32+
select {
33+
case <-deadline:
34+
return
35+
default:
36+
runtime.Callers(0, pcs)
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)