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:
parent
8c086eb7ef
commit
7c03f5e2cd
6 changed files with 139 additions and 60 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(®isters);
|
mDebugger->PopulateRegisters(®isters);
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue