@@ -492,6 +492,16 @@ void crashHandlerSignalHandler(int signal, siginfo_t* info, void* ucontext) {
492
492
static std::string miniDumpDirectory = " C:\\ temp" ;
493
493
static std::mutex miniDumpLock;
494
494
495
+ // TODO - ATM if these are put inside the function MSVC complains that they
496
+ // are not captured in the lambda, but a lamba with captures cannot convert
497
+ // to a function pointer. Since these are constexpr is should not be necessary
498
+ // to capture them, and with C++20 enabled MSVC no longer complains.
499
+ // So once we have upgraded to C++20 these should be move into the function.
500
+ constexpr DWORD64 blockSize = 1024 ; // 1kb
501
+ constexpr DWORD64 maxStackAddrs = 2048 ;
502
+ constexpr DWORD maxNumAddrs = 160000 ;
503
+ constexpr DWORD numRegs = 16 ;
504
+
495
505
void createMiniDump (EXCEPTION_POINTERS* pointers) {
496
506
// we have to serialize calls to MiniDumpWriteDump
497
507
std::lock_guard<std::mutex> lock (miniDumpLock);
@@ -525,10 +535,106 @@ void createMiniDump(EXCEPTION_POINTERS* pointers) {
525
535
exceptionInfo.ThreadId = GetCurrentThreadId ();
526
536
exceptionInfo.ExceptionPointers = pointers;
527
537
exceptionInfo.ClientPointers = FALSE ;
538
+
539
+ // We try to gather some additional information from referenced memory
540
+ // In total we gather up to 16000 memory blocks of 1kb each.
541
+ // We consider only addresses that reference some memory block that can
542
+ // actually be read (-> !IsBadReadPtr).
543
+
544
+ // we want to have enough addresses to cover all 16 registers plus all indirections and all
545
+ // maxStackAddrs stack addresses
546
+ static_assert (maxNumAddrs > maxStackAddrs + numRegs * (blockSize / sizeof (void *)));
547
+
548
+ DWORD64 addrs[maxNumAddrs];
549
+ DWORD numAddrs = 0 ;
550
+
551
+ if (pointers) {
552
+ auto addAddr = [&addrs, &numAddrs](DWORD64 reg) {
553
+ auto base = reg & ~(blockSize - 1 );
554
+ if (base == 0 || IsBadReadPtr ((void *)base, blockSize) || numAddrs >= maxNumAddrs) {
555
+ return ;
556
+ }
557
+ for (DWORD i = 0 ; i < numAddrs; ++i) {
558
+ if (addrs[i] == base) {
559
+ return ;
560
+ }
561
+ }
562
+ addrs[numAddrs++] = base;
563
+ };
564
+
565
+ // we take the values of all general purpose registers
566
+ auto & ctx = *pointers->ContextRecord ;
567
+ addAddr (ctx.Rax );
568
+ addAddr (ctx.Rcx );
569
+ addAddr (ctx.Rdx );
570
+ addAddr (ctx.Rbx );
571
+ addAddr (ctx.Rsp );
572
+ addAddr (ctx.Rbp );
573
+ addAddr (ctx.Rsi );
574
+ addAddr (ctx.Rdi );
575
+ addAddr (ctx.R8 );
576
+ addAddr (ctx.R9 );
577
+ addAddr (ctx.R10 );
578
+ addAddr (ctx.R11 );
579
+ addAddr (ctx.R12 );
580
+ addAddr (ctx.R13 );
581
+ addAddr (ctx.R14 );
582
+ addAddr (ctx.R15 );
583
+ TRI_ASSERT (numAddrs <= numRegs);
584
+
585
+ // Take the first 2048 pointers from the stack and add them to the address list.
586
+ // We use the thread information block (TIB) to get the base address of the stack
587
+ // to handle the (unlikely) cases where the stack has less than 2048 items.
588
+ auto * tib = (PNT_TIB)NtCurrentTeb ();
589
+ auto numStackAddrs = std::min (((DWORD64)tib->StackBase - ctx.Rsp ) / sizeof (void *), maxStackAddrs);
590
+ void ** p = (void **)ctx.Rsp ;
591
+ for (DWORD64 i = 0 ; i < numStackAddrs; ++i) {
592
+ addAddr ((DWORD64)p[i]);
593
+ }
594
+
595
+ // Now we take all the addresses we gathered so far and add all indirect addresses,
596
+ // i.e., we take each 1024 byte block and add all 128 potential addresses from that
597
+ // block (as long as we don't exceed our limit).
598
+ // That way can follow at least one level of indirection when analyzing the dump.
599
+ DWORD idx = numAddrs;
600
+ do {
601
+ --idx;
602
+ void ** p = (void **)addrs[idx];
603
+ for (DWORD i = 0 ; i < blockSize / sizeof (void *) && numAddrs < maxNumAddrs; ++i) {
604
+ auto base = (DWORD64)p[i] & ~(blockSize - 1 );
605
+ if (base != 0 ) {
606
+ addrs[numAddrs++] = base;
607
+ }
608
+ }
609
+ } while (idx != 0 && numAddrs < maxNumAddrs);
610
+ }
611
+
612
+ struct CallbackParam {
613
+ DWORD64* addrs;
614
+ DWORD idx;
615
+ DWORD numAddrs;
616
+ };
617
+ CallbackParam param{addrs, 0 , numAddrs};
618
+
619
+ auto callback = [](PVOID callbackParam, PMINIDUMP_CALLBACK_INPUT callbackInput, PMINIDUMP_CALLBACK_OUTPUT callbackOutput) -> BOOL {
620
+ auto * param = static_cast <CallbackParam*>(callbackParam);
621
+ if (callbackInput->CallbackType == MemoryCallback && param->idx < param->numAddrs ) {
622
+ callbackOutput->MemoryBase = param->addrs [param->idx ];
623
+ callbackOutput->MemorySize = blockSize;
624
+ ++param->idx ;
625
+ }
626
+ return TRUE ;
627
+ };
628
+
629
+ MINIDUMP_CALLBACK_INFORMATION callbackInfo{
630
+ callback,
631
+ ¶m
632
+ };
528
633
529
634
if (MiniDumpWriteDump (GetCurrentProcess (), GetCurrentProcessId (), hFile,
530
- MINIDUMP_TYPE (MiniDumpNormal | MiniDumpWithProcessThreadData | MiniDumpWithDataSegs),
531
- pointers ? &exceptionInfo : nullptr , nullptr , nullptr )) {
635
+ MINIDUMP_TYPE (MiniDumpNormal | MiniDumpWithProcessThreadData | MiniDumpWithDataSegs | MiniDumpIgnoreInaccessibleMemory),
636
+ pointers ? &exceptionInfo : nullptr , nullptr ,
637
+ pointers ? &callbackInfo : nullptr )) {
532
638
char * p = &buffer[0 ];
533
639
appendNullTerminatedString (" Wrote minidump: " , p);
534
640
appendNullTerminatedString (filename, p);
0 commit comments