github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/runtime/signal_windows_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // build c program 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 // build go exe 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 // run test program in same thread 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 // run test program in a new thread 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 //TODO: remove this skip and update testwinlib/main.c 84 // once windows/arm supports c-shared buildmode. 85 // See go.dev/issues/43800. 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 // build go dll 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 // build c program 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 // run test program 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 // TODO: remove when windows/arm64 and windows/arm support SEH stack unwinding. 120 expectedOutput = "exceptionCount: 1\ncontinueCount: 1\nunhandledCount: 0\n" 121 } else { 122 expectedOutput = "exceptionCount: 1\ncontinueCount: 1\nunhandledCount: 1\n" 123 } 124 // cleaning output 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 // TestCtrlHandler tests that Go can gracefully handle closing the console window. 148 // See https://golang.org/issues/41884. 149 func TestCtrlHandler(t *testing.T) { 150 testenv.MustHaveGoBuild(t) 151 t.Parallel() 152 153 // build go program 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 // run test program 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 // keep inPipe alive until the end of the test 172 defer inPipe.Close() 173 174 // in a new command window 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 // check child exited gracefully, did not timeout 189 if err := cmd.Wait(); err != nil { 190 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 191 } 192 193 // check child received, handled SIGTERM 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 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events. 200 // See https://golang.org/issues/35965. 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 // build go dll 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 // build c program 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 // run test program 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 // build go dll 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 // build c program 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 // run test program 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 }