github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/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 testenv.MustHaveGoBuild(t) 25 testenv.MustHaveCGO(t) 26 testenv.MustHaveExecPath(t, "g++") 27 testprog.Lock() 28 defer testprog.Unlock() 29 dir := t.TempDir() 30 31 // build c program 32 dll := filepath.Join(dir, "veh.dll") 33 cmd := exec.Command("g++", "-shared", "-o", dll, "testdata/testwinlibthrow/veh.cpp", "-static", "-lstdc++") 34 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 35 if err != nil { 36 t.Fatalf("failed to build c exe: %s\n%s", err, out) 37 } 38 39 // build go exe 40 exe := filepath.Join(dir, "test.exe") 41 cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinlibthrow/main.go") 42 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 43 if err != nil { 44 t.Fatalf("failed to build go library: %s\n%s", err, out) 45 } 46 47 // run test program in same thread 48 cmd = exec.Command(exe) 49 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 50 if err == nil { 51 t.Fatal("error expected") 52 } 53 if _, ok := err.(*exec.ExitError); ok && len(out) > 0 { 54 if !bytes.Contains(out, []byte("Exception 0x2a")) { 55 t.Fatalf("unexpected failure while running executable: %s\n%s", err, out) 56 } 57 } else { 58 t.Fatalf("unexpected error while running executable: %s\n%s", err, out) 59 } 60 // run test program in a new thread 61 cmd = exec.Command(exe, "thread") 62 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 63 if err == nil { 64 t.Fatal("error expected") 65 } 66 if err, ok := err.(*exec.ExitError); ok { 67 if err.ExitCode() != 42 { 68 t.Fatalf("unexpected failure while running executable: %s\n%s", err, out) 69 } 70 } else { 71 t.Fatalf("unexpected error while running executable: %s\n%s", err, out) 72 } 73 } 74 75 func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) { 76 if *flagQuick { 77 t.Skip("-quick") 78 } 79 if runtime.GOARCH != "amd64" { 80 t.Skip("this test can only run on windows/amd64") 81 } 82 testenv.MustHaveGoBuild(t) 83 testenv.MustHaveCGO(t) 84 testenv.MustHaveExecPath(t, "gcc") 85 testprog.Lock() 86 defer testprog.Unlock() 87 dir := t.TempDir() 88 89 // build go dll 90 dll := filepath.Join(dir, "testwinlib.dll") 91 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", "testdata/testwinlib/main.go") 92 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 93 if err != nil { 94 t.Fatalf("failed to build go library: %s\n%s", err, out) 95 } 96 97 // build c program 98 exe := filepath.Join(dir, "test.exe") 99 cmd = exec.Command("gcc", "-L"+dir, "-I"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c") 100 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 101 if err != nil { 102 t.Fatalf("failed to build c exe: %s\n%s", err, out) 103 } 104 105 // run test program 106 cmd = exec.Command(exe) 107 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 108 if err != nil { 109 t.Fatalf("failure while running executable: %s\n%s", err, out) 110 } 111 expectedOutput := "exceptionCount: 1\ncontinueCount: 1\n" 112 // cleaning output 113 cleanedOut := strings.ReplaceAll(string(out), "\r\n", "\n") 114 if cleanedOut != expectedOutput { 115 t.Errorf("expected output %q, got %q", expectedOutput, cleanedOut) 116 } 117 } 118 119 func sendCtrlBreak(pid int) error { 120 kernel32, err := syscall.LoadDLL("kernel32.dll") 121 if err != nil { 122 return fmt.Errorf("LoadDLL: %v\n", err) 123 } 124 generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent") 125 if err != nil { 126 return fmt.Errorf("FindProc: %v\n", err) 127 } 128 result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) 129 if result == 0 { 130 return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err) 131 } 132 return nil 133 } 134 135 // TestCtrlHandler tests that Go can gracefully handle closing the console window. 136 // See https://golang.org/issues/41884. 137 func TestCtrlHandler(t *testing.T) { 138 testenv.MustHaveGoBuild(t) 139 t.Parallel() 140 141 // build go program 142 exe := filepath.Join(t.TempDir(), "test.exe") 143 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinsignal/main.go") 144 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 145 if err != nil { 146 t.Fatalf("failed to build go exe: %v\n%s", err, out) 147 } 148 149 // run test program 150 cmd = exec.Command(exe) 151 var stdout strings.Builder 152 var stderr strings.Builder 153 cmd.Stdout = &stdout 154 cmd.Stderr = &stderr 155 inPipe, err := cmd.StdinPipe() 156 if err != nil { 157 t.Fatalf("Failed to create stdin pipe: %v", err) 158 } 159 // keep inPipe alive until the end of the test 160 defer inPipe.Close() 161 162 // in a new command window 163 const _CREATE_NEW_CONSOLE = 0x00000010 164 cmd.SysProcAttr = &syscall.SysProcAttr{ 165 CreationFlags: _CREATE_NEW_CONSOLE, 166 HideWindow: true, 167 } 168 if err := cmd.Start(); err != nil { 169 t.Fatalf("Start failed: %v", err) 170 } 171 defer func() { 172 cmd.Process.Kill() 173 cmd.Wait() 174 }() 175 176 // check child exited gracefully, did not timeout 177 if err := cmd.Wait(); err != nil { 178 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 179 } 180 181 // check child received, handled SIGTERM 182 if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(stdout.String()); expected != got { 183 t.Fatalf("Expected '%s' got: %s", expected, got) 184 } 185 } 186 187 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events. 188 // See https://golang.org/issues/35965. 189 func TestLibraryCtrlHandler(t *testing.T) { 190 if *flagQuick { 191 t.Skip("-quick") 192 } 193 if runtime.GOARCH != "amd64" { 194 t.Skip("this test can only run on windows/amd64") 195 } 196 testenv.MustHaveGoBuild(t) 197 testenv.MustHaveCGO(t) 198 testenv.MustHaveExecPath(t, "gcc") 199 testprog.Lock() 200 defer testprog.Unlock() 201 dir := t.TempDir() 202 203 // build go dll 204 dll := filepath.Join(dir, "dummy.dll") 205 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", "testdata/testwinlibsignal/dummy.go") 206 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 207 if err != nil { 208 t.Fatalf("failed to build go library: %s\n%s", err, out) 209 } 210 211 // build c program 212 exe := filepath.Join(dir, "test.exe") 213 cmd = exec.Command("gcc", "-o", exe, "testdata/testwinlibsignal/main.c") 214 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 215 if err != nil { 216 t.Fatalf("failed to build c exe: %s\n%s", err, out) 217 } 218 219 // run test program 220 cmd = exec.Command(exe) 221 var stderr bytes.Buffer 222 cmd.Stderr = &stderr 223 outPipe, err := cmd.StdoutPipe() 224 if err != nil { 225 t.Fatalf("Failed to create stdout pipe: %v", err) 226 } 227 outReader := bufio.NewReader(outPipe) 228 229 cmd.SysProcAttr = &syscall.SysProcAttr{ 230 CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 231 } 232 if err := cmd.Start(); err != nil { 233 t.Fatalf("Start failed: %v", err) 234 } 235 236 errCh := make(chan error, 1) 237 go func() { 238 if line, err := outReader.ReadString('\n'); err != nil { 239 errCh <- fmt.Errorf("could not read stdout: %v", err) 240 } else if strings.TrimSpace(line) != "ready" { 241 errCh <- fmt.Errorf("unexpected message: %v", line) 242 } else { 243 errCh <- sendCtrlBreak(cmd.Process.Pid) 244 } 245 }() 246 247 if err := <-errCh; err != nil { 248 t.Fatal(err) 249 } 250 if err := cmd.Wait(); err != nil { 251 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 252 } 253 }