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