You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
New cross platform exception handler for libopenshot! Logs basic stacktrace on segmentation fault. This will be a huge help in finding bugs and crashes.
This commit is contained in:
86
include/CrashHandler.h
Normal file
86
include/CrashHandler.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Header file for CrashHandler class
|
||||
* @author Jonathan Thomas <jonathan@openshot.org>
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (c) 2008-2014 OpenShot Studios, LLC
|
||||
* <http://www.openshotstudios.com/>. This file is part of
|
||||
* OpenShot Library (libopenshot), an open-source project dedicated to
|
||||
* delivering high quality video editing and animation solutions to the
|
||||
* world. For more information visit <http://www.openshot.org/>.
|
||||
*
|
||||
* OpenShot Library (libopenshot) is free software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* OpenShot Library (libopenshot) is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPENSHOT_CRASH_HANDLER_H
|
||||
#define OPENSHOT_CRASH_HANDLER_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#ifdef __MINGW32__
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <cxxabi.h>
|
||||
#include "ZmqLogger.h"
|
||||
|
||||
namespace openshot {
|
||||
|
||||
/**
|
||||
* @brief This class is designed to catch exceptions thrown by libc (SIGABRT, SIGSEGV, SIGILL, SIGFPE)
|
||||
*
|
||||
* This class is a singleton which only needs to be instantiated 1 time, and it will register as a signal
|
||||
* handler with libc, and log errors using the ZmqLogger class.
|
||||
*/
|
||||
class CrashHandler {
|
||||
private:
|
||||
/// Default constructor
|
||||
CrashHandler(){}; // Don't allow user to create an instance of this singleton
|
||||
|
||||
/// Default copy method
|
||||
CrashHandler(CrashHandler const&){}; // Don't allow the user to copy this instance
|
||||
|
||||
/// Default assignment operator
|
||||
CrashHandler & operator=(CrashHandler const&){}; // Don't allow the user to assign this instance
|
||||
|
||||
/// Private variable to keep track of singleton instance
|
||||
static CrashHandler *m_pInstance;
|
||||
|
||||
public:
|
||||
/// Create or get an instance of this crash handler singleton (invoke the class with this method). This also
|
||||
/// registers the instance as a signal handler for libc
|
||||
static CrashHandler *Instance();
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// TODO: Windows exception handling methods
|
||||
static void abortHandler(int signum);
|
||||
#else
|
||||
/// Method which handles crashes and logs error
|
||||
static void abortHandler(int signum, siginfo_t* si, void* unused);
|
||||
#endif
|
||||
|
||||
/// Method which prints a stacktrace
|
||||
static void printStackTrace(FILE *out, unsigned int max_frames);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "CacheMemory.h"
|
||||
#include "Color.h"
|
||||
#include "Clip.h"
|
||||
#include "CrashHandler.h"
|
||||
#include "Point.h"
|
||||
#include "EffectBase.h"
|
||||
#include "Effects.h"
|
||||
|
||||
@@ -107,6 +107,9 @@ namespace openshot {
|
||||
|
||||
/// Log message to all subscribers of this logger (if any)
|
||||
void Log(string message);
|
||||
|
||||
/// Log message to a file (if path set)
|
||||
void LogToFile(string message);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -196,6 +196,7 @@ SET ( OPENSHOT_SOURCE_FILES
|
||||
Clip.cpp
|
||||
ClipBase.cpp
|
||||
Coordinate.cpp
|
||||
CrashHandler.cpp
|
||||
DummyReader.cpp
|
||||
ReaderBase.cpp
|
||||
RendererBase.cpp
|
||||
@@ -289,6 +290,11 @@ SET ( REQUIRED_LIBRARIES
|
||||
SET ( REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} ${BLACKMAGIC_LIBRARY_DIR} )
|
||||
ENDIF (BLACKMAGIC_FOUND)
|
||||
|
||||
IF (WIN32)
|
||||
# Required for exception handling on Windows
|
||||
SET ( REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} "imagehlp" )
|
||||
ENDIF(WIN32)
|
||||
|
||||
# Link all referenced libraries
|
||||
target_link_libraries(openshot ${REQUIRED_LIBRARIES})
|
||||
|
||||
|
||||
319
src/CrashHandler.cpp
Normal file
319
src/CrashHandler.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Source file for CrashHandler class
|
||||
* @author Jonathan Thomas <jonathan@openshot.org>
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (c) 2008-2014 OpenShot Studios, LLC
|
||||
* <http://www.openshotstudios.com/>. This file is part of
|
||||
* OpenShot Library (libopenshot), an open-source project dedicated to
|
||||
* delivering high quality video editing and animation solutions to the
|
||||
* world. For more information visit <http://www.openshot.org/>.
|
||||
*
|
||||
* OpenShot Library (libopenshot) is free software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* OpenShot Library (libopenshot) is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../include/CrashHandler.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace openshot;
|
||||
|
||||
|
||||
// Global reference to logger
|
||||
CrashHandler *CrashHandler::m_pInstance = NULL;
|
||||
|
||||
// Create or Get an instance of the logger singleton
|
||||
CrashHandler *CrashHandler::Instance()
|
||||
{
|
||||
if (!m_pInstance) {
|
||||
// Create the actual instance of crash handler only once
|
||||
m_pInstance = new CrashHandler;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// TODO: Windows exception handling methods
|
||||
signal(SIGSEGV, CrashHandler::abortHandler);
|
||||
|
||||
#else
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = CrashHandler::abortHandler;
|
||||
sigemptyset( &sa.sa_mask );
|
||||
|
||||
// Register abortHandler function callback
|
||||
sigaction( SIGABRT, &sa, NULL );
|
||||
sigaction( SIGSEGV, &sa, NULL );
|
||||
sigaction( SIGBUS, &sa, NULL );
|
||||
sigaction( SIGILL, &sa, NULL );
|
||||
sigaction( SIGFPE, &sa, NULL );
|
||||
sigaction( SIGPIPE, &sa, NULL );
|
||||
#endif
|
||||
}
|
||||
|
||||
return m_pInstance;
|
||||
}
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// Windows exception handler
|
||||
void CrashHandler::abortHandler(int signum)
|
||||
{
|
||||
// Associate each signal with a signal name string.
|
||||
const char* name = NULL;
|
||||
switch( signum )
|
||||
{
|
||||
case SIGABRT: name = "SIGABRT"; break;
|
||||
case SIGSEGV: name = "SIGSEGV"; break;
|
||||
case SIGILL: name = "SIGILL"; break;
|
||||
case SIGFPE: name = "SIGFPE"; break;
|
||||
}
|
||||
|
||||
// Notify the user which signal was caught
|
||||
if ( name )
|
||||
fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
|
||||
else
|
||||
fprintf( stderr, "Caught signal %d\n", signum );
|
||||
|
||||
// Dump a stack trace.
|
||||
printStackTrace(stderr, 63);
|
||||
|
||||
// Quit
|
||||
exit( signum );
|
||||
}
|
||||
#else
|
||||
// Linux and Mac Exception Handler
|
||||
void CrashHandler::abortHandler( int signum, siginfo_t* si, void* unused )
|
||||
{
|
||||
// Associate each signal with a signal name string.
|
||||
const char* name = NULL;
|
||||
switch( signum )
|
||||
{
|
||||
case SIGABRT: name = "SIGABRT"; break;
|
||||
case SIGSEGV: name = "SIGSEGV"; break;
|
||||
case SIGBUS: name = "SIGBUS"; break;
|
||||
case SIGILL: name = "SIGILL"; break;
|
||||
case SIGFPE: name = "SIGFPE"; break;
|
||||
case SIGPIPE: name = "SIGPIPE"; break;
|
||||
}
|
||||
|
||||
// Notify the user which signal was caught
|
||||
if ( name )
|
||||
fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
|
||||
else
|
||||
fprintf( stderr, "Caught signal %d\n", signum );
|
||||
|
||||
// Dump a stack trace.
|
||||
printStackTrace(stderr, 63);
|
||||
|
||||
// Quit
|
||||
exit( signum );
|
||||
}
|
||||
#endif
|
||||
|
||||
void CrashHandler::printStackTrace(FILE *out, unsigned int max_frames)
|
||||
{
|
||||
fprintf(out, "---- Unhandled Exception: Stack Trace ----\n");
|
||||
ZmqLogger::Instance()->LogToFile("---- Unhandled Exception: Stack Trace ----\n");
|
||||
stringstream stack_output;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// Windows stack unwinding
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
|
||||
CONTEXT context;
|
||||
memset(&context, 0, sizeof(CONTEXT));
|
||||
context.ContextFlags = CONTEXT_FULL;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
|
||||
DWORD image;
|
||||
STACKFRAME64 stackframe;
|
||||
ZeroMemory(&stackframe, sizeof(STACKFRAME64));
|
||||
|
||||
#ifdef _M_IX86
|
||||
image = IMAGE_FILE_MACHINE_I386;
|
||||
stackframe.AddrPC.Offset = context.Eip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Ebp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Esp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
image = IMAGE_FILE_MACHINE_AMD64;
|
||||
stackframe.AddrPC.Offset = context.Rip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Rsp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Rsp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
image = IMAGE_FILE_MACHINE_IA64;
|
||||
stackframe.AddrPC.Offset = context.StIIP;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.IntSp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrBStore.Offset = context.RsBSP;
|
||||
stackframe.AddrBStore.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.IntSp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#endif
|
||||
|
||||
// Loop through the entire stack
|
||||
for (size_t i = 0; i < max_frames; i++) {
|
||||
|
||||
BOOL result = StackWalk64(
|
||||
image, process, thread,
|
||||
&stackframe, &context, NULL,
|
||||
SymFunctionTableAccess64, SymGetModuleBase64, NULL);
|
||||
|
||||
if (i <= 2) { continue; } // Skip the first 3 elements (those relate to these functions)
|
||||
if (!result) { break; }
|
||||
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
WINBOOL found_symbol = SymFromAddr(process, stackframe.AddrPC.Offset, NULL, symbol);
|
||||
|
||||
if (found_symbol) {
|
||||
printf("[%i] %s, address 0x%0X\n", i, symbol->Name, symbol->Address);
|
||||
stack_output << left << setw(30) << symbol->Name << " " << setw(40) << std::hex << symbol->Address << std::dec << endl;
|
||||
} else {
|
||||
printf("[%i] ???\n", i);
|
||||
stack_output << left << setw(30) << "???" << endl;
|
||||
}
|
||||
}
|
||||
SymCleanup(process);
|
||||
|
||||
#else
|
||||
// Storage array for stack trace address data
|
||||
void* addrlist[max_frames+1];
|
||||
|
||||
// Retrieve current stack addresses
|
||||
unsigned int addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* ));
|
||||
|
||||
if ( addrlen == 0 )
|
||||
{
|
||||
fprintf(out, " No stack trace found (addrlen == 0)\n");
|
||||
ZmqLogger::Instance()->LogToFile(" No stack trace found (addrlen == 0)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve addresses into strings containing "filename(function+address)",
|
||||
// Actually it will be ## program address function + offset
|
||||
// this array must be free()-ed
|
||||
char** symbollist = backtrace_symbols( addrlist, addrlen );
|
||||
|
||||
size_t funcnamesize = 1024;
|
||||
char funcname[1024];
|
||||
|
||||
// Iterate over the returned symbol lines. Skip the first 4, it is the
|
||||
// address of this function.
|
||||
for ( unsigned int i = 4; i < addrlen; i++ )
|
||||
{
|
||||
char* begin_name = NULL;
|
||||
char* begin_offset = NULL;
|
||||
char* end_offset = NULL;
|
||||
|
||||
// Find parentheses and +address offset surrounding the mangled name
|
||||
#ifdef DARWIN
|
||||
// OSX style stack trace
|
||||
for ( char *p = symbollist[i]; *p; ++p )
|
||||
{
|
||||
if (( *p == '_' ) && ( *(p-1) == ' ' ))
|
||||
begin_name = p-1;
|
||||
else if ( *p == '+' )
|
||||
begin_offset = p-1;
|
||||
}
|
||||
|
||||
if ( begin_name && begin_offset && ( begin_name < begin_offset ))
|
||||
{
|
||||
*begin_name++ = '\0';
|
||||
*begin_offset++ = '\0';
|
||||
|
||||
// Mangled name is now in [begin_name, begin_offset) and caller
|
||||
// offset in [begin_offset, end_offset). now apply
|
||||
// __cxa_demangle():
|
||||
int status;
|
||||
char* ret = abi::__cxa_demangle( begin_name, &funcname[0], &funcnamesize, &status );
|
||||
if ( status == 0 )
|
||||
{
|
||||
funcname = ret; // Use possibly realloc()-ed string
|
||||
fprintf( out, " %-30s %-40s %s\n", symbollist[i], funcname, begin_offset );
|
||||
stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << funcname << " " << begin_offset << endl;
|
||||
} else {
|
||||
// Demangling failed. Output function name as a C function with
|
||||
// no arguments.
|
||||
fprintf( out, " %-30s %-38s() %s\n", symbollist[i], begin_name, begin_offset );
|
||||
stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(38) << begin_name << " " << begin_offset << endl;
|
||||
}
|
||||
|
||||
#else // !DARWIN - but is posix
|
||||
// not OSX style
|
||||
// ./module(function+0x15c) [0x8048a6d]
|
||||
for ( char *p = symbollist[i]; *p; ++p )
|
||||
{
|
||||
if ( *p == '(' )
|
||||
begin_name = p;
|
||||
else if ( *p == '+' )
|
||||
begin_offset = p;
|
||||
else if ( *p == ')' && ( begin_offset || begin_name ))
|
||||
end_offset = p;
|
||||
}
|
||||
|
||||
if ( begin_name && end_offset && ( begin_name < end_offset ))
|
||||
{
|
||||
*begin_name++ = '\0';
|
||||
*end_offset++ = '\0';
|
||||
if ( begin_offset )
|
||||
*begin_offset++ = '\0';
|
||||
|
||||
// Mangled name is now in [begin_name, begin_offset) and caller
|
||||
// offset in [begin_offset, end_offset). now apply
|
||||
// __cxa_demangle():
|
||||
int status = 0;
|
||||
char* ret = abi::__cxa_demangle( begin_name, funcname, &funcnamesize, &status );
|
||||
char* fname = begin_name;
|
||||
if ( status == 0 )
|
||||
fname = ret;
|
||||
|
||||
if ( begin_offset )
|
||||
{
|
||||
fprintf( out, " %-30s ( %-40s + %-6s) %s\n", symbollist[i], fname, begin_offset, end_offset );
|
||||
stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << begin_offset << " " << end_offset << endl;
|
||||
|
||||
} else {
|
||||
fprintf( out, " %-30s ( %-40s %-6s) %s\n", symbollist[i], fname, "", end_offset );
|
||||
stack_output << left << " " << setw(30) << symbollist[i] << " " << setw(40) << fname << " " << end_offset << endl;
|
||||
|
||||
}
|
||||
#endif // !DARWIN - but is posix
|
||||
} else {
|
||||
// Couldn't parse the line? print the whole line.
|
||||
fprintf(out, " %-40s\n", symbollist[i]);
|
||||
stack_output << left << " " << setw(40) << symbollist[i] << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Free array
|
||||
free(symbollist);
|
||||
#endif
|
||||
|
||||
// Write stacktrace to file (if log path set)
|
||||
ZmqLogger::Instance()->LogToFile(stack_output.str());
|
||||
|
||||
fprintf(out, "---- End of Stack Trace ----\n");
|
||||
ZmqLogger::Instance()->LogToFile("---- End of Stack Trace ----\n");
|
||||
}
|
||||
@@ -277,7 +277,7 @@ void FFmpegReader::UpdateAudioInfo()
|
||||
info.acodec = aCodecCtx->codec->name;
|
||||
info.channels = aCodecCtx->channels;
|
||||
if (aCodecCtx->channel_layout == 0)
|
||||
aCodecCtx->channel_layout = av_get_default_channel_layout( aCodecCtx->channels );;
|
||||
aCodecCtx->channel_layout = av_get_default_channel_layout( aCodecCtx->channels );
|
||||
info.channel_layout = (ChannelLayout) aCodecCtx->channel_layout;
|
||||
info.sample_rate = aCodecCtx->sample_rate;
|
||||
info.audio_bit_rate = aCodecCtx->bit_rate;
|
||||
|
||||
@@ -33,6 +33,9 @@ using namespace openshot;
|
||||
Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int channels, ChannelLayout channel_layout) :
|
||||
is_open(false), auto_map_clips(true)
|
||||
{
|
||||
// Create CrashHandler and Attach (incase of errors)
|
||||
CrashHandler::Instance();
|
||||
|
||||
// Init viewport size (curve based, because it can be animated)
|
||||
viewport_scale = Keyframe(100.0);
|
||||
viewport_x = Keyframe(0.0);
|
||||
|
||||
@@ -113,7 +113,15 @@ void ZmqLogger::Log(string message)
|
||||
|
||||
// Write to log file (if opened, and force it to write to disk in case of a crash)
|
||||
if (log_file.is_open())
|
||||
log_file << message << std::flush;;
|
||||
log_file << message << std::flush;
|
||||
}
|
||||
|
||||
// Log message to a file (if path set)
|
||||
void ZmqLogger::LogToFile(string message)
|
||||
{
|
||||
// Write to log file (if opened, and force it to write to disk in case of a crash)
|
||||
if (log_file.is_open())
|
||||
log_file << message << std::flush;
|
||||
}
|
||||
|
||||
void ZmqLogger::Path(string new_path)
|
||||
|
||||
Reference in New Issue
Block a user