Skip to content

Record the last fatal error message for crash reporting on Windows #72718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/swift/Runtime/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ static inline const char *CRGetCrashLogMessage() {
SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE
static inline void CRSetCrashLogMessage(const char *) {}

#if defined(_WIN32)
extern "C" {
SWIFT_ATTRIBUTE_FOR_EXPORTS
extern char* gLastFatalErrorMessage;
}
#endif // _WIN32

#endif

namespace swift {
Expand Down
7 changes: 7 additions & 0 deletions stdlib/public/runtime/CrashReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ struct crashreporter_annotations_t gCRAnnotations __attribute__((
}

#endif

#if defined(_WIN32)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the redeclaration necessary given #include "swift/Runtime/Debug.h"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, note that the one in Debug.h is extern and this here is the real definition / storage location.

extern "C" {
SWIFT_ATTRIBUTE_FOR_EXPORTS
char* gLastFatalErrorMessage = nullptr;
}
#endif
31 changes: 23 additions & 8 deletions stdlib/public/runtime/Errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
#include "swift/Runtime/Atomic.h"
#endif // SWIFT_HAVE_CRASHREPORTERCLIENT

#if defined(_WIN32)
#include "swift/Runtime/Atomic.h"
#endif // _WIN32

#include "BacktracePrivate.h"

namespace FatalErrorFlags {
Expand Down Expand Up @@ -268,16 +272,15 @@ void swift::printCurrentBacktrace(unsigned framesToSkip) {
fprintf(stderr, "<backtrace unavailable>\n");
}

// Report a message to any forthcoming crash log.
static void
reportOnCrash(uint32_t flags, const char *message)
{
#ifdef SWIFT_HAVE_CRASHREPORTERCLIENT
#if defined(SWIFT_HAVE_CRASHREPORTERCLIENT) || defined(_WIN32)
// Update the last fatal error message for crash reporting.
static void updateLastFatalErrorMessage(void* location,
const char* message) {
char *oldMessage = nullptr;
char *newMessage = nullptr;

oldMessage = std::atomic_load_explicit(
(volatile std::atomic<char *> *)&gCRAnnotations.message,
(volatile std::atomic<char *> *)location,
SWIFT_MEMORY_ORDER_CONSUME);

do {
Expand All @@ -292,13 +295,25 @@ reportOnCrash(uint32_t flags, const char *message)
newMessage = strdup(message);
}
} while (!std::atomic_compare_exchange_strong_explicit(
(volatile std::atomic<char *> *)&gCRAnnotations.message,
(volatile std::atomic<char *> *)location,
&oldMessage, newMessage,
std::memory_order_release,
SWIFT_MEMORY_ORDER_CONSUME));
}
#endif // SWIFT_HAVE_CRASHREPORTERCLIENT || _WIN32

// Report a message to any forthcoming crash log.
static void
reportOnCrash(uint32_t flags, const char *message)
{
#ifdef SWIFT_HAVE_CRASHREPORTERCLIENT
updateLastFatalErrorMessage(&gCRAnnotations.message, message);
#elif defined(_WIN32)
// Make the fatal error message accessible for Windows dump tools.
updateLastFatalErrorMessage(&gLastFatalErrorMessage, message);
#else
// empty
#endif // SWIFT_HAVE_CRASHREPORTERCLIENT
#endif // SWIFT_HAVE_CRASHREPORTERCLIENT || _WIN32
}

// Report a message to system console and stderr.
Expand Down
25 changes: 25 additions & 0 deletions test/Runtime/Inputs/setup-handler-win.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import WinSDK

func setupHandler() {
SetUnhandledExceptionFilter { exception_pointers in
guard let hSwiftCore = GetModuleHandleA("swiftCore.dll") else {
return EXCEPTION_EXECUTE_HANDLER
}
guard let ppLastFatalErrorMessage = unsafeBitCast(
GetProcAddress(hSwiftCore, "gLastFatalErrorMessage"),
to: Optional<UnsafeMutablePointer<Optional<UnsafeMutablePointer<UInt8>>>>.self) else {
return EXCEPTION_EXECUTE_HANDLER
}
guard let pLastFatalErrorMessage = ppLastFatalErrorMessage.pointee else {
return EXCEPTION_EXECUTE_HANDLER
}
let message = "Fatal error message: \(String(cString: pLastFatalErrorMessage))"
message.utf8CString.withUnsafeBufferPointer { buf in
let addr = buf.baseAddress!
let count = DWORD(buf.count)
let stderr = GetStdHandle(STD_ERROR_HANDLE)
WriteFile(stderr, addr, count, nil, nil)
}
return EXCEPTION_EXECUTE_HANDLER
}
}
20 changes: 20 additions & 0 deletions test/Runtime/fatal-error-message-win.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s %S/Inputs/setup-handler-win.swift -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: not %target-run %t/a.out 2>&1 | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: OS=windows-msvc

// CHECK: Fatal error message: a/fatal-error-message-win.swift:{{[0-9]+}}: Fatal error: Fatal crash!

func crash() {
fatalError("Fatal crash!")
}

@main
struct Test {
static func main() throws {
setupHandler()
crash()
}
}
30 changes: 30 additions & 0 deletions test/Runtime/fatal-try-error-message-win.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s %S/Inputs/setup-handler-win.swift -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: not %target-run %t/a.out 2>&1 | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: OS=windows-msvc

// CHECK: Fatal error message: a/fatal-try-error-message-win.swift:{{[0-9]+}}: Fatal error: 'try!' expression unexpectedly raised an error: a.Errs.getMyDescription("Try crash!")

enum Errs: Error {
case getMyDescription(String)
}

func throwings(_ i: Int) throws {
if i == 0 {
throw Errs.getMyDescription("Try crash!")
}
}

func crash() {
try! throwings(0)
}

@main
struct Test {
static func main() throws {
setupHandler()
crash()
}
}