Skip to content

Commit b242af8

Browse files
author
Mike Kaufman
committed
[MERGE #5426 @mike-kaufman] Adding CPU times to timing counters where UI thread is blocked by GC
Merge pull request #5426 from mike-kaufman:build/mkaufman/get-cpu-time-for-wait-periods We've been capturing wall-clock times for how long the user thread is blocked by GC. This is useful but the data can be skewed by suspended tabs in the browser. Adding capture of CPU time to give us better insight here.
2 parents b691e7a + 0a85e0d commit b242af8

File tree

3 files changed

+91
-4
lines changed

3 files changed

+91
-4
lines changed

lib/Common/Memory/Recycler.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5712,6 +5712,22 @@ Recycler::FinishConcurrentCollectWrapped(CollectionFlags flags)
57125712
return collected;
57135713
}
57145714

5715+
5716+
/**
5717+
* Compute ft1 - ft2, return result as a uint64
5718+
*/
5719+
uint64 DiffFileTimes(LPFILETIME ft1, LPFILETIME ft2)
5720+
{
5721+
ULARGE_INTEGER ul1;
5722+
ULARGE_INTEGER ul2;
5723+
ul1.HighPart = ft1->dwHighDateTime;
5724+
ul1.LowPart = ft1->dwLowDateTime;
5725+
ul2.HighPart = ft2->dwHighDateTime;
5726+
ul2.LowPart = ft2->dwLowDateTime;
5727+
ULONGLONG result = ul1.QuadPart - ul2.QuadPart;
5728+
return result;
5729+
}
5730+
57155731
BOOL
57165732
Recycler::WaitForConcurrentThread(DWORD waitTime, RecyclerWaitReason caller)
57175733
{
@@ -5726,8 +5742,22 @@ Recycler::WaitForConcurrentThread(DWORD waitTime, RecyclerWaitReason caller)
57265742
}
57275743

57285744
#ifdef ENABLE_BASIC_TELEMETRY
5729-
bool isBlockingMainThread = this->telemetryStats.IsOnScriptThread();
5730-
Js::Tick start = Js::Tick::Now();
5745+
bool isBlockingMainThread = false;
5746+
Js::Tick start;
5747+
FILETIME kernelTime1;
5748+
FILETIME userTime1;
5749+
HANDLE hProcess = GetCurrentProcess();
5750+
if (this->telemetryStats.ShouldStartTelemetryCapture())
5751+
{
5752+
isBlockingMainThread = this->telemetryStats.IsOnScriptThread();
5753+
if (isBlockingMainThread)
5754+
{
5755+
start = Js::Tick::Now();
5756+
FILETIME creationTime;
5757+
FILETIME exitTime;
5758+
GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime1, &userTime1);
5759+
}
5760+
}
57315761
#endif
57325762

57335763
DWORD ret = WaitForSingleObject(concurrentWorkDoneEvent, waitTime);
@@ -5737,7 +5767,24 @@ Recycler::WaitForConcurrentThread(DWORD waitTime, RecyclerWaitReason caller)
57375767
{
57385768
Js::Tick end = Js::Tick::Now();
57395769
Js::TickDelta elapsed = end - start;
5770+
5771+
FILETIME creationTime;
5772+
FILETIME exitTime;
5773+
FILETIME kernelTime2;
5774+
FILETIME userTime2;
5775+
5776+
GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime2, &userTime2);
5777+
uint64 kernelTime = DiffFileTimes(&kernelTime2 , &kernelTime1);
5778+
uint64 userTime = DiffFileTimes(&userTime2, &userTime1);
5779+
5780+
// userTime & kernelTime reported from GetProcessTimes is the number of 100-nanosecond ticks
5781+
// for consistency convert to microseconds.
5782+
kernelTime = kernelTime / 10;
5783+
userTime = userTime / 10;
5784+
57405785
this->telemetryStats.IncrementUserThreadBlockedCount(elapsed.ToMicroseconds(), caller);
5786+
this->telemetryStats.IncrementUserThreadBlockedCpuTimeUser(userTime, caller);
5787+
this->telemetryStats.IncrementUserThreadBlockedCpuTimeKernel(kernelTime, caller);
57415788
}
57425789
#endif
57435790

lib/Common/Memory/RecyclerTelemetryInfo.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,41 @@ namespace Memory
252252
}
253253
}
254254

255+
256+
void RecyclerTelemetryInfo::IncrementUserThreadBlockedCpuTimeUser(uint64 userMicroseconds, RecyclerWaitReason caller)
257+
{
258+
RecyclerTelemetryGCPassStats* lastPassStats = this->GetLastPassStats();
259+
#ifdef DBG
260+
if (this->inPassActiveState)
261+
{
262+
AssertMsg(lastPassStats != nullptr && lastPassStats->isGCPassActive == true, "unexpected Value in RecyclerTelemetryInfo::IncrementUserThreadBlockedCpuTimeUser");
263+
}
264+
#endif
265+
266+
if (this->inPassActiveState && lastPassStats != nullptr)
267+
{
268+
AssertOnValidThread(this, RecyclerTelemetryInfo::IncrementUserThreadBlockedCpuTimeUser);
269+
lastPassStats->uiThreadBlockedCpuTimesUser[caller] += userMicroseconds;
270+
}
271+
}
272+
273+
void RecyclerTelemetryInfo::IncrementUserThreadBlockedCpuTimeKernel(uint64 kernelMicroseconds, RecyclerWaitReason caller)
274+
{
275+
RecyclerTelemetryGCPassStats* lastPassStats = this->GetLastPassStats();
276+
#ifdef DBG
277+
if (this->inPassActiveState)
278+
{
279+
AssertMsg(lastPassStats != nullptr && lastPassStats->isGCPassActive == true, "unexpected Value in RecyclerTelemetryInfo::IncrementUserThreadBlockedCpuTimeKernel");
280+
}
281+
#endif
282+
283+
if (this->inPassActiveState && lastPassStats != nullptr)
284+
{
285+
AssertOnValidThread(this, RecyclerTelemetryInfo::IncrementUserThreadBlockedCpuTimeKernel);
286+
lastPassStats->uiThreadBlockedCpuTimesKernel[caller] += kernelMicroseconds;
287+
}
288+
}
289+
255290
bool RecyclerTelemetryInfo::IsOnScriptThread() const
256291
{
257292
bool isValid = false;

lib/Common/Memory/RecyclerTelemetryInfo.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ namespace Memory
5959
Js::TickDelta computeBucketStatsElapsedTime;
6060
FILETIME lastScriptExecutionEndTime;
6161
Js::TickDelta uiThreadBlockedTimes[static_cast<size_t>(RecyclerWaitReason::Other) + 1];
62+
uint64 uiThreadBlockedCpuTimesUser[static_cast<size_t>(RecyclerWaitReason::Other) + 1];
63+
uint64 uiThreadBlockedCpuTimesKernel[static_cast<size_t>(RecyclerWaitReason::Other) + 1];
6264
bool isInScript;
6365
bool isScriptActive;
6466
bool isGCPassActive;
@@ -99,7 +101,9 @@ namespace Memory
99101
void StartPass(CollectionState collectionState);
100102
void EndPass(CollectionState collectionState);
101103
void IncrementUserThreadBlockedCount(Js::TickDelta waitTime, RecyclerWaitReason source);
102-
104+
void IncrementUserThreadBlockedCpuTimeUser(uint64 userMicroseconds, RecyclerWaitReason caller);
105+
void IncrementUserThreadBlockedCpuTimeKernel(uint64 kernelMicroseconds, RecyclerWaitReason caller);
106+
103107
inline const Js::Tick& GetRecyclerStartTime() const { return this->recyclerStartTime; }
104108
RecyclerTelemetryGCPassStats* GetLastPassStats() const;
105109
inline const Js::Tick& GetLastTransmitTime() const { return this->lastTransmitTime; }
@@ -116,6 +120,8 @@ namespace Memory
116120
AllocatorDecommitStats* GetRecyclerWithBarrierPageAllocator_decommitStats() { return &this->recyclerWithBarrierPageAllocator_decommitStats; }
117121
#endif
118122

123+
bool ShouldStartTelemetryCapture() const;
124+
119125
private:
120126
Recycler* recycler;
121127
DWORD mainThreadID;
@@ -135,7 +141,6 @@ namespace Memory
135141
AllocatorDecommitStats recyclerWithBarrierPageAllocator_decommitStats;
136142
#endif
137143

138-
bool ShouldStartTelemetryCapture() const;
139144
bool ShouldTransmit() const;
140145
void FreeGCPassStats();
141146
void Reset();

0 commit comments

Comments
 (0)