HotScanner.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. #include "HotScanner.h"
  2. #include "WinDebugger.h"
  3. #include "BeefySysLib/util/AllocDebug.h"
  4. USING_NS_BF_DBG;
  5. DbgHotScanner::DbgHotScanner(WinDebugger* debugger)
  6. {
  7. mDebugger = debugger;
  8. }
  9. NS_BF_DBG_BEGIN
  10. namespace TCFake
  11. {
  12. struct Span
  13. {
  14. addr_target start; // Starting page number
  15. addr_target length; // Number of pages in span
  16. addr_target next; // Used when in link list
  17. addr_target prev; // Used when in link list
  18. addr_target objects; // Linked list of free objects
  19. addr_target freeingObjects; // BCF- waiting to be freed
  20. addr_target freeingObjectsTail; // Note: *((intptr_t*)freeingObjectsTail) is freeingCount
  21. unsigned int refcount : 16; // Number of non-free objects
  22. unsigned int sizeclass : 8; // Size-class for small objects (or 0)
  23. unsigned int location : 2; // Is the span on a freelist, and if so, which?
  24. unsigned int decommitDelay : 3;
  25. unsigned int sample : 1; // Sampled object?
  26. #undef SPAN_HISTORY
  27. #ifdef SPAN_HISTORY
  28. // For debugging, we can keep a log events per span
  29. int nexthistory;
  30. char history[64];
  31. int value[64];
  32. #endif
  33. // What freelist the span is on: IN_USE if on none, or normal or returned
  34. enum { IN_USE, ON_NORMAL_FREELIST, ON_RETURNED_FREELIST };
  35. };
  36. }
  37. struct Fake_BfObject_WithFlags
  38. {
  39. union
  40. {
  41. addr_target mClassVData;
  42. struct
  43. {
  44. BfObjectFlags mObjectFlags;
  45. uint8 mClassVDataBytes[sizeof(addr_target) - 1];
  46. };
  47. };
  48. union
  49. {
  50. addr_target mAllocCheckPtr;
  51. addr_target mDbgAllocInfo; // Meaning depends on object flags- could be PC at allocation
  52. };
  53. };
  54. struct Fake_Type_Data
  55. {
  56. int32 mSize;
  57. int32 mTypeId;
  58. int32 mBoxedType;
  59. int32 mTypeFlags;
  60. };
  61. struct Fake_Delegate_Data
  62. {
  63. addr_target mFuncPtr;
  64. addr_target Target;
  65. };
  66. struct Fake_DbgRawAllocData
  67. {
  68. addr_target mType;
  69. addr_target mMarkFunc;
  70. int32 mMaxStackTrace; // Only 0, 1, >1 matters
  71. };
  72. #define BF_OBJECTFLAG_DELETED 0x80
  73. NS_BF_END
  74. using namespace TCFake;
  75. void DbgHotScanner::AddSubProgram(DbgSubprogram* subProgram, bool followInlineParent, const Beefy::StringImpl& prefix)
  76. {
  77. if ((followInlineParent) && (subProgram->mInlineeInfo != NULL) && (subProgram->mInlineeInfo->mInlineParent != NULL))
  78. AddSubProgram(subProgram->mInlineeInfo->mInlineParent, true, prefix);
  79. subProgram->mCompileUnit->mDbgModule->ParseSymbolData();
  80. String methodName;
  81. addr_target offset;
  82. if (mDebugger->mDebugTarget->FindSymbolAt(subProgram->mBlock.mLowPC, &methodName, &offset))
  83. {
  84. if (offset == 0)
  85. {
  86. if (!prefix.IsEmpty())
  87. methodName.Insert(0, prefix);
  88. if (subProgram->mCompileUnit->mDbgModule->mHotIdx != 0)
  89. methodName += StrFormat("\t%d", subProgram->mCompileUnit->mDbgModule->mHotIdx);
  90. mDebugger->mHotResolveData->mBeefCallStackEntries.Add(methodName);
  91. }
  92. }
  93. }
  94. void DbgHotScanner::PopulateHotCallstacks()
  95. {
  96. auto prevActiveThread = mDebugger->mActiveThread;
  97. for (auto threadInfo : mDebugger->mThreadList)
  98. {
  99. mDebugger->mActiveThread = threadInfo;
  100. mDebugger->ClearCallStack();
  101. mDebugger->UpdateCallStack(false);
  102. for (int stackFrameIdx = 0; stackFrameIdx < (int)mDebugger->mCallStack.size(); stackFrameIdx++)
  103. mDebugger->UpdateCallStackMethod(stackFrameIdx);
  104. for (int stackFrameIdx = 0; stackFrameIdx < (int)mDebugger->mCallStack.size(); stackFrameIdx++)
  105. {
  106. auto stackFrame = mDebugger->mCallStack[stackFrameIdx];
  107. if ((stackFrame->mSubProgram != NULL) && (stackFrame->mSubProgram->GetLanguage() == DbgLanguage_Beef))
  108. {
  109. auto subProgram = stackFrame->mSubProgram;
  110. if (subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced)
  111. subProgram = mDebugger->TryFollowHotJump(subProgram, stackFrame->mRegisters.GetPC());
  112. AddSubProgram(subProgram, false, "");
  113. }
  114. }
  115. }
  116. mDebugger->mActiveThread = prevActiveThread;
  117. mDebugger->ClearCallStack();
  118. }
  119. void DbgHotScanner::ScanSpan(TCFake::Span* span, int expectedStartPage, int memKind)
  120. {
  121. if (span->location != TCFake::Span::IN_USE)
  122. return;
  123. if (span->start != expectedStartPage)
  124. {
  125. return;
  126. }
  127. intptr pageSize = (intptr)1 << kPageShift;
  128. int spanSize = pageSize * span->length;
  129. addr_target spanStart = ((addr_target)span->start << kPageShift);
  130. if (spanSize > mScanData.size())
  131. {
  132. mScanData.Resize(spanSize);
  133. }
  134. void* spanPtr = &mScanData[0];
  135. mDebugger->ReadMemory(spanStart, spanSize, spanPtr);
  136. void* spanEnd = (void*)((intptr)spanPtr + spanSize);
  137. //BF_LOGASSERT((spanStart >= TCFake::PageHeap::sAddressStart) && (spanEnd <= TCFake::PageHeap::sAddressEnd));
  138. int elementSize = mDbgGCData.mSizeClasses[span->sizeclass];
  139. if (elementSize == 0)
  140. elementSize = spanSize;
  141. //BF_LOGASSERT(elementSize >= sizeof(bf::System::Object));
  142. auto _MarkTypeUsed = [&](int typeId, intptr size)
  143. {
  144. if (typeId < 0)
  145. return;
  146. while (mDebugger->mHotResolveData->mTypeData.size() <= typeId)
  147. mDebugger->mHotResolveData->mTypeData.Add(DbgHotResolveData::TypeData());
  148. auto& typeData = mDebugger->mHotResolveData->mTypeData[typeId];
  149. typeData.mSize += size;
  150. typeData.mCount++;
  151. };
  152. int objectSize = ((mDbgGCData.mDbgFlags & BfRtFlags_ObjectHasDebugFlags) != 0) ? sizeof(addr_target)*2 : sizeof(addr_target);
  153. while (spanPtr <= (uint8*)spanEnd - elementSize)
  154. {
  155. addr_target classVDataAddr = 0;
  156. if (memKind == 0)
  157. {
  158. // Obj
  159. Fake_BfObject_WithFlags* obj = (Fake_BfObject_WithFlags*)spanPtr;
  160. if (obj->mAllocCheckPtr != 0)
  161. {
  162. int objectFlags = obj->mObjectFlags;
  163. if ((objectFlags & BF_OBJECTFLAG_DELETED) == 0)
  164. {
  165. classVDataAddr = obj->mClassVData & ~0xFF;
  166. }
  167. }
  168. }
  169. else
  170. {
  171. // Raw
  172. addr_target rawAllocDataAddr = *(addr_target*)((uint8*)spanPtr + elementSize - sizeof(addr_target));
  173. if (rawAllocDataAddr != 0)
  174. {
  175. if (rawAllocDataAddr == mDbgGCData.mRawObjectSentinel)
  176. {
  177. classVDataAddr = *((addr_target*)spanPtr);
  178. if ((mDbgGCData.mDbgFlags & BfRtFlags_ObjectHasDebugFlags) != 0)
  179. classVDataAddr = classVDataAddr & ~0xFF;
  180. }
  181. else
  182. {
  183. int* rawTypeIdPtr = NULL;
  184. if (mFoundRawAllocDataAddrs.TryAdd(rawAllocDataAddr, NULL, &rawTypeIdPtr))
  185. {
  186. *rawTypeIdPtr = -1;
  187. Fake_DbgRawAllocData rawAllocData = mDebugger->ReadMemory<Fake_DbgRawAllocData>(rawAllocDataAddr);
  188. if (rawAllocData.mType != NULL)
  189. {
  190. int* typeAddrIdPtr = NULL;
  191. if (mFoundTypeAddrs.TryAdd(rawAllocData.mType, NULL, &typeAddrIdPtr))
  192. {
  193. *typeAddrIdPtr = -1;
  194. Fake_Type_Data typeData;
  195. if (mDebugger->ReadMemory(rawAllocData.mType + objectSize, sizeof(typeData), &typeData))
  196. {
  197. *typeAddrIdPtr = typeData.mTypeId;
  198. *rawTypeIdPtr = typeData.mTypeId;
  199. _MarkTypeUsed(typeData.mTypeId, elementSize);
  200. }
  201. }
  202. else
  203. {
  204. _MarkTypeUsed(*typeAddrIdPtr, elementSize);
  205. }
  206. }
  207. }
  208. else
  209. {
  210. _MarkTypeUsed(*rawTypeIdPtr, elementSize);
  211. }
  212. }
  213. }
  214. }
  215. if (classVDataAddr != 0)
  216. {
  217. int* typeIdPtr = NULL;
  218. if (mFoundClassVDataAddrs.TryAdd(classVDataAddr, NULL, &typeIdPtr))
  219. {
  220. addr_target typeAddr = mDebugger->ReadMemory<addr_target>(classVDataAddr);
  221. Fake_Type_Data typeData;
  222. mDebugger->ReadMemory(typeAddr + objectSize, sizeof(typeData), &typeData);
  223. *typeIdPtr = typeData.mTypeId;
  224. _MarkTypeUsed(typeData.mTypeId, elementSize);
  225. if ((typeData.mTypeFlags & BfTypeFlags_Delegate) != 0)
  226. {
  227. Fake_Delegate_Data* dlg = (Fake_Delegate_Data*)((uint8*)spanPtr + objectSize);
  228. if (mFoundFuncPtrs.Add(dlg->mFuncPtr))
  229. {
  230. auto subProgram = mDebugger->mDebugTarget->FindSubProgram(dlg->mFuncPtr, DbgOnDemandKind_None);
  231. if ((subProgram != NULL) && (subProgram->GetLanguage() == DbgLanguage_Beef))
  232. AddSubProgram(subProgram, true, "D ");
  233. }
  234. }
  235. }
  236. else
  237. {
  238. _MarkTypeUsed(*typeIdPtr, elementSize);
  239. }
  240. }
  241. spanPtr = (void*)((intptr)spanPtr + elementSize);
  242. }
  243. }
  244. void DbgHotScanner::ScanRoot(addr_target rootPtr, int memKind)
  245. {
  246. mDebugger->ReadMemory(rootPtr, sizeof(mRoot), &mRoot);
  247. #ifdef BF_DBG_32
  248. for (int rootIdx = 0; rootIdx < PageHeap::PageMap::ROOT_LENGTH; rootIdx++)
  249. {
  250. addr_target nodeAddr = mRoot.ptrs[rootIdx];
  251. if (nodeAddr == 0)
  252. continue;
  253. mDebugger->ReadMemory(nodeAddr, sizeof(mNode), &mNode);
  254. for (int leafIdx = 0; leafIdx < PageHeap::PageMap::LEAF_LENGTH; leafIdx++)
  255. {
  256. addr_target spanAddr = mNode.ptrs[leafIdx];
  257. if (spanAddr == 0)
  258. continue;
  259. int expectedStartPage = (rootIdx * PageHeap::PageMap::LEAF_LENGTH) + leafIdx;
  260. TCFake::Span span;
  261. mDebugger->ReadMemory(spanAddr, sizeof(span), &span);
  262. ScanSpan(&span, expectedStartPage, memKind);
  263. }
  264. }
  265. #else
  266. int bits = PageHeap::PageMap::BITS;
  267. int interiorLen = PageHeap::PageMap::INTERIOR_LENGTH;
  268. int leafLen = PageHeap::PageMap::LEAF_LENGTH;
  269. for (int pageIdx1 = 0; pageIdx1 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx1++)
  270. {
  271. addr_target node1Addr = mRoot.ptrs[pageIdx1];
  272. if (node1Addr == 0)
  273. continue;
  274. mDebugger->ReadMemory(node1Addr, sizeof(mNode1), &mNode1);
  275. for (int pageIdx2 = 0; pageIdx2 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx2++)
  276. {
  277. addr_target node2Addr = mNode1.ptrs[pageIdx2];
  278. if (node2Addr == 0)
  279. continue;
  280. mDebugger->ReadMemory(node2Addr, sizeof(mNode2), &mNode2);
  281. for (int pageIdx3 = 0; pageIdx3 < PageHeap::PageMap::LEAF_LENGTH; pageIdx3++)
  282. {
  283. addr_target spanAddr = mNode2.ptrs[pageIdx3];
  284. if (spanAddr == 0)
  285. continue;
  286. int expectedStartPage = ((pageIdx1 * PageHeap::PageMap::INTERIOR_LENGTH) + pageIdx2) * PageHeap::PageMap::LEAF_LENGTH + pageIdx3;
  287. TCFake::Span span;
  288. mDebugger->ReadMemory(spanAddr, sizeof(span), &span);
  289. ScanSpan(&span, expectedStartPage, memKind);
  290. }
  291. }
  292. }
  293. #endif
  294. }
  295. void DbgHotScanner::Scan(DbgHotResolveFlags flags)
  296. {
  297. auto prevRunState = mDebugger->mRunState;
  298. if ((mDebugger->mRunState == RunState_Running) && ((flags & DbgHotResolveFlag_KeepThreadState) == 0))
  299. {
  300. mDebugger->ThreadRestorePause(NULL, NULL);
  301. mDebugger->mRunState = RunState_Paused;
  302. }
  303. if ((flags & DbgHotResolveFlag_ActiveMethods) != 0)
  304. PopulateHotCallstacks();
  305. if ((flags & DbgHotResolveFlag_Allocations) != 0)
  306. {
  307. addr_target gcDbgDataAddr = 0;
  308. for (auto module : mDebugger->mDebugTarget->mDbgModules)
  309. {
  310. if ((module->mFilePath.Contains("Beef")) && (module->mFilePath.Contains("Dbg")))
  311. {
  312. module->ParseSymbolData();
  313. auto entry = module->mSymbolNameMap.Find("gGCDbgData");
  314. if ((entry != NULL) && (entry->mValue != NULL))
  315. gcDbgDataAddr = entry->mValue->mAddress;
  316. }
  317. }
  318. if (gcDbgDataAddr == 0)
  319. return;
  320. bool success = mDebugger->ReadMemory(gcDbgDataAddr, sizeof(DbgGCData), &mDbgGCData);
  321. if (!success)
  322. {
  323. BF_ASSERT("Failed to read DbgGCData");
  324. success = mDebugger->ReadMemory(gcDbgDataAddr, sizeof(DbgGCData), &mDbgGCData);
  325. }
  326. BF_ASSERT(mDbgGCData.mObjRootPtr != NULL);
  327. BF_ASSERT(mDbgGCData.mRawRootPtr != NULL);
  328. if (mDbgGCData.mObjRootPtr != NULL)
  329. ScanRoot(mDbgGCData.mObjRootPtr, 0);
  330. if (mDbgGCData.mRawRootPtr != NULL)
  331. ScanRoot(mDbgGCData.mRawRootPtr, 1);
  332. }
  333. if ((prevRunState == RunState_Running) && ((flags & DbgHotResolveFlag_KeepThreadState) == 0))
  334. {
  335. mDebugger->ThreadRestoreUnpause();
  336. mDebugger->mRunState = prevRunState;
  337. }
  338. }