Skip to content

Commit 4b67b27

Browse files
committedApr 25, 2017
Add QgsStackTrace class
1 parent c183285 commit 4b67b27

File tree

7 files changed

+267
-135
lines changed

7 files changed

+267
-135
lines changed
 

‎src/app/qgscrashhandler.cpp

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -21,104 +21,19 @@
2121
#include "qgsproject.h"
2222
#include "qgscrashdialog.h"
2323
#include "qgscrashreport.h"
24+
#include "qgsstacktrace.h"
2425

2526
#ifdef Q_OS_WIN
2627
LONG WINAPI QgsCrashHandler::handle( struct _EXCEPTION_POINTERS *ExceptionInfo )
2728
{
28-
HANDLE process = GetCurrentProcess();
29-
// TOOD Pull symbols from symbol server.
30-
// TOOD Move this logic to generic stack trace class to handle each
31-
// platform.
32-
SymInitialize( process, NULL, TRUE );
33-
34-
// StackWalk64() may modify context record passed to it, so we will
35-
// use a copy.
36-
CONTEXT context_record = *ExceptionInfo->ContextRecord;
37-
// Initialize stack walking.
38-
STACKFRAME64 stack_frame;
39-
memset( &stack_frame, 0, sizeof( stack_frame ) );
40-
#if defined(_WIN64)
41-
int machine_type = IMAGE_FILE_MACHINE_AMD64;
42-
stack_frame.AddrPC.Offset = context_record.Rip;
43-
stack_frame.AddrFrame.Offset = context_record.Rbp;
44-
stack_frame.AddrStack.Offset = context_record.Rsp;
45-
#else
46-
int machine_type = IMAGE_FILE_MACHINE_I386;
47-
stack_frame.AddrPC.Offset = context_record.Eip;
48-
stack_frame.AddrFrame.Offset = context_record.Ebp;
49-
stack_frame.AddrStack.Offset = context_record.Esp;
50-
#endif
51-
stack_frame.AddrPC.Mode = AddrModeFlat;
52-
stack_frame.AddrFrame.Mode = AddrModeFlat;
53-
stack_frame.AddrStack.Mode = AddrModeFlat;
54-
55-
SYMBOL_INFO *symbol = ( SYMBOL_INFO * ) qgsMalloc( sizeof( SYMBOL_INFO ) + MAX_SYM_NAME );
56-
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
57-
symbol->MaxNameLen = MAX_SYM_NAME;
58-
59-
IMAGEHLP_LINE *line = ( IMAGEHLP_LINE * ) qgsMalloc( sizeof( IMAGEHLP_LINE ) );
60-
line->SizeOfStruct = sizeof( IMAGEHLP_LINE );
61-
62-
IMAGEHLP_MODULE *module = ( IMAGEHLP_MODULE * ) qgsMalloc( sizeof( IMAGEHLP_MODULE ) );
63-
module->SizeOfStruct = sizeof( IMAGEHLP_MODULE );
64-
65-
QList<QgsCrashReport::StackLine> stack;
66-
while ( StackWalk64( machine_type,
67-
GetCurrentProcess(),
68-
GetCurrentThread(),
69-
&stack_frame,
70-
&context_record,
71-
NULL,
72-
&SymFunctionTableAccess64,
73-
&SymGetModuleBase64,
74-
NULL ) )
75-
{
76-
77-
DWORD64 displacement = 0;
78-
79-
if ( SymFromAddr( process, ( DWORD64 )stack_frame.AddrPC.Offset, &displacement, symbol ) )
80-
{
81-
DWORD dwDisplacement;
82-
QString fileName;
83-
QString lineNumber;
84-
QString moduleName;
85-
if ( SymGetLineFromAddr( process, ( DWORD )( stack_frame.AddrPC.Offset ), &dwDisplacement, line ) )
86-
{
87-
fileName = QString( line->FileName );
88-
lineNumber = QString::number( line->LineNumber );
89-
}
90-
else
91-
{
92-
fileName = "(unknown file)";
93-
lineNumber = "(unknown line)";
94-
}
95-
if ( SymGetModuleInfo( process, ( DWORD )( stack_frame.AddrPC.Offset ), module ) )
96-
{
97-
moduleName = QString( module->ModuleName );
98-
}
99-
else
100-
{
101-
moduleName = "(unknown module)";
102-
}
103-
QgsCrashReport::StackLine stackline;
104-
stackline.moduleName = moduleName;
105-
stackline.fileName = fileName;
106-
stackline.lineNumber = lineNumber;
107-
stackline.symbolName = QString( symbol->Name );
108-
stack.append( stackline );
109-
}
110-
}
111-
112-
qgsFree( symbol );
113-
qgsFree( line );
114-
qgsFree( module );
115-
29+
QgsStackLines stack = QgsStackTrace::trace( ExceptionInfo );
11630
showCrashDialog( stack );
11731

11832
return EXCEPTION_EXECUTE_HANDLER;
11933
}
34+
#endif
12035

121-
void QgsCrashHandler::showCrashDialog( const QList<QgsCrashReport::StackLine> &stack )
36+
void QgsCrashHandler::showCrashDialog( const QgsStackLines &stack )
12237
{
12338

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

14358
}
144-
#endif

‎src/app/qgscrashhandler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class APP_EXPORT QgsCrashHandler
4040
* Show the crash dialog.
4141
* @param stack The current stack of the crash point.
4242
*/
43-
static void showCrashDialog( const QList<QgsCrashReport::StackLine> &stack );
43+
static void showCrashDialog( const QgsStackLines &stack );
4444

4545
/**
4646
* Restart the application.

‎src/app/qgscrashreport.cpp

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const QString QgsCrashReport::toString() const
4949
}
5050
else
5151
{
52-
Q_FOREACH ( const QgsCrashReport::StackLine &line, mStackTrace )
52+
Q_FOREACH ( const QgsStackTrace::StackLine &line, mStackTrace )
5353
{
5454
QFileInfo fileInfo( line.fileName );
5555
QString filename( fileInfo.fileName() );
@@ -119,12 +119,12 @@ const QString QgsCrashReport::toString() const
119119
const QString QgsCrashReport::crashID() const
120120
{
121121
if ( mStackTrace.isEmpty() )
122-
return "ID not generated due to missing information";
122+
return "ID not generated due to missing information\n\n Your version of QGIS install might not have debug information included.";
123123

124124
QString data = QString::null;
125125

126126
// Hashes the full stack.
127-
Q_FOREACH ( QgsCrashReport::StackLine line, mStackTrace )
127+
Q_FOREACH ( const QgsStackTrace::StackLine &line, mStackTrace )
128128
{
129129
QFileInfo fileInfo( line.fileName );
130130
QString filename( fileInfo.fileName() );
@@ -137,16 +137,3 @@ const QString QgsCrashReport::crashID() const
137137
QString hash = QString( QCryptographicHash::hash( data.toAscii(), QCryptographicHash::Sha1 ).toHex() );
138138
return hash;
139139
}
140-
141-
bool QgsCrashReport::StackLine::isQgisModule() const
142-
{
143-
return moduleName.toLower().contains( "qgis" );
144-
}
145-
146-
bool QgsCrashReport::StackLine::isValid() const
147-
{
148-
return !( fileName.toLower().contains( "exe_common" ) ||
149-
fileName.toLower().contains( "unknown" ) ||
150-
lineNumber.toLower().contains( "unknown" ) );
151-
152-
}

‎src/app/qgscrashreport.h

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#define QGSCRASHREPORT_H
1818

1919
#include "qgis_app.h"
20+
#include "qgsstacktrace.h"
2021

2122
#include <QObject>
23+
#include <QVector>
2224

2325

2426
/**
@@ -28,30 +30,6 @@ class APP_EXPORT QgsCrashReport
2830
{
2931
public:
3032

31-
/**
32-
* Represents a line from a stack trace.
33-
*/
34-
struct StackLine
35-
{
36-
QString moduleName;
37-
QString symbolName;
38-
QString fileName;
39-
QString lineNumber;
40-
41-
/**
42-
* Check if this stack line is part of QGIS.
43-
* \return True if part of QGIS.
44-
*/
45-
bool isQgisModule() const;
46-
47-
/**
48-
* Check if this stack line is valid. Considered valid when the filename and line
49-
* number are known.
50-
* \return True of the line is valid.
51-
*/
52-
bool isValid() const;
53-
};
54-
5533
/**
5634
* Include information to generate user friendly crash report for QGIS.
5735
*/
@@ -73,13 +51,13 @@ class APP_EXPORT QgsCrashReport
7351
* Sets the stack trace for the crash report.
7452
* \param value A string list for each line in the stack trace.
7553
*/
76-
void setStackTrace( const QList<QgsCrashReport::StackLine> &value ) { mStackTrace = value; }
54+
void setStackTrace( const QgsStackLines &value ) { mStackTrace = value; }
7755

7856
/**
7957
* Returns the stack trace for this report.
8058
* \return A string list for each line in the stack trace.
8159
*/
82-
QList<QgsCrashReport::StackLine> StackTrace() const { return mStackTrace; }
60+
QgsStackLines StackTrace() const { return mStackTrace; }
8361

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

10886
private:
10987
Flags mFlags;
110-
QList<QgsCrashReport::StackLine> mStackTrace;
88+
QgsStackLines mStackTrace;
11189
};
11290

11391
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsCrashReport::Flags )

‎src/core/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ SET(QGIS_CORE_SRCS
268268
qgsmapthemecollection.cpp
269269
qgsxmlutils.cpp
270270
qgssettings.cpp
271+
qgsstacktrace.cpp
271272

272273
composer/qgsaddremoveitemcommand.cpp
273274
composer/qgsaddremovemultiframecommand.cpp
@@ -463,6 +464,7 @@ IF (WITH_INTERNAL_QEXTSERIALPORT)
463464
gps/qextserialport/win_qextserialport.cpp
464465
)
465466
ADD_DEFINITIONS(-D_TTY_WIN_)
467+
466468
ELSE(WIN32)
467469
SET(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
468470
gps/qextserialport/posix_qextserialport.cpp
@@ -1061,7 +1063,7 @@ ADD_DEPENDENCIES(qgis_core version)
10611063
# because of htonl
10621064
IF (WIN32)
10631065
FIND_LIBRARY(SETUPAPI_LIBRARY NAMES setupapi PATHS $ENV{LIB})
1064-
TARGET_LINK_LIBRARIES(qgis_core wsock32 ${SETUPAPI_LIBRARY})
1066+
TARGET_LINK_LIBRARIES(qgis_core wsock32 ${SETUPAPI_LIBRARY} DbgHelp)
10651067
ENDIF (WIN32)
10661068

10671069
IF(APPLE)

‎src/core/qgsstacktrace.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/***************************************************************************
2+
qgsstacktrace.cpp - QgsStackTrace
3+
4+
---------------------
5+
begin : 24.4.2017
6+
copyright : (C) 2017 by Nathan Woodrow
7+
email : woodrow.nathan@gmail.com
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#include "qgsstacktrace.h"
17+
18+
#include <QVector>
19+
20+
#ifdef QGISDEBUG
21+
#ifdef WIN32
22+
#include <windows.h>
23+
#include <dbghelp.h>
24+
#endif
25+
#endif
26+
27+
#include "qgis.h"
28+
29+
///@cond PRIVATE
30+
31+
#ifdef Q_OS_WIN
32+
QVector<QgsStackTrace::StackLine> QgsStackTrace::trace( _EXCEPTION_POINTERS *ExceptionInfo )
33+
{
34+
QgsStackLines stack;
35+
#ifndef QGISDEBUG
36+
return stack;
37+
#endif
38+
39+
HANDLE process = GetCurrentProcess();
40+
// TOOD Pull symbols from symbol server.
41+
SymSetOptions( SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME );
42+
SymInitialize( process, NULL, TRUE );
43+
44+
// StackWalk64() may modify context record passed to it, so we will
45+
// use a copy.
46+
CONTEXT context_record = *ExceptionInfo->ContextRecord;
47+
// Initialize stack walking.
48+
STACKFRAME64 stack_frame;
49+
memset( &stack_frame, 0, sizeof( stack_frame ) );
50+
#if defined(_WIN64)
51+
int machine_type = IMAGE_FILE_MACHINE_AMD64;
52+
stack_frame.AddrPC.Offset = context_record.Rip;
53+
stack_frame.AddrFrame.Offset = context_record.Rbp;
54+
stack_frame.AddrStack.Offset = context_record.Rsp;
55+
#else
56+
int machine_type = IMAGE_FILE_MACHINE_I386;
57+
stack_frame.AddrPC.Offset = context_record.Eip;
58+
stack_frame.AddrFrame.Offset = context_record.Ebp;
59+
stack_frame.AddrStack.Offset = context_record.Esp;
60+
#endif
61+
stack_frame.AddrPC.Mode = AddrModeFlat;
62+
stack_frame.AddrFrame.Mode = AddrModeFlat;
63+
stack_frame.AddrStack.Mode = AddrModeFlat;
64+
65+
SYMBOL_INFO *symbol = ( SYMBOL_INFO * ) qgsMalloc( sizeof( SYMBOL_INFO ) + MAX_SYM_NAME );
66+
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
67+
symbol->MaxNameLen = MAX_SYM_NAME;
68+
69+
IMAGEHLP_LINE *line = ( IMAGEHLP_LINE * ) qgsMalloc( sizeof( IMAGEHLP_LINE ) );
70+
line->SizeOfStruct = sizeof( IMAGEHLP_LINE );
71+
72+
IMAGEHLP_MODULE *module = ( IMAGEHLP_MODULE * ) qgsMalloc( sizeof( IMAGEHLP_MODULE ) );
73+
module->SizeOfStruct = sizeof( IMAGEHLP_MODULE );
74+
75+
while ( StackWalk64( machine_type,
76+
GetCurrentProcess(),
77+
GetCurrentThread(),
78+
&stack_frame,
79+
&context_record,
80+
NULL,
81+
&SymFunctionTableAccess64,
82+
&SymGetModuleBase64,
83+
NULL ) )
84+
{
85+
86+
DWORD64 displacement = 0;
87+
88+
if ( SymFromAddr( process, ( DWORD64 )stack_frame.AddrPC.Offset, &displacement, symbol ) )
89+
{
90+
DWORD dwDisplacement;
91+
QString fileName;
92+
QString lineNumber;
93+
QString moduleName;
94+
if ( SymGetLineFromAddr( process, ( DWORD )( stack_frame.AddrPC.Offset ), &dwDisplacement, line ) )
95+
{
96+
fileName = QString( line->FileName );
97+
lineNumber = QString::number( line->LineNumber );
98+
}
99+
else
100+
{
101+
fileName = "(unknown file)";
102+
lineNumber = "(unknown line)";
103+
}
104+
if ( SymGetModuleInfo( process, ( DWORD )( stack_frame.AddrPC.Offset ), module ) )
105+
{
106+
moduleName = QString( module->ModuleName );
107+
}
108+
else
109+
{
110+
moduleName = "(unknown module)";
111+
}
112+
QgsStackTrace::StackLine stackline;
113+
stackline.moduleName = moduleName;
114+
stackline.fileName = fileName;
115+
stackline.lineNumber = lineNumber;
116+
stackline.symbolName = QString( symbol->Name );
117+
stack.append( stackline );
118+
}
119+
}
120+
121+
qgsFree( symbol );
122+
qgsFree( line );
123+
qgsFree( module );
124+
return stack;
125+
126+
}
127+
#endif
128+
129+
#ifdef Q_OS_LINUX
130+
QVector<QgsStackTrace::StackLine> QgsStackTrace::trace( unsigned int maxFrames )
131+
{
132+
Q_UNUSED( maxFrames );
133+
QgsStackLines stack;
134+
#ifndef QGISDEBUG
135+
return stack;
136+
#endif
137+
138+
// TODO Add linux stack trace support. Pull it from main.cpp
139+
return stack;
140+
}
141+
#endif
142+
143+
QgsStackTrace::QgsStackTrace()
144+
{
145+
146+
}
147+
148+
bool QgsStackTrace::StackLine::isQgisModule() const
149+
{
150+
return moduleName.toLower().contains( "qgis" );
151+
}
152+
153+
bool QgsStackTrace::StackLine::isValid() const
154+
{
155+
return !( fileName.toLower().contains( "exe_common" ) ||
156+
fileName.toLower().contains( "unknown" ) ||
157+
lineNumber.toLower().contains( "unknown" ) );
158+
159+
}
160+
///@endcond

‎src/core/qgsstacktrace.h

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/***************************************************************************
2+
qgsstacktrace.h - QgsStackTrace
3+
4+
---------------------
5+
begin : 24.4.2017
6+
copyright : (C) 2017 by Nathan Woodrow
7+
email : woodrow.nathan@gmail.com
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSSTACKTRACE_H
17+
#define QGSSTACKTRACE_H
18+
19+
#include "qgis_core.h"
20+
21+
#include <QStringList>
22+
23+
///@cond PRIVATE
24+
25+
26+
/**
27+
* \ingroup core
28+
* The QgsStacktrace class provides an interface to generate a stack trace for
29+
* displaying additional debug information when things go wrong.
30+
*
31+
* \note Not available in python
32+
* \note Added in QGIS 3.0
33+
*/
34+
class CORE_EXPORT QgsStackTrace
35+
{
36+
public:
37+
38+
/**
39+
* Represents a line from a stack trace.
40+
*/
41+
struct StackLine
42+
{
43+
QString moduleName;
44+
QString symbolName;
45+
QString fileName;
46+
QString lineNumber;
47+
48+
/**
49+
* Check if this stack line is part of QGIS.
50+
* \return True if part of QGIS.
51+
*/
52+
bool isQgisModule() const;
53+
54+
/**
55+
* Check if this stack line is valid. Considered valid when the filename and line
56+
* number are known.
57+
* \return True of the line is valid.
58+
*/
59+
bool isValid() const;
60+
};
61+
62+
#ifdef Q_OS_WIN
63+
64+
/**
65+
* Return a demangled stack backtrace of the caller function.
66+
*
67+
* \note Added in QGIS 3.0
68+
*/
69+
static QVector<QgsStackTrace::StackLine> trace( struct _EXCEPTION_POINTERS *ExceptionInfo );
70+
#endif
71+
72+
#ifdef Q_OS_LINUX
73+
74+
/**
75+
* Return a demangled stack backtrace of the caller function.
76+
*
77+
* \note Added in QGIS 3.0
78+
*/
79+
static QVector<QgsStackTrace::StackLine> trace( unsigned int maxFrames = 63 );
80+
#endif
81+
82+
private:
83+
QgsStackTrace();
84+
85+
};
86+
87+
typedef QVector<QgsStackTrace::StackLine> QgsStackLines;
88+
89+
///@endcond
90+
91+
#endif // QGSSTACKTRACE_H

0 commit comments

Comments
 (0)
Please sign in to comment.