Profiler.cpp 18 KB

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