1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-07 19:18:19 +02:00
Beef/IDEHelper/HotScanner.cpp

436 lines
No EOL
12 KiB
C++

#include "HotScanner.h"
#include "WinDebugger.h"
#include "BeefySysLib/util/AllocDebug.h"
USING_NS_BF_DBG;
DbgHotScanner::DbgHotScanner(WinDebugger* debugger)
{
mDebugger = debugger;
mBfTypesInfoAddr = 0;
mDbgGCData = { 0 };
}
NS_BF_DBG_BEGIN
namespace TCFake
{
struct Span
{
addr_target start; // Starting page number
addr_target length; // Number of pages in span
addr_target next; // Used when in link list
addr_target prev; // Used when in link list
addr_target objects; // Linked list of free objects
addr_target freeingObjects; // BCF- waiting to be freed
addr_target freeingObjectsTail; // Note: *((intptr_t*)freeingObjectsTail) is freeingCount
unsigned int refcount : 16; // Number of non-free objects
unsigned int sizeclass : 8; // Size-class for small objects (or 0)
unsigned int location : 2; // Is the span on a freelist, and if so, which?
unsigned int decommitDelay : 3;
unsigned int sample : 1; // Sampled object?
#undef SPAN_HISTORY
#ifdef SPAN_HISTORY
// For debugging, we can keep a log events per span
int nexthistory;
char history[64];
int value[64];
#endif
// What freelist the span is on: IN_USE if on none, or normal or returned
enum { IN_USE, ON_NORMAL_FREELIST, ON_RETURNED_FREELIST };
};
}
struct Fake_BfObject_WithFlags
{
union
{
addr_target mClassVData;
struct
{
BfObjectFlags mObjectFlags;
uint8 mClassVDataBytes[sizeof(addr_target) - 1];
};
};
union
{
addr_target mAllocCheckPtr;
addr_target mDbgAllocInfo; // Meaning depends on object flags- could be PC at allocation
};
};
struct Fake_Type_Data
{
int32 mSize;
int32 mTypeId;
int32 mBoxedType;
int32 mTypeFlags;
};
struct Fake_Delegate_Data
{
addr_target mFuncPtr;
addr_target Target;
};
struct Fake_DbgRawAllocData
{
addr_target mType;
addr_target mMarkFunc;
int32 mMaxStackTrace; // Only 0, 1, >1 matters
};
#define BF_OBJECTFLAG_DELETED 0x80
NS_BF_END
using namespace TCFake;
void DbgHotScanner::AddSubProgram(DbgSubprogram* subProgram, bool followInlineParent, const Beefy::StringImpl& prefix)
{
if ((followInlineParent) && (subProgram->mInlineeInfo != NULL) && (subProgram->mInlineeInfo->mInlineParent != NULL))
AddSubProgram(subProgram->mInlineeInfo->mInlineParent, true, prefix);
subProgram->mCompileUnit->mDbgModule->ParseSymbolData();
String methodName;
addr_target offset;
if (mDebugger->mDebugTarget->FindSymbolAt(subProgram->mBlock.mLowPC, &methodName, &offset))
{
if (offset == 0)
{
if (!prefix.IsEmpty())
methodName.Insert(0, prefix);
if (subProgram->mCompileUnit->mDbgModule->mHotIdx != 0)
methodName += StrFormat("\t%d", subProgram->mCompileUnit->mDbgModule->mHotIdx);
mDebugger->mHotResolveData->mBeefCallStackEntries.Add(methodName);
}
}
}
void DbgHotScanner::PopulateHotCallstacks()
{
auto prevActiveThread = mDebugger->mActiveThread;
for (auto threadInfo : mDebugger->mThreadList)
{
mDebugger->mActiveThread = threadInfo;
mDebugger->ClearCallStack();
mDebugger->UpdateCallStack(false);
for (int stackFrameIdx = 0; stackFrameIdx < (int)mDebugger->mCallStack.size(); stackFrameIdx++)
mDebugger->UpdateCallStackMethod(stackFrameIdx);
for (int stackFrameIdx = 0; stackFrameIdx < (int)mDebugger->mCallStack.size(); stackFrameIdx++)
{
auto stackFrame = mDebugger->mCallStack[stackFrameIdx];
if ((stackFrame->mSubProgram != NULL) && (stackFrame->mSubProgram->GetLanguage() == DbgLanguage_Beef))
{
auto subProgram = stackFrame->mSubProgram;
if (subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced)
subProgram = mDebugger->TryFollowHotJump(subProgram, stackFrame->mRegisters.GetPC());
AddSubProgram(subProgram, false, "");
}
}
}
mDebugger->mActiveThread = prevActiveThread;
mDebugger->ClearCallStack();
}
void DbgHotScanner::ScanSpan(TCFake::Span* span, int expectedStartPage, int memKind)
{
if (span->location != TCFake::Span::IN_USE)
return;
if (span->start != expectedStartPage)
{
return;
}
intptr pageSize = (intptr)1 << kPageShift;
int spanSize = pageSize * span->length;
addr_target spanStart = ((addr_target)span->start << kPageShift);
if (spanSize > mScanData.size())
{
mScanData.Resize(spanSize);
}
void* spanPtr = &mScanData[0];
mDebugger->ReadMemory(spanStart, spanSize, spanPtr);
void* spanEnd = (void*)((intptr)spanPtr + spanSize);
//BF_LOGASSERT((spanStart >= TCFake::PageHeap::sAddressStart) && (spanEnd <= TCFake::PageHeap::sAddressEnd));
int elementSize = mDbgGCData.mSizeClasses[span->sizeclass];
if (elementSize == 0)
elementSize = spanSize;
//BF_LOGASSERT(elementSize >= sizeof(bf::System::Object));
auto _MarkTypeUsed = [&](int typeId, intptr size)
{
if (typeId < 0)
return;
while (mDebugger->mHotResolveData->mTypeData.size() <= typeId)
mDebugger->mHotResolveData->mTypeData.Add(DbgHotResolveData::TypeData());
auto& typeData = mDebugger->mHotResolveData->mTypeData[typeId];
typeData.mSize += size;
typeData.mCount++;
};
int objectSize = ((mDbgGCData.mDbgFlags & BfRtFlags_ObjectHasDebugFlags) != 0) ? sizeof(addr_target)*2 : sizeof(addr_target);
mDebugger->mDebugTarget->GetCompilerSettings();
if (mDebugger->mDebugTarget->mBfObjectSize != 0)
objectSize = mDebugger->mDebugTarget->mBfObjectSize;
while (spanPtr <= (uint8*)spanEnd - elementSize)
{
addr_target classVDataAddr = 0;
if (memKind == 0)
{
// Obj
Fake_BfObject_WithFlags* obj = (Fake_BfObject_WithFlags*)spanPtr;
if (obj->mAllocCheckPtr != 0)
{
int objectFlags = obj->mObjectFlags;
if ((objectFlags & BF_OBJECTFLAG_DELETED) == 0)
{
classVDataAddr = obj->mClassVData & ~0xFF;
}
}
}
else
{
// Raw
addr_target rawAllocDataAddr = *(addr_target*)((uint8*)spanPtr + elementSize - sizeof(addr_target));
if (rawAllocDataAddr != 0)
{
if (rawAllocDataAddr == mDbgGCData.mRawObjectSentinel)
{
classVDataAddr = *((addr_target*)spanPtr);
if ((mDbgGCData.mDbgFlags & BfRtFlags_ObjectHasDebugFlags) != 0)
classVDataAddr = classVDataAddr & ~0xFF;
}
else
{
int* rawTypeIdPtr = NULL;
if (mFoundRawAllocDataAddrs.TryAdd(rawAllocDataAddr, NULL, &rawTypeIdPtr))
{
*rawTypeIdPtr = -1;
Fake_DbgRawAllocData rawAllocData = mDebugger->ReadMemory<Fake_DbgRawAllocData>(rawAllocDataAddr);
if (rawAllocData.mType != NULL)
{
int* typeAddrIdPtr = NULL;
if (mFoundTypeAddrs.TryAdd(rawAllocData.mType, NULL, &typeAddrIdPtr))
{
*typeAddrIdPtr = -1;
Fake_Type_Data typeData;
if (mDebugger->ReadMemory(rawAllocData.mType + objectSize, sizeof(typeData), &typeData))
{
*typeAddrIdPtr = typeData.mTypeId;
*rawTypeIdPtr = typeData.mTypeId;
_MarkTypeUsed(typeData.mTypeId, elementSize);
}
}
else
{
_MarkTypeUsed(*typeAddrIdPtr, elementSize);
}
}
}
else
{
_MarkTypeUsed(*rawTypeIdPtr, elementSize);
}
}
}
}
if (classVDataAddr != 0)
{
int* typeIdPtr = NULL;
if (mFoundClassVDataAddrs.TryAdd(classVDataAddr, NULL, &typeIdPtr))
{
if (mBfTypesInfoAddr > 0)
{
addr_target typeId = mDebugger->ReadMemory<int32>(classVDataAddr);
addr_target arrayAddr = mBfTypesInfoAddr + typeId * sizeof(addr_target);
addr_target typeAddr = mDebugger->ReadMemory<addr_target>(arrayAddr);
Fake_Type_Data typeData;
mDebugger->ReadMemory(typeAddr + objectSize, sizeof(typeData), &typeData);
*typeIdPtr = typeData.mTypeId;
_MarkTypeUsed(typeData.mTypeId, elementSize);
if ((typeData.mTypeFlags & BfTypeFlags_Delegate) != 0)
{
Fake_Delegate_Data* dlg = (Fake_Delegate_Data*)((uint8*)spanPtr + objectSize);
if (mFoundFuncPtrs.Add(dlg->mFuncPtr))
{
auto subProgram = mDebugger->mDebugTarget->FindSubProgram(dlg->mFuncPtr, DbgOnDemandKind_None);
if ((subProgram != NULL) && (subProgram->GetLanguage() == DbgLanguage_Beef))
AddSubProgram(subProgram, true, "D ");
}
}
}
}
else
{
_MarkTypeUsed(*typeIdPtr, elementSize);
}
}
spanPtr = (void*)((intptr)spanPtr + elementSize);
}
}
void DbgHotScanner::ScanRoot(addr_target rootPtr, int memKind)
{
mDebugger->ReadMemory(rootPtr, sizeof(mRoot), &mRoot);
#ifdef BF_DBG_32
for (int rootIdx = 0; rootIdx < PageHeap::PageMap::ROOT_LENGTH; rootIdx++)
{
addr_target nodeAddr = mRoot.ptrs[rootIdx];
if (nodeAddr == 0)
continue;
mDebugger->ReadMemory(nodeAddr, sizeof(mNode), &mNode);
for (int leafIdx = 0; leafIdx < PageHeap::PageMap::LEAF_LENGTH; leafIdx++)
{
addr_target spanAddr = mNode.ptrs[leafIdx];
if (spanAddr == 0)
continue;
int expectedStartPage = (rootIdx * PageHeap::PageMap::LEAF_LENGTH) + leafIdx;
TCFake::Span span;
mDebugger->ReadMemory(spanAddr, sizeof(span), &span);
ScanSpan(&span, expectedStartPage, memKind);
}
}
#else
int bits = PageHeap::PageMap::BITS;
int interiorLen = PageHeap::PageMap::INTERIOR_LENGTH;
int leafLen = PageHeap::PageMap::LEAF_LENGTH;
for (int pageIdx1 = 0; pageIdx1 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx1++)
{
addr_target node1Addr = mRoot.ptrs[pageIdx1];
if (node1Addr == 0)
continue;
mDebugger->ReadMemory(node1Addr, sizeof(mNode1), &mNode1);
for (int pageIdx2 = 0; pageIdx2 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx2++)
{
addr_target node2Addr = mNode1.ptrs[pageIdx2];
if (node2Addr == 0)
continue;
mDebugger->ReadMemory(node2Addr, sizeof(mNode2), &mNode2);
for (int pageIdx3 = 0; pageIdx3 < PageHeap::PageMap::LEAF_LENGTH; pageIdx3++)
{
addr_target spanAddr = mNode2.ptrs[pageIdx3];
if (spanAddr == 0)
continue;
int expectedStartPage = ((pageIdx1 * PageHeap::PageMap::INTERIOR_LENGTH) + pageIdx2) * PageHeap::PageMap::LEAF_LENGTH + pageIdx3;
TCFake::Span span;
mDebugger->ReadMemory(spanAddr, sizeof(span), &span);
ScanSpan(&span, expectedStartPage, memKind);
}
}
}
#endif
}
void DbgHotScanner::Scan(DbgHotResolveFlags flags)
{
auto prevRunState = mDebugger->mRunState;
if ((mDebugger->mRunState == RunState_Running) && ((flags & DbgHotResolveFlag_KeepThreadState) == 0))
{
mDebugger->ThreadRestorePause(NULL, NULL);
mDebugger->mRunState = RunState_Paused;
}
if ((flags & DbgHotResolveFlag_ActiveMethods) != 0)
PopulateHotCallstacks();
if ((flags & DbgHotResolveFlag_Allocations) != 0)
{
addr_target gcDbgDataAddr = 0;
for (auto module : mDebugger->mDebugTarget->mDbgModules)
{
if ((module->mFilePath.Contains("Beef")) && (module->mFilePath.Contains("Dbg")))
{
module->ParseTypeData();
module->ParseSymbolData();
auto entry = module->mSymbolNameMap.Find("gGCDbgData");
if ((entry != NULL) && (entry->mValue != NULL))
gcDbgDataAddr = entry->mValue->mAddress;
}
}
auto module = mDebugger->mDebugTarget->mTargetBinary;
if (module->mBfTypesInfoAddr == 0)
{
module->mBfTypesInfoAddr = -1;
auto typeTypeEntry = module->FindType("System.Type", DbgLanguage_Beef);
if ((typeTypeEntry != NULL) && (typeTypeEntry->mValue != NULL))
{
auto typeType = typeTypeEntry->mValue;
module->mBfTypeType = typeType;
if (typeType->mNeedsGlobalsPopulated)
typeType->mCompileUnit->mDbgModule->PopulateTypeGlobals(typeType);
for (auto member : typeType->mMemberList)
{
if ((member->mIsStatic) && (member->mName != NULL) && (strcmp(member->mName, "sTypes") == 0) && (member->mLocationData != NULL))
{
DbgAddrType addrType;
module->mBfTypesInfoAddr = member->mCompileUnit->mDbgModule->EvaluateLocation(NULL, member->mLocationData, member->mLocationLen, NULL, &addrType);
}
}
if (module->mBfTypesInfoAddr <= 0)
{
auto entry = module->mSymbolNameMap.Find(
#ifdef BF_DBG_64
"?sTypes@Type@System@bf@@2PEAPEAV123@A"
#else
"?sTypes@Type@System@bf@@2PAPAV123@A"
#endif
);
if (entry)
module->mBfTypesInfoAddr = entry->mValue->mAddress;
}
}
}
mBfTypesInfoAddr = module->mBfTypesInfoAddr;
if (gcDbgDataAddr == 0)
return;
bool success = mDebugger->ReadMemory(gcDbgDataAddr, sizeof(DbgGCData), &mDbgGCData);
if (!success)
{
BF_ASSERT("Failed to read DbgGCData");
success = mDebugger->ReadMemory(gcDbgDataAddr, sizeof(DbgGCData), &mDbgGCData);
}
BF_ASSERT(mDbgGCData.mObjRootPtr != NULL);
BF_ASSERT(mDbgGCData.mRawRootPtr != NULL);
if (mDbgGCData.mObjRootPtr != NULL)
ScanRoot(mDbgGCData.mObjRootPtr, 0);
if (mDbgGCData.mRawRootPtr != NULL)
ScanRoot(mDbgGCData.mRawRootPtr, 1);
}
if ((prevRunState == RunState_Running) && ((flags & DbgHotResolveFlag_KeepThreadState) == 0))
{
mDebugger->ThreadRestoreUnpause();
mDebugger->mRunState = prevRunState;
}
}