Skip to content

Commit

Permalink
Add QgsStackTrace class
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanW2 committed Apr 25, 2017
1 parent c183285 commit 4b67b27
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 135 deletions.
94 changes: 4 additions & 90 deletions src/app/qgscrashhandler.cpp
Expand Up @@ -21,104 +21,19 @@
#include "qgsproject.h"
#include "qgscrashdialog.h"
#include "qgscrashreport.h"
#include "qgsstacktrace.h"

#ifdef Q_OS_WIN
LONG WINAPI QgsCrashHandler::handle( struct _EXCEPTION_POINTERS *ExceptionInfo )
{
HANDLE process = GetCurrentProcess();
// TOOD Pull symbols from symbol server.
// TOOD Move this logic to generic stack trace class to handle each
// platform.
SymInitialize( process, NULL, TRUE );

// StackWalk64() may modify context record passed to it, so we will
// use a copy.
CONTEXT context_record = *ExceptionInfo->ContextRecord;
// Initialize stack walking.
STACKFRAME64 stack_frame;
memset( &stack_frame, 0, sizeof( stack_frame ) );
#if defined(_WIN64)
int machine_type = IMAGE_FILE_MACHINE_AMD64;
stack_frame.AddrPC.Offset = context_record.Rip;
stack_frame.AddrFrame.Offset = context_record.Rbp;
stack_frame.AddrStack.Offset = context_record.Rsp;
#else
int machine_type = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = context_record.Eip;
stack_frame.AddrFrame.Offset = context_record.Ebp;
stack_frame.AddrStack.Offset = context_record.Esp;
#endif
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Mode = AddrModeFlat;

SYMBOL_INFO *symbol = ( SYMBOL_INFO * ) qgsMalloc( sizeof( SYMBOL_INFO ) + MAX_SYM_NAME );
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
symbol->MaxNameLen = MAX_SYM_NAME;

IMAGEHLP_LINE *line = ( IMAGEHLP_LINE * ) qgsMalloc( sizeof( IMAGEHLP_LINE ) );
line->SizeOfStruct = sizeof( IMAGEHLP_LINE );

IMAGEHLP_MODULE *module = ( IMAGEHLP_MODULE * ) qgsMalloc( sizeof( IMAGEHLP_MODULE ) );
module->SizeOfStruct = sizeof( IMAGEHLP_MODULE );

QList<QgsCrashReport::StackLine> stack;
while ( StackWalk64( machine_type,
GetCurrentProcess(),
GetCurrentThread(),
&stack_frame,
&context_record,
NULL,
&SymFunctionTableAccess64,
&SymGetModuleBase64,
NULL ) )
{

DWORD64 displacement = 0;

if ( SymFromAddr( process, ( DWORD64 )stack_frame.AddrPC.Offset, &displacement, symbol ) )
{
DWORD dwDisplacement;
QString fileName;
QString lineNumber;
QString moduleName;
if ( SymGetLineFromAddr( process, ( DWORD )( stack_frame.AddrPC.Offset ), &dwDisplacement, line ) )
{
fileName = QString( line->FileName );
lineNumber = QString::number( line->LineNumber );
}
else
{
fileName = "(unknown file)";
lineNumber = "(unknown line)";
}
if ( SymGetModuleInfo( process, ( DWORD )( stack_frame.AddrPC.Offset ), module ) )
{
moduleName = QString( module->ModuleName );
}
else
{
moduleName = "(unknown module)";
}
QgsCrashReport::StackLine stackline;
stackline.moduleName = moduleName;
stackline.fileName = fileName;
stackline.lineNumber = lineNumber;
stackline.symbolName = QString( symbol->Name );
stack.append( stackline );
}
}

qgsFree( symbol );
qgsFree( line );
qgsFree( module );

QgsStackLines stack = QgsStackTrace::trace( ExceptionInfo );
showCrashDialog( stack );

return EXCEPTION_EXECUTE_HANDLER;
}
#endif

void QgsCrashHandler::showCrashDialog( const QList<QgsCrashReport::StackLine> &stack )
void QgsCrashHandler::showCrashDialog( const QgsStackLines &stack )
{

QgsCrashDialog dlg( QApplication::activeWindow() );
Expand All @@ -141,4 +56,3 @@ void QgsCrashHandler::restartApplication()
QProcess::startDetached( path, arguments, QDir::toNativeSeparators( QCoreApplication::applicationDirPath() ) );

}
#endif
2 changes: 1 addition & 1 deletion src/app/qgscrashhandler.h
Expand Up @@ -40,7 +40,7 @@ class APP_EXPORT QgsCrashHandler
* Show the crash dialog.
* @param stack The current stack of the crash point.
*/
static void showCrashDialog( const QList<QgsCrashReport::StackLine> &stack );
static void showCrashDialog( const QgsStackLines &stack );

/**
* Restart the application.
Expand Down
19 changes: 3 additions & 16 deletions src/app/qgscrashreport.cpp
Expand Up @@ -49,7 +49,7 @@ const QString QgsCrashReport::toString() const
}
else
{
Q_FOREACH ( const QgsCrashReport::StackLine &line, mStackTrace )
Q_FOREACH ( const QgsStackTrace::StackLine &line, mStackTrace )
{
QFileInfo fileInfo( line.fileName );
QString filename( fileInfo.fileName() );
Expand Down Expand Up @@ -119,12 +119,12 @@ const QString QgsCrashReport::toString() const
const QString QgsCrashReport::crashID() const
{
if ( mStackTrace.isEmpty() )
return "ID not generated due to missing information";
return "ID not generated due to missing information\n\n Your version of QGIS install might not have debug information included.";

QString data = QString::null;

// Hashes the full stack.
Q_FOREACH ( QgsCrashReport::StackLine line, mStackTrace )
Q_FOREACH ( const QgsStackTrace::StackLine &line, mStackTrace )
{
QFileInfo fileInfo( line.fileName );
QString filename( fileInfo.fileName() );
Expand All @@ -137,16 +137,3 @@ const QString QgsCrashReport::crashID() const
QString hash = QString( QCryptographicHash::hash( data.toAscii(), QCryptographicHash::Sha1 ).toHex() );
return hash;
}

bool QgsCrashReport::StackLine::isQgisModule() const
{
return moduleName.toLower().contains( "qgis" );
}

bool QgsCrashReport::StackLine::isValid() const
{
return !( fileName.toLower().contains( "exe_common" ) ||
fileName.toLower().contains( "unknown" ) ||
lineNumber.toLower().contains( "unknown" ) );

}
32 changes: 5 additions & 27 deletions src/app/qgscrashreport.h
Expand Up @@ -17,8 +17,10 @@
#define QGSCRASHREPORT_H

#include "qgis_app.h"
#include "qgsstacktrace.h"

#include <QObject>
#include <QVector>


/**
Expand All @@ -28,30 +30,6 @@ class APP_EXPORT QgsCrashReport
{
public:

/**
* Represents a line from a stack trace.
*/
struct StackLine
{
QString moduleName;
QString symbolName;
QString fileName;
QString lineNumber;

/**
* Check if this stack line is part of QGIS.
* \return True if part of QGIS.
*/
bool isQgisModule() const;

/**
* Check if this stack line is valid. Considered valid when the filename and line
* number are known.
* \return True of the line is valid.
*/
bool isValid() const;
};

/**
* Include information to generate user friendly crash report for QGIS.
*/
Expand All @@ -73,13 +51,13 @@ class APP_EXPORT QgsCrashReport
* Sets the stack trace for the crash report.
* \param value A string list for each line in the stack trace.
*/
void setStackTrace( const QList<QgsCrashReport::StackLine> &value ) { mStackTrace = value; }
void setStackTrace( const QgsStackLines &value ) { mStackTrace = value; }

/**
* Returns the stack trace for this report.
* \return A string list for each line in the stack trace.
*/
QList<QgsCrashReport::StackLine> StackTrace() const { return mStackTrace; }
QgsStackLines StackTrace() const { return mStackTrace; }

/**
* Set the flags to mark which features are included in this crash report.
Expand Down Expand Up @@ -107,7 +85,7 @@ class APP_EXPORT QgsCrashReport

private:
Flags mFlags;
QList<QgsCrashReport::StackLine> mStackTrace;
QgsStackLines mStackTrace;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsCrashReport::Flags )
Expand Down
4 changes: 3 additions & 1 deletion src/core/CMakeLists.txt
Expand Up @@ -268,6 +268,7 @@ SET(QGIS_CORE_SRCS
qgsmapthemecollection.cpp
qgsxmlutils.cpp
qgssettings.cpp
qgsstacktrace.cpp

composer/qgsaddremoveitemcommand.cpp
composer/qgsaddremovemultiframecommand.cpp
Expand Down Expand Up @@ -463,6 +464,7 @@ IF (WITH_INTERNAL_QEXTSERIALPORT)
gps/qextserialport/win_qextserialport.cpp
)
ADD_DEFINITIONS(-D_TTY_WIN_)

ELSE(WIN32)
SET(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
gps/qextserialport/posix_qextserialport.cpp
Expand Down Expand Up @@ -1061,7 +1063,7 @@ ADD_DEPENDENCIES(qgis_core version)
# because of htonl
IF (WIN32)
FIND_LIBRARY(SETUPAPI_LIBRARY NAMES setupapi PATHS $ENV{LIB})
TARGET_LINK_LIBRARIES(qgis_core wsock32 ${SETUPAPI_LIBRARY})
TARGET_LINK_LIBRARIES(qgis_core wsock32 ${SETUPAPI_LIBRARY} DbgHelp)
ENDIF (WIN32)

IF(APPLE)
Expand Down

0 comments on commit 4b67b27

Please sign in to comment.