Skip to content

Commit 353d5b6

Browse files
zhizheng052bufflig
authored andcommitted
runtime: enable crash dump creation on Windows
This change provides ability to create dumps on Windows that can be used by "dlv core" command. Currently only full dumps can be correctly read by Delve. Below are the steps to create and use the dumps. 1. Configure Windows OS to collect dumps before running the program. Instructions on how to do the configuration are here: https://docs.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps. In order for Delve to read the dump, set the DumpType to full dump, i.e. DumpType=2. 2. Go program only generates dumps when the environment variable GOTRACEBACK is set to crash. Run command "set GOTRACEBACK=crash" before running the program. 3. Dump files will be generated in %LOCALAPPDATA%\CrashDumps 4. Use Delve command "dlv core" to open the dump, e.g.: "dlv core a.exe a.exe.3840.dmp". Fixes #20498 Change-Id: Ib9aa82e7aea9da19594dc49348876997b24e9600 Reviewed-on: https://go-review.googlesource.com/c/go/+/307372 Run-TryBot: Alessandro Arzilli <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Patrik Nyblom <[email protected]> Trust: Alex Brainman <[email protected]>
1 parent d8fc7f7 commit 353d5b6

File tree

3 files changed

+67
-22
lines changed

3 files changed

+67
-22
lines changed

src/runtime/os_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const (
4040
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
4141
//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
4242
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
43+
//go:cgo_import_dynamic runtime._RaiseException RaiseException%4 "kernel32.dll"
4344
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
4445
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
4546
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
@@ -93,6 +94,7 @@ var (
9394
_PostQueuedCompletionStatus,
9495
_QueryPerformanceCounter,
9596
_QueryPerformanceFrequency,
97+
_RaiseException,
9698
_ResumeThread,
9799
_SetConsoleCtrlHandler,
98100
_SetErrorMode,
@@ -120,6 +122,7 @@ var (
120122
_AddVectoredContinueHandler,
121123
_LoadLibraryExA,
122124
_LoadLibraryExW,
125+
_WerSetFlags,
123126
_ stdFunction
124127

125128
// Use RtlGenRandom to generate cryptographically random data.
@@ -254,6 +257,7 @@ func loadOptionalSyscalls() {
254257
_AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000"))
255258
_LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000"))
256259
_LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000"))
260+
_WerSetFlags = windowsFindfunc(k32, []byte("WerSetFlags\000"))
257261
useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)
258262

259263
var advapi32dll = []byte("advapi32.dll\000")

src/runtime/panic.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,11 @@ var runningPanicDefers uint32
975975
// panicking is incremented and decremented atomically.
976976
var panicking uint32
977977

978+
// tracebackprinted is zero before gopanic() prints the traceback. After
979+
// traceback is printed, it sets to 1 so that the subsequent exception handler
980+
// won't print the traceback again.
981+
var tracebackprinted uint32
982+
978983
// paniclk is held while printing the panic information and stack trace,
979984
// so that two concurrent panics don't overlap their output.
980985
var paniclk mutex
@@ -1018,6 +1023,9 @@ func fatalthrow() {
10181023
startpanic_m()
10191024

10201025
if dopanic_m(gp, pc, sp) {
1026+
// At this point, traceback has already been printed.
1027+
// Set tracebackprinted to 1 to avoid printing traceback again
1028+
tracebackprinted = 1
10211029
// crash uses a decent amount of nosplit stack and we're already
10221030
// low on stack in throw, so crash on the system stack (unlike
10231031
// fatalpanic).
@@ -1059,6 +1067,9 @@ func fatalpanic(msgs *_panic) {
10591067
})
10601068

10611069
if docrash {
1070+
// At this point, traceback has already been printed.
1071+
// Set tracebackprinted to 1 to avoid printing traceback again
1072+
tracebackprinted = 1
10621073
// By crashing outside the above systemstack call, debuggers
10631074
// will not be confused when generating a backtrace.
10641075
// Function crash is marked nosplit to avoid stack growth.

src/runtime/signal_windows.go

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@ func disableWER() {
2222
stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
2323
}
2424

25+
// enableWERNoUI re-enables Windows error reporting without fault reporting UI.
26+
// It returns false on older Windows versions (XP and earlier) where WerSetFlags() is not supported.
27+
//
28+
// This is marked nosplit since it is used during crash.
29+
//
30+
//go:nosplit
31+
func enableWERNoUI() bool {
32+
if _WerSetFlags == nil {
33+
return false
34+
}
35+
36+
// Disable Fault reporting UI
37+
const (
38+
WER_FAULT_REPORTING_NO_UI = 0x0020
39+
)
40+
if stdcall1(_WerSetFlags, WER_FAULT_REPORTING_NO_UI) != 0 {
41+
return false
42+
}
43+
44+
// re-enable Windows Error Reporting
45+
stdcall1(_SetErrorMode, 0)
46+
return true
47+
}
48+
2549
// in sys_windows_386.s and sys_windows_amd64.s
2650
func exceptiontramp()
2751
func firstcontinuetramp()
@@ -108,6 +132,7 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
108132
// Don't go through any more of the Windows handler chain.
109133
// Crash now.
110134
winthrow(info, r, gp)
135+
exit(2)
111136
}
112137

113138
// After this point, it is safe to grow the stack.
@@ -196,18 +221,22 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
196221
}
197222

198223
winthrow(info, r, gp)
224+
225+
_, _, docrash := gotraceback()
226+
if docrash {
227+
// trigger crash dump creation
228+
if enableWERNoUI() {
229+
return _EXCEPTION_CONTINUE_SEARCH
230+
}
231+
}
232+
exit(2)
199233
return 0 // not reached
200234
}
201235

202236
//go:nosplit
203237
func winthrow(info *exceptionrecord, r *context, gp *g) {
204238
_g_ := getg()
205239

206-
if panicking != 0 { // traceback already printed
207-
exit(2)
208-
}
209-
panicking = 1
210-
211240
// In case we're handling a g0 stack overflow, blow away the
212241
// g0 stack bounds so we have room to print the traceback. If
213242
// this somehow overflows the stack, the OS will trap it.
@@ -229,18 +258,16 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
229258
_g_.m.throwing = 1
230259
_g_.m.caughtsig.set(gp)
231260

232-
level, _, docrash := gotraceback()
261+
level, _, _ := gotraceback()
233262
if level > 0 {
234-
tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
235-
tracebackothers(gp)
263+
// only print traceback when it hasn't been printed
264+
if tracebackprinted == 0 {
265+
tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
266+
tracebackothers(gp)
267+
tracebackprinted = 1
268+
}
236269
dumpregs(r)
237270
}
238-
239-
if docrash {
240-
crash()
241-
}
242-
243-
exit(2)
244271
}
245272

246273
func sigpanic() {
@@ -312,14 +339,17 @@ func signame(sig uint32) string {
312339

313340
//go:nosplit
314341
func crash() {
315-
// TODO: This routine should do whatever is needed
316-
// to make the Windows program abort/crash as it
317-
// would if Go was not intercepting signals.
318-
// On Unix the routine would remove the custom signal
319-
// handler and then raise a signal (like SIGABRT).
320-
// Something like that should happen here.
321-
// It's okay to leave this empty for now: if crash returns
322-
// the ordinary exit-after-panic happens.
342+
// When GOTRACEBACK==crash, raise the same exception
343+
// from kernel32.dll, so that Windows gets a chance
344+
// to handle the exception by creating a crash dump.
345+
346+
// Get the Exception code that caused the crash
347+
gp := getg()
348+
exceptionCode := gp.sig
349+
350+
// RaiseException() here will not be handled in exceptionhandler()
351+
// because it comes from kernel32.dll
352+
stdcall4(_RaiseException, uintptr(unsafe.Pointer(&exceptionCode)), 0, 0, 0)
323353
}
324354

325355
// gsignalStack is unused on Windows.

0 commit comments

Comments
 (0)