Skip to content

Commit 4fa4077

Browse files
committed
Add SignalHandling to Windows
1 parent 60854b0 commit 4fa4077

File tree

2 files changed

+173
-147
lines changed

2 files changed

+173
-147
lines changed

libsrc/utils/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ target_link_libraries(hyperion-utils
103103
Qt${QT_VERSION_MAJOR}::Network
104104
)
105105

106+
if(WIN32)
107+
target_link_libraries(hyperion-utils dbghelp)
108+
endif()
109+
106110
if(ENABLE_EFFECTENGINE)
107111
target_link_libraries(hyperion-utils python)
108112
endif()
Lines changed: 169 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,206 @@
1-
#ifndef _WIN32
2-
31
#include <utils/DefaultSignalHandler.h>
42
#include <utils/Logger.h>
53

4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
8+
#include <QCoreApplication>
9+
10+
#ifdef _WIN32
11+
#include <windows.h>
12+
#include <dbghelp.h>
13+
#include <signal.h>
14+
#pragma comment(lib, "dbghelp.lib")
15+
#else
616
#include <ctype.h>
717
#include <cxxabi.h>
818
#include <execinfo.h>
919
#include <unistd.h>
1020
#include <signal.h>
11-
#include <string.h>
12-
#include <stdio.h>
13-
#include <stdlib.h>
21+
#endif
1422

15-
#include <QCoreApplication>
23+
namespace DefaultSignalHandler {
1624

17-
namespace DefaultSignalHandler
18-
{
19-
struct Signal
20-
{
21-
int number;
22-
const char * name;
23-
};
24-
25-
const Signal ALL_SIGNALS[] = {
26-
{ SIGABRT, "SIGABRT" },
27-
{ SIGBUS, "SIGBUS" },
28-
{ SIGFPE, "SIGFPE" },
29-
{ SIGSEGV, "SIGSEGV" },
30-
{ SIGTERM, "SIGTERM" },
31-
{ SIGHUP, "SIGHUP" },
32-
{ SIGINT, "SIGINT" },
33-
{ SIGPIPE, "SIGPIPE" },
34-
};
35-
36-
void write_to_stderr(const char* data, size_t size)
37-
{
38-
int res = write(STDERR_FILENO, data, size);
39-
40-
Q_UNUSED(res);
41-
}
42-
43-
void write_to_stderr(const char* data)
44-
{
45-
write_to_stderr(data, strlen(data));
46-
}
47-
48-
std::string decipher_trace(const std::string &trace)
49-
{
50-
std::string result;
51-
52-
if(trace.empty())
53-
{
54-
result += "??\n";
55-
return result;
25+
#ifdef _WIN32
26+
void write_to_stderr(const char* data) {
27+
DWORD written;
28+
WriteFile(GetStdHandle(STD_ERROR_HANDLE), data, strlen(data), &written, NULL);
5629
}
30+
#else
31+
void write_to_stderr(const char* data, size_t size) {
32+
int res = write(STDERR_FILENO, data, size);
33+
Q_UNUSED(res);
34+
}
35+
36+
void write_to_stderr(const char* data) {
37+
write_to_stderr(data, strlen(data));
38+
}
39+
#endif
40+
41+
#ifndef _WIN32
42+
std::string decipher_trace(const std::string& trace) {
43+
std::string result;
5744

58-
auto* begin = strchr(trace.c_str(), '(') + 1;
59-
auto* end = strchr(begin, '+');
45+
if (trace.empty()) {
46+
result += "??\n";
47+
return result;
48+
}
6049

61-
if(!end)
62-
end = strchr(begin, ')');
50+
auto* begin = strchr(trace.c_str(), '(') + 1;
51+
auto* end = strchr(begin, '+');
52+
if (!end) end = strchr(begin, ')');
6353

64-
std::string mangled_name(begin, end);
54+
std::string mangled_name(begin, end);
55+
int status;
56+
char* realname = abi::__cxa_demangle(mangled_name.c_str(), 0, 0, &status);
6557

66-
int status;
67-
char * realname = abi::__cxa_demangle(mangled_name.c_str(), 0, 0, &status);
68-
result.insert(result.end(), trace.c_str(), begin);
58+
result.insert(result.end(), trace.c_str(), begin);
59+
if (realname) result += realname;
60+
else result.insert(result.end(), begin, end);
61+
free(realname);
62+
result.insert(result.size(), end);
6963

70-
if(realname)
71-
result += realname;
72-
else
73-
result.insert(result.end(), begin, end);
64+
return result;
65+
}
66+
#endif
7467

75-
free(realname);
76-
result.insert(result.size(), end);
68+
void print_trace() {
69+
Logger* log = Logger::getInstance("CORE");
7770

78-
return result;
79-
}
71+
#ifdef _WIN32
72+
void* stack[50];
73+
HANDLE process = GetCurrentProcess();
74+
SymInitialize(process, NULL, TRUE);
8075

81-
void print_trace()
82-
{
83-
const int MAX_SIZE = 50;
84-
void * addresses[MAX_SIZE];
85-
int size = backtrace(addresses, MAX_SIZE);
76+
USHORT frames = CaptureStackBackTrace(0, 50, stack, NULL);
77+
SYMBOL_INFO* symbol = (SYMBOL_INFO*)malloc(sizeof(SYMBOL_INFO) + 256);
78+
symbol->MaxNameLen = 255;
79+
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
8680

87-
if (!size)
88-
return;
81+
for (USHORT i = 2; i < frames; ++i) {
82+
if (SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol)) {
83+
Error(log, "\t%s\n", symbol->Name);
84+
}
85+
}
8986

90-
Logger* log = Logger::getInstance("CORE");
91-
char ** symbols = backtrace_symbols(addresses, size);
87+
free(symbol);
88+
#else
89+
const int MAX_SIZE = 50;
90+
void* addresses[MAX_SIZE];
91+
int size = backtrace(addresses, MAX_SIZE);
9292

93-
/* Skip first 2 frames as they are signal
94-
* handler and print_trace functions. */
95-
for (int i = 2; i < size; ++i)
96-
{
97-
const std::string line = "\t" + decipher_trace(symbols[i]);
98-
Error(log, "%s", line.c_str());
99-
}
93+
if (!size) return;
10094

101-
free(symbols);
102-
}
103-
104-
void install_default_handler(int signum)
105-
{
106-
struct sigaction action{};
107-
sigemptyset(&action.sa_mask);
108-
action.sa_handler = SIG_DFL;
109-
(void)sigaction(signum, &action, nullptr);
110-
}
111-
112-
/* Note that this signal handler is not async signal safe !
113-
* Ideally a signal handler should only flip a bit and defer
114-
* heavy work to some kind of bottom-half processing. */
115-
void signal_handler(int signum, siginfo_t * /*info*/, void * /*context*/)
116-
{
117-
const char * name = "UNKNOWN SIGNAL";
118-
119-
for (const auto& s : ALL_SIGNALS) {
120-
if (s.number == signum) {
121-
name = s.name;
122-
break;
95+
char** symbols = backtrace_symbols(addresses, size);
96+
for (int i = 2; i < size; ++i) {
97+
const std::string line = "\t" + decipher_trace(symbols[i]);
98+
Error(log, "%s", line.c_str());
12399
}
100+
101+
free(symbols);
102+
#endif
124103
}
125104

126-
write_to_stderr("\n");
127-
write_to_stderr(QCoreApplication::applicationName().toLocal8Bit());
128-
write_to_stderr(" caught signal :");
129-
write_to_stderr(name);
130-
write_to_stderr("\n");
105+
#ifdef _WIN32
106+
void signal_handler(int signum) {
107+
const char* name = "UNKNOWN SIGNAL";
108+
109+
switch (signum) {
110+
case SIGABRT: name = "SIGABRT"; break;
111+
case SIGFPE: name = "SIGFPE"; break;
112+
case SIGSEGV: name = "SIGSEGV"; break;
113+
case SIGINT: name = "SIGINT"; break;
114+
case SIGTERM: name = "SIGTERM"; break;
115+
}
131116

132-
/* Anything below here is unsafe ! */
117+
write_to_stderr("\n");
118+
write_to_stderr(QCoreApplication::applicationName().toLocal8Bit());
119+
write_to_stderr(" caught signal: ");
120+
write_to_stderr(name);
121+
write_to_stderr("\n");
133122

134-
switch(signum)
135-
{
136-
case SIGBUS:
137-
case SIGSEGV:
138-
case SIGABRT:
139-
case SIGFPE :
140123
print_trace();
141124

142-
/* Don't catch our own signal */
143-
install_default_handler(signum);
144-
145-
kill(getpid(), signum);
146-
return;
147-
case SIGINT :
148-
case SIGTERM:
149-
case SIGPIPE:
150-
default:
151-
/* If the signal_handler is hit before the event loop is started,
152-
* following call will do nothing. So we queue the call. */
153125
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
154-
155-
// Reset signal handler to default (in case this handler is not capable of stopping)
156-
install_default_handler(signum);
157126
}
158-
}
127+
#else
128+
struct Signal {
129+
int number;
130+
const char* name;
131+
};
132+
133+
const Signal ALL_SIGNALS[] = {
134+
{ SIGABRT, "SIGABRT" },
135+
{ SIGBUS, "SIGBUS" },
136+
{ SIGFPE, "SIGFPE" },
137+
{ SIGSEGV, "SIGSEGV" },
138+
{ SIGTERM, "SIGTERM" },
139+
{ SIGHUP, "SIGHUP" },
140+
{ SIGINT, "SIGINT" },
141+
{ SIGPIPE, "SIGPIPE" },
142+
};
143+
144+
void install_default_handler(int signum) {
145+
struct sigaction action {};
146+
sigemptyset(&action.sa_mask);
147+
action.sa_handler = SIG_DFL;
148+
(void)sigaction(signum, &action, nullptr);
149+
}
159150

160-
} // namespace DefaultSignalHandler
161-
#endif // _WIN32
151+
void signal_handler(int signum, siginfo_t*, void*) {
152+
const char* name = "UNKNOWN SIGNAL";
162153

163-
namespace DefaultSignalHandler
164-
{
165-
void install()
166-
{
167-
#ifndef _WIN32
168-
Logger* log = Logger::getInstance("CORE");
169-
170-
struct sigaction action{};
171-
sigemptyset(&action.sa_mask);
172-
action.sa_sigaction = signal_handler;
173-
action.sa_flags |= SA_SIGINFO;
174-
175-
for (const auto& s : ALL_SIGNALS)
176-
{
177-
if (sigaction(s.number, &action, nullptr)!= 0)
178-
{
179-
Error(log, "Failed to install handler for %s]\n", s.name);
154+
for (const auto& s : ALL_SIGNALS) {
155+
if (s.number == signum) {
156+
name = s.name;
157+
break;
158+
}
180159
}
160+
161+
write_to_stderr("\n");
162+
write_to_stderr(QCoreApplication::applicationName().toLocal8Bit());
163+
write_to_stderr(" caught signal: ");
164+
write_to_stderr(name);
165+
write_to_stderr("\n");
166+
167+
switch (signum) {
168+
case SIGBUS:
169+
case SIGSEGV:
170+
case SIGABRT:
171+
case SIGFPE:
172+
print_trace();
173+
install_default_handler(signum);
174+
kill(getpid(), signum);
175+
return;
176+
default:
177+
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
178+
install_default_handler(signum);
179+
}
180+
}
181+
#endif
182+
183+
void install() {
184+
Logger* log = Logger::getInstance("CORE");
185+
186+
#ifdef _WIN32
187+
signal(SIGABRT, signal_handler);
188+
signal(SIGFPE, signal_handler);
189+
signal(SIGSEGV, signal_handler);
190+
signal(SIGINT, signal_handler);
191+
signal(SIGTERM, signal_handler);
192+
#else
193+
struct sigaction action {};
194+
sigemptyset(&action.sa_mask);
195+
action.sa_sigaction = signal_handler;
196+
action.sa_flags |= SA_SIGINFO;
197+
198+
for (const auto& s : ALL_SIGNALS) {
199+
if (sigaction(s.number, &action, nullptr) != 0) {
200+
Error(log, "Failed to install handler for %s\n", s.name);
201+
}
202+
}
203+
#endif
181204
}
182-
#endif // _WIN32
183-
}
205+
184206
} // namespace DefaultSignalHandler

0 commit comments

Comments
 (0)