exceptioncode uint32
exceptionflags uint32
exceptionrecord *exceptionrecord
- exceptionaddress *byte
+ exceptionaddress uintptr
numberparameters uint32
exceptioninformation [15]uintptr
}
const MaxArgs = maxArgs
var (
- TestingWER = &testingWER
OsYield = osyield
TimeBeginPeriodRetValue = &timeBeginPeriodRetValue
)
GOTRACEBACK=crash is like “system” but crashes in an operating system-specific
manner instead of exiting. For example, on Unix systems, the crash raises
SIGABRT to trigger a core dump.
+GOTRACEBACK=wer is like “crash” but doesn't disable Windows Error Reporting (WER).
For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for
none, all, and system, respectively.
The runtime/debug package's SetTraceback function allows increasing the
// osRelax is called by the scheduler when transitioning to and from
// all Ps being idle.
func osRelax(relax bool) {}
+
+// enableWER is called by setTraceback("wer").
+// Windows Error Reporting (WER) is only supported on Windows.
+func enableWER() {}
//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._GetCurrentThreadId GetCurrentThreadId%0 "kernel32.dll"
//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetErrorMode GetErrorMode%0 "kernel32.dll"
//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._GetQueuedCompletionStatusEx GetQueuedCompletionStatusEx%6 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
+//go:cgo_import_dynamic runtime._RaiseFailFastException RaiseFailFastException%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._WaitForMultipleObjects WaitForMultipleObjects%4 "kernel32.dll"
+//go:cgo_import_dynamic runtime._WerGetFlags WerGetFlags%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._WerSetFlags WerSetFlags%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
_GetConsoleMode,
_GetCurrentThreadId,
_GetEnvironmentStringsW,
+ _GetErrorMode,
_GetProcAddress,
_GetProcessAffinityMask,
_GetQueuedCompletionStatusEx,
_PostQueuedCompletionStatus,
_QueryPerformanceCounter,
_QueryPerformanceFrequency,
+ _RaiseFailFastException,
_ResumeThread,
_SetConsoleCtrlHandler,
_SetErrorMode,
_VirtualQuery,
_WaitForSingleObject,
_WaitForMultipleObjects,
+ _WerGetFlags,
+ _WerSetFlags,
_WriteConsoleW,
_WriteFile,
_ stdFunction
loadOptionalSyscalls()
- disableWER()
+ preventErrorDialogs()
initExceptionHandler()
t = 2<<tracebackShift | tracebackAll
case "crash":
t = 2<<tracebackShift | tracebackAll | tracebackCrash
+ case "wer":
+ if GOOS == "windows" {
+ t = 2<<tracebackShift | tracebackAll | tracebackCrash
+ enableWER()
+ break
+ }
+ fallthrough
default:
t = tracebackAll
if n, ok := atoi(level); ok && n == int(uint32(n)) {
"unsafe"
)
-func disableWER() {
- // do not display Windows Error Reporting dialogue
- const (
- SEM_FAILCRITICALERRORS = 0x0001
- SEM_NOGPFAULTERRORBOX = 0x0002
- SEM_NOALIGNMENTFAULTEXCEPT = 0x0004
- SEM_NOOPENFILEERRORBOX = 0x8000
- )
- errormode := uint32(stdcall1(_SetErrorMode, SEM_NOGPFAULTERRORBOX))
- stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
+const (
+ _SEM_FAILCRITICALERRORS = 0x0001
+ _SEM_NOGPFAULTERRORBOX = 0x0002
+ _SEM_NOOPENFILEERRORBOX = 0x8000
+
+ _WER_FAULT_REPORTING_NO_UI = 0x0020
+)
+
+func preventErrorDialogs() {
+ errormode := stdcall0(_GetErrorMode)
+ stdcall1(_SetErrorMode, errormode|_SEM_FAILCRITICALERRORS|_SEM_NOGPFAULTERRORBOX|_SEM_NOOPENFILEERRORBOX)
+
+ // Disable WER fault reporting UI.
+ // Do this even if WER is disabled as a whole,
+ // as WER might be enabled later with setTraceback("wer")
+ // and we still want the fault reporting UI to be disabled if this happens.
+ var werflags uintptr
+ stdcall2(_WerGetFlags, currentProcess, uintptr(unsafe.Pointer(&werflags)))
+ stdcall1(_WerSetFlags, werflags|_WER_FAULT_REPORTING_NO_UI)
+}
+
+// enableWER re-enables Windows error reporting without fault reporting UI.
+func enableWER() {
+ // re-enable Windows Error Reporting
+ errormode := stdcall0(_GetErrorMode)
+ if errormode&_SEM_NOGPFAULTERRORBOX != 0 {
+ stdcall1(_SetErrorMode, errormode^_SEM_NOGPFAULTERRORBOX)
+ }
}
// in sys_windows_386.s, sys_windows_amd64.s, sys_windows_arm.s, and sys_windows_arm64.s
return _EXCEPTION_CONTINUE_EXECUTION
}
-var testingWER bool
-
// lastcontinuehandler is reached, because runtime cannot handle
// current exception. lastcontinuehandler will print crash info and exit.
//
// should not take responsibility of crashing the process.
return _EXCEPTION_CONTINUE_SEARCH
}
- if testingWER {
- return _EXCEPTION_CONTINUE_SEARCH
- }
// VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap
// illegal instructions during runtime initialization to determine
}
if docrash {
- crash()
+ dieFromException(info, r)
}
exit(2)
//go:nosplit
func crash() {
- // TODO: This routine should do whatever is needed
- // to make the Windows program abort/crash as it
- // would if Go was not intercepting signals.
- // On Unix the routine would remove the custom signal
- // handler and then raise a signal (like SIGABRT).
- // Something like that should happen here.
- // It's okay to leave this empty for now: if crash returns
- // the ordinary exit-after-panic happens.
+ dieFromException(nil, nil)
+}
+
+// dieFromException raises an exception that bypasses all exception handlers.
+// This provides the expected exit status for the shell.
+//
+//go:nosplit
+func dieFromException(info *exceptionrecord, r *context) {
+ if info == nil {
+ gp := getg()
+ if gp.sig != 0 {
+ // Try to reconstruct an exception record from
+ // the exception information stored in gp.
+ info = &exceptionrecord{
+ exceptionaddress: gp.sigpc,
+ exceptioncode: gp.sig,
+ numberparameters: 2,
+ }
+ info.exceptioninformation[0] = gp.sigcode0
+ info.exceptioninformation[1] = gp.sigcode1
+ } else {
+ // By default, a failing Go application exits with exit code 2.
+ // Use this value when gp does not contain exception info.
+ info = &exceptionrecord{
+ exceptioncode: 2,
+ }
+ }
+ }
+ const FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1
+ stdcall3(_RaiseFailFastException, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(r)), FAIL_FAST_GENERATE_EXCEPTION_ADDRESS)
}
// gsignalStack is unused on Windows.
}
func TestWERDialogue(t *testing.T) {
- if os.Getenv("TESTING_WER_DIALOGUE") == "1" {
- defer os.Exit(0)
-
- *runtime.TestingWER = true
+ const exitcode = 0xbad
+ if os.Getenv("TEST_WER_DIALOGUE") == "1" {
const EXCEPTION_NONCONTINUABLE = 1
mod := syscall.MustLoadDLL("kernel32.dll")
proc := mod.MustFindProc("RaiseException")
- proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
- println("RaiseException should not return")
+ proc.Call(exitcode, EXCEPTION_NONCONTINUABLE, 0, 0)
+ t.Fatal("RaiseException should not return")
return
}
- cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue")
- cmd.Env = []string{"TESTING_WER_DIALOGUE=1"}
+ cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestWERDialogue"))
+ cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer")
// Child process should not open WER dialogue, but return immediately instead.
- cmd.CombinedOutput()
+ _, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Error("test program succeeded unexpectedly")
+ } else if ee, ok := err.(*exec.ExitError); !ok {
+ t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
+ } else if got := ee.ExitCode(); got != exitcode {
+ t.Fatalf("got exit code %d; want %d", got, exitcode)
+ }
}
func TestWindowsStackMemory(t *testing.T) {