Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common/assert.cpp
4214 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "assert.h"
5
#include "crash_handler.h"
6
7
#include <cstdio>
8
#include <cstdlib>
9
10
#ifdef _WIN32
11
12
#include "windows_headers.h"
13
#include <intrin.h>
14
#include <tlhelp32.h>
15
16
#include <mutex>
17
18
#ifdef __clang__
19
#pragma clang diagnostic ignored "-Winvalid-noreturn"
20
#endif
21
22
static std::mutex s_AssertFailedMutex;
23
24
static HANDLE FreezeThreads()
25
{
26
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
27
if (hSnapshot != INVALID_HANDLE_VALUE)
28
{
29
THREADENTRY32 threadEntry;
30
if (Thread32First(hSnapshot, &threadEntry))
31
{
32
do
33
{
34
if (threadEntry.th32ThreadID == GetCurrentThreadId())
35
continue;
36
37
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID);
38
if (hThread != NULL)
39
{
40
SuspendThread(hThread);
41
CloseHandle(hThread);
42
}
43
} while (Thread32Next(hSnapshot, &threadEntry));
44
}
45
}
46
47
return hSnapshot;
48
}
49
50
static void ResumeThreads(HANDLE hSnapshot)
51
{
52
if (hSnapshot != INVALID_HANDLE_VALUE)
53
{
54
THREADENTRY32 threadEntry;
55
if (Thread32First(hSnapshot, &threadEntry))
56
{
57
do
58
{
59
if (threadEntry.th32ThreadID == GetCurrentThreadId())
60
continue;
61
62
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID);
63
if (hThread != NULL)
64
{
65
ResumeThread(hThread);
66
CloseHandle(hThread);
67
}
68
} while (Thread32Next(hSnapshot, &threadEntry));
69
}
70
CloseHandle(hSnapshot);
71
}
72
}
73
74
#else
75
76
#ifdef __ANDROID__
77
// Define as a weak symbol for ancient devices that don't have it.
78
extern "C" __attribute__((weak)) void android_set_abort_message(const char*);
79
#endif
80
81
[[noreturn]] ALWAYS_INLINE static void AbortWithMessage(const char* szMsg)
82
{
83
#ifndef __ANDROID__
84
std::fputs(szMsg, stderr);
85
std::fflush(stderr);
86
std::abort();
87
#else
88
if (&android_set_abort_message)
89
android_set_abort_message(szMsg);
90
91
std::abort();
92
#endif
93
}
94
95
#endif // _WIN32
96
97
void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine)
98
{
99
char szMsg[512];
100
std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine);
101
102
#if defined(_WIN32)
103
std::unique_lock lock(s_AssertFailedMutex);
104
HANDLE pHandle = FreezeThreads();
105
106
SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
107
WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL);
108
OutputDebugStringA(szMsg);
109
110
std::snprintf(
111
szMsg, sizeof(szMsg),
112
"%s in function %s (%s:%u)\nPress Abort to exit, Retry to break to debugger, or Ignore to attempt to continue.",
113
szMessage, szFunction, szFile, uLine);
114
115
int result = MessageBoxA(NULL, szMsg, NULL, MB_ABORTRETRYIGNORE | MB_ICONERROR);
116
if (result == IDRETRY)
117
{
118
__debugbreak();
119
}
120
else if (result != IDIGNORE)
121
{
122
CrashHandler::WriteDumpForCaller(szMsg);
123
TerminateProcess(GetCurrentProcess(), 0xBAADC0DE);
124
}
125
126
ResumeThreads(pHandle);
127
#else
128
AbortWithMessage(szMsg);
129
#endif
130
}
131
132
[[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine)
133
{
134
char szMsg[512];
135
std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine);
136
137
#if defined(_WIN32)
138
std::unique_lock guard(s_AssertFailedMutex);
139
HANDLE pHandle = FreezeThreads();
140
141
SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
142
WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL);
143
OutputDebugStringA(szMsg);
144
145
std::snprintf(szMsg, sizeof(szMsg),
146
"%s in function %s (%s:%u)\nDo you want to attempt to break into a debugger? Pressing Cancel will "
147
"abort the application.",
148
szMessage, szFunction, szFile, uLine);
149
150
int result = MessageBoxA(NULL, szMsg, NULL, MB_OKCANCEL | MB_ICONERROR);
151
if (result == IDOK)
152
__debugbreak();
153
else
154
CrashHandler::WriteDumpForCaller(szMsg);
155
156
TerminateProcess(GetCurrentProcess(), 0xBAADC0DE);
157
158
ResumeThreads(pHandle);
159
#else
160
AbortWithMessage(szMsg);
161
#endif
162
}
163
164