23 #include "precompiled.h"
43 #ifdef _DEBUG // (avoid "expression has no effect" warning in release builds)
47 flags |= _CRTDBG_ALLOC_MEM_DF;
48 flags |= _CRTDBG_LEAK_CHECK_DF;
50 flags |= _CRTDBG_CHECK_ALWAYS_DF;
51 flags |= _CRTDBG_DELAY_FREE_MEM_DF;
54 _CrtSetDbgFlag(flags);
58 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
59 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
73 ok = _CrtCheckMemory();
75 __except(EXCEPTION_EXECUTE_HANDLER)
92 #if !defined(NDEBUG) && ARCH_IA32 && 0
93 # define ENABLE_LEAK_INSTRUMENTATION 1
95 # define ENABLE_LEAK_INSTRUMENTATION 0
98 #if ENABLE_LEAK_INSTRUMENTATION
120 struct _CrtMemBlockHeader
122 struct _CrtMemBlockHeader* next;
123 struct _CrtMemBlockHeader* prev;
134 long allocationNumber;
141 if(prev && prev->next !=
this)
143 if(next && next->prev !=
this)
145 if((
unsigned)blockType > 4)
147 if(userDataSize > 1*
GiB)
149 if(allocationNumber == 0)
151 for(
int i = 0; i < 4; i++)
163 __except(EXCEPTION_EXECUTE_HANDLER)
172 static _CrtMemBlockHeader* HeaderFromData(
void* userData)
174 _CrtMemBlockHeader*
const header = ((_CrtMemBlockHeader*)userData)-1;
190 static _CrtMemBlockHeader* GetHeapListHead(
int operation,
void* userData,
bool& hasChanged)
192 static _CrtMemBlockHeader* s_heapListHead;
201 _CrtMemState
state = {0};
202 _CrtMemCheckpoint(&state);
203 s_heapListHead = state.pBlockHeader;
209 if(s_heapListHead->prev)
211 s_heapListHead = s_heapListHead->prev;
223 if(operation != _HOOK_ALLOC && userData == s_heapListHead+1)
225 s_heapListHead = s_heapListHead->next;
231 return s_heapListHead;
250 : m_address(0), m_length(0)
254 ModuleExtents(
const wchar_t* dllName)
256 HMODULE hModule = GetModuleHandleW(dllName);
257 PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((
u8*)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew);
258 m_address = (uintptr_t)hModule + ntHeaders->OptionalHeader.BaseOfCode;
259 MEMORY_BASIC_INFORMATION mbi = {0};
260 VirtualQuery((
void*)m_address, &mbi,
sizeof(mbi));
261 m_length = mbi.RegionSize;
264 uintptr_t Address()
const
274 bool Contains(uintptr_t address)
const
276 return (address - m_address) < m_length;
289 template<
typename T,
size_t maxItems>
295 m_arrayEnd = m_array;
300 if(m_arrayEnd == m_array+maxItems)
310 return std::find(m_array, const_cast<const T*>(m_arrayEnd), t) != m_arrayEnd;
313 void RemoveDuplicates()
315 std::sort(m_array, m_arrayEnd);
316 m_arrayEnd = std::unique(m_array, m_arrayEnd);
330 AddRuntimeLibraryToIgnoreList();
332 m_isRecordingKnownCallers =
true;
334 m_isRecordingKnownCallers =
false;
335 m_knownCallers.RemoveDuplicates();
338 Status NotifyOfCaller(uintptr_t pc)
340 if(!m_isRecordingKnownCallers)
344 if(pc == (uintptr_t)&CallerFilter::CallHeapFunctions)
349 m_knownCallers.Add(pc);
353 bool IsKnownCaller(uintptr_t pc)
const
355 for(
size_t i = 0; i < numModules; i++)
357 if(m_moduleIgnoreList[i].Contains(pc))
361 return m_knownCallers.Find(pc);
365 static const size_t numModules = 2;
367 void AddRuntimeLibraryToIgnoreList()
369 #if MSC_VERSION && _DLL // DLL runtime library
371 static const wchar_t* dllNameFormat = L
"msvc%c%d" L
".dll";
373 static const wchar_t* dllNameFormat = L
"msvc%c%d" L
"d" L
".dll";
377 for(
int i = 0; i < numModules; i++)
379 static const char modules[numModules] = {
'r',
'p' };
382 m_moduleIgnoreList[i] = ModuleExtents(dllName);
387 static void CallHeapFunctions()
390 void* p1 = malloc(1);
391 void* p2 = realloc(p1, 111);
407 ModuleExtents m_moduleIgnoreList[numModules];
412 bool m_isRecordingKnownCallers;
413 ArraySet<uintptr_t, 500> m_knownCallers;
426 static const size_t numQuantizedPcBits =
sizeof(uintptr_t)*CHAR_BIT - 2;
428 static uintptr_t Quantize(uintptr_t pc)
438 static uintptr_t Expand(uintptr_t pc)
444 static const size_t numEncodedLengthBits = 2;
445 static const size_t maxCallers = (
sizeof(
char*)+
sizeof(
int))*CHAR_BIT / (2+14);
447 static size_t NumBitsForEncodedLength(
size_t encodedLength)
449 static const size_t numBitsForEncodedLength[1u << numEncodedLengthBits] =
456 return numBitsForEncodedLength[encodedLength];
459 static size_t EncodedLength(uintptr_t quantizedOffset)
461 for(
size_t encodedLength = 0; encodedLength < 1u << numEncodedLengthBits; encodedLength++)
463 const size_t numBits = NumBitsForEncodedLength(encodedLength);
464 const uintptr_t maxValue = (1u << numBits)-1;
465 if(quantizedOffset <= maxValue)
466 return encodedLength;
474 static uintptr_t codeSegmentAddress;
475 static uintptr_t quantizedCodeSegmentAddress;
476 static uintptr_t quantizedCodeSegmentLength;
478 static void FindCodeSegment()
480 const wchar_t* dllName = 0;
481 ModuleExtents extents(dllName);
482 codeSegmentAddress = extents.Address();
483 quantizedCodeSegmentAddress = Quantize(codeSegmentAddress);
484 quantizedCodeSegmentLength = Quantize(extents.Length());
491 BitStream(
u8* storage,
size_t storageSize)
492 : m_remainderBits(0), m_numRemainderBits(0)
493 , m_pos(storage), m_bitsLeft((size_t)storageSize*8)
497 size_t BitsLeft()
const
502 void Write(
const size_t numOutputBits, uintptr_t outputValue)
505 wdbg_assert(outputValue < ((uintptr_t)1u << numOutputBits));
507 size_t outputBitsLeft = numOutputBits;
508 while(outputBitsLeft > 0)
510 const size_t numBits = std::min(outputBitsLeft,
size_t(8));
511 m_bitsLeft -= numBits;
515 const uintptr_t outputByte = outputValue & 0xFF;
517 outputBitsLeft -= numBits;
519 m_remainderBits |= outputByte << m_numRemainderBits;
520 m_numRemainderBits += numBits;
521 if(m_numRemainderBits >= 8)
523 const u8 remainderByte = (m_remainderBits & 0xFF);
524 m_remainderBits >>= 8;
525 m_numRemainderBits -= 8;
527 *m_pos++ = remainderByte;
534 const size_t partialBits = m_numRemainderBits % 8;
537 m_bitsLeft -= 8-partialBits;
538 m_numRemainderBits += 8-partialBits;
540 while(m_numRemainderBits)
542 const u8 remainderByte = (m_remainderBits & 0xFF);
543 *m_pos++ = remainderByte;
544 m_remainderBits >>= 8;
545 m_numRemainderBits -= 8;
556 uintptr_t
Read(
const size_t numInputBits)
560 uintptr_t inputValue = 0;
561 size_t inputBitsLeft = numInputBits;
562 while(inputBitsLeft > 0)
564 const size_t numBits = std::min(inputBitsLeft,
size_t(8));
565 m_bitsLeft -= numBits;
567 if(m_numRemainderBits < numBits)
569 const size_t inputByte = *m_pos++;
570 m_remainderBits |= inputByte << m_numRemainderBits;
571 m_numRemainderBits += 8;
574 const uintptr_t remainderByte = (m_remainderBits & ((1u << numBits)-1));
575 m_remainderBits >>= numBits;
576 m_numRemainderBits -= numBits;
577 inputValue |= remainderByte << (numInputBits-inputBitsLeft);
579 inputBitsLeft -= numBits;
586 uintptr_t m_remainderBits;
587 size_t m_numRemainderBits;
593 static void StashCallers(_CrtMemBlockHeader* header,
const uintptr_t* callers,
size_t numCallers)
596 uintptr_t quantizedPcSet[maxCallers];
597 std::transform(callers, callers+numCallers, quantizedPcSet, Quantize);
598 std::sort(quantizedPcSet, quantizedPcSet+numCallers);
599 uintptr_t*
const end = std::unique(quantizedPcSet, quantizedPcSet+numCallers);
600 const size_t quantizedPcSetSize = end-quantizedPcSet;
603 uintptr_t quantizedOffsets[maxCallers];
604 if(quantizedPcSet[0] >= quantizedCodeSegmentAddress)
605 quantizedOffsets[0] = quantizedPcSet[0] - quantizedCodeSegmentAddress;
608 quantizedOffsets[0] = quantizedPcSet[0];
611 wdbg_assert(quantizedOffsets[0] >= quantizedCodeSegmentLength);
613 for(
size_t i = 1; i < numCallers; i++)
614 quantizedOffsets[i] = quantizedPcSet[i] - quantizedPcSet[i-1];
617 BitStream bitStream((
u8*)&header->file,
sizeof(header->file)+
sizeof(header->line));
618 for(
size_t i = 0; i < quantizedPcSetSize; i++)
620 const uintptr_t quantizedOffset = quantizedOffsets[i];
621 const size_t encodedLength = EncodedLength(quantizedOffset);
622 const size_t numBits = NumBitsForEncodedLength(encodedLength);
623 if(bitStream.BitsLeft() < numEncodedLengthBits+numBits)
625 bitStream.Write(numEncodedLengthBits, encodedLength);
626 bitStream.Write(numBits, quantizedOffset);
633 static void RetrieveCallers(_CrtMemBlockHeader* header, uintptr_t* callers,
size_t& numCallers)
636 uintptr_t quantizedOffsets[maxCallers];
638 BitStream bitStream((
u8*)&header->file,
sizeof(header->file)+
sizeof(header->line));
641 if(bitStream.BitsLeft() < numEncodedLengthBits)
643 const size_t encodedLength = bitStream.Read(numEncodedLengthBits);
644 const size_t numBits = NumBitsForEncodedLength(encodedLength);
645 if(bitStream.BitsLeft() < numBits)
647 const uintptr_t quantizedOffset = bitStream.Read(numBits);
650 quantizedOffsets[numCallers++] = quantizedOffset;
657 if(quantizedOffsets[0] <= quantizedCodeSegmentLength)
658 callers[0] = Expand(quantizedOffsets[0] + quantizedCodeSegmentAddress);
660 callers[0] = Expand(quantizedOffsets[0]);
661 for(
size_t i = 1; i < numCallers; i++)
662 callers[i] = callers[i-1] + Expand(quantizedOffsets[i]);
681 std::fill(m_callers+m_numCallers, m_callers+maxCallers, 0);
684 const uintptr_t* Callers()
const
689 size_t NumCallers()
const
695 Status OnFrame(
const STACKFRAME64* frame)
697 const uintptr_t pc = frame->AddrPC.Offset;
703 Status ret = m_filter.NotifyOfCaller(pc);
710 if(m_numCallers >= maxCallers)
713 if(!m_filter.IsKnownCaller(pc))
714 m_callers[m_numCallers++] = pc;
718 static Status OnFrame_Trampoline(
const STACKFRAME64* frame, uintptr_t cbData)
720 CallStack* this_ = (CallStack*)cbData;
721 return this_->OnFrame(frame);
724 CallerFilter m_filter;
726 uintptr_t m_callers[maxCallers];
739 wdbg_assert(s_instance == 0 && s_previousHook == 0);
741 s_previousHook = _CrtSetAllocHook(Hook);
746 _CRT_ALLOC_HOOK removedHook = _CrtSetAllocHook(s_previousHook);
757 virtual void OnHeapOperation(
int operation,
void* userData,
size_t size,
long allocationNumber) = 0;
760 static int __cdecl Hook(
int operation,
void* userData,
size_t size,
int blockType,
long allocationNumber,
const unsigned char* file,
int line)
762 static bool busy =
false;
765 s_instance->OnHeapOperation(operation, userData, size, allocationNumber);
769 return s_previousHook(operation, userData, size, blockType, allocationNumber, file, line);
775 static AllocationHook* s_instance;
776 static _CRT_ALLOC_HOOK s_previousHook;
779 AllocationHook* AllocationHook::s_instance;
780 _CRT_ALLOC_HOOK AllocationHook::s_previousHook;
796 static intptr_t s_numAllocations;
800 return s_numAllocations;
803 class AllocationTracker :
public AllocationHook
807 : m_pendingAllocationNumber(0)
811 virtual void OnHeapOperation(
int operation,
void* userData,
size_t size,
long allocationNumber)
815 if(operation == _HOOK_ALLOC || operation == _HOOK_REALLOC)
819 _CrtMemBlockHeader* head = GetHeapListHead(operation, userData, hasChanged);
824 wdbg_assert(head->allocationNumber == m_pendingAllocationNumber);
828 StashCallers(head, m_pendingCallStack.Callers(), m_pendingCallStack.NumCallers());
832 m_pendingCallStack.Gather();
833 m_pendingAllocationNumber = allocationNumber;
837 long m_pendingAllocationNumber;
838 CallStack m_pendingCallStack;
844 static void PrintCallStack(
const uintptr_t* callers,
size_t numCallers)
846 if(!numCallers || callers[0] == 0)
852 wdbg_printf(L
"\n partial, unordered call stack:\n");
853 for(
size_t i = 0; i < numCallers; i++)
859 wdbg_printf(L
"(error %d resolving PC=%p) ", err, callers[i]);
866 static int __cdecl ReportHook(
int reportType,
wchar_t* message,
int*
out)
884 state = WaitingForDump;
888 if(!wcscmp(message, L
"Dumping objects ->\n"))
889 state = WaitingForBlock;
895 const wchar_t* addressString = wcsstr(message, L
"0x");
898 const uintptr_t address = wcstoul(addressString, 0, 0);
899 _CrtMemBlockHeader* header = HeaderFromData((
void*)address);
900 uintptr_t callers[maxCallers];
size_t numCallers;
901 RetrieveCallers(header, callers, numCallers);
902 PrintCallStack(callers, numCallers);
904 state = WaitingForBlock;
911 case WaitingForBlock:
912 if(message[0] ==
'{')
916 else if(wcschr(message,
'('))
939 #if ENABLE_LEAK_INSTRUMENTATION
940 static AllocationTracker* s_tracker;
945 #if ENABLE_LEAK_INSTRUMENTATION
952 int ret = _CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, ReportHook);
956 s_tracker =
new AllocationTracker;
966 #if ENABLE_LEAK_INSTRUMENTATION
Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT &context, const wchar_t *lastFuncToSkip)
Iterate over a call stack, invoking a callback for each frame encountered.
static const size_t DEBUG_SYMBOL_CHARS
Maximum number of characters (including null terminator) written to user's buffers by debug_ResolveSy...
static void out(const wchar_t *fmt,...)
intptr_t cpu_AtomicAdd(volatile intptr_t *location, intptr_t increment)
add a signed value to a variable without the possibility of interference from other threads/CPUs...
int swprintf_s(wchar_t *buf, size_t max_chars, const wchar_t *fmt,...) WPRINTF_ARGS(3)
const Status ALL_COMPLETE
LIB_API Status debug_CaptureContext(void *context)
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
void wdbg_heap_Enable(bool enable)
enable or disable manual and automatic heap validity checking.
#define WINIT_REGISTER_LATE_SHUTDOWN2(func)
static Status wdbg_heap_Shutdown()
static const size_t DEBUG_FILE_CHARS
#define SAFE_DELETE(p)
delete memory ensuing from new and set the pointer to zero (thus making double-frees safe / a no-op) ...
i64 Status
Error handling system.
#define T(string_literal)
void wdbg_printf(const wchar_t *fmt,...)
same as debug_printf except that some type conversions aren't supported (in particular, no floating point) and output is limited to 1024+1 characters.
intptr_t wdbg_heap_NumberOfAllocations()
void wdbg_heap_Validate()
check heap integrity.
static Status wdbg_heap_Init()
LIB_API Status debug_ResolveSymbol(void *ptr_of_interest, wchar_t *sym_name, wchar_t *file, int *line)
read and return symbol information for the given address.
#define wdbg_assert(expr)
similar to ENSURE but safe to use during critical init or while under the heap or dbghelp locks...
static float Length(const SVec3 v)
void Write(u64 reg, u64 value)
#define WINIT_REGISTER_EARLY_INIT2(func)