1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-14 14:24:10 +02:00

Added CPU usage to profiler thread list

This commit is contained in:
Brian Fiete 2021-12-28 08:30:33 -05:00
parent 8c086eb7ef
commit 7c03f5e2cd
6 changed files with 139 additions and 60 deletions

View file

@ -10,12 +10,23 @@ namespace System.Diagnostics
struct ProfileInstance : int32 struct ProfileInstance : int32
{ {
public void Dispose() public bool HasValue
{ {
get
{
return this != 0;
}
}
public void Dispose() mut
{
if (this == 0)
return;
String str = scope String(); String str = scope String();
str.Append("StopSampling\t"); str.Append("StopSampling\t");
((int32)this).ToString(str); ((int32)this).ToString(str);
Internal.ProfilerCmd(str); Internal.ProfilerCmd(str);
this = 0;
} }
} }

View file

@ -79,6 +79,7 @@ namespace IDE.Compiler
public int32 mTextVersion = -1; public int32 mTextVersion = -1;
public bool mIsUserRequested; public bool mIsUserRequested;
public Stopwatch mStopwatch ~ delete _; public Stopwatch mStopwatch ~ delete _;
public ProfileInstance mProfileInstance ~ _.Dispose();
} }
public class BfParser : ILeakIdentifiable public class BfParser : ILeakIdentifiable

View file

@ -293,6 +293,10 @@ namespace IDE.ui
} }
} }
struct ThreadEntry : this(int32 mThreadId, int32 mCPUUsage, StringView mName)
{
}
void PopulateThreadList(Menu menu) void PopulateThreadList(Menu menu)
{ {
if (mProfiler == null) if (mProfiler == null)
@ -307,6 +311,7 @@ namespace IDE.ui
var threadListStr = scope String(); var threadListStr = scope String();
mProfiler.GetThreadList(threadListStr); mProfiler.GetThreadList(threadListStr);
List<ThreadEntry> entries = scope .();
for (var entry in threadListStr.Split('\n')) for (var entry in threadListStr.Split('\n'))
{ {
if (entry.Length == 0) if (entry.Length == 0)
@ -314,20 +319,35 @@ namespace IDE.ui
var dataItr = entry.Split('\t'); var dataItr = entry.Split('\t');
int32 threadId = int32.Parse(dataItr.GetNext()); ThreadEntry threadEntry = default;
StringView threadName = dataItr.GetNext(); threadEntry.mThreadId = int32.Parse(dataItr.GetNext());
threadEntry.mCPUUsage = int32.Parse(dataItr.GetNext());
threadEntry.mName = dataItr.GetNext();
entries.Add(threadEntry);
}
entries.Sort(scope (lhs, rhs) =>
{
int cmp = rhs.mCPUUsage <=> lhs.mCPUUsage;
if (cmp == 0)
cmp = lhs.mThreadId <=> rhs.mThreadId;
return cmp;
});
for (var entry in entries)
{
String threadStr = null; String threadStr = null;
var str = scope String(); var str = scope String();
str.AppendF("{0}", threadId); str.AppendF("{0}", entry.mThreadId);
if (!threadName.IsEmpty) str.AppendF($" ({entry.mCPUUsage}%)");
if (!entry.mName.IsEmpty)
{ {
threadStr = new String(threadName); threadStr = new String(entry.mName);
str.AppendF(" - {0}", threadName); str.AppendF($" - {entry.mName}");
} }
subItem = menu.AddItem(str); subItem = menu.AddItem(str);
subItem.mOnMenuItemSelected.Add(new (item) => { Show(threadId, threadStr); } ~ delete threadStr); subItem.mOnMenuItemSelected.Add(new (item) => { Show(entry.mThreadId, threadStr); } ~ delete threadStr);
} }
} }

View file

@ -620,6 +620,8 @@ namespace IDE.ui
ResolveParams resolveParams = new ResolveParams(); ResolveParams resolveParams = new ResolveParams();
if (gApp.mDbgTimeAutocomplete) if (gApp.mDbgTimeAutocomplete)
resolveParams.mStopwatch = new .()..Start(); resolveParams.mStopwatch = new .()..Start();
if (gApp.mDbgPerfAutocomplete)
resolveParams.mProfileInstance = Profiler.StartSampling("Autocomplete").GetValueOrDefault();
resolveParams.mIsUserRequested = options.HasFlag(.UserRequested); resolveParams.mIsUserRequested = options.HasFlag(.UserRequested);
Classify(.Autocomplete, resolveParams); Classify(.Autocomplete, resolveParams);
if (!resolveParams.mInDeferredList) if (!resolveParams.mInDeferredList)

View file

@ -81,7 +81,7 @@ struct SYSTEM_PROCESS_INFORMATION
SYSTEM_THREAD Threads[1]; SYSTEM_THREAD Threads[1];
}; };
typedef NTSTATUS(NTAPI *NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); typedef NTSTATUS(NTAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
static NtQuerySystemInformation_t NtQuerySystemInformation = NULL; static NtQuerySystemInformation_t NtQuerySystemInformation = NULL;
static HMODULE ntdll = NULL; static HMODULE ntdll = NULL;
@ -101,6 +101,12 @@ DbgProfiler::DbgProfiler(WinDebugger* debugger) : mShutdownEvent(true)
mEndTick = 0; mEndTick = 0;
mDebugger->AddProfiler(this); mDebugger->AddProfiler(this);
mIdleSymbolNames.Add("ZwDelayExecution");
mIdleSymbolNames.Add("ZwWaitForWorkViaWorkerFactory");
mIdleSymbolNames.Add("NtWaitForAlertByThreadId");
mIdleSymbolNames.Add("NtWaitForSingleObject");
mIdleSymbolNames.Add("NtWaitForMultipleObjects");
} }
DbgProfiler::~DbgProfiler() DbgProfiler::~DbgProfiler()
@ -148,7 +154,7 @@ void DbgProfiler::DoClear()
delete val.mProcId; delete val.mProcId;
} }
ProfileProcId* DbgProfiler::Get(const StringImpl& str) ProfileProcId* DbgProfiler::Get(const StringImpl& str, bool* outIsNew)
{ {
ProfileProdIdEntry checkEntry; ProfileProdIdEntry checkEntry;
checkEntry.mProcId = (ProfileProcId*)&str; checkEntry.mProcId = (ProfileProcId*)&str;
@ -158,11 +164,16 @@ ProfileProcId* DbgProfiler::Get(const StringImpl& str)
{ {
auto procId = new ProfileProcId(); auto procId = new ProfileProcId();
procId->mProcName = str; procId->mProcName = str;
procId->mIsIdle = false;
entryPtr->mProcId = procId; entryPtr->mProcId = procId;
if (outIsNew != NULL)
*outIsNew = true;
return procId; return procId;
} }
else else
{ {
if (outIsNew != NULL)
*outIsNew = false;
return entryPtr->mProcId; return entryPtr->mProcId;
} }
} }
@ -222,6 +233,8 @@ void DbgProfiler::ThreadProc()
SYSTEM_PROCESS_INFORMATION* processData = NULL; SYSTEM_PROCESS_INFORMATION* processData = NULL;
std::unique_ptr<SYSTEM_PROCESS_INFORMATION> ptrDelete(processData); std::unique_ptr<SYSTEM_PROCESS_INFORMATION> ptrDelete(processData);
//processData = CaptureProcessInfo();
auto curProcessData = processData; auto curProcessData = processData;
while (true) while (true)
{ {
@ -235,6 +248,7 @@ void DbgProfiler::ThreadProc()
auto& threadInfo = curProcessData->Threads[threadIdx]; auto& threadInfo = curProcessData->Threads[threadIdx];
if ((threadInfo.State == StateWait) || (threadInfo.State == StateTerminated)) if ((threadInfo.State == StateWait) || (threadInfo.State == StateTerminated))
idleThreadSet.Add((int)(intptr)threadInfo.ClientId.UniqueThread); idleThreadSet.Add((int)(intptr)threadInfo.ClientId.UniqueThread);
break;
} }
} }
@ -321,10 +335,12 @@ void DbgProfiler::ThreadProc()
profileThreadInfo = *profileThreadInfoPtr; profileThreadInfo = *profileThreadInfoPtr;
} }
profileThreadInfo->mTotalSamples += curSampleCount;
bool isThreadIdle = idleThreadSet.Contains(thread->mThreadId); bool isThreadIdle = idleThreadSet.Contains(thread->mThreadId);
profileThreadInfo->mTotalSamples += curSampleCount;
if (isThreadIdle)
profileThreadInfo->mTotalIdleSamples += curSampleCount;
mDebugger->mActiveThread = thread; mDebugger->mActiveThread = thread;
::SuspendThread(thread->mHThread); ::SuspendThread(thread->mHThread);
@ -333,7 +349,7 @@ void DbgProfiler::ThreadProc()
mDebugger->PopulateRegisters(&registers); mDebugger->PopulateRegisters(&registers);
int stackSize = 0; int stackSize = 0;
for (int stackIdx = 0 ; stackIdx < maxStackTrace; stackIdx++) for (int stackIdx = 0; stackIdx < maxStackTrace; stackIdx++)
{ {
auto pc = registers.GetPC(); auto pc = registers.GetPC();
if (pc <= 0xFFFF) if (pc <= 0xFFFF)
@ -439,7 +455,10 @@ String DbgProfiler::GetThreadList()
for (auto threadId : mThreadIdList) for (auto threadId : mThreadIdList)
{ {
auto threadInfo = mThreadInfo[threadId]; auto threadInfo = mThreadInfo[threadId];
result += StrFormat("%d\t%s\n", threadId, threadInfo->mName.c_str()); int cpuUsage = 0;
if (threadInfo->mTotalSamples > 0)
cpuUsage = 100 - (threadInfo->mTotalIdleSamples * 100 / threadInfo->mTotalSamples);
result += StrFormat("%d\t%d\t%s\n", threadId, cpuUsage, threadInfo->mName.c_str());
} }
return result; return result;
@ -608,7 +627,10 @@ void DbgProfiler::HandlePendingEntries()
symbolName += StrFormat("0x%@", addr); symbolName += StrFormat("0x%@", addr);
} }
procId = Get(symbolName); bool isNew = false;
procId = Get(symbolName, &isNew);
if (isNew)
procId->mIsIdle = mIdleSymbolNames.Contains(symbolName);
} }
if (reverse) if (reverse)
@ -654,6 +676,25 @@ void DbgProfiler::Process()
for (auto& val : mProfileProcEntrySet) for (auto& val : mProfileProcEntrySet)
mProfileProcEntries[val.mEntryIdx] = &val; mProfileProcEntries[val.mEntryIdx] = &val;
for (auto threadKV : mThreadInfo)
{
auto threadInfo = threadKV.mValue;
for (auto addrEntryIdx : threadInfo->mProfileAddrEntries)
{
if (addrEntryIdx < 0)
{
addrEntryIdx = -addrEntryIdx;
}
int procEntryIdx = mProfileAddrToProcMap[addrEntryIdx];
auto procEntry = mProfileProcEntries[procEntryIdx];
auto curProc = procEntry->mData[0];
if (curProc->mIsIdle)
threadInfo->mTotalIdleSamples++;
}
}
} }
String DbgProfiler::GetCallTree(int threadId, bool reverse) String DbgProfiler::GetCallTree(int threadId, bool reverse)

View file

@ -67,6 +67,7 @@ class ProfileProcId
{ {
public: public:
String mProcName; String mProcName;
bool mIsIdle;
}; };
class ProfileProdIdEntry class ProfileProdIdEntry
@ -104,12 +105,14 @@ class ProfileThreadInfo
public: public:
Array<int> mProfileAddrEntries; Array<int> mProfileAddrEntries;
int mTotalSamples; int mTotalSamples;
int mTotalIdleSamples;
String mName; String mName;
public: public:
ProfileThreadInfo() ProfileThreadInfo()
{ {
mTotalSamples = 0; mTotalSamples = 0;
mTotalIdleSamples = 0;
} }
}; };
@ -143,6 +146,7 @@ public:
Dictionary<void*, ProfileProcId*> mProcMap; // Keyed on either DwSubprogram or DwSymbol. Multiple pointers can reference the same ProfileProcId (in the case of inlined functions, for example) Dictionary<void*, ProfileProcId*> mProcMap; // Keyed on either DwSubprogram or DwSymbol. Multiple pointers can reference the same ProfileProcId (in the case of inlined functions, for example)
HashSet<ProfileProdIdEntry> mUniqueProcSet; HashSet<ProfileProdIdEntry> mUniqueProcSet;
HashSet<String> mIdleSymbolNames;
public: public:
void ThreadProc(); void ThreadProc();
@ -171,7 +175,7 @@ public:
void HandlePendingEntries(); void HandlePendingEntries();
void Process(); void Process();
void DoClear(); void DoClear();
ProfileProcId* Get(const StringImpl& str); ProfileProcId* Get(const StringImpl& str, bool* outIsNew = NULL);
public: public:
DbgProfiler(WinDebugger* debugger); DbgProfiler(WinDebugger* debugger);