Profiler.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. #include "Profiler.h"
  2. #include "DebugManager.h"
  3. #include <mmsystem.h>
  4. #include <ntsecapi.h> // UNICODE_STRING
  5. #define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
  6. typedef LONG KPRIORITY;
  7. USING_NS_BF_DBG;
  8. enum SYSTEM_INFORMATION_CLASS
  9. {
  10. SystemProcessInformation = 5
  11. }; // SYSTEM_INFORMATION_CLASS
  12. struct CLIENT_ID
  13. {
  14. HANDLE UniqueProcess;
  15. HANDLE UniqueThread;
  16. }; // struct CLIENT_ID
  17. enum THREAD_STATE
  18. {
  19. StateInitialized,
  20. StateReady,
  21. StateRunning,
  22. StateStandby,
  23. StateTerminated,
  24. StateWait,
  25. StateTransition,
  26. StateUnknown
  27. };
  28. struct VM_COUNTERS
  29. {
  30. SIZE_T PeakVirtualSize;
  31. SIZE_T VirtualSize;
  32. ULONG PageFaultCount;
  33. SIZE_T PeakWorkingSetSize;
  34. SIZE_T WorkingSetSize;
  35. SIZE_T QuotaPeakPagedPoolUsage;
  36. SIZE_T QuotaPagedPoolUsage;
  37. SIZE_T QuotaPeakNonPagedPoolUsage;
  38. SIZE_T QuotaNonPagedPoolUsage;
  39. SIZE_T PagefileUsage;
  40. SIZE_T PeakPagefileUsage;
  41. SIZE_T PrivatePageCount;
  42. };
  43. struct SYSTEM_THREAD {
  44. LARGE_INTEGER KernelTime;
  45. LARGE_INTEGER UserTime;
  46. LARGE_INTEGER CreateTime;
  47. ULONG WaitTime;
  48. LPVOID StartAddress;
  49. CLIENT_ID ClientId;
  50. DWORD Priority;
  51. LONG BasePriority;
  52. ULONG ContextSwitchCount;
  53. THREAD_STATE State;
  54. ULONG WaitReason;
  55. };
  56. struct SYSTEM_PROCESS_INFORMATION
  57. {
  58. ULONG NextEntryOffset;
  59. ULONG NumberOfThreads;
  60. LARGE_INTEGER Reserved[3];
  61. LARGE_INTEGER CreateTime;
  62. LARGE_INTEGER UserTime;
  63. LARGE_INTEGER KernelTime;
  64. UNICODE_STRING ImageName;
  65. KPRIORITY BasePriority;
  66. HANDLE ProcessId;
  67. HANDLE InheritedFromProcessId;
  68. ULONG HandleCount;
  69. ULONG Reserved2[2];
  70. ULONG PrivatePageCount;
  71. VM_COUNTERS VirtualMemoryCounters;
  72. IO_COUNTERS IoCounters;
  73. SYSTEM_THREAD Threads[1];
  74. };
  75. typedef NTSTATUS(NTAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
  76. static NtQuerySystemInformation_t NtQuerySystemInformation = NULL;
  77. static HMODULE ntdll = NULL;
  78. DbgProfiler::DbgProfiler(WinDebugger* debugger) : mShutdownEvent(true)
  79. {
  80. mDebugger = debugger;
  81. mIsRunning = false;
  82. mWantsClear = false;
  83. mSamplesPerSecond = 1000;
  84. mTotalVirtualSamples = 0;
  85. mTotalActualSamples = 0;
  86. mTotalActiveSamplingMS = 0;
  87. mStartTick = BFTickCount();
  88. mEndTick = 0;
  89. mDebugger->AddProfiler(this);
  90. mIdleSymbolNames.Add("NtUserGetMessage");
  91. mIdleSymbolNames.Add("NtWaitForAlertByThreadId");
  92. mIdleSymbolNames.Add("NtWaitForMultipleObjects");
  93. mIdleSymbolNames.Add("NtWaitForSingleObject");
  94. mIdleSymbolNames.Add("ZwDelayExecution");
  95. mIdleSymbolNames.Add("ZwRemoveIoCompletion");
  96. mIdleSymbolNames.Add("ZwWaitForWorkViaWorkerFactory");
  97. }
  98. DbgProfiler::~DbgProfiler()
  99. {
  100. mDebugger->RemoveProfiler(this);
  101. Stop();
  102. DoClear();
  103. }
  104. static SYSTEM_PROCESS_INFORMATION* CaptureProcessInfo()
  105. {
  106. WCHAR path[MAX_PATH];
  107. GetSystemDirectory(path, MAX_PATH);
  108. wcscat(path, L"\\ntdll.dll");
  109. ntdll = GetModuleHandle(path);
  110. if (ntdll == NULL)
  111. return NULL;
  112. NtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(ntdll, "NtQuerySystemInformation");
  113. uint allocSize = 1024;
  114. uint8* data = NULL;
  115. while (true)
  116. {
  117. data = new uint8[allocSize];
  118. ULONG wantSize = 0;
  119. NTSTATUS status = ::NtQuerySystemInformation(SystemProcessInformation, data, allocSize, &wantSize);
  120. if (status != STATUS_INFO_LENGTH_MISMATCH)
  121. return (SYSTEM_PROCESS_INFORMATION*)data;
  122. allocSize = wantSize + 4096;
  123. delete data;
  124. }
  125. }
  126. void DbgProfiler::DoClear()
  127. {
  128. for (auto& kv : mThreadInfo)
  129. delete kv.mValue;
  130. for (auto& val : mUniqueProcSet)
  131. delete val.mProcId;
  132. }
  133. ProfileProcId* DbgProfiler::Get(const StringImpl& str, bool* outIsNew)
  134. {
  135. ProfileProdIdEntry checkEntry;
  136. checkEntry.mProcId = (ProfileProcId*)&str;
  137. ProfileProdIdEntry* entryPtr;
  138. if (mUniqueProcSet.TryAdd(checkEntry, &entryPtr))
  139. {
  140. auto procId = new ProfileProcId();
  141. procId->mProcName = str;
  142. procId->mIsIdle = false;
  143. entryPtr->mProcId = procId;
  144. if (outIsNew != NULL)
  145. *outIsNew = true;
  146. return procId;
  147. }
  148. else
  149. {
  150. if (outIsNew != NULL)
  151. *outIsNew = false;
  152. return entryPtr->mProcId;
  153. }
  154. }
  155. void DbgProfiler::ThreadProc()
  156. {
  157. //TODO: Do timing smarter, handle delays and slow stack traces and stuff
  158. //timeBeginPeriod(1);
  159. BF_ASSERT(mTotalVirtualSamples == 0);
  160. ProfileAddrEntry profileEntry;
  161. const int maxStackTrace = 1024;
  162. addr_target stackTrace[maxStackTrace];
  163. DWORD prevSampleTick = timeGetTime();
  164. uint32 accumMS = 0;
  165. int totalWait = 0;
  166. int totalWait2 = 0;
  167. int iterations = 0;
  168. HashSet<int> idleThreadSet;
  169. while (true)
  170. {
  171. if (mWantsClear)
  172. {
  173. DoClear();
  174. mTotalVirtualSamples = 0;
  175. mTotalActualSamples = 0;
  176. mTotalActiveSamplingMS = 0;
  177. mAlloc.Clear();
  178. mThreadInfo.Clear();
  179. mThreadIdList.Clear();
  180. mProfileAddrEntrySet.Clear();
  181. mProfileAddrEntries.Clear();
  182. mPendingProfileEntries.Clear();
  183. mProfileProcEntrySet.Clear();
  184. mProfileProcEntries.Clear();
  185. mProfileAddrToProcMap.Clear();
  186. mProcMap.Clear();
  187. mUniqueProcSet.Clear();
  188. mWantsClear = false;
  189. accumMS = 0;
  190. prevSampleTick = timeGetTime();
  191. }
  192. iterations++;
  193. DWORD startTick0 = timeGetTime();
  194. idleThreadSet.Clear();
  195. SYSTEM_PROCESS_INFORMATION* processData = NULL;
  196. std::unique_ptr<SYSTEM_PROCESS_INFORMATION> ptrDelete(processData);
  197. //processData = CaptureProcessInfo();
  198. auto curProcessData = processData;
  199. while (true)
  200. {
  201. if (processData == NULL)
  202. break;
  203. if ((DWORD)(uintptr)curProcessData->ProcessId == mDebugger->mProcessInfo.dwProcessId)
  204. {
  205. for (int threadIdx = 0; threadIdx < (int)curProcessData->NumberOfThreads; threadIdx++)
  206. {
  207. auto& threadInfo = curProcessData->Threads[threadIdx];
  208. if ((threadInfo.State == StateWait) || (threadInfo.State == StateTerminated))
  209. idleThreadSet.Add((int)(intptr)threadInfo.ClientId.UniqueThread);
  210. break;
  211. }
  212. }
  213. if (curProcessData->NextEntryOffset == 0)
  214. break;
  215. curProcessData = (SYSTEM_PROCESS_INFORMATION*)((intptr)curProcessData + curProcessData->NextEntryOffset);
  216. }
  217. if (mShutdownEvent.WaitFor(0))
  218. {
  219. break;
  220. }
  221. DWORD tickNow = timeGetTime();
  222. accumMS += (int)(tickNow - prevSampleTick);
  223. prevSampleTick = tickNow;
  224. int wantVirtualSamples = (int)((int64)accumMS * mSamplesPerSecond / 1000);
  225. int curSampleCount = wantVirtualSamples - mTotalVirtualSamples;
  226. BF_ASSERT(curSampleCount >= 0);
  227. if (curSampleCount == 0)
  228. continue;
  229. {
  230. AutoCrit autoCrit(mDebugger->mDebugManager->mCritSect);
  231. if ((mDebugger->mRunState == RunState_NotStarted) ||
  232. (mDebugger->mRunState == RunState_Terminated))
  233. break;
  234. if (mDebugger->mRunState != RunState_Running)
  235. {
  236. // Reset timer
  237. accumMS = (int)((int64)mTotalVirtualSamples * 1000 / mSamplesPerSecond);
  238. continue;
  239. }
  240. }
  241. int maxSamplesPerIteration = mSamplesPerSecond / 10;
  242. if (curSampleCount > maxSamplesPerIteration)
  243. {
  244. curSampleCount = maxSamplesPerIteration;
  245. wantVirtualSamples = mTotalVirtualSamples + curSampleCount;
  246. accumMS = (int)((int64)wantVirtualSamples * 1000 / mSamplesPerSecond);
  247. }
  248. mTotalVirtualSamples += curSampleCount;
  249. mTotalActualSamples++;
  250. int threadIdx = 0;
  251. while (true)
  252. {
  253. int startTick = timeGetTime();
  254. AutoCrit autoCrit(mDebugger->mDebugManager->mCritSect);
  255. if (mDebugger->mRunState != RunState_Running)
  256. break;
  257. if (threadIdx >= mDebugger->mThreadList.size())
  258. break;
  259. auto thread = mDebugger->mThreadList[threadIdx];
  260. if ((mTargetThreadId > 0) && (thread->mThreadId != mTargetThreadId))
  261. {
  262. threadIdx++;
  263. continue;
  264. }
  265. ProfileThreadInfo* profileThreadInfo;
  266. ProfileThreadInfo** profileThreadInfoPtr;
  267. if (mThreadInfo.TryAdd(thread->mThreadId, NULL, &profileThreadInfoPtr))
  268. {
  269. mDebugger->TryGetThreadName(thread);
  270. profileThreadInfo = new ProfileThreadInfo();
  271. profileThreadInfo->mName = thread->mName;
  272. *profileThreadInfoPtr = profileThreadInfo;
  273. mThreadIdList.Add(thread->mThreadId);
  274. }
  275. else
  276. {
  277. profileThreadInfo = *profileThreadInfoPtr;
  278. }
  279. bool isThreadIdle = idleThreadSet.Contains(thread->mThreadId);
  280. profileThreadInfo->mTotalSamples += curSampleCount;
  281. if (isThreadIdle)
  282. profileThreadInfo->mTotalIdleSamples += curSampleCount;
  283. mDebugger->mActiveThread = thread;
  284. ::SuspendThread(thread->mHThread);
  285. CPURegisters registers;
  286. mDebugger->PopulateRegisters(&registers);
  287. int stackSize = 0;
  288. for (int stackIdx = 0; stackIdx < maxStackTrace; stackIdx++)
  289. {
  290. auto pc = registers.GetPC();
  291. if (pc <= 0xFFFF)
  292. {
  293. break;
  294. }
  295. stackTrace[stackSize++] = pc;
  296. auto prevSP = registers.GetSP();
  297. if (!mDebugger->RollBackStackFrame(&registers, stackIdx == 0))
  298. break;
  299. if (registers.GetSP() <= prevSP)
  300. {
  301. // SP went the wrong direction, stop rolling back
  302. break;
  303. }
  304. }
  305. ProfileAddrEntry* insertedProfileEntry = AddToSet(mProfileAddrEntrySet, stackTrace, stackSize);
  306. if (insertedProfileEntry->mEntryIdx == -1)
  307. {
  308. insertedProfileEntry->mEntryIdx = (int)mProfileAddrEntrySet.size(); // Starts at '1'
  309. mPendingProfileEntries.Add(*insertedProfileEntry);
  310. }
  311. for (int i = 0; i < curSampleCount; i++)
  312. {
  313. int entryIdx = insertedProfileEntry->mEntryIdx;
  314. if (isThreadIdle)
  315. entryIdx = -entryIdx;
  316. profileThreadInfo->mProfileAddrEntries.push_back(entryIdx);
  317. }
  318. ::ResumeThread(thread->mHThread);
  319. int elapsedTime = timeGetTime() - startTick;
  320. mTotalActiveSamplingMS += elapsedTime;
  321. mDebugger->mActiveThread = NULL;
  322. threadIdx++;
  323. HandlePendingEntries();
  324. }
  325. }
  326. mIsRunning = false;
  327. mEndTick = BFTickCount();
  328. }
  329. static void BFP_CALLTYPE ThreadProcThunk(void* profiler)
  330. {
  331. ((DbgProfiler*)profiler)->ThreadProc();
  332. }
  333. void DbgProfiler::Start()
  334. {
  335. BF_ASSERT(!mIsRunning);
  336. mNeedsProcessing = true;
  337. mIsRunning = true;
  338. auto thread = BfpThread_Create(ThreadProcThunk, (void*)this, 128 * 1024, BfpThreadCreateFlag_StackSizeReserve);
  339. BfpThread_Release(thread);
  340. }
  341. void DbgProfiler::Stop()
  342. {
  343. mShutdownEvent.Set();
  344. while (mIsRunning)
  345. BfpThread_Yield();
  346. //Process();
  347. }
  348. void DbgProfiler::Clear()
  349. {
  350. mWantsClear = true;
  351. }
  352. String DbgProfiler::GetOverview()
  353. {
  354. String str;
  355. uint32 curTick = BFTickCount();
  356. if (mEndTick == 0)
  357. str += StrFormat("%d", curTick - mStartTick);
  358. else
  359. str += StrFormat("%d\t%d", mEndTick - mStartTick, curTick - mEndTick);
  360. str += "\n";
  361. str += StrFormat("%d\t%d\t%d\n", mSamplesPerSecond, mTotalVirtualSamples, mTotalActualSamples);
  362. str += mDescription;
  363. return str;
  364. }
  365. String DbgProfiler::GetThreadList()
  366. {
  367. BF_ASSERT(!mIsRunning);
  368. String result;
  369. for (auto threadId : mThreadIdList)
  370. {
  371. auto threadInfo = mThreadInfo[threadId];
  372. int cpuUsage = 0;
  373. if (threadInfo->mTotalSamples > 0)
  374. cpuUsage = 100 - (threadInfo->mTotalIdleSamples * 100 / threadInfo->mTotalSamples);
  375. result += StrFormat("%d\t%d\t%s\n", threadId, cpuUsage, threadInfo->mName.c_str());
  376. }
  377. return result;
  378. }
  379. void DbgProfiler::AddEntries(String& str, Array<ProfileProcEntry*>& procEntries, int rangeStart, int rangeEnd, int stackIdx, ProfileProcId* findProc)
  380. {
  381. struct _QueuedEntry
  382. {
  383. int mRangeIdx;
  384. int mRangeEnd;
  385. int mStackIdx;
  386. };
  387. Array<_QueuedEntry> workQueue;
  388. auto _AddEntries = [&](int rangeStart, int rangeEnd, int stackIdx, ProfileProcId* findProc)
  389. {
  390. int selfSampleCount = 0;
  391. int childSampleCount = 0;
  392. // First arrange list so we only contain items that match 'findProc'
  393. if (stackIdx != -1)
  394. {
  395. for (int idx = rangeStart; idx < rangeEnd; idx++)
  396. {
  397. auto procEntry = procEntries[idx];
  398. if (procEntry->mUsed)
  399. continue;
  400. // The range should only contain candidates
  401. BF_ASSERT(procEntry->mSize > stackIdx);
  402. auto curProc = procEntry->mData[procEntry->mSize - 1 - stackIdx];
  403. bool doRemove = false;
  404. if (findProc != curProc)
  405. {
  406. // Not a match
  407. doRemove = true;
  408. }
  409. else
  410. {
  411. if (procEntry->mSize == stackIdx + 1)
  412. {
  413. BF_ASSERT(selfSampleCount == 0);
  414. selfSampleCount = procEntry->mSampleCount;
  415. // We are a the full actual entry, so we're done here...
  416. procEntry->mUsed = true;
  417. doRemove = true;
  418. }
  419. else
  420. {
  421. childSampleCount += procEntry->mSampleCount;
  422. }
  423. }
  424. if (doRemove)
  425. {
  426. // 'fast remove' entry
  427. BF_SWAP(procEntries[rangeEnd - 1], procEntries[idx]);
  428. rangeEnd--;
  429. idx--;
  430. continue;
  431. }
  432. }
  433. if (findProc == NULL)
  434. str += "???";
  435. else
  436. str += findProc->mProcName;
  437. char cStr[256];
  438. sprintf(cStr, "\t%d\t%d\n", selfSampleCount, childSampleCount);
  439. str += cStr;
  440. }
  441. _QueuedEntry entry;
  442. entry.mRangeIdx = rangeStart;
  443. entry.mRangeEnd = rangeEnd;
  444. entry.mStackIdx = stackIdx;
  445. workQueue.Add(entry);
  446. };
  447. _AddEntries(rangeStart, rangeEnd, stackIdx, findProc);
  448. while (!workQueue.IsEmpty())
  449. {
  450. auto& entry = workQueue.back();
  451. bool addedChild = false;
  452. while (entry.mRangeIdx < entry.mRangeEnd)
  453. {
  454. auto procEntry = procEntries[entry.mRangeIdx];
  455. if (procEntry->mUsed)
  456. {
  457. entry.mRangeIdx++;
  458. continue;
  459. }
  460. int nextStackIdx = entry.mStackIdx + 1;
  461. auto nextFindProc = procEntry->mData[procEntry->mSize - 1 - nextStackIdx];
  462. _AddEntries(entry.mRangeIdx, entry.mRangeEnd, nextStackIdx, nextFindProc);
  463. addedChild = true;
  464. break;
  465. }
  466. if (!addedChild)
  467. {
  468. if (entry.mStackIdx != -1)
  469. str += "-\n";
  470. workQueue.pop_back();
  471. }
  472. }
  473. }
  474. void DbgProfiler::HandlePendingEntries()
  475. {
  476. bool reverse = false;
  477. const int maxStackTrace = 1024;
  478. ProfileProcId* procStackTrace[maxStackTrace];
  479. while (mProfileAddrToProcMap.size() < mProfileAddrEntrySet.size() + 1)
  480. mProfileAddrToProcMap.push_back(-1);
  481. for (int addrEntryIdx = 0; addrEntryIdx < (int)mPendingProfileEntries.size(); addrEntryIdx++)
  482. {
  483. const ProfileAddrEntry* addrEntry = &mPendingProfileEntries[addrEntryIdx];
  484. ProfileProcId** procStackTraceHead = reverse ? (procStackTrace + maxStackTrace) : procStackTrace;
  485. int stackTraceProcIdx = 0;
  486. for (int addrIdx = 0; addrIdx < addrEntry->mSize; addrIdx++)
  487. {
  488. addr_target addr = addrEntry->mData[addrIdx];
  489. if (addrIdx > 0)
  490. {
  491. // To reference from SourcePC (calling address, not return address)
  492. addr--;
  493. }
  494. ProfileProcId* procId = NULL;
  495. auto subProgram = mDebugger->mDebugTarget->FindSubProgram(addr, DbgOnDemandKind_LocalOnly);
  496. while (stackTraceProcIdx < maxStackTrace)
  497. {
  498. if (subProgram != NULL)
  499. {
  500. ProfileProcId** procIdPtr;
  501. if (mProcMap.TryAdd(subProgram, NULL, &procIdPtr))
  502. {
  503. String procName = subProgram->ToString();
  504. procId = Get(procName);
  505. *procIdPtr = procId;
  506. }
  507. else
  508. procId = *procIdPtr;
  509. }
  510. else
  511. {
  512. String symbolName;
  513. addr_target symbolOffset = 0;
  514. if ((!mDebugger->mDebugTarget->FindSymbolAt(addr, &symbolName, &symbolOffset, NULL, false)) || (symbolOffset > 64 * 1024))
  515. {
  516. auto dbgModule = mDebugger->mDebugTarget->FindDbgModuleForAddress(addr);
  517. if (dbgModule != NULL)
  518. symbolName += dbgModule->mDisplayName + "!";
  519. symbolName += StrFormat("0x%@", addr);
  520. }
  521. bool isNew = false;
  522. procId = Get(symbolName, &isNew);
  523. if (isNew)
  524. procId->mIsIdle = mIdleSymbolNames.Contains(symbolName);
  525. }
  526. if (reverse)
  527. *(--procStackTraceHead) = procId;
  528. else
  529. procStackTraceHead[stackTraceProcIdx] = procId;
  530. stackTraceProcIdx++;
  531. if (subProgram == NULL)
  532. break;
  533. if (subProgram->mInlineeInfo == NULL)
  534. break;
  535. subProgram = subProgram->mInlineeInfo->mInlineParent;
  536. }
  537. }
  538. auto procEntry = AddToSet(mProfileProcEntrySet, procStackTraceHead, stackTraceProcIdx);
  539. if (procEntry->mEntryIdx == -1)
  540. {
  541. procEntry->mEntryIdx = (int)mProfileProcEntrySet.size() - 1; // Start at '0'
  542. }
  543. mProfileAddrToProcMap[addrEntry->mEntryIdx] = procEntry->mEntryIdx;
  544. }
  545. mPendingProfileEntries.Clear();
  546. }
  547. void DbgProfiler::Process()
  548. {
  549. AutoCrit autoCrit(mDebugger->mDebugManager->mCritSect);
  550. mNeedsProcessing = false;
  551. int time = mTotalActiveSamplingMS;
  552. BF_ASSERT(mProfileAddrEntries.IsEmpty());
  553. mProfileAddrEntries.Resize(mProfileAddrEntrySet.size() + 1);
  554. for (auto& val : mProfileAddrEntrySet)
  555. mProfileAddrEntries[val.mEntryIdx] = &val;
  556. BF_ASSERT(mProfileProcEntries.IsEmpty());
  557. mProfileProcEntries.Resize(mProfileProcEntrySet.size());
  558. for (auto& val : mProfileProcEntrySet)
  559. mProfileProcEntries[val.mEntryIdx] = &val;
  560. for (auto threadKV : mThreadInfo)
  561. {
  562. auto threadInfo = threadKV.mValue;
  563. for (auto addrEntryIdx : threadInfo->mProfileAddrEntries)
  564. {
  565. if (addrEntryIdx < 0)
  566. {
  567. addrEntryIdx = -addrEntryIdx;
  568. }
  569. int procEntryIdx = mProfileAddrToProcMap[addrEntryIdx];
  570. auto procEntry = mProfileProcEntries[procEntryIdx];
  571. auto curProc = procEntry->mData[0];
  572. if (curProc->mIsIdle)
  573. threadInfo->mTotalIdleSamples++;
  574. }
  575. }
  576. }
  577. String DbgProfiler::GetCallTree(int threadId, bool reverse)
  578. {
  579. if (mNeedsProcessing)
  580. Process();
  581. AutoCrit autoCrit(mDebugger->mDebugManager->mCritSect);
  582. BF_ASSERT(!mIsRunning);
  583. Array<ProfileProcEntry*> procEntries;
  584. String str;
  585. for (auto procEntry : mProfileProcEntries)
  586. {
  587. procEntry->mUsed = false;
  588. procEntry->mSampleCount = 0;
  589. }
  590. bool showIdleEntries = false;
  591. int totalSampleCount = 0;
  592. int totalActiveSampleCount = 0;
  593. for (auto& threadPair : mThreadInfo)
  594. {
  595. if ((threadId == 0) || (threadId == threadPair.mKey))
  596. {
  597. auto threadInfo = threadPair.mValue;
  598. totalSampleCount += threadInfo->mTotalSamples;
  599. for (auto addrEntryIdx : threadInfo->mProfileAddrEntries)
  600. {
  601. if (addrEntryIdx < 0)
  602. {
  603. if (showIdleEntries)
  604. addrEntryIdx = -addrEntryIdx;
  605. else
  606. continue;
  607. }
  608. int procEntryIdx = mProfileAddrToProcMap[addrEntryIdx];
  609. auto procEntry = mProfileProcEntries[procEntryIdx];
  610. if (procEntry->mSampleCount == 0)
  611. procEntries.push_back(procEntry);
  612. procEntry->mSampleCount++;
  613. totalActiveSampleCount++;
  614. }
  615. }
  616. }
  617. if (threadId == 0)
  618. str += "All Threads";
  619. else
  620. str += StrFormat("Thread %d", threadId);
  621. str += StrFormat("\t0\t%d\n", totalSampleCount);
  622. int idleTicks = totalSampleCount - totalActiveSampleCount;
  623. if (idleTicks != 0)
  624. str += StrFormat("<Idle>\t%d\t0\n-\n", idleTicks);
  625. AddEntries(str, procEntries, 0, (int)procEntries.size(), -1, NULL);
  626. str += "-\n";
  627. //BF_ASSERT(childSampleCount == totalSampleCount);
  628. return str;
  629. }