1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +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
{
public void Dispose()
public bool HasValue
{
get
{
return this != 0;
}
}
public void Dispose() mut
{
if (this == 0)
return;
String str = scope String();
str.Append("StopSampling\t");
((int32)this).ToString(str);
Internal.ProfilerCmd(str);
this = 0;
}
}

View file

@ -79,6 +79,7 @@ namespace IDE.Compiler
public int32 mTextVersion = -1;
public bool mIsUserRequested;
public Stopwatch mStopwatch ~ delete _;
public ProfileInstance mProfileInstance ~ _.Dispose();
}
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)
{
if (mProfiler == null)
@ -307,6 +311,7 @@ namespace IDE.ui
var threadListStr = scope String();
mProfiler.GetThreadList(threadListStr);
List<ThreadEntry> entries = scope .();
for (var entry in threadListStr.Split('\n'))
{
if (entry.Length == 0)
@ -314,20 +319,35 @@ namespace IDE.ui
var dataItr = entry.Split('\t');
int32 threadId = int32.Parse(dataItr.GetNext());
StringView threadName = dataItr.GetNext();
ThreadEntry threadEntry = default;
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;
var str = scope String();
str.AppendF("{0}", threadId);
if (!threadName.IsEmpty)
str.AppendF("{0}", entry.mThreadId);
str.AppendF($" ({entry.mCPUUsage}%)");
if (!entry.mName.IsEmpty)
{
threadStr = new String(threadName);
str.AppendF(" - {0}", threadName);
threadStr = new String(entry.mName);
str.AppendF($" - {entry.mName}");
}
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();
if (gApp.mDbgTimeAutocomplete)
resolveParams.mStopwatch = new .()..Start();
if (gApp.mDbgPerfAutocomplete)
resolveParams.mProfileInstance = Profiler.StartSampling("Autocomplete").GetValueOrDefault();
resolveParams.mIsUserRequested = options.HasFlag(.UserRequested);
Classify(.Autocomplete, resolveParams);
if (!resolveParams.mInDeferredList)

View file

@ -101,6 +101,12 @@ DbgProfiler::DbgProfiler(WinDebugger* debugger) : mShutdownEvent(true)
mEndTick = 0;
mDebugger->AddProfiler(this);
mIdleSymbolNames.Add("ZwDelayExecution");
mIdleSymbolNames.Add("ZwWaitForWorkViaWorkerFactory");
mIdleSymbolNames.Add("NtWaitForAlertByThreadId");
mIdleSymbolNames.Add("NtWaitForSingleObject");
mIdleSymbolNames.Add("NtWaitForMultipleObjects");
}
DbgProfiler::~DbgProfiler()
@ -148,7 +154,7 @@ void DbgProfiler::DoClear()
delete val.mProcId;
}
ProfileProcId* DbgProfiler::Get(const StringImpl& str)
ProfileProcId* DbgProfiler::Get(const StringImpl& str, bool* outIsNew)
{
ProfileProdIdEntry checkEntry;
checkEntry.mProcId = (ProfileProcId*)&str;
@ -158,11 +164,16 @@ ProfileProcId* DbgProfiler::Get(const StringImpl& str)
{
auto procId = new ProfileProcId();
procId->mProcName = str;
procId->mIsIdle = false;
entryPtr->mProcId = procId;
if (outIsNew != NULL)
*outIsNew = true;
return procId;
}
else
{
if (outIsNew != NULL)
*outIsNew = false;
return entryPtr->mProcId;
}
}
@ -222,6 +233,8 @@ void DbgProfiler::ThreadProc()
SYSTEM_PROCESS_INFORMATION* processData = NULL;
std::unique_ptr<SYSTEM_PROCESS_INFORMATION> ptrDelete(processData);
//processData = CaptureProcessInfo();
auto curProcessData = processData;
while (true)
{
@ -235,6 +248,7 @@ void DbgProfiler::ThreadProc()
auto& threadInfo = curProcessData->Threads[threadIdx];
if ((threadInfo.State == StateWait) || (threadInfo.State == StateTerminated))
idleThreadSet.Add((int)(intptr)threadInfo.ClientId.UniqueThread);
break;
}
}
@ -321,10 +335,12 @@ void DbgProfiler::ThreadProc()
profileThreadInfo = *profileThreadInfoPtr;
}
profileThreadInfo->mTotalSamples += curSampleCount;
bool isThreadIdle = idleThreadSet.Contains(thread->mThreadId);
profileThreadInfo->mTotalSamples += curSampleCount;
if (isThreadIdle)
profileThreadInfo->mTotalIdleSamples += curSampleCount;
mDebugger->mActiveThread = thread;
::SuspendThread(thread->mHThread);
@ -439,7 +455,10 @@ String DbgProfiler::GetThreadList()
for (auto threadId : mThreadIdList)
{
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;
@ -608,7 +627,10 @@ void DbgProfiler::HandlePendingEntries()
symbolName += StrFormat("0x%@", addr);
}
procId = Get(symbolName);
bool isNew = false;
procId = Get(symbolName, &isNew);
if (isNew)
procId->mIsIdle = mIdleSymbolNames.Contains(symbolName);
}
if (reverse)
@ -654,6 +676,25 @@ void DbgProfiler::Process()
for (auto& val : mProfileProcEntrySet)
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)

View file

@ -67,6 +67,7 @@ class ProfileProcId
{
public:
String mProcName;
bool mIsIdle;
};
class ProfileProdIdEntry
@ -104,12 +105,14 @@ class ProfileThreadInfo
public:
Array<int> mProfileAddrEntries;
int mTotalSamples;
int mTotalIdleSamples;
String mName;
public:
ProfileThreadInfo()
{
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)
HashSet<ProfileProdIdEntry> mUniqueProcSet;
HashSet<String> mIdleSymbolNames;
public:
void ThreadProc();
@ -171,7 +175,7 @@ public:
void HandlePendingEntries();
void Process();
void DoClear();
ProfileProcId* Get(const StringImpl& str);
ProfileProcId* Get(const StringImpl& str, bool* outIsNew = NULL);
public:
DbgProfiler(WinDebugger* debugger);