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 
33 using namespace std;
34 using namespace openshot;
35 
36 
37 // Global reference to logger
38 CrashHandler *CrashHandler::m_pInstance = NULL;
39 
40 // Create or Get an instance of the logger singleton
41 CrashHandler *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
72 void 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
98 void 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 
126 void 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
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:47