From fd4fd43ce307a3dcef542bdd39253ff860d8f58f Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Sun, 29 Dec 2024 11:02:17 -0800 Subject: [PATCH] Improved hotswapping with extension modules --- BeefySysLib/BeefySysLib.vcxproj | 1 + BeefySysLib/BeefySysLib.vcxproj.filters | 3 + BeefySysLib/util/Array.h | 29 +- BeefySysLib/util/BumpAllocator.h | 28 +- BeefySysLib/util/Deque.h | 8 +- BeefySysLib/util/Dictionary.h | 2 +- BeefySysLib/util/HashSet.h | 2 +- BeefySysLib/util/MultiDictionary.h | 523 +++++++++++++++++++++++ BeefySysLib/util/MultiHashSet.h | 20 +- BeefySysLib/util/SizedArray.h | 18 +- IDEHelper/Compiler/BfModule.h | 4 +- IDEHelper/Compiler/BfResolvedTypeUtils.h | 8 +- IDEHelper/Compiler/BfSystem.h | 2 +- IDEHelper/DbgModule.cpp | 396 ++++++++++------- IDEHelper/DbgModule.h | 6 +- IDEHelper/DebugTarget.cpp | 7 +- IDEHelper/DebugTarget.h | 1 + IDEHelper/IDEHelper.vcxproj.user | 2 +- IDEHelper/WinDebugger.cpp | 8 + 19 files changed, 836 insertions(+), 232 deletions(-) create mode 100644 BeefySysLib/util/MultiDictionary.h diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj index 66e34fd1..e201b14e 100644 --- a/BeefySysLib/BeefySysLib.vcxproj +++ b/BeefySysLib/BeefySysLib.vcxproj @@ -2184,6 +2184,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters index 64a87168..85fb8ba1 100644 --- a/BeefySysLib/BeefySysLib.vcxproj.filters +++ b/BeefySysLib/BeefySysLib.vcxproj.filters @@ -1132,6 +1132,9 @@ src\third_party\putty + + src\util + diff --git a/BeefySysLib/util/Array.h b/BeefySysLib/util/Array.h index 6781e333..7ef9c577 100644 --- a/BeefySysLib/util/Array.h +++ b/BeefySysLib/util/Array.h @@ -4,16 +4,16 @@ NS_BF_BEGIN; -template class AllocatorCLib { public: + template T* allocate(intptr count) { return (T*)malloc(sizeof(T) * count); } - void deallocate(T* ptr) + void deallocate(void* ptr) { free(ptr); } @@ -27,9 +27,14 @@ public: { free(ptr); } + + bool deallocateAll() + { + return false; + } }; -template > +template class ArrayBase : public TAlloc { public: @@ -445,7 +450,7 @@ protected: void SetBufferSize(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -625,7 +630,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -653,7 +658,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -682,7 +687,7 @@ public: { intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -764,7 +769,7 @@ public: protected: void SetBufferSize(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -928,7 +933,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -956,7 +961,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -984,7 +989,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -1102,7 +1107,7 @@ public: } }; -template > +template class Array : public ArrayImpl::value> { public: diff --git a/BeefySysLib/util/BumpAllocator.h b/BeefySysLib/util/BumpAllocator.h index 5b2037b3..9a136fda 100644 --- a/BeefySysLib/util/BumpAllocator.h +++ b/BeefySysLib/util/BumpAllocator.h @@ -255,23 +255,24 @@ class BumpAllocator : public BumpAllocatorT<0x2000> }; -template -class AllocatorBump +template +class AllocatorBumpT { public: BumpAllocatorT* mAlloc; - AllocatorBump() + AllocatorBumpT() { mAlloc = NULL; } + template T* allocate(intptr count) { return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T)); } - void deallocate(T* ptr) + void deallocate(void* ptr) { } @@ -286,26 +287,9 @@ public: } }; -template -class RawAllocatorBump +class AllocatorBump : public AllocatorBumpT<0x2000> { -public: - BumpAllocatorT* mAlloc; - RawAllocatorBump() - { - mAlloc = NULL; - } - - void* rawAllocate(intptr size) - { - return mAlloc->AllocBytes(size, 16); - } - - void rawDeallocate(void* ptr) - { - - } }; NS_BF_END diff --git a/BeefySysLib/util/Deque.h b/BeefySysLib/util/Deque.h index 096bb671..af436ca2 100644 --- a/BeefySysLib/util/Deque.h +++ b/BeefySysLib/util/Deque.h @@ -8,7 +8,7 @@ NS_BF_BEGIN; #define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize]) #define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize]) -template > +template class DequeBase : public TAlloc { public: @@ -370,7 +370,7 @@ protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -707,7 +707,7 @@ class DequeImpl : public DequeBase protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -1043,7 +1043,7 @@ public: } }; -template > +template class Deque : public DequeImpl::value> { public: diff --git a/BeefySysLib/util/Dictionary.h b/BeefySysLib/util/Dictionary.h index 11912fd7..d9f5341e 100644 --- a/BeefySysLib/util/Dictionary.h +++ b/BeefySysLib/util/Dictionary.h @@ -9,7 +9,7 @@ NS_BF_BEGIN; #ifdef NEW_DICTIONAY -template > +template class Dictionary : public TAlloc { public: diff --git a/BeefySysLib/util/HashSet.h b/BeefySysLib/util/HashSet.h index 725c2876..d72ff78d 100644 --- a/BeefySysLib/util/HashSet.h +++ b/BeefySysLib/util/HashSet.h @@ -5,7 +5,7 @@ NS_BF_BEGIN; -template > +template class HashSet : public TAlloc { public: diff --git a/BeefySysLib/util/MultiDictionary.h b/BeefySysLib/util/MultiDictionary.h new file mode 100644 index 00000000..a960f110 --- /dev/null +++ b/BeefySysLib/util/MultiDictionary.h @@ -0,0 +1,523 @@ +#pragma once + +#include "../Common.h" +#include "Array.h" + +NS_BF_BEGIN; + +struct MultiDictionaryFuncs : AllocatorCLib +{ + template + size_t GetHash(const T& value) + { + return BeefHash()(value); + } + + template + bool Matches(const T& lhs, const T& rhs) + { + return lhs == rhs; + } +}; + +template +class MultiDictionary : public TFuncs +{ +public: + struct Entry + { + TKey mKey; + TValue mValue; + int mNext; + int mHashCode; + }; + + struct EntryRef + { + public: + MultiDictionary* mSet; + int mIndex; + + public: + EntryRef() + { + mSet = NULL; + mIndex = -1; + } + + EntryRef(MultiDictionary* set, int index) + { + mSet = set; + mIndex = index; + } + + Entry* operator*() + { + return &this->mSet->mEntries[this->mIndex]; + } + + Entry* operator->() + { + return &this->mSet->mEntries[this->mIndex]; + } + + operator bool() const + { + return this->mIndex != -1; + } + }; + + struct Iterator + { + public: + MultiDictionary* mSet; + int mCurEntry; + int mCurBucket; + + public: + Iterator(MultiDictionary* set) + { + this->mSet = set; + this->mCurBucket = 0; + this->mCurEntry = -1; + } + + Iterator& operator++() + { + if (this->mCurEntry != -1) + { + this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext; + if (this->mCurEntry != -1) + return *this; + this->mCurBucket++; + } + + if (mSet->mHashHeads == NULL) + { + this->mCurBucket = this->mSet->mHashSize; + return *this; // At end + } + + while (this->mCurBucket < mSet->mHashSize) + { + this->mCurEntry = this->mSet->mHashHeads[mCurBucket]; + if (this->mCurEntry != -1) + return *this; + this->mCurBucket++; + } + + return *this; // At end + } + + TKey GetKey() + { + return this->mSet->mEntries[this->mCurEntry].mKey; + } + + TValue GetValue() + { + return this->mSet->mEntries[this->mCurEntry].mValue; + } + + bool operator!=(const Iterator& itr) const + { + return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket)); + } + + operator bool() const + { + return this->mCurEntry != -1; + } + + void MoveToNextHashMatch() + { + int wantHash = this->mSet->mEntries[this->mCurEntry].mHashCode; + do + { + this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext; + } while ((this->mCurEntry != -1) && (this->mSet->mEntries[this->mCurEntry].mHashCode != wantHash)); + } + }; + +protected: + + int GetPrimeish(int min) + { + // This is a minimal effort to help address-aligned dataa + return (min | 1); + } + + int ExpandSize(int oldSize) + { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when mAllocSize overflowed thanks to the (uint) cast + /*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + }*/ + + return GetPrimeish(newSize); + } + + void ResizeEntries() + { + ResizeEntries(ExpandSize(mCount)); + } + + void ResizeEntries(int newSize) + { + BF_ASSERT(newSize >= mAllocSize); + Entry* newEntries = TFuncs::allocate(newSize); + + for (int i = 0; i < mCount; i++) + { + auto& newEntry = newEntries[i]; + auto& oldEntry = mEntries[i]; + newEntry.mHashCode = oldEntry.mHashCode; + newEntry.mNext = oldEntry.mNext; + new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey)); + new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue)); + } + for (int i = mCount; i < newSize; i++) + { + newEntries[i].mHashCode = -1; + } + + TFuncs::deallocate(mEntries); + + mEntries = newEntries; + mAllocSize = (int)newSize; + } + + void FreeIdx(int entryIdx) + { + this->mEntries[entryIdx].mNext = this->mFreeList; + this->mFreeList = entryIdx; + this->mFreeCount++; + } + + int AllocEntry() + { + int index; + if (this->mFreeCount > 0) + { + index = this->mFreeList; + this->mFreeList = this->mEntries[index].mNext; + this->mFreeCount--; + } + else + { + if (this->mCount == this->mAllocSize) + ResizeEntries(); + index = mCount; + this->mCount++; + } + return index; + } + +public: + int* mHashHeads; + int mAllocSize; + Entry* mEntries; + int mFreeList; + int mFreeCount; + + static const int cDefaultHashSize = 17; + int mHashSize; + int mCount; + + MultiDictionary() + { + this->mHashHeads = NULL; + this->mHashSize = cDefaultHashSize; + this->mEntries = NULL; + this->mAllocSize = 0; + this->mCount = 0; + this->mFreeList = -1; + this->mFreeCount = 0; + } + + ~MultiDictionary() + { + this->Clear(); + } + + void EnsureFreeCount(int wantFreeCount) + { + int freeCount = mFreeCount + (mAllocSize - mCount); + if (freeCount >= wantFreeCount) + return; + ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount)); + } + + int GetCount() const + { + return mCount - mFreeCount; + } + + int size() const + { + return mCount - mFreeCount; + } + + EntryRef AddRaw(int hash) + { + if (this->mHashHeads == NULL) + { + this->mHashHeads = TFuncs::allocate(mHashSize); + memset(this->mHashHeads, -1, sizeof(int) * mHashSize); + } + + int index = AllocEntry(); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int headEntry = this->mHashHeads[hashIdx]; + + Entry* newEntry = &mEntries[index]; + newEntry->mValue = T(); + newEntry->mNext = headEntry; + newEntry->mHashCode = hash; + + mHashHeads[hashIdx] = index; + + return EntryRef(this, index); + } + + void Add(TKey key, TValue value) + { + if (this->mHashHeads == NULL) + { + this->mHashHeads = TFuncs::allocate(mHashSize); + memset(this->mHashHeads, -1, sizeof(int) * mHashSize); + } + + int index = AllocEntry(); + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int headEntry = this->mHashHeads[hashIdx]; + + Entry* newEntry = &mEntries[index]; + newEntry->mKey = key; + newEntry->mValue = value; + newEntry->mNext = headEntry; + newEntry->mHashCode = hash; + + mHashHeads[hashIdx] = index; + } + + void AddAfter(TKey key, TValue value, Entry* afterEntry) + { + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + BF_ASSERT(hash == afterEntry->mHashCode); + + int index = AllocEntry(); + Entry* newEntry = &mEntries[index]; + newEntry->mKey = key; + newEntry->mValue = value; + newEntry->mNext = afterEntry->mNext; + newEntry->mHashCode = hash; + + afterEntry->mNext = index; + } + + void Rehash(int newHashSize) + { + auto newHashHeads = TFuncs::allocate(newHashSize); + memset(newHashHeads, -1, sizeof(int) * newHashSize); + + if (mHashHeads != NULL) + { + SizedArray entryList; + for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++) + { + int checkEntryIdx = mHashHeads[hashIdx]; + if (checkEntryIdx != -1) + { + // We want to keep elements with equal hashes in their insert order so we need to + // iterate through the linked list in reverse + entryList.Clear(); + + while (checkEntryIdx != -1) + { + entryList.Add(checkEntryIdx); + checkEntryIdx = mEntries[checkEntryIdx].mNext; + } + + for (int i = (int)entryList.mSize - 1; i >= 0; i--) + { + int checkEntryIdx = entryList[i]; + auto checkEntry = &mEntries[checkEntryIdx]; + int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize; + checkEntry->mNext = newHashHeads[newHashIdx]; + newHashHeads[newHashIdx] = checkEntryIdx; + } + } + } + + TFuncs::deallocate(mHashHeads); + } + mHashHeads = newHashHeads; + mHashSize = newHashSize; + } + + void CheckRehash() + { + // Make the lookup load reasonable + if (mHashSize < mCount) + { + this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1); + } + } + + template + bool TryGet(const TKey& key, TKey* outKey, TValue* outValue) + { + if (mHashHeads == NULL) + return false; + + this->CheckRehash(); + + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int checkEntryIdx = this->mHashHeads[hashIdx]; + while (checkEntryIdx != -1) + { + Entry* checkEntry = &mEntries[checkEntryIdx]; + if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey))) + { + if (outKey != NULL) + *outKey = checkEntry->mKey; + if (outValue != NULL) + *outValue = checkEntry->mValue; + return true; + } + checkEntryIdx = checkEntry->mNext; + } + return false; + } + + template + Iterator TryGet(const TKey& key) + { + if (mHashHeads == NULL) + return end(); + + this->CheckRehash(); + + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int checkEntryIdx = this->mHashHeads[hashIdx]; + while (checkEntryIdx != -1) + { + auto checkEntry = &this->mEntries[checkEntryIdx]; + if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey))) + { + Iterator itr(this); + itr.mCurEntry = checkEntryIdx; + itr.mCurBucket = hashIdx; + return itr; + } + checkEntryIdx = checkEntry->mNext; + } + + return end(); + } + + template + bool Remove(const TKey& key) + { + if (mHashHeads == NULL) + return false; + + this->CheckRehash(); + + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + + int* srcCheckEntryPtr = &this->mHashHeads[hashIdx]; + int checkEntryIdx = *srcCheckEntryPtr; + while (checkEntryIdx != -1) + { + auto checkEntry = &mEntries[checkEntryIdx]; + if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey))) + { + *srcCheckEntryPtr = checkEntry->mNext; + FreeIdx(checkEntryIdx); + return true; + } + srcCheckEntryPtr = &checkEntry->mNext; + checkEntryIdx = checkEntry->mNext; + } + return false; + } + + Iterator Erase(const Iterator& itr) + { + Iterator next = itr; + ++next; + + bool found = false; + + auto entryIdx = itr.mCurEntry; + auto entry = &mEntries[entryIdx]; + int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize; + + int* srcCheckEntryPtr = &this->mHashHeads[hashIdx]; + int checkEntryIdx = *srcCheckEntryPtr; + while (checkEntryIdx != -1) + { + auto checkEntry = &mEntries[checkEntryIdx]; + if (checkEntryIdx == itr.mCurEntry) + { + *srcCheckEntryPtr = checkEntry->mNext; + found = true; + } + srcCheckEntryPtr = &checkEntry->mNext; + checkEntryIdx = checkEntry->mNext; + } + + BF_ASSERT(found); + FreeIdx(entryIdx); + + return next; + } + + void Clear() + { + if (!TFuncs::deallocateAll()) + { + auto itr = begin(); + auto endItr = end(); + while (itr != endItr) + { + auto entry = itr.mCurEntry; + ++itr; + FreeIdx(entry); + } + TFuncs::deallocate(this->mHashHeads); + TFuncs::deallocate(this->mEntries); + } + + this->mHashSize = cDefaultHashSize; + this->mHashHeads = NULL; + this->mEntries = NULL; + this->mCount = 0; + } + + Iterator begin() + { + return ++Iterator(this); + } + + Iterator end() + { + Iterator itr(this); + itr.mCurBucket = this->mHashSize; + return itr; + } +}; + +NS_BF_END; \ No newline at end of file diff --git a/BeefySysLib/util/MultiHashSet.h b/BeefySysLib/util/MultiHashSet.h index 79b04b11..8e6cc16d 100644 --- a/BeefySysLib/util/MultiHashSet.h +++ b/BeefySysLib/util/MultiHashSet.h @@ -30,7 +30,7 @@ struct MultiHashSetFuncs } }; -template > +template class MultiHashSet : public TFuncs { public: @@ -180,7 +180,7 @@ protected: void ResizeEntries(int newSize) { BF_ASSERT(newSize >= mAllocSize); - Entry* newEntries = (Entry*)TFuncs::Allocate(sizeof(Entry) * newSize, alignof(Entry)); + Entry* newEntries = TFuncs::allocate(newSize); for (int i = 0; i < mCount; i++) { @@ -195,7 +195,7 @@ protected: newEntries[i].mHashCode = -1; } - TFuncs::Deallocate(mEntries); + TFuncs::deallocate(mEntries); mEntries = newEntries; mAllocSize = (int)newSize; @@ -276,7 +276,7 @@ public: { if (this->mHashHeads == NULL) { - this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int)); + this->mHashHeads = TFuncs::allocate(mHashSize); memset(this->mHashHeads, -1, sizeof(int) * mHashSize); } @@ -298,7 +298,7 @@ public: { if (this->mHashHeads == NULL) { - this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int)); + this->mHashHeads = TFuncs::allocate(mHashSize); memset(this->mHashHeads, -1, sizeof(int) * mHashSize); } @@ -332,7 +332,7 @@ public: void Rehash(int newHashSize) { - auto newHashHeads = (int*)TFuncs::Allocate(sizeof(int) * newHashSize, alignof(int)); + auto newHashHeads = TFuncs::allocate(newHashSize); memset(newHashHeads, -1, sizeof(int) * newHashSize); if (mHashHeads != NULL) @@ -364,7 +364,7 @@ public: } } - TFuncs::Deallocate(mHashHeads); + TFuncs::deallocate(mHashHeads); } mHashHeads = newHashHeads; mHashSize = newHashSize; @@ -492,7 +492,7 @@ public: void Clear() { - if (!TFuncs::DeallocateAll()) + if (!TFuncs::deallocateAll()) { auto itr = begin(); auto endItr = end(); @@ -502,8 +502,8 @@ public: ++itr; FreeIdx(entry); } - TFuncs::Deallocate(this->mHashHeads); - TFuncs::Deallocate(this->mEntries); + TFuncs::deallocate(this->mHashHeads); + TFuncs::deallocate(this->mEntries); } this->mHashSize = cDefaultHashSize; diff --git a/BeefySysLib/util/SizedArray.h b/BeefySysLib/util/SizedArray.h index 51071e59..772635dc 100644 --- a/BeefySysLib/util/SizedArray.h +++ b/BeefySysLib/util/SizedArray.h @@ -6,7 +6,7 @@ NS_BF_BEGIN; -template > +template class SizedArrayBase : protected TAlloc { public: @@ -378,7 +378,7 @@ protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -533,7 +533,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -562,7 +562,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -638,7 +638,7 @@ public: protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -761,7 +761,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -790,7 +790,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -854,14 +854,14 @@ public: } }; -template > +template class SizedArrayImpl : public SizedArrayBaseT::value> { public: typedef SizedArrayBaseT::value> _Base; }; -template > +template class SizedArray : public SizedArrayImpl { public: diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 374564dd..8e96ec9a 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1063,8 +1063,8 @@ public: BfIRBlock mIRHeadBlock; BfIRBlock mIRInitBlock; BfIRBlock mIREntryBlock; - Array > mLocals; - HashSet > mLocalVarSet; + Array mLocals; + HashSet mLocalVarSet; Array mLocalMethods; Dictionary mLocalMethodMap; Dictionary mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index ae9e8494..bd9f85e8 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -2690,11 +2690,7 @@ public: void ReportMemory(MemReporter* memReporter); }; -class BfResolvedTypeSetFuncs : public MultiHashSetFuncs -{ -}; - -class BfResolvedTypeSet : public MultiHashSet +class BfResolvedTypeSet : public MultiHashSet { public: enum BfHashFlags @@ -2749,7 +2745,7 @@ public: BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL); }; - class Iterator : public MultiHashSet::Iterator + class Iterator : public MultiHashSet::Iterator { public: Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set) diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index d08c1b81..c3c4fa9c 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -1254,7 +1254,7 @@ public: bool HasCustomAttributes(); }; -struct BfTypeDefMapFuncs : public MultiHashSetFuncs +struct BfTypeDefMapFuncs : public AllocatorCLib { int GetHash(BfTypeDef* typeDef) { diff --git a/IDEHelper/DbgModule.cpp b/IDEHelper/DbgModule.cpp index 90334231..68f6b699 100644 --- a/IDEHelper/DbgModule.cpp +++ b/IDEHelper/DbgModule.cpp @@ -21,6 +21,7 @@ #include "Compiler/BfDemangler.h" #include "BeefySysLib/util/Hash.h" #include "BeefySysLib/util/BeefPerf.h" +#include "BeefySysLib/util/MultiDictionary.h" #include "DbgSymSrv.h" #include "MiniDumpDebugger.h" @@ -5173,8 +5174,7 @@ void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolved stream->Read(&coffReloc, sizeof(COFFRelocation)); PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18]; - //const char* symName = mSymbolData[coffReloc.mSymbolTableIndex]; - + bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC; if (symInfo->mNameOfs[0] != 0) @@ -5313,27 +5313,20 @@ void DbgModule::CommitHotTargetSections() } } -void DbgModule::HotReplaceType(DbgType* newType) +bool DbgModule::HasHotReplacedMethods(DbgType* type) +{ + for (auto newMethod : type->mMethodList) + { + if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize)) + return true; + } + return false; +} + +void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType) { auto linkedModule = GetLinkedModule(); - newType->PopulateType(); - DbgType* primaryType = linkedModule->GetPrimaryType(newType); - if (primaryType == newType) - { - // There was no previous type - BF_ASSERT(primaryType->mHotNewType == NULL); - return; - } - - if (primaryType->mHotNewType != newType) - { - // We have already pulled in the new data from a previous new type - BF_ASSERT(primaryType->mHotNewType == NULL); - return; - } - primaryType->mHotNewType = NULL; - primaryType->PopulateType(); linkedModule->ParseGlobalsData(); linkedModule->ParseSymbolData(); @@ -5354,183 +5347,264 @@ void DbgModule::HotReplaceType(DbgType* newType) // Now actually remove the linedata from the defining module HashSet checkedFiles; - for (auto method : primaryType->mMethodList) - { - //method->mWasModuleHotReplaced = true; - method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned + //for (auto method : primaryType->mMethodList) - if (method->mLineInfo == NULL) - continue; + auto _RemoveLineInfo = [&](DbgSubprogram* method) + { + //method->mWasModuleHotReplaced = true; + method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned - //FIXME: Hot replacing lines - DbgSrcFile* lastSrcFile = NULL; - checkedFiles.Clear(); + if (method->mLineInfo == NULL) + return; - int prevCtx = -1; - auto inlineRoot = method->GetRootInlineParent(); - for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++) - { - auto& lineData = method->mLineInfo->mLines[lineIdx]; - if (lineData.mCtxIdx != prevCtx) + //FIXME: Hot replacing lines + DbgSrcFile* lastSrcFile = NULL; + checkedFiles.Clear(); + + int prevCtx = -1; + auto inlineRoot = method->GetRootInlineParent(); + for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++) { - auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx]; - auto srcFile = ctxInfo.mSrcFile; - prevCtx = lineData.mCtxIdx; - if (srcFile != lastSrcFile) + auto& lineData = method->mLineInfo->mLines[lineIdx]; + if (lineData.mCtxIdx != prevCtx) { - if (checkedFiles.Add(srcFile)) + auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx]; + auto srcFile = ctxInfo.mSrcFile; + prevCtx = lineData.mCtxIdx; + if (srcFile != lastSrcFile) { - // Remove linedata for old type - // These go into a hot-replaced list so we can still bind to them -- that is necessary because - // we may still have old versions of this method running (and may forever, if its in a loop on some thread) - // since we only patch entry points - //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true); + if (checkedFiles.Add(srcFile)) + { + // Remove linedata for old type + // These go into a hot-replaced list so we can still bind to them -- that is necessary because + // we may still have old versions of this method running (and may forever, if its in a loop on some thread) + // since we only patch entry points + //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true); - //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true); - srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true); + //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true); + srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true); + } + + lastSrcFile = srcFile; } - - lastSrcFile = srcFile; } } - } - } + }; //DbgType* primaryType = newType->GetPrimaryType(); - // We need to keep a persistent list of hot replaced methods so we can set hot jumps - // in old methods that may still be on the callstack. These entries get removed when - // we unload unused hot files in - while (!primaryType->mMethodList.IsEmpty()) + MultiDictionary oldProgramMap; + auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod) + { + int hotIdx = oldMethod->mCompileUnit->mDbgModule->mHotIdx; + if ((hotIdx > 0) && (hotIdx < mDebugTarget->mVDataHotIdx)) + { + // Too old + return; + } + + oldMethod->PopulateSubprogram(); + if (oldMethod->mBlock.IsEmpty()) + return; + auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC); + if (symInfo != NULL) + { + oldProgramMap.Add(StringView(symInfo->mName), oldMethod); + } + }; + + if (newType != primaryType) { - auto method = primaryType->mMethodList.PopFront(); - - method->PopulateSubprogram(); - - primaryType->mHotReplacedMethodList.PushFront(method); - mHotPrimaryTypes.Add(primaryType); + // We need to keep a persistent list of hot replaced methods so we can set hot jumps + // in old methods that may still be on the callstack. These entries get removed when + // we unload unused hot files in + while (!primaryType->mMethodList.IsEmpty()) + { + auto method = primaryType->mMethodList.PopFront(); + method->PopulateSubprogram(); + primaryType->mHotReplacedMethodList.PushFront(method); + mHotPrimaryTypes.Add(primaryType); + } } - - Dictionary oldProgramMap; + for (auto oldMethod : primaryType->mHotReplacedMethodList) { - oldMethod->PopulateSubprogram(); - if (oldMethod->mBlock.IsEmpty()) - continue; - auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC); - if (symInfo != NULL) - { - oldProgramMap.TryAdd(symInfo->mName, oldMethod); - } + _AddToHotReplacedMethodList(oldMethod); } bool setHotJumpFailed = false; - while (!newType->mMethodList.IsEmpty()) - { - DbgSubprogram* newMethod = newType->mMethodList.PopFront(); - if (!newMethod->mBlock.IsEmpty()) + + auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod) { - BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC); + bool doHotJump = false; - newMethod->PopulateSubprogram(); - - auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC); - if (symInfo != NULL) + if (oldMethod->Equals(newMethod)) { - DbgSubprogram* oldMethod = NULL; - if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod)) + doHotJump = true; + } + else + { + // When mangles match but the actual signatures don't match, that can mean that the call signature was changed + // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed. + // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard + // fail if the old code gets called to avoid confusion of "why aren't my changes working?" + + // If we removed captures then we can still do the hot jump. Otherwise we have to fail... + doHotJump = false; + if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) && + (oldMethod->mHasThis) && (newMethod->mHasThis)) { - bool doHotJump = false; + auto oldParam = oldMethod->mParams.front(); + auto newParam = newMethod->mParams.front(); - if (oldMethod->Equals(newMethod)) + if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer())) { - doHotJump = true; - } - else - { - // When mangles match but the actual signatures don't match, that can mean that the call signature was changed - // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed. - // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard - // fail if the old code gets called to avoid confusion of "why aren't my changes working?" - - // If we removed captures then we can still do the hot jump. Otherwise we have to fail... - doHotJump = false; - if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) && - (oldMethod->mHasThis) && (newMethod->mHasThis)) + auto oldType = oldParam->mType->mTypeParam->GetPrimaryType(); + oldType->PopulateType(); + auto newType = newParam->mType->mTypeParam->GetPrimaryType(); + newType->PopulateType(); + if ((oldType->IsStruct()) && (newType->IsStruct())) { - auto oldParam = oldMethod->mParams.front(); - auto newParam = newMethod->mParams.front(); + bool wasMatch = true; - if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer())) + auto oldMember = oldType->mMemberList.front(); + auto newMember = newType->mMemberList.front(); + while (newMember != NULL) { - auto oldType = oldParam->mType->mTypeParam->GetPrimaryType(); - oldType->PopulateType(); - auto newType = newParam->mType->mTypeParam->GetPrimaryType(); - newType->PopulateType(); - if ((oldType->IsStruct()) && (newType->IsStruct())) + if (oldMember == NULL) { - bool wasMatch = true; - - auto oldMember = oldType->mMemberList.front(); - auto newMember = newType->mMemberList.front(); - while (newMember != NULL) - { - if (oldMember == NULL) - { - wasMatch = false; - break; - } - - if ((oldMember->mName == NULL) || (newMember->mName == NULL)) - { - wasMatch = false; - break; - } - - if (strcmp(oldMember->mName, newMember->mName) != 0) - { - wasMatch = false; - break; - } - - if (!oldMember->mType->Equals(newMember->mType)) - { - wasMatch = false; - break; - } - - oldMember = oldMember->mNext; - newMember = newMember->mNext; - } - - if (wasMatch) - doHotJump = true; + wasMatch = false; + break; } + + if ((oldMember->mName == NULL) || (newMember->mName == NULL)) + { + wasMatch = false; + break; + } + + if (strcmp(oldMember->mName, newMember->mName) != 0) + { + wasMatch = false; + break; + } + + if (!oldMember->mType->Equals(newMember->mType)) + { + wasMatch = false; + break; + } + + oldMember = oldMember->mNext; + newMember = newMember->mNext; } - if (!doHotJump) - { - mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC); - oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid; - } + if (wasMatch) + doHotJump = true; } } - if (doHotJump) + if (!doHotJump) { - if (!setHotJumpFailed) - { - if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC))) - setHotJumpFailed = true; - } - oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced; + mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC); + oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid; } } } - } - newMethod->mParentType = primaryType; - primaryType->mMethodList.PushBack(newMethod); + + if (doHotJump) + { + if (!setHotJumpFailed) + { + if (mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC))) + { + _RemoveLineInfo(oldMethod); + } + else + setHotJumpFailed = true; + } + oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced; + } + }; + + auto _ReplaceMethod = [&](DbgSubprogram* newMethod) + { + if (!newMethod->mBlock.IsEmpty()) + { + BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC); + + newMethod->PopulateSubprogram(); + + auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC); + if (symInfo != NULL) + { + DbgSubprogram* oldMethod = NULL; + for (auto itr = oldProgramMap.TryGet(StringView(symInfo->mName)); itr != oldProgramMap.end(); ++itr) + { + auto oldMethod = itr.GetValue(); + _HotJump(oldMethod, newMethod); + } + } + } + }; + + if (newType == primaryType) + { + // In-place method swapping + + auto newMethod = newType->mMethodList.front(); + while (newMethod != NULL) + { + if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize)) + { + // Our object file contains this function + _ReplaceMethod(newMethod); + auto nextMethod = newMethod->mNext; + newType->mMethodList.Remove(newMethod); + primaryType->mHotReplacedMethodList.PushFront(newMethod); + newMethod = nextMethod; + } + else + { + _AddToHotReplacedMethodList(newMethod); + } + newMethod = newMethod->mNext; + } } + else + { + while (!newType->mMethodList.IsEmpty()) + { + DbgSubprogram* newMethod = newType->mMethodList.PopFront(); + _ReplaceMethod(newMethod); + newMethod->mParentType = primaryType; + primaryType->mMethodList.PushBack(newMethod); + } + } +} + +void DbgModule::HotReplaceType(DbgType* newType) +{ + auto linkedModule = GetLinkedModule(); + + newType->PopulateType(); + DbgType* primaryType = linkedModule->GetPrimaryType(newType); + if (primaryType == newType) + { + // There was no previous type + BF_ASSERT(primaryType->mHotNewType == NULL); + return; + } + + if (primaryType->mHotNewType != newType) + { + // We have already pulled in the new data from a previous new type + BF_ASSERT(primaryType->mHotNewType == NULL); + return; + } + primaryType->mHotNewType = NULL; + + HotReplaceMethods(newType, primaryType); //mDebugTarget->mSymbolMap.Get() @@ -6506,17 +6580,21 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind) { auto dbgType = *itr; auto primaryType = dbgType->GetPrimaryType(); + + if ((primaryType->mHotNewType == NULL) && (HasHotReplacedMethods(primaryType)) && (dbgType == primaryType)) + HotReplaceMethods(dbgType, primaryType); + if (primaryType != dbgType) { mHotPrimaryTypes.Remove(itr); mHotPrimaryTypes.Add(primaryType); didReplaceType = true; break; - } + } } if (!didReplaceType) break; - } + } BF_ASSERT(mTypes.size() == 0); for (int typeIdx = mStartTypeIdx; typeIdx < (int)linkedModule->mTypes.size(); typeIdx++) @@ -6525,7 +6603,7 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind) //if (!newType->mMethodList.IsEmpty()) if (!newType->mIsDeclaration) HotReplaceType(newType); - } + } } if (needHotTargetMemory != 0) diff --git a/IDEHelper/DbgModule.h b/IDEHelper/DbgModule.h index 813401d8..6087ac2b 100644 --- a/IDEHelper/DbgModule.h +++ b/IDEHelper/DbgModule.h @@ -665,8 +665,8 @@ public: class SubprogramRecord { public: - Array > mContexts; - Array > mLines; + Array mContexts; + Array mLines; int mCurContext; bool mHasInlinees; }; @@ -1273,6 +1273,8 @@ public: void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo); void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs); void CommitHotTargetSections(); + bool HasHotReplacedMethods(DbgType* type); + void HotReplaceMethods(DbgType* newType, DbgType* primaryType); void HotReplaceType(DbgType* newType); void ProcessHotSwapVariables(); virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; } diff --git a/IDEHelper/DebugTarget.cpp b/IDEHelper/DebugTarget.cpp index 17eb7102..9437e741 100644 --- a/IDEHelper/DebugTarget.cpp +++ b/IDEHelper/DebugTarget.cpp @@ -33,6 +33,7 @@ DebugTarget::DebugTarget(WinDebugger* debugger) mHotHeapAddr = 0; mHotHeapReserveSize = 0; mLastHotHeapCleanIdx = 0; + mVDataHotIdx = -1; mIsEmpty = false; mWasLocallyBuilt = false; mCurModuleId = 0; @@ -2489,15 +2490,17 @@ DbgBreakKind DebugTarget::GetDbgBreakKind(addr_target address, CPURegisters* reg return DbgBreakKind_None; } +#define ADDR_ROUGH(address) ((address) & ~0x3FFF) + DbgModule* DebugTarget::FindDbgModuleForAddress(addr_target address) { - addr_target checkAddr = address & ~0xFFFF; + addr_target checkAddr = ADDR_ROUGH(address); FindDbgModuleCacheEntry* valuePtr = NULL; if (mFindDbgModuleCache.TryAdd(checkAddr, NULL, &valuePtr)) { for (auto dwarf : mDbgModules) { - if ((address >= dwarf->mImageBase) && (address < dwarf->mImageBase + dwarf->mImageSize)) + if ((checkAddr >= ADDR_ROUGH(dwarf->mImageBase)) && (checkAddr <= ADDR_ROUGH(dwarf->mImageBase + dwarf->mImageSize))) { if (valuePtr->mFirst == NULL) { diff --git a/IDEHelper/DebugTarget.h b/IDEHelper/DebugTarget.h index 1b5f060c..312744c7 100644 --- a/IDEHelper/DebugTarget.h +++ b/IDEHelper/DebugTarget.h @@ -49,6 +49,7 @@ public: addr_target mHotHeapAddr; int64 mHotHeapReserveSize; int mLastHotHeapCleanIdx; + int mVDataHotIdx; String mTargetPath; DbgModule* mLaunchBinary; DbgModule* mTargetBinary; diff --git a/IDEHelper/IDEHelper.vcxproj.user b/IDEHelper/IDEHelper.vcxproj.user index 1ce90950..75967093 100644 --- a/IDEHelper/IDEHelper.vcxproj.user +++ b/IDEHelper/IDEHelper.vcxproj.user @@ -20,6 +20,6 @@ _NO_DEBUG_HEAP=1 - true + false \ No newline at end of file diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index 8b3f8d73..7783c148 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -1151,9 +1151,14 @@ void WinDebugger::HotLoad(const Array& objectFiles, int hotIdx) int startingModuleIdx = (int)mDebugTarget->mDbgModules.size(); + bool hasHotVData = false; + bool failed = false; for (auto fileName : objectFiles) { + if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1)) + hasHotVData = true; + BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str()); DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx); if ((newBinary != NULL) && (newBinary->mFailed)) @@ -1186,6 +1191,9 @@ void WinDebugger::HotLoad(const Array& objectFiles, int hotIdx) mDebugTarget->RehupSrcFiles(); + if (hasHotVData) + mDebugTarget->mVDataHotIdx = hotIdx; + for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++) { auto breakpoint = mBreakpoints[breakIdx];