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 <iostream>
17 
18 #define _NO_CVCONST_H
19 #define _CRT_STDIO_ISO_WIDE_SPECIFIERS
20 
21 #include "qgsstacktrace.h"
22 
23 #include <QDir>
24 #include <QVector>
25 #include <QFile>
26 #include <QTextStream>
27 
28 #include <inttypes.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <tlhelp32.h>
33 
34 #include <Windows.h>
35 #include <DbgHelp.h>
36 
37 ///@cond PRIVATE
38 
39 #ifdef Q_OS_WIN
40 enum BasicType
41 {
42   btNoType = 0,
43   btVoid = 1,
44   btChar = 2,
45   btWChar = 3,
46   btInt = 6,
47   btUInt = 7,
48   btFloat = 8,
49   btBCD = 9,
50   btBool = 10,
51   btLong = 13,
52   btULong = 14,
53   btCurrency = 25,
54   btDate = 26,
55   btVariant = 27,
56   btComplex = 28,
57   btBit = 29,
58   btBSTR = 30,
59   btHresult = 31,
60 };
61 
62 #define WIDEN_(x) L ## x
63 #define WIDEN(x) WIDEN_(x)
64 
65 typedef struct _StackTrace
66 {
67   wchar_t message[2 * 1024 * 1024];
68   int written;
69   HANDLE process;
70   HANDLE thread;
71   PCONTEXT contextRecord;
72   STACKFRAME64 currentStackFrame;
73   bool isFirstParameter;
74   LPVOID scratchSpace;
75 } StackTrace;
76 
printBasicType(StackTrace * stackTrace,PSYMBOL_INFOW pSymInfo,ULONG typeIndex,void * valueLocation,bool whilePrintingPointer)77 bool printBasicType( StackTrace *stackTrace, PSYMBOL_INFOW pSymInfo, ULONG typeIndex, void *valueLocation, bool whilePrintingPointer )
78 {
79   enum BasicType basicType;
80   if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, typeIndex, TI_GET_BASETYPE, &basicType ) )
81   {
82     return false;
83   }
84 
85   switch ( basicType )
86   {
87     case btChar:
88     {
89       if ( !whilePrintingPointer )
90       {
91         char value;
92         ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
93 
94         stackTrace->written +=
95           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
96                       L"'%c'", value );
97       }
98       else
99       {
100         char *value = ( char * )valueLocation;
101 
102         MEMORY_BASIC_INFORMATION pageInfo = { 0 };
103         if ( VirtualQueryEx( stackTrace->process, value, &pageInfo, sizeof( pageInfo ) ) == 0 )
104         {
105           return false;
106         }
107 
108         PVOID pageEndAddress = ( char * )pageInfo.BaseAddress + pageInfo.RegionSize;
109 
110         stackTrace->written +=
111           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
112                       L"\"" );
113 
114         for ( int charsWritten = 0; charsWritten < 100; )
115         {
116           if ( ( void * )value < pageEndAddress )
117           {
118             char next;
119             ReadProcessMemory( stackTrace->process, value, &next, sizeof( next ), NULL );
120 
121             if ( next != '\0' )
122             {
123               if ( charsWritten == 100 - 1 )
124               {
125                 stackTrace->written +=
126                   swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
127                               L"..." );
128                 break;
129               }
130               else
131               {
132                 stackTrace->written +=
133                   swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
134                               L"%c", next );
135                 charsWritten++;
136                 value++;
137               }
138             }
139             else
140             {
141               break;
142             }
143           }
144           else
145           {
146             if ( VirtualQueryEx( stackTrace->process, pageEndAddress, &pageInfo, sizeof( pageInfo ) ) == 0 )
147             {
148               stackTrace->written +=
149                 swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
150                             L"<Bad memory>" );
151               break;
152             }
153 
154             pageEndAddress = ( char * )pageInfo.BaseAddress + pageInfo.RegionSize;
155           }
156         }
157 
158         stackTrace->written +=
159           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
160                       L"\"" );
161       }
162 
163       break;
164     }
165 
166     case btWChar:
167     {
168       if ( !whilePrintingPointer )
169       {
170         wchar_t value;
171         ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
172 
173         stackTrace->written +=
174           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
175                       L"'%lc'", value );
176       }
177       else
178       {
179         wchar_t *value = ( wchar_t * )valueLocation;
180 
181         MEMORY_BASIC_INFORMATION pageInfo = { 0 };
182         if ( VirtualQueryEx( stackTrace->process, value, &pageInfo, sizeof( pageInfo ) ) == 0 )
183         {
184           return false;
185         }
186 
187         PVOID pageEndAddress = ( char * )pageInfo.BaseAddress + pageInfo.RegionSize;
188 
189         stackTrace->written +=
190           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
191                       L"L\"" );
192 
193         for ( int charsWritten = 0; charsWritten < 100; )
194         {
195           if ( ( void * )( ( char * )value + 1 ) < pageEndAddress )
196           {
197             wchar_t next;
198             ReadProcessMemory( stackTrace->process, value, &next, sizeof( next ), NULL );
199 
200             if ( next != L'\0' )
201             {
202               if ( charsWritten == 100 - 1 )
203               {
204                 stackTrace->written +=
205                   swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
206                               L"..." );
207                 break;
208               }
209               else
210               {
211                 stackTrace->written +=
212                   swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
213                               L"%lc", next );
214                 charsWritten++;
215                 value++;
216               }
217             }
218             else
219             {
220               break;
221             }
222           }
223           else
224           {
225             if ( VirtualQueryEx( stackTrace->process, pageEndAddress, &pageInfo, sizeof( pageInfo ) ) == 0 )
226             {
227               stackTrace->written +=
228                 swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
229                             L"<Bad memory>" );
230               break;
231             }
232 
233             pageEndAddress = ( char * )pageInfo.BaseAddress + pageInfo.RegionSize;
234           }
235         }
236 
237         stackTrace->written +=
238           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
239                       L"\"" );
240       }
241 
242       break;
243     }
244 
245     case btInt:
246     {
247       ULONG64 length;
248       if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, typeIndex, TI_GET_LENGTH, &length ) )
249       {
250         return false;
251       }
252 
253       switch ( length )
254       {
255         case sizeof( int8_t ) :
256         {
257           int8_t value;
258           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
259 
260           stackTrace->written +=
261             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
262                         L"%" WIDEN( PRId8 ), value );
263           break;
264         }
265 
266         case sizeof( int16_t ) :
267         {
268           int16_t value;
269           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
270 
271           stackTrace->written +=
272             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
273                         L"%" WIDEN( PRId16 ), value );
274           break;
275         }
276 
277         case sizeof( int32_t ) :
278         {
279           int32_t value;
280           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
281 
282           stackTrace->written +=
283             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
284                         L"%" WIDEN( PRId32 ), value );
285           break;
286         }
287 
288         case sizeof( int64_t ) :
289         {
290           int64_t value;
291           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
292 
293           stackTrace->written +=
294             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
295                         L"%" WIDEN( PRId64 ), value );
296           break;
297         }
298 
299         default:
300         {
301           return false;
302           break;
303         }
304       }
305 
306       break;
307     }
308 
309     case btUInt:
310     {
311       ULONG64 length;
312       if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, typeIndex, TI_GET_LENGTH, &length ) )
313       {
314         return false;
315       }
316 
317       switch ( length )
318       {
319         case sizeof( uint8_t ) :
320         {
321           uint8_t value;
322           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
323 
324           stackTrace->written +=
325             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
326                         L"%" WIDEN( PRIu8 ), value );
327           break;
328         }
329 
330         case sizeof( uint16_t ) :
331         {
332           uint16_t value;
333           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
334 
335           stackTrace->written +=
336             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
337                         L"%" WIDEN( PRIu16 ), value );
338           break;
339         }
340 
341         case sizeof( uint32_t ) :
342         {
343           uint32_t value;
344           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
345 
346           stackTrace->written +=
347             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
348                         L"%" WIDEN( PRIu32 ), value );
349           break;
350         }
351 
352         case sizeof( uint64_t ) :
353         {
354           uint64_t value;
355           ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
356 
357           stackTrace->written +=
358             swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
359                         L"%" WIDEN( PRIu64 ), value );
360           break;
361         }
362 
363         default:
364         {
365           return false;
366           break;
367         }
368       }
369 
370       break;
371     }
372 
373     case btFloat:
374     {
375       float value;
376       ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
377 
378       stackTrace->written +=
379         swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
380                     L"%f", value );
381       break;
382     }
383 
384     case btLong:
385     {
386       long value;
387       ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
388 
389       stackTrace->written +=
390         swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
391                     L"%ld", value );
392       break;
393     }
394 
395     case btULong:
396     {
397       unsigned long value;
398       ReadProcessMemory( stackTrace->process, valueLocation, &value, sizeof( value ), NULL );
399 
400       stackTrace->written +=
401         swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
402                     L"%lu", value );
403       break;
404     }
405 
406     default:
407     {
408       return false;
409       break;
410     }
411   }
412 
413   return true;
414 }
415 
printGivenType(StackTrace * stackTrace,PSYMBOL_INFOW pSymInfo,enum SymTagEnum symbolTag,ULONG typeIndex,void * valueLocation,bool whilePrintingPointer)416 bool printGivenType( StackTrace *stackTrace, PSYMBOL_INFOW pSymInfo, enum SymTagEnum symbolTag, ULONG typeIndex, void *valueLocation, bool whilePrintingPointer )
417 {
418   switch ( symbolTag )
419   {
420     case SymTagBaseType:
421     {
422       if ( !printBasicType( stackTrace, pSymInfo, typeIndex, valueLocation, whilePrintingPointer ) )
423       {
424         return false;
425       }
426 
427       break;
428     }
429 
430     case SymTagPointerType:
431     {
432       void *pointedValueLocation;
433       ReadProcessMemory( stackTrace->process, valueLocation, &pointedValueLocation, sizeof( pointedValueLocation ), NULL );
434 
435 
436       stackTrace->written +=
437         swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
438                     L"0x%p -> ", pointedValueLocation );
439 
440       if ( pointedValueLocation < ( void * )0x1000 )
441       {
442         stackTrace->written +=
443           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
444                       L"?" );
445         break;
446       }
447 
448       DWORD pointedTypeIndex;
449       if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, typeIndex, TI_GET_TYPE, &pointedTypeIndex ) )
450       {
451         return false;
452       }
453 
454       ULONG64 pointedTypeLength;
455       if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, pointedTypeIndex, TI_GET_LENGTH, &pointedTypeLength ) )
456       {
457         return false;
458       }
459 
460       MEMORY_BASIC_INFORMATION pageInfo = { 0 };
461       if ( VirtualQueryEx( stackTrace->process, pointedValueLocation, &pageInfo, sizeof( pageInfo ) ) == 0 )
462       {
463         return false;
464       }
465 
466       for ( ;; )
467       {
468         PVOID pageEndAddress = ( char * )pageInfo.BaseAddress + pageInfo.RegionSize;
469 
470         if ( ( void * )( ( char * )pointedValueLocation + pointedTypeLength ) >= pageEndAddress )
471         {
472           if ( VirtualQueryEx( stackTrace->process, pageEndAddress, &pageInfo, sizeof( pageInfo ) ) == 0 )
473           {
474             return false;
475           }
476         }
477         else
478         {
479           break;
480         }
481       }
482 
483       enum SymTagEnum pointedSymbolTag;
484       if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, pointedTypeIndex, TI_GET_SYMTAG, &pointedSymbolTag ) )
485       {
486         return false;
487       }
488 
489       return printGivenType( stackTrace, pSymInfo, pointedSymbolTag, pointedTypeIndex, pointedValueLocation, true );
490     }
491 
492     default:
493     {
494       return false;
495     }
496   }
497 
498   return true;
499 }
500 
printType(StackTrace * stackTrace,PSYMBOL_INFOW pSymInfo,void * valueLocation)501 bool printType( StackTrace *stackTrace, PSYMBOL_INFOW pSymInfo, void *valueLocation )
502 {
503   enum SymTagEnum symbolTag;
504   if ( !SymGetTypeInfo( stackTrace->process, pSymInfo->ModBase, pSymInfo->TypeIndex, TI_GET_SYMTAG, &symbolTag ) )
505   {
506     return false;
507   }
508 
509   return printGivenType( stackTrace, pSymInfo, symbolTag, pSymInfo->TypeIndex, valueLocation, false );
510 }
511 
512 
enumParams(_In_ PSYMBOL_INFOW pSymInfo,_In_ ULONG SymbolSize,_In_opt_ PVOID UserContext)513 BOOL CALLBACK enumParams(
514   _In_ PSYMBOL_INFOW pSymInfo,
515   _In_ ULONG SymbolSize,
516   _In_opt_ PVOID UserContext )
517 {
518   if ( ( pSymInfo->Flags & SYMFLAG_LOCAL ) == 0 )
519   {
520     return TRUE;
521   }
522 
523   StackTrace *stackTrace = ( StackTrace * )UserContext;
524 
525   stackTrace->written +=
526     swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
527                 L"\n" );
528 
529   if ( pSymInfo->Flags & SYMFLAG_PARAMETER )
530   {
531     stackTrace->written +=
532       swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
533                   L"Parm: " );
534   }
535   else if ( pSymInfo->Flags & SYMFLAG_LOCAL )
536   {
537     stackTrace->written +=
538       swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
539                   L"Local: " );
540   }
541 
542   stackTrace->written +=
543     swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
544                 L"%ls = ", pSymInfo->Name );
545 
546   void *valueLocation = NULL;
547   if ( pSymInfo->Flags & SYMFLAG_REGISTER )
548   {
549     if ( stackTrace->scratchSpace == NULL )
550     {
551       stackTrace->written +=
552         swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
553                     L"<Could not allocate memory to write register value>" );
554       return TRUE;
555     }
556 
557     valueLocation = stackTrace->scratchSpace;
558 
559     switch ( pSymInfo->Register )
560     {
561 #ifndef _WIN64
562       case 17:
563         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Eax, sizeof( stackTrace->contextRecord->Eax ), NULL );
564         break;
565       case 18:
566         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Ecx, sizeof( stackTrace->contextRecord->Ecx ), NULL );
567         break;
568       case 19:
569         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Edx, sizeof( stackTrace->contextRecord->Edx ), NULL );
570         break;
571       case 20:
572         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Ebx, sizeof( stackTrace->contextRecord->Ebx ), NULL );
573         break;
574 #else
575       case 328:
576         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Rax, sizeof( stackTrace->contextRecord->Rax ), NULL );
577         break;
578 
579       case 329:
580         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Rbx, sizeof( stackTrace->contextRecord->Rbx ), NULL );
581         break;
582 
583       case 330:
584         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Rcx, sizeof( stackTrace->contextRecord->Rcx ), NULL );
585         break;
586 
587       case 331:
588         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Rdx, sizeof( stackTrace->contextRecord->Rdx ), NULL );
589         break;
590 
591       case 336:
592         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->R8, sizeof( stackTrace->contextRecord->R8 ), NULL );
593         break;
594 
595       case 337:
596         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->R9, sizeof( stackTrace->contextRecord->R9 ), NULL );
597         break;
598 
599       case 154:
600         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Xmm0, sizeof( stackTrace->contextRecord->Xmm0 ), NULL );
601         break;
602 
603       case 155:
604         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Xmm1, sizeof( stackTrace->contextRecord->Xmm1 ), NULL );
605         break;
606 
607       case 156:
608         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Xmm2, sizeof( stackTrace->contextRecord->Xmm2 ), NULL );
609         break;
610 
611       case 157:
612         WriteProcessMemory( stackTrace->process, valueLocation, &stackTrace->contextRecord->Xmm3, sizeof( stackTrace->contextRecord->Xmm3 ), NULL );
613         break;
614 #endif
615 
616       default:
617         stackTrace->written +=
618           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
619                       L"<Unknown register %lu>", pSymInfo->Register );
620         return TRUE;
621     }
622   }
623   else if ( pSymInfo->Flags & SYMFLAG_LOCAL )
624   {
625 #ifndef _WIN64
626     valueLocation = ( ( char * )stackTrace->contextRecord->Ebp ) + pSymInfo->Address;
627 #else
628     valueLocation = ( ( char * )stackTrace->contextRecord->Rbp ) + pSymInfo->Address;
629 #endif
630   }
631   else if ( pSymInfo->Flags & SYMFLAG_REGREL )
632   {
633     switch ( pSymInfo->Register )
634     {
635 #ifndef _WIN64
636       case 22:
637         valueLocation = ( ( char * )stackTrace->contextRecord->Ebp ) + pSymInfo->Address;
638         break;
639 #else
640       case 334:
641         valueLocation = ( ( char * )stackTrace->contextRecord->Rbp ) + pSymInfo->Address;
642         break;
643       case 335:
644         valueLocation = ( ( char * )stackTrace->contextRecord->Rsp ) + 0x20 + pSymInfo->Address;
645         break;
646 #endif
647 
648       default:
649         stackTrace->written +=
650           swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
651                       L"<Relative to unknown register %lu>", pSymInfo->Register );
652         return TRUE;
653     }
654   }
655   else
656   {
657     valueLocation = ( void * )( stackTrace->currentStackFrame.AddrFrame.Offset + pSymInfo->Address );
658   }
659 
660   if ( !printType( stackTrace, pSymInfo, valueLocation ) )
661   {
662     stackTrace->written +=
663       swprintf_s( stackTrace->message + stackTrace->written, sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
664                   L"?" );
665   }
666 
667   return TRUE;
668 }
669 
670 //Returns the last Win32 error, in string format. Returns an empty string if there is no error.
GetLastErrorAsString()671 void GetLastErrorAsString()
672 {
673   //Get the error message, if any.
674   DWORD errorMessageID = ::GetLastError();
675 
676   LPSTR messageBuffer = nullptr;
677   size_t size = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
678                                 NULL, errorMessageID, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), ( LPSTR )&messageBuffer, 0, NULL );
679 
680   std::string message( messageBuffer, size );
681 
682   std::cout << message << std::endl;
683 
684   //Free the buffer.
685   LocalFree( messageBuffer );
686 }
687 
getStackTrace(StackTrace * stackTrace,QString symbolPath,QgsStackTrace * trace)688 void getStackTrace( StackTrace *stackTrace, QString symbolPath, QgsStackTrace *trace )
689 {
690   SymSetOptions( SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME );
691   if ( !SymInitialize( stackTrace->process, symbolPath.toStdString().c_str(), TRUE ) )
692   {
693     trace->symbolsLoaded = false;
694     return;
695   }
696   trace->symbolsLoaded = true;
697 
698   SYMBOL_INFOW *symbol = ( SYMBOL_INFOW * )calloc( sizeof( *symbol ) + 256 * sizeof( wchar_t ), 1 );
699   symbol->MaxNameLen = 255;
700   symbol->SizeOfStruct = sizeof( SYMBOL_INFOW );
701 
702 #ifndef _WIN64
703   DWORD machineType = IMAGE_FILE_MACHINE_I386;
704 #else
705   DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
706 #endif
707 
708   for ( int i = 0; ; i++ )
709   {
710     if ( !StackWalk64( machineType,
711                        stackTrace->process,
712                        stackTrace->thread,
713                        &stackTrace->currentStackFrame,
714                        stackTrace->contextRecord,
715                        NULL,
716                        SymFunctionTableAccess64,
717                        SymGetModuleBase64,
718                        NULL ) )
719     {
720       break;
721     }
722 
723     if ( SymFromAddrW( stackTrace->process, stackTrace->currentStackFrame.AddrPC.Offset, NULL, symbol ) )
724     {
725       QgsStackTrace::StackLine stackline;
726       stackline.symbolName = QString::fromWCharArray( symbol->Name );
727 
728       stackTrace->written +=
729         swprintf_s( &stackTrace->message[stackTrace->written], sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written - 1,
730                     L">%02i: 0x%08llX %ls", i, symbol->Address, symbol->Name );
731 
732       IMAGEHLP_STACK_FRAME stackFrame = { 0 };
733       stackFrame.InstructionOffset = symbol->Address;
734       if ( SymSetContext( stackTrace->process, &stackFrame, NULL ) )
735       {
736         stackTrace->isFirstParameter = true;
737         SymEnumSymbolsW( stackTrace->process, 0, NULL, enumParams, stackTrace );
738       }
739 
740       DWORD pos;
741       IMAGEHLP_LINEW64 lineInfo = { 0 };
742       lineInfo.SizeOfStruct = sizeof( lineInfo );
743       if ( SymGetLineFromAddrW64( stackTrace->process, stackTrace->currentStackFrame.AddrPC.Offset, &pos, &lineInfo ) )
744       {
745         stackline.fileName = QString::fromWCharArray( lineInfo.FileName );
746         stackline.lineNumber = QString::number( lineInfo.LineNumber );
747 
748         stackTrace->written +=
749           swprintf_s( &stackTrace->message[stackTrace->written], sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
750                       L"\nSource: %ls:%lu", lineInfo.FileName, lineInfo.LineNumber );
751       }
752 
753       stackTrace->written +=
754         swprintf_s( &stackTrace->message[stackTrace->written], sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
755                     L"\n" );
756 
757       trace->lines.append( stackline );
758     }
759     else
760     {
761       stackTrace->written +=
762         swprintf_s( &stackTrace->message[stackTrace->written], sizeof( stackTrace->message ) / sizeof( stackTrace->message[0] ) - stackTrace->written,
763                     L">%02i: 0x%08llX ?\n", i, stackTrace->currentStackFrame.AddrPC.Offset );
764     }
765   }
766 
767   free( symbol );
768 
769   SymCleanup( stackTrace->process );
770 }
771 
trace(DWORD processId,DWORD threadId,LPEXCEPTION_POINTERS exception,QString symbolPath)772 QgsStackTrace *QgsStackTrace::trace( DWORD processId, DWORD threadId, LPEXCEPTION_POINTERS exception, QString symbolPath )
773 {
774   QgsStackTrace *trace = new QgsStackTrace();
775   EXCEPTION_POINTERS remoteException = { 0 };
776   CONTEXT remoteContextRecord = { 0 };
777 
778   StackTrace *stackTrace = ( StackTrace * )calloc( sizeof( *stackTrace ), 1 );
779   stackTrace->process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, processId );
780   stackTrace->thread = OpenThread( THREAD_ALL_ACCESS, FALSE, threadId );
781   trace->process = stackTrace->process;
782   trace->thread = stackTrace->thread;
783 
784   HANDLE h = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
785   if ( h != INVALID_HANDLE_VALUE )
786   {
787     THREADENTRY32 te;
788     te.dwSize = sizeof( te );
789     if ( Thread32First( h, &te ) )
790     {
791       do
792       {
793         if ( te.dwSize >= FIELD_OFFSET( THREADENTRY32, th32OwnerProcessID ) +
794              sizeof( te.th32OwnerProcessID ) )
795         {
796           if ( te.th32OwnerProcessID == processId )
797           {
798             HANDLE threadHandle = OpenThread( THREAD_ALL_ACCESS, FALSE, te.th32ThreadID );
799             trace->threads.push_back( threadHandle );
800             SuspendThread( threadHandle );
801           }
802         }
803         te.dwSize = sizeof( te );
804       }
805       while ( Thread32Next( h, &te ) );
806     }
807     CloseHandle( h );
808   }
809 
810   ReadProcessMemory( stackTrace->process, exception, &remoteException, sizeof( remoteException ), NULL );
811   ReadProcessMemory( stackTrace->process, remoteException.ContextRecord, &remoteContextRecord, sizeof( remoteContextRecord ), NULL );
812 
813 #ifndef _WIN64
814   stackTrace->currentStackFrame.AddrPC.Offset = remoteContextRecord.Eip;
815   stackTrace->currentStackFrame.AddrPC.Mode = AddrModeFlat;
816   stackTrace->currentStackFrame.AddrFrame.Offset = remoteContextRecord.Ebp;
817   stackTrace->currentStackFrame.AddrFrame.Mode = AddrModeFlat;
818   stackTrace->currentStackFrame.AddrStack.Offset = remoteContextRecord.Esp;
819   stackTrace->currentStackFrame.AddrStack.Mode = AddrModeFlat;
820 #else
821   stackTrace->currentStackFrame.AddrPC.Offset = remoteContextRecord.Rip;
822   stackTrace->currentStackFrame.AddrPC.Mode = AddrModeFlat;
823   stackTrace->currentStackFrame.AddrFrame.Offset = remoteContextRecord.Rbp;
824   stackTrace->currentStackFrame.AddrFrame.Mode = AddrModeFlat;
825   stackTrace->currentStackFrame.AddrStack.Offset = remoteContextRecord.Rsp;
826   stackTrace->currentStackFrame.AddrStack.Mode = AddrModeFlat;
827 #endif
828 
829   stackTrace->contextRecord = &remoteContextRecord;
830 
831   // The biggest registers are the XMM registers (128 bits), so reserve enough space for them.
832   stackTrace->scratchSpace = VirtualAllocEx( stackTrace->process, NULL, 128 / 8, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
833 
834   getStackTrace( stackTrace, symbolPath, trace );
835   trace->fullStack = QString::fromWCharArray( stackTrace->message );
836 
837   return trace;
838 }
839 
840 #endif // Q_OS_WIN
841 
842 #ifdef Q_OS_LINUX
trace(unsigned int maxFrames)843 QVector<QgsStackTrace::StackLine> QgsStackTrace::trace( unsigned int maxFrames )
844 {
845   Q_UNUSED( maxFrames )
846   QgsStackLines stack;
847 #ifndef QGISDEBUG
848   return stack;
849 #endif
850 
851   // TODO Add linux stack trace support. Pull it from main.cpp
852   return stack;
853 }
854 #endif
855 
isQgisModule() const856 bool QgsStackTrace::StackLine::isQgisModule() const
857 {
858   return moduleName.contains( QLatin1String( "qgis" ), Qt::CaseInsensitive );
859 }
860 
isValid() const861 bool QgsStackTrace::StackLine::isValid() const
862 {
863   return !( fileName.contains( QLatin1String( "exe_common" ), Qt::CaseInsensitive ) ||
864             fileName.contains( QLatin1String( "unknown" ), Qt::CaseInsensitive ) ||
865             lineNumber.contains( QLatin1String( "unknown" ), Qt::CaseInsensitive ) );
866 
867 }
868 
869 ///@endcond
870