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