Skip to content

Commit

Permalink
Add stack info to crash dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanW2 committed Apr 22, 2017
1 parent 11bb234 commit 608d519
Show file tree
Hide file tree
Showing 8 changed files with 562 additions and 135 deletions.
1 change: 1 addition & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -21,6 +21,7 @@ SET(QGIS_APP_SRCS
qgsclipboard.cpp
qgscustomization.cpp
qgscustomprojectiondialog.cpp
qgscrashreport.cpp
qgsdecorationitem.cpp
qgsdecorationcopyright.cpp
qgsdecorationcopyrightdialog.cpp
Expand Down
56 changes: 29 additions & 27 deletions src/app/main.cpp
Expand Up @@ -269,33 +269,35 @@ static void dumpBacktrace( unsigned int depth )
#elif defined(Q_OS_WIN)
void **buffer = new void *[ depth ];

SymSetOptions( SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME );
SymInitialize( GetCurrentProcess(), "http://msdl.microsoft.com/download/symbols;http://download.osgeo.org/osgeo4w/symstore", TRUE );

unsigned short nFrames = CaptureStackBackTrace( 1, depth, buffer, nullptr );
SYMBOL_INFO *symbol = ( SYMBOL_INFO * ) qgsMalloc( sizeof( SYMBOL_INFO ) + 256 );
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
IMAGEHLP_LINE *line = ( IMAGEHLP_LINE * ) qgsMalloc( sizeof( IMAGEHLP_LINE ) );
line->SizeOfStruct = sizeof( IMAGEHLP_LINE );

for ( int i = 0; i < nFrames; i++ )
{
DWORD dwDisplacement;
SymFromAddr( GetCurrentProcess(), ( DWORD64 )( buffer[ i ] ), 0, symbol );
symbol->Name[ 255 ] = 0;
if ( SymGetLineFromAddr( GetCurrentProcess(), ( DWORD64 )( buffer[i] ), &dwDisplacement, line ) )
{
myPrint( "%s(%d) : (%s) frame %d, address %x\n", line->FileName, line->LineNumber, symbol->Name, i, symbol->Address );
}
else
{
myPrint( "%s(%d) : (%s) unknown source location, frame %d, address %x [GetLastError()=%d]\n", __FILE__, __LINE__, symbol->Name, i, symbol->Address, GetLastError() );
}
}

qgsFree( symbol );
qgsFree( line );
// SymSetOptions( SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME );
// SymInitialize( GetCurrentProcess(), "http://msdl.microsoft.com/download/symbols;http://download.osgeo.org/osgeo4w/symstore", TRUE );



// unsigned short nFrames = CaptureStackBackTrace( 1, depth, buffer, nullptr );
// SYMBOL_INFO *symbol = ( SYMBOL_INFO * ) qgsMalloc( sizeof( SYMBOL_INFO ) + 256 );
// symbol->MaxNameLen = 255;
// symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
// IMAGEHLP_LINE *line = ( IMAGEHLP_LINE * ) qgsMalloc( sizeof( IMAGEHLP_LINE ) );
// line->SizeOfStruct = sizeof( IMAGEHLP_LINE );

// for ( int i = 0; i < nFrames; i++ )
// {
// DWORD dwDisplacement;
// SymFromAddr( GetCurrentProcess(), ( DWORD64 )( buffer[ i ] ), 0, symbol );
// symbol->Name[ 255 ] = 0;
// if ( SymGetLineFromAddr( GetCurrentProcess(), ( DWORD64 )( buffer[i] ), &dwDisplacement, line ) )
// {
// myPrint( "%s(%d) : (%s) frame %d, address %x\n", line->FileName, line->LineNumber, symbol->Name, i, symbol->Address );
// }
// else
// {
// myPrint( "%s(%d) : (%s) unknown source location, frame %d, address %x [GetLastError()=%d]\n", __FILE__, __LINE__, symbol->Name, i, symbol->Address, GetLastError() );
// }
// }

// qgsFree( symbol );
// qgsFree( line );
#else
Q_UNUSED( depth );
#endif
Expand Down
114 changes: 84 additions & 30 deletions src/app/qgisapp.cpp
Expand Up @@ -111,6 +111,7 @@
//

#include "qgscrashdialog.h"
#include "qgscrashreport.h"
#include "qgisapp.h"
#include "qgisappinterface.h"
#include "qgisappstylesheet.h"
Expand Down Expand Up @@ -12551,45 +12552,98 @@ void QgisApp::transactionGroupCommitError( const QString &error )
#ifdef Q_OS_WIN
LONG WINAPI QgisApp::qgisCrashDump( struct _EXCEPTION_POINTERS *ExceptionInfo )
{
// Crash dump creation will be move to a new class in the near future.
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;

#if 0
QString dumpName = QDir::toNativeSeparators(
QString( "%1\\qgis-%2-%3-%4-%5.dmp" )
.arg( QDir::tempPath() )
.arg( QDateTime::currentDateTime().toString( "yyyyMMdd-hhmmss" ) )
.arg( GetCurrentProcessId() )
.arg( GetCurrentThreadId() )
.arg( Qgis::QGIS_DEV_VERSION )
);
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 );

QString msg;
HANDLE hDumpFile = CreateFile( dumpName.toLocal8Bit(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0 );
if ( hDumpFile != INVALID_HANDLE_VALUE )
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 ) )
{
MINIDUMP_EXCEPTION_INFORMATION ExpParam;
ExpParam.ThreadId = GetCurrentThreadId();
ExpParam.ExceptionPointers = ExceptionInfo;
ExpParam.ClientPointers = TRUE;

if ( MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, ExceptionInfo ? &ExpParam : nullptr, nullptr, nullptr ) )
{
msg = QObject::tr( "minidump written to %1" ).arg( dumpName );
}
else
DWORD64 displacement = 0;

if ( SymFromAddr( process, ( DWORD64 )stack_frame.AddrPC.Offset, &displacement, symbol ) )
{
msg = QObject::tr( "writing of minidump to %1 failed (%2)" ).arg( dumpName ).arg( GetLastError(), 0, 16 );
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 );
}

CloseHandle( hDumpFile );
}
else
{
msg = QObject::tr( "creation of minidump to %1 failed (%2)" ).arg( dumpName ).arg( GetLastError(), 0, 16 );
}
#endif

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

QgsCrashDialog dlg( QApplication::activeWindow() );
QgsCrashReport report;
report.setStackTrace( stack );
dlg.setBugReport( report.toString() );
if ( dlg.exec() )
{
QStringList arguments;
Expand Down
35 changes: 31 additions & 4 deletions src/app/qgscrashdialog.cpp
Expand Up @@ -17,16 +17,43 @@

#include "qgscrashdialog.h"

#include <QClipboard>


QgsCrashDialog::QgsCrashDialog( QWidget *parent )
: QDialog( parent )
{
setupUi( this );
setWindowTitle( tr( "Oh. Snap!" ) );
setWindowTitle( tr( "Oh Uh!" ) );

mCrashHeaderMessage->setText( tr( "Ouch!" ) );
mCrashMessage->setText( tr("Sorry it looks like QGIS crashed. \nSomething unexpected happened that we didn't handle correctly."));
mCrashHeaderMessage->setText( tr( "Oh Uh!" ) );
mCrashMessage->setText( tr( "Sorry. It looks something unexpected happened that we didn't handle and QGIS crashed." ) );
connect( mCopyReportButton, &QPushButton::clicked, this, &QgsCrashDialog::createBugReport );

mHelpLabel->setText( tr( "Keen to help us fix it? <br> <a href=\"http://qgis.org/en/site/getinvolved/development/bugreporting.html#bugs-features-and-issues\"> Follow the steps to help devs.<a>" ) );
mHelpLabel->setText( tr( "Keen to help us fix bugs?"
"<a href=\"http://qgis.org/en/site/getinvolved/development/bugreporting.html#bugs-features-and-issues\">Follow the steps to help our developers.</a>"
"<br><br>"
"You can also send us a helpful bug report using the Copy Report button and opening a ticket at "
"<a href=\"http://hub.qgis.org/\">hub.qgis.org</a>" ) );
mHelpLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
mHelpLabel->setOpenExternalLinks( true );

}

void QgsCrashDialog::setBugReport( const QString &reportData )
{
mReportDetailsText->setPlainText( reportData );
}

void QgsCrashDialog::showReportWidget()
{
}

void QgsCrashDialog::createBugReport()
{
QClipboard *clipboard = QApplication::clipboard();
QString userText = "User Feedback\n=============\n" + mUserFeedbackText->toPlainText();
QString details = "Report Details\n==============\n" + mReportDetailsText->toPlainText();
QString finalText = userText + "\n\n" + details;
clipboard->setText( finalText );
}
11 changes: 11 additions & 0 deletions src/app/qgscrashdialog.h
Expand Up @@ -18,8 +18,13 @@
#define QGSCRASHDIALOG_H

#include <QDialog>
#include <QFormLayout>
#include <QPlainTextEdit>
#include <QPushButton>

#include "ui_qgscrashdialog.h"
#include "qgis_app.h"
#include "qgspanelwidget.h"

/**
* A dialog to show a nicer crash dialog to the user.
Expand All @@ -33,6 +38,12 @@ class APP_EXPORT QgsCrashDialog : public QDialog, private Ui::QgsCrashDialog
* A dialog to show a nicer crash dialog to the user.
*/
QgsCrashDialog( QWidget *parent = nullptr );

void setBugReport( const QString &reportData );

private slots:
void showReportWidget();
void createBugReport();
};

#endif // QGSCRASHDIALOG_H

2 comments on commit 608d519

@nyalldawson
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I request a backport to 2.18? Are there any objections? This info would be so useful for windows crashes in the stable branch too

@NathanW2
Copy link
Member Author

@NathanW2 NathanW2 commented on 608d519 Apr 22, 2017 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.