Source file
src/runtime/unsafepoint_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "internal/abi"
9 "internal/testenv"
10 "os"
11 "os/exec"
12 "reflect"
13 "regexp"
14 "runtime"
15 "strconv"
16 "strings"
17 "testing"
18 "unsafe"
19 )
20
21
22
23 func setGlobalPointer() {
24 globalPointer = nil
25 }
26
27 var globalPointer *int
28
29 func TestUnsafePoint(t *testing.T) {
30 testenv.MustHaveExec(t)
31 switch runtime.GOARCH {
32 case "amd64", "arm64":
33 default:
34 t.Skipf("test not enabled for %s", runtime.GOARCH)
35 }
36
37
38
39 f := runtime.FuncForPC(reflect.ValueOf(setGlobalPointer).Pointer())
40
41
42
43
44
45
46 cmd := exec.Command(testenv.GoToolPath(t), "tool", "objdump", "-s", "setGlobalPointer", os.Args[0])
47 out, err := cmd.CombinedOutput()
48 if err != nil {
49 t.Fatalf("can't objdump %v:\n%s", err, out)
50 }
51 lines := strings.Split(string(out), "\n")[1:]
52
53
54 var entry uint64
55 var startedWB bool
56 var doneWB bool
57 instructionCount := 0
58 unsafeCount := 0
59 for _, line := range lines {
60 line = strings.TrimSpace(line)
61 t.Logf("%s", line)
62 parts := strings.Fields(line)
63 if len(parts) < 4 {
64 continue
65 }
66 if !strings.HasPrefix(parts[0], "unsafepoint_test.go:") {
67 continue
68 }
69 pc, err := strconv.ParseUint(parts[1][2:], 16, 64)
70 if err != nil {
71 t.Fatalf("can't parse pc %s: %v", parts[1], err)
72 }
73 if entry == 0 {
74 entry = pc
75 }
76
77
78
79 unsafe := runtime.UnsafePoint(f.Entry() + uintptr(pc-entry))
80 t.Logf("unsafe: %v\n", unsafe)
81 instructionCount++
82 if unsafe {
83 unsafeCount++
84 }
85
86
87 if startedWB && !doneWB && !unsafe {
88 t.Errorf("instruction %s must be marked unsafe, but isn't", parts[1])
89 }
90
91
92 switch runtime.GOARCH {
93 case "arm64":
94 if parts[3] == "MOVWU" {
95
96
97 startedWB = true
98 }
99 if parts[3] == "MOVD" && parts[4] == "ZR," {
100
101
102 doneWB = true
103 }
104 case "amd64":
105 if parts[3] == "CMPL" {
106 startedWB = true
107 }
108 if parts[3] == "MOVQ" && parts[4] == "$0x0," {
109 doneWB = true
110 }
111 }
112 }
113
114 if instructionCount == 0 {
115 t.Errorf("no instructions")
116 }
117 if unsafeCount == instructionCount {
118 t.Errorf("no interruptible instructions")
119 }
120
121
122
123
124
125 }
126
127
128
129
130
131
132
133
134
135
136
137 type tailCallInner interface{ M() int }
138
139 type tailCallImpl struct{}
140
141 func (tailCallImpl) M() int { return 42 }
142
143 type tailCallOuter struct{ tailCallInner }
144
145 var tailCallValue tailCallInner = tailCallOuter{tailCallImpl{}}
146
147 func TestUnsafePointTailCall(t *testing.T) {
148 switch runtime.GOARCH {
149 case "ppc64", "ppc64le":
150 default:
151 t.Skipf("test not enabled for %s", runtime.GOARCH)
152 }
153 testenv.MustHaveExec(t)
154
155 if got := tailCallValue.M(); got != 42 {
156 t.Fatalf("tailCallValue.M() = %d, want 42", got)
157 }
158
159
160
161 iface := (*struct {
162 tab *abi.ITab
163 data unsafe.Pointer
164 })(unsafe.Pointer(&tailCallValue))
165 f := runtime.FuncForPC(iface.tab.Fun[0])
166 if f == nil {
167 t.Fatal("no func for the tailCallOuter.M wrapper")
168 }
169
170
171 cmd := exec.Command(testenv.GoToolPath(t), "tool", "objdump", "-s", "^"+regexp.QuoteMeta(f.Name())+"$", os.Args[0])
172 out, err := cmd.CombinedOutput()
173 if err != nil {
174 t.Fatalf("can't objdump %v:\n%s", err, out)
175 }
176
177
178
179 var entry uint64
180 branches := 0
181 for _, line := range strings.Split(string(out), "\n")[1:] {
182 parts := strings.Fields(strings.TrimSpace(line))
183 if len(parts) < 4 || !strings.HasPrefix(parts[0], "<autogenerated>:") {
184 continue
185 }
186 pc, err := strconv.ParseUint(parts[1][2:], 16, 64)
187 if err != nil {
188 t.Fatalf("can't parse pc %s: %v", parts[1], err)
189 }
190 if entry == 0 {
191 entry = pc
192 }
193 t.Logf("%s", strings.TrimSpace(line))
194 if parts[3] != "BR" || parts[4] != "(CTR)" {
195 continue
196 }
197 branches++
198 if !runtime.UnsafePoint(f.Entry() + uintptr(pc-entry)) {
199 t.Errorf("%s\n\tbranch through CTR must be marked unsafe, but isn't", strings.TrimSpace(line))
200 }
201 }
202 if branches != 1 {
203 t.Errorf("found %d branches through CTR in %s, want 1; output:\n%s", branches, f.Name(), out)
204 }
205 }
206
View as plain text