|
1 | | -#ifndef _WIN32 |
2 | | - |
3 | 1 | #include <utils/DefaultSignalHandler.h> |
4 | 2 | #include <utils/Logger.h> |
5 | 3 |
|
| 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 |
6 | 16 | #include <ctype.h> |
7 | 17 | #include <cxxabi.h> |
8 | 18 | #include <execinfo.h> |
9 | 19 | #include <unistd.h> |
10 | 20 | #include <signal.h> |
11 | | -#include <string.h> |
12 | | -#include <stdio.h> |
13 | | -#include <stdlib.h> |
| 21 | +#endif |
14 | 22 |
|
15 | | -#include <QCoreApplication> |
| 23 | +namespace DefaultSignalHandler { |
16 | 24 |
|
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); |
56 | 29 | } |
| 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; |
57 | 44 |
|
58 | | - auto* begin = strchr(trace.c_str(), '(') + 1; |
59 | | - auto* end = strchr(begin, '+'); |
| 45 | + if (trace.empty()) { |
| 46 | + result += "??\n"; |
| 47 | + return result; |
| 48 | + } |
60 | 49 |
|
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, ')'); |
63 | 53 |
|
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); |
65 | 57 |
|
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); |
69 | 63 |
|
70 | | - if(realname) |
71 | | - result += realname; |
72 | | - else |
73 | | - result.insert(result.end(), begin, end); |
| 64 | + return result; |
| 65 | + } |
| 66 | +#endif |
74 | 67 |
|
75 | | - free(realname); |
76 | | - result.insert(result.size(), end); |
| 68 | + void print_trace() { |
| 69 | + Logger* log = Logger::getInstance("CORE"); |
77 | 70 |
|
78 | | - return result; |
79 | | -} |
| 71 | +#ifdef _WIN32 |
| 72 | + void* stack[50]; |
| 73 | + HANDLE process = GetCurrentProcess(); |
| 74 | + SymInitialize(process, NULL, TRUE); |
80 | 75 |
|
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); |
86 | 80 |
|
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 | + } |
89 | 86 |
|
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); |
92 | 92 |
|
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; |
100 | 94 |
|
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()); |
123 | 99 | } |
| 100 | + |
| 101 | + free(symbols); |
| 102 | +#endif |
124 | 103 | } |
125 | 104 |
|
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 | + } |
131 | 116 |
|
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"); |
133 | 122 |
|
134 | | - switch(signum) |
135 | | - { |
136 | | - case SIGBUS: |
137 | | - case SIGSEGV: |
138 | | - case SIGABRT: |
139 | | - case SIGFPE : |
140 | 123 | print_trace(); |
141 | 124 |
|
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. */ |
153 | 125 | 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); |
157 | 126 | } |
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 | + } |
159 | 150 |
|
160 | | -} // namespace DefaultSignalHandler |
161 | | -#endif // _WIN32 |
| 151 | + void signal_handler(int signum, siginfo_t*, void*) { |
| 152 | + const char* name = "UNKNOWN SIGNAL"; |
162 | 153 |
|
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 | + } |
180 | 159 | } |
| 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 |
181 | 204 | } |
182 | | -#endif // _WIN32 |
183 | | -} |
| 205 | + |
184 | 206 | } // namespace DefaultSignalHandler |
0 commit comments