OpenShot Library | libopenshot 1.1.1
Loading...
Searching...
No Matches
CrashHandler.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "CrashHandler.h"
14
15#include <iostream>
16#include <iomanip>
17#include <sstream>
18
19using namespace std;
20using namespace openshot;
21
22
23// Global reference to logger
24CrashHandler *CrashHandler::m_pInstance = NULL;
25
26// Create or Get an instance of the logger singleton
28{
29 if (!m_pInstance) {
30 // Create the actual instance of crash handler only once
31 m_pInstance = new CrashHandler;
32
33#ifdef __MINGW32__
34 // TODO: Windows exception handling methods
35 signal(SIGSEGV, CrashHandler::abortHandler);
36
37#else
38 struct sigaction sa;
39 sa.sa_flags = SA_SIGINFO;
40 sa.sa_sigaction = CrashHandler::abortHandler;
41 sigemptyset( &sa.sa_mask );
42
43 // Register abortHandler function callback
44 sigaction( SIGABRT, &sa, NULL );
45 sigaction( SIGSEGV, &sa, NULL );
46 sigaction( SIGBUS, &sa, NULL );
47 sigaction( SIGILL, &sa, NULL );
48 sigaction( SIGFPE, &sa, NULL );
49 // Preserve the host's SIGPIPE policy. Python ignores it so a closed
50 // output pipe reports EPIPE instead of terminating the entire editor.
51#endif
52 }
53
54 return m_pInstance;
55}
56
57#ifdef __MINGW32__
58// Windows exception handler
59void CrashHandler::abortHandler(int signum)
60{
61 // Associate each signal with a signal name string.
62 const char* name = NULL;
63 switch( signum )
64 {
65 case SIGABRT: name = "SIGABRT"; break;
66 case SIGSEGV: name = "SIGSEGV"; break;
67 case SIGILL: name = "SIGILL"; break;
68 case SIGFPE: name = "SIGFPE"; break;
69 }
70
71 // Notify the user which signal was caught
72 if ( name )
73 fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
74 else
75 fprintf( stderr, "Caught signal %d\n", signum );
76
77 // Dump a stack trace.
78 printStackTrace(stderr, 63);
79
80 // Quit
81 exit( signum );
82}
83#else
84// Linux and Mac Exception Handler
85void CrashHandler::abortHandler( int signum, siginfo_t* si, void* unused )
86{
87 // Associate each signal with a signal name string.
88 const char* name = NULL;
89 switch( signum )
90 {
91 case SIGABRT: name = "SIGABRT"; break;
92 case SIGSEGV: name = "SIGSEGV"; break;
93 case SIGBUS: name = "SIGBUS"; break;
94 case SIGILL: name = "SIGILL"; break;
95 case SIGFPE: name = "SIGFPE"; break;
96 case SIGPIPE: name = "SIGPIPE"; break;
97 }
98
99 // Notify the user which signal was caught
100 if ( name )
101 fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
102 else
103 fprintf( stderr, "Caught signal %d\n", signum );
104
105 // Dump a stack trace.
106 printStackTrace(stderr, 63);
107
108 // Quit
109 exit( signum );
110}
111#endif
112
113void CrashHandler::printStackTrace(FILE *out, unsigned int max_frames)
114{
115 fprintf(out, "---- Unhandled Exception: Stack Trace ----\n");
116 Logger::Instance()->LogToFile("---- Unhandled Exception: Stack Trace ----\n");
117 stringstream stack_output;
118
119#ifdef __MINGW32__
120 // Windows stack unwinding
121 HANDLE process = GetCurrentProcess();
122 HANDLE thread = GetCurrentThread();
123
124 CONTEXT context;
125 memset(&context, 0, sizeof(CONTEXT));
126 context.ContextFlags = CONTEXT_FULL;
127 RtlCaptureContext(&context);
128
129 SymInitialize(process, NULL, TRUE);
130
131 DWORD image;
132 STACKFRAME64 stackframe;
133 ZeroMemory(&stackframe, sizeof(STACKFRAME64));
134
135#ifdef _M_IX86
136 image = IMAGE_FILE_MACHINE_I386;
137 stackframe.AddrPC.Offset = context.Eip;
138 stackframe.AddrPC.Mode = AddrModeFlat;
139 stackframe.AddrFrame.Offset = context.Ebp;
140 stackframe.AddrFrame.Mode = AddrModeFlat;
141 stackframe.AddrStack.Offset = context.Esp;
142 stackframe.AddrStack.Mode = AddrModeFlat;
143#elif _M_X64
144 image = IMAGE_FILE_MACHINE_AMD64;
145 stackframe.AddrPC.Offset = context.Rip;
146 stackframe.AddrPC.Mode = AddrModeFlat;
147 stackframe.AddrFrame.Offset = context.Rsp;
148 stackframe.AddrFrame.Mode = AddrModeFlat;
149 stackframe.AddrStack.Offset = context.Rsp;
150 stackframe.AddrStack.Mode = AddrModeFlat;
151#elif _M_IA64
152 image = IMAGE_FILE_MACHINE_IA64;
153 stackframe.AddrPC.Offset = context.StIIP;
154 stackframe.AddrPC.Mode = AddrModeFlat;
155 stackframe.AddrFrame.Offset = context.IntSp;
156 stackframe.AddrFrame.Mode = AddrModeFlat;
157 stackframe.AddrBStore.Offset = context.RsBSP;
158 stackframe.AddrBStore.Mode = AddrModeFlat;
159 stackframe.AddrStack.Offset = context.IntSp;
160 stackframe.AddrStack.Mode = AddrModeFlat;
161#endif
162
163 // Loop through the entire stack
164 for (size_t i = 0; i < max_frames; i++) {
165
166 BOOL result = StackWalk64(
167 image, process, thread,
168 &stackframe, &context, NULL,
169 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
170
171 if (i <= 2) { continue; } // Skip the first 3 elements (those relate to these functions)
172 if (!result) { break; }
173
174 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
175 PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
176 symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
177 symbol->MaxNameLen = MAX_SYM_NAME;
178 WINBOOL found_symbol = SymFromAddr(process, stackframe.AddrPC.Offset, NULL, symbol);
179
180 if (found_symbol) {
181 printf("[%i] %s, address 0x%0X\n", i, symbol->Name, symbol->Address);
182 stack_output << left << setw(30) << symbol->Name << " " << setw(40) << std::hex << symbol->Address << std::dec << endl;
183 } else {
184 printf("[%i] ???\n", i);
185 stack_output << left << setw(30) << "???" << endl;
186 }
187 }
188 SymCleanup(process);
189
190#else
191 // Linux and Mac stack unwinding
192 // Storage array for stack trace address data
193 void* addrlist[max_frames+1];
194
195 // Retrieve current stack addresses
196 unsigned int addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* ));
197
198 if ( addrlen == 0 )
199 {
200 fprintf(out, " No stack trace found (addrlen == 0)\n");
201 Logger::Instance()->LogToFile(" No stack trace found (addrlen == 0)\n");
202 return;
203 }
204
205 // Resolve addresses into strings containing "filename(function+address)",
206 // Actually it will be ## program address function + offset
207 // this array must be free()-ed
208 char** symbollist = backtrace_symbols( addrlist, addrlen );
209
210 size_t funcnamesize = 1024;
211 char funcname[1024];
212
213 // Iterate over the returned symbol lines. Skip the first 4, it is the
214 // address of this function.
215 for ( unsigned int i = 4; i < addrlen; i++ )
216 {
217 char* begin_name = NULL;
218 char* begin_offset = NULL;
219 char* end_offset = NULL;
220
221 // Find parentheses and +address offset surrounding the mangled name
222#ifdef DARWIN
223 // OSX style stack trace
224 for ( char *p = symbollist[i]; *p; ++p )
225 {
226 if (( *p == '_' ) && ( *(p-1) == ' ' ))
227 begin_name = p-1;
228 else if ( *p == '+' )
229 begin_offset = p-1;
230 }
231
232 if ( begin_name && begin_offset && ( begin_name < begin_offset ))
233 {
234 *begin_name++ = '\0';
235 *begin_offset++ = '\0';
236
237 // Mangled name is now in [begin_name, begin_offset) and caller
238 // offset in [begin_offset, end_offset). now apply
239 // __cxa_demangle():
240 int status;
241 char* ret = abi::__cxa_demangle( begin_name, &funcname[0], &funcnamesize, &status );
242 if ( status == 0 )
243 {
244 funcname = ret; // Use possibly realloc()-ed string
245 fprintf( out, " %-30s %-40s %s\n", symbollist[i], funcname, begin_offset );
246 stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << funcname << " " << begin_offset << endl;
247 } else {
248 // Demangling failed. Output function name as a C function with
249 // no arguments.
250 fprintf( out, " %-30s %-38s() %s\n", symbollist[i], begin_name, begin_offset );
251 stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(38) << begin_name << " " << begin_offset << endl;
252 }
253
254#else // !DARWIN - but is posix
255 // not OSX style
256 // ./module(function+0x15c) [0x8048a6d]
257 for ( char *p = symbollist[i]; *p; ++p )
258 {
259 if ( *p == '(' )
260 begin_name = p;
261 else if ( *p == '+' )
262 begin_offset = p;
263 else if ( *p == ')' && ( begin_offset || begin_name ))
264 end_offset = p;
265 }
266
267 if ( begin_name && end_offset && ( begin_name < end_offset ))
268 {
269 *begin_name++ = '\0';
270 *end_offset++ = '\0';
271 if ( begin_offset )
272 *begin_offset++ = '\0';
273
274 // Mangled name is now in [begin_name, begin_offset) and caller
275 // offset in [begin_offset, end_offset). now apply
276 // __cxa_demangle():
277 int status = 0;
278 char* ret = abi::__cxa_demangle( begin_name, funcname, &funcnamesize, &status );
279 char* fname = begin_name;
280 if ( status == 0 )
281 fname = ret;
282
283 if ( begin_offset )
284 {
285 fprintf( out, " %-30s ( %-40s + %-6s) %s\n", symbollist[i], fname, begin_offset, end_offset );
286 stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << begin_offset << " " << end_offset << endl;
287
288 } else {
289 fprintf( out, " %-30s ( %-40s %-6s) %s\n", symbollist[i], fname, "", end_offset );
290 stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << end_offset << endl;
291
292 }
293#endif // !DARWIN - but is posix
294 } else {
295 // Couldn't parse the line? print the whole line.
296 fprintf(out, " %-40s\n", symbollist[i]);
297 stack_output << left << " " << setw(40) << symbollist[i] << endl;
298 }
299 }
300
301 // Free array
302 free(symbollist);
303#endif
304
305 // Write stacktrace to file (if log path set)
306 Logger::Instance()->LogToFile(stack_output.str());
307
308 fprintf(out, "---- End of Stack Trace ----\n");
309 Logger::Instance()->LogToFile("---- End of Stack Trace ----\n");
310}
Header file for CrashHandler class.
This class is designed to catch exceptions thrown by libc (SIGABRT, SIGSEGV, SIGILL,...
static void abortHandler(int signum, siginfo_t *si, void *unused)
Method which handles crashes and logs error.
static void printStackTrace(FILE *out, unsigned int max_frames)
Method which prints a stacktrace.
static CrashHandler * Instance()
void LogToFile(std::string message)
Unfiltered raw output, retained for the existing crash handler.
Definition Logger.cpp:142
static Logger * Instance()
Definition Logger.cpp:55
This namespace is the default namespace for all code in the openshot library.