Source file
src/runtime/signal_windows_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "internal/testenv"
12 "os/exec"
13 "path/filepath"
14 "runtime"
15 "strings"
16 "syscall"
17 "testing"
18 )
19
20 func TestVectoredHandlerExceptionInNonGoThread(t *testing.T) {
21 if *flagQuick {
22 t.Skip("-quick")
23 }
24 if strings.HasPrefix(testenv.Builder(), "windows-amd64-2012") {
25 testenv.SkipFlaky(t, 49681)
26 }
27 testenv.MustHaveGoBuild(t)
28 testenv.MustHaveCGO(t)
29 testenv.MustHaveExecPath(t, "gcc")
30 testprog.Lock()
31 defer testprog.Unlock()
32 dir := t.TempDir()
33
34
35 dll := filepath.Join(dir, "veh.dll")
36 cmd := exec.Command("gcc", "-shared", "-o", dll, "testdata/testwinlibthrow/veh.c")
37 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
38 if err != nil {
39 t.Fatalf("failed to build c exe: %s\n%s", err, out)
40 }
41
42
43 exe := filepath.Join(dir, "test.exe")
44 cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinlibthrow/main.go")
45 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
46 if err != nil {
47 t.Fatalf("failed to build go library: %s\n%s", err, out)
48 }
49
50
51 cmd = exec.Command(exe)
52 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
53 if err == nil {
54 t.Fatal("error expected")
55 }
56 if _, ok := err.(*exec.ExitError); ok && len(out) > 0 {
57 if !bytes.Contains(out, []byte("Exception 0x2a")) {
58 t.Fatalf("unexpected failure while running executable: %s\n%s", err, out)
59 }
60 } else {
61 t.Fatalf("unexpected error while running executable: %s\n%s", err, out)
62 }
63
64 cmd = exec.Command(exe, "thread")
65 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
66 if err == nil {
67 t.Fatal("error expected")
68 }
69 if err, ok := err.(*exec.ExitError); ok {
70 if err.ExitCode() != 42 {
71 t.Fatalf("unexpected failure while running executable: %s\n%s", err, out)
72 }
73 } else {
74 t.Fatalf("unexpected error while running executable: %s\n%s", err, out)
75 }
76 }
77
78 func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) {
79 if *flagQuick {
80 t.Skip("-quick")
81 }
82 if runtime.GOARCH == "arm" {
83
84
85
86 t.Skip("this test can't run on windows/arm")
87 }
88 testenv.MustHaveGoBuild(t)
89 testenv.MustHaveCGO(t)
90 testenv.MustHaveExecPath(t, "gcc")
91 testprog.Lock()
92 defer testprog.Unlock()
93 dir := t.TempDir()
94
95
96 dll := filepath.Join(dir, "testwinlib.dll")
97 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", "testdata/testwinlib/main.go")
98 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
99 if err != nil {
100 t.Fatalf("failed to build go library: %s\n%s", err, out)
101 }
102
103
104 exe := filepath.Join(dir, "test.exe")
105 cmd = exec.Command("gcc", "-L"+dir, "-I"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c")
106 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
107 if err != nil {
108 t.Fatalf("failed to build c exe: %s\n%s", err, out)
109 }
110
111
112 cmd = exec.Command(exe)
113 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
114 if err != nil {
115 t.Fatalf("failure while running executable: %s\n%s", err, out)
116 }
117 var expectedOutput string
118 if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
119
120 expectedOutput = "exceptionCount: 1\ncontinueCount: 1\nunhandledCount: 0\n"
121 } else {
122 expectedOutput = "exceptionCount: 1\ncontinueCount: 1\nunhandledCount: 1\n"
123 }
124
125 cleanedOut := strings.ReplaceAll(string(out), "\r\n", "\n")
126 if cleanedOut != expectedOutput {
127 t.Errorf("expected output %q, got %q", expectedOutput, cleanedOut)
128 }
129 }
130
131 func sendCtrlBreak(pid int) error {
132 kernel32, err := syscall.LoadDLL("kernel32.dll")
133 if err != nil {
134 return fmt.Errorf("LoadDLL: %v\n", err)
135 }
136 generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent")
137 if err != nil {
138 return fmt.Errorf("FindProc: %v\n", err)
139 }
140 result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))
141 if result == 0 {
142 return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err)
143 }
144 return nil
145 }
146
147
148
149 func TestCtrlHandler(t *testing.T) {
150 testenv.MustHaveGoBuild(t)
151 t.Parallel()
152
153
154 exe := filepath.Join(t.TempDir(), "test.exe")
155 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinsignal/main.go")
156 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
157 if err != nil {
158 t.Fatalf("failed to build go exe: %v\n%s", err, out)
159 }
160
161
162 cmd = exec.Command(exe)
163 var stdout strings.Builder
164 var stderr strings.Builder
165 cmd.Stdout = &stdout
166 cmd.Stderr = &stderr
167 inPipe, err := cmd.StdinPipe()
168 if err != nil {
169 t.Fatalf("Failed to create stdin pipe: %v", err)
170 }
171
172 defer inPipe.Close()
173
174
175 const _CREATE_NEW_CONSOLE = 0x00000010
176 cmd.SysProcAttr = &syscall.SysProcAttr{
177 CreationFlags: _CREATE_NEW_CONSOLE,
178 HideWindow: true,
179 }
180 if err := cmd.Start(); err != nil {
181 t.Fatalf("Start failed: %v", err)
182 }
183 defer func() {
184 cmd.Process.Kill()
185 cmd.Wait()
186 }()
187
188
189 if err := cmd.Wait(); err != nil {
190 t.Fatalf("Program exited with error: %v\n%s", err, &stderr)
191 }
192
193
194 if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(stdout.String()); expected != got {
195 t.Fatalf("Expected '%s' got: %s", expected, got)
196 }
197 }
198
199
200
201 func TestLibraryCtrlHandler(t *testing.T) {
202 if *flagQuick {
203 t.Skip("-quick")
204 }
205 if runtime.GOARCH != "amd64" {
206 t.Skip("this test can only run on windows/amd64")
207 }
208 testenv.MustHaveGoBuild(t)
209 testenv.MustHaveCGO(t)
210 testenv.MustHaveExecPath(t, "gcc")
211 testprog.Lock()
212 defer testprog.Unlock()
213 dir := t.TempDir()
214
215
216 dll := filepath.Join(dir, "dummy.dll")
217 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", "testdata/testwinlibsignal/dummy.go")
218 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
219 if err != nil {
220 t.Fatalf("failed to build go library: %s\n%s", err, out)
221 }
222
223
224 exe := filepath.Join(dir, "test.exe")
225 cmd = exec.Command("gcc", "-o", exe, "testdata/testwinlibsignal/main.c")
226 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
227 if err != nil {
228 t.Fatalf("failed to build c exe: %s\n%s", err, out)
229 }
230
231
232 cmd = exec.Command(exe)
233 var stderr bytes.Buffer
234 cmd.Stderr = &stderr
235 outPipe, err := cmd.StdoutPipe()
236 if err != nil {
237 t.Fatalf("Failed to create stdout pipe: %v", err)
238 }
239 outReader := bufio.NewReader(outPipe)
240
241 cmd.SysProcAttr = &syscall.SysProcAttr{
242 CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
243 }
244 if err := cmd.Start(); err != nil {
245 t.Fatalf("Start failed: %v", err)
246 }
247
248 errCh := make(chan error, 1)
249 go func() {
250 if line, err := outReader.ReadString('\n'); err != nil {
251 errCh <- fmt.Errorf("could not read stdout: %v", err)
252 } else if strings.TrimSpace(line) != "ready" {
253 errCh <- fmt.Errorf("unexpected message: %v", line)
254 } else {
255 errCh <- sendCtrlBreak(cmd.Process.Pid)
256 }
257 }()
258
259 if err := <-errCh; err != nil {
260 t.Fatal(err)
261 }
262 if err := cmd.Wait(); err != nil {
263 t.Fatalf("Program exited with error: %v\n%s", err, &stderr)
264 }
265 }
266
267 func TestIssue59213(t *testing.T) {
268 if runtime.GOOS != "windows" {
269 t.Skip("skipping windows only test")
270 }
271 if *flagQuick {
272 t.Skip("-quick")
273 }
274 testenv.MustHaveGoBuild(t)
275 testenv.MustHaveCGO(t)
276
277 goEnv := func(arg string) string {
278 cmd := testenv.Command(t, testenv.GoToolPath(t), "env", arg)
279 cmd.Stderr = new(bytes.Buffer)
280
281 line, err := cmd.Output()
282 if err != nil {
283 t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
284 }
285 out := string(bytes.TrimSpace(line))
286 t.Logf("%v: %q", cmd, out)
287 return out
288 }
289
290 cc := goEnv("CC")
291 cgoCflags := goEnv("CGO_CFLAGS")
292
293 t.Parallel()
294
295 tmpdir := t.TempDir()
296 dllfile := filepath.Join(tmpdir, "test.dll")
297 exefile := filepath.Join(tmpdir, "gotest.exe")
298
299
300 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", dllfile, "-buildmode", "c-shared", "testdata/testwintls/main.go")
301 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
302 if err != nil {
303 t.Fatalf("failed to build go library: %s\n%s", err, out)
304 }
305
306
307 cmd = testenv.Command(t, cc, "-o", exefile, "testdata/testwintls/main.c")
308 testenv.CleanCmdEnv(cmd)
309 cmd.Env = append(cmd.Env, "CGO_CFLAGS="+cgoCflags)
310 out, err = cmd.CombinedOutput()
311 if err != nil {
312 t.Fatalf("failed to build c exe: %s\n%s", err, out)
313 }
314
315
316 cmd = testenv.Command(t, exefile, dllfile, "GoFunc")
317 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
318 if err != nil {
319 t.Fatalf("failed: %s\n%s", err, out)
320 }
321 }
322
View as plain text