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