QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsstacktrace.cpp
Go to the documentation of this file.
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 WIN32
21 #include <windows.h>
22 #include <dbghelp.h>
23 #endif
24 
25 #include "qgis.h"
26 
28 
29 #ifdef _MSC_VER
30 QVector<QgsStackTrace::StackLine> QgsStackTrace::trace( _EXCEPTION_POINTERS *ExceptionInfo )
31 {
32  QgsStackLines stack;
33 #ifndef QGISDEBUG
34  return stack;
35 #endif
36 
37  HANDLE process = GetCurrentProcess();
38  SymSetOptions( SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME );
39 
40  PCSTR paths;
41  if ( QgsStackTrace::mSymbolPaths.isEmpty() )
42  {
43  paths = NULL;
44  }
45  else
46  {
47  paths = QgsStackTrace::mSymbolPaths.toStdString().c_str();
48  }
49 
50  SymInitialize( process, paths, TRUE );
51 
52  // StackWalk64() may modify context record passed to it, so we will
53  // use a copy.
54  CONTEXT context_record;
55  if ( ExceptionInfo )
56  context_record = *ExceptionInfo->ContextRecord;
57  else
58  RtlCaptureContext( &context_record );
59 
60  // Initialize stack walking.
61  STACKFRAME64 stack_frame;
62  memset( &stack_frame, 0, sizeof( stack_frame ) );
63 #if defined(_WIN64)
64  int machine_type = IMAGE_FILE_MACHINE_AMD64;
65  stack_frame.AddrPC.Offset = context_record.Rip;
66  stack_frame.AddrFrame.Offset = context_record.Rbp;
67  stack_frame.AddrStack.Offset = context_record.Rsp;
68 #else
69  int machine_type = IMAGE_FILE_MACHINE_I386;
70  stack_frame.AddrPC.Offset = context_record.Eip;
71  stack_frame.AddrFrame.Offset = context_record.Ebp;
72  stack_frame.AddrStack.Offset = context_record.Esp;
73 #endif
74  stack_frame.AddrPC.Mode = AddrModeFlat;
75  stack_frame.AddrFrame.Mode = AddrModeFlat;
76  stack_frame.AddrStack.Mode = AddrModeFlat;
77 
78  SYMBOL_INFO *symbol = ( SYMBOL_INFO * ) qgsMalloc( sizeof( SYMBOL_INFO ) + MAX_SYM_NAME );
79  symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
80  symbol->MaxNameLen = MAX_SYM_NAME;
81 
82  IMAGEHLP_LINE *line = ( IMAGEHLP_LINE * ) qgsMalloc( sizeof( IMAGEHLP_LINE ) );
83  line->SizeOfStruct = sizeof( IMAGEHLP_LINE );
84 
85  IMAGEHLP_MODULE *module = ( IMAGEHLP_MODULE * ) qgsMalloc( sizeof( IMAGEHLP_MODULE ) );
86  module->SizeOfStruct = sizeof( IMAGEHLP_MODULE );
87 
88  while ( StackWalk64( machine_type,
89  GetCurrentProcess(),
90  GetCurrentThread(),
91  &stack_frame,
92  &context_record,
93  NULL,
94  &SymFunctionTableAccess64,
95  &SymGetModuleBase64,
96  NULL ) )
97  {
98 
99  DWORD64 displacement = 0;
100 
101  if ( SymFromAddr( process, ( DWORD64 )stack_frame.AddrPC.Offset, &displacement, symbol ) )
102  {
103  DWORD dwDisplacement;
104 
105  QgsStackTrace::StackLine stackline;
106  stackline.symbolName = QString( symbol->Name );
107 
108  if ( SymGetLineFromAddr( process, ( DWORD64 ) stack_frame.AddrPC.Offset, &dwDisplacement, line ) )
109  {
110  stackline.fileName = QString( line->FileName );
111  stackline.lineNumber = QString::number( line->LineNumber );
112  }
113  else
114  {
115  stackline.fileName = "(unknown file)";
116  stackline.lineNumber = "(unknown line)";
117  }
118 
119  if ( SymGetModuleInfo( process, ( DWORD64 ) stack_frame.AddrPC.Offset, module ) )
120  {
121  stackline.moduleName = module->ModuleName;
122  }
123  else
124  {
125  stackline.moduleName = "(unknown module)";
126  }
127 
128  stack.append( stackline );
129  }
130  }
131 
132  qgsFree( symbol );
133  qgsFree( line );
134  qgsFree( module );
135  SymCleanup( process );
136  return stack;
137 }
138 
139 QString QgsStackTrace::mSymbolPaths;
140 
141 void QgsStackTrace::setSymbolPath( QString symbolPaths )
142 {
143  mSymbolPaths = symbolPaths;
144 }
145 
146 #endif // _MSC_VER
147 
148 #ifdef Q_OS_LINUX
149 QVector<QgsStackTrace::StackLine> QgsStackTrace::trace( unsigned int maxFrames )
150 {
151  Q_UNUSED( maxFrames )
152  QgsStackLines stack;
153 #ifndef QGISDEBUG
154  return stack;
155 #endif
156 
157  // TODO Add linux stack trace support. Pull it from main.cpp
158  return stack;
159 }
160 #endif
161 
162 bool QgsStackTrace::StackLine::isQgisModule() const
163 {
164  return moduleName.contains( QLatin1String( "qgis" ), Qt::CaseInsensitive );
165 }
166 
167 bool QgsStackTrace::StackLine::isValid() const
168 {
169  return !( fileName.contains( QLatin1String( "exe_common" ), Qt::CaseInsensitive ) ||
170  fileName.contains( QLatin1String( "unknown" ), Qt::CaseInsensitive ) ||
171  lineNumber.contains( QLatin1String( "unknown" ), Qt::CaseInsensitive ) );
172 
173 }
void * qgsMalloc(size_t size)
Allocates size bytes and returns a pointer to the allocated memory.
Definition: qgis.cpp:118
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:148