Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
ThreadHeartBeat.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Containers/Map.h"
5#include "CoreTypes.h"
6#include "Delegates/Delegate.h"
7#include "Delegates/DelegateCombinations.h" // for DECLARE_DELEGATE_OneParam
8#include "HAL/CriticalSection.h"
9#include "HAL/Runnable.h"
10#include "HAL/ThreadSafeBool.h"
11#include "HAL/ThreadSafeCounter.h"
12#include "Misc/AssertionMacros.h"
13#include "UObject/NameTypes.h"
14
16#include "Unix/UnixSignalHeartBeat.h"
17#endif
18
19DECLARE_DELEGATE_OneParam(FOnThreadStuck, uint32);
20DECLARE_DELEGATE_OneParam(FOnThreadUnstuck, uint32);
21
23DECLARE_DELEGATE_OneParam(FOnHangDelegate, uint32);
24#endif
25
26/**
27 * Our own local clock.
28 * Platforms that support suspend/resume have problems where a suspended title acts like
29 * a long hitch, causing the hang detector to fire incorrectly when the title is resumed.
30 *
31 * To solve this, we accumulate our own time on the hang detector thread.
32 * When the title is suspended, this thread is also suspended, and the local clock stops.
33 * The delta is clamped so if we are resumed, the clock continues from where it left off.
34 */
36{
39 const uint64 MaxTimeStepCycles;
40
41public:
42 FThreadHeartBeatClock(double InMaxTimeStep);
43
44 void Tick();
45 double Seconds();
46};
47
48/**
49 * Thread heartbeat check class.
50 * Used by crash handling code to check for hangs.
51 */
53{
55
56 /** Holds per-thread info about the heartbeat */
58 {
61 , LastHangTime(0.0)
63 , HangDuration(0)
64 , LastStuckTime(0.0)
65 , StuckDuration(0.0)
67 {}
68
69 /** Time we last received a heartbeat for the current thread */
71 /** Time we last detected a hang due to lack of heartbeats for the current thread */
73 /** Suspended counter */
75 /** The timeout for this thread */
77
78 /** Time we last detected thread stuck due to lack of heartbeats for the current thread */
80 /** How long it's benn stuck thread */
82 /** An optional FName */
84
85 /** Suspends this thread's heartbeat */
86 void Suspend()
87 {
89 }
90 /** Resumes this thread's heartbeat */
91 void Resume(double CurrentTime)
92 {
93 check(SuspendedCount > 0);
94 if (--SuspendedCount == 0)
95 {
96 LastHeartBeatTime = CurrentTime;
97 }
98 }
99 };
100 /** Thread to run the worker FRunnable on */
102 /** Stops this thread */
104 /** Synch object for the heartbeat */
106 /** Keeps track of the last heartbeat time for threads */
108 /** The last heartbeat time for the rendering or RHI thread frame present. */
110
111 /** Synch object for the function heartbeat */
113 /** Keeps track of the last heartbeat time for a function, can't be nested */
115
116 /** Synch object for the checkpoint heartbeat */
118 /** Keeps track of the last heartbeat time for a checkpoint */
120
121 /** True if heartbeat should be measured */
123 /** Max time the thread is allowed to not send the heartbeat*/
130
132
133 /** CRC of the last hang's callstack */
135 /** Id of the last thread that hung */
137
139
141
144
146
147 FOnThreadStuck OnStuck;
148 FOnThreadUnstuck OnUnstuck;
151#endif
152
155
157
158 void FORCENOINLINE OnHang(double HangDuration, uint32 ThreadThatHung);
159 void FORCENOINLINE OnPresentHang(double HangDuration);
160
161 bool IsEnabled();
162
163public:
164
166 {
167 /** Invalid thread Id used by CheckHeartBeat */
168 InvalidThreadId = (uint32)-1,
169
170 /** Id used to track presented frames (supported platforms only). */
171 PresentThreadId = (uint32)-2
172 };
173
174 /** Gets the heartbeat singleton */
177
178 /** Begin measuring heartbeat */
179 void Start();
180 /** Called from a thread once per frame to update the heartbeat time */
181 void HeartBeat(bool bReadConfig = false);
182 /** Called from the rendering or RHI thread when the platform RHI presents a frame (supported platforms only). */
184 /** Called by a supervising thread to check the threads' health */
185 uint32 CheckHeartBeat(double& OutHangDuration);
186 /** Called by a thread when it's no longer expecting to be ticked */
188
189 /** Called from a thread once on entry to a function to be monitored */
191 /** Called by a thread when a function has completed and no longer needs to be monitored */
193 /** Called by a supervising thread to check all function calls' being monitored health */
194 uint32 CheckFunctionHeartBeat(double& OutHangDuration);
195
196 /*
197 Called from a thread to register a checkpoint to be monitored
198 @param EndCheckPoint name of the checkpoint that needs to be reached. TimeToReachCheckPoint the time duration we have to reach the specified checkpoint.
199 */
200 void MonitorCheckpointStart(FName EndCheckPoint, double TimeToReachCheckpoint);
201 /* Called from a thread when a checkpoint has ended */
202 void MonitorCheckpointEnd(FName CheckPoint);
203 /* Called by a supervising thread to check all checkpoints forward progress */
204 uint32 CheckCheckpointHeartBeat(double& OutHangDuration);
205
206 /**
207 * Suspend heartbeat measuring for the current thread if the thread has already had a heartbeat
208 * @param bAllThreads If true, suspends heartbeat for all threads, not only the current one
209 */
210 void SuspendHeartBeat(bool bAllThreads = false);
211 /**
212 * Resume heartbeat measuring for the current thread
213 * @param bAllThreads If true, resumes heartbeat for all threads, not only the current one
214 */
215 void ResumeHeartBeat(bool bAllThreads = false);
216
217 /**
218 * Returns true/false if this thread is currently performing heartbeat monitoring
219 */
220 bool IsBeating();
221
222 /**
223 * Sets a multiplier to the hang duration (>= 1.0).
224 * Can be used to extend the duration during loading screens etc.
225 */
226 void SetDurationMultiplier(double NewMultiplier);
227
228 /*
229 * Get the Id of the last thread to trigger the hang detector.
230 * Returns InvalidThreadId if hang detector has not been triggered.
231 */
232 uint32 GetLastHungThreadId() const { return LastHungThreadId; }
233
234 /*
235 * Get the Id of the last thread to pass the stuck thread time.
236 * Returns InvalidThreadId if hang detector has not been triggered.
237 */
238 uint32 GetLastStuckThreadId() const { return LastStuckThreadId; }
239
240 /*
241 * Get delegate for callback on stuck or unstuck thread.
242 */
243 FOnThreadStuck& GetOnThreadStuck() { return OnStuck; }
244 FOnThreadUnstuck& GetOnThreadUnstuck() { return OnUnstuck; }
245
246 /*
247 * Get delegate for callback on hang.
248 * Delegate implementation will be called from the hang detector thread and not from the hung thread
249 * Disabled in shipping build
250 */
253#endif
254
255 /*
256 * Get hang duration threshold.
257 */
258 double GetHangDuration() const { return ConfigHangDuration; };
259
260 //~ Begin FRunnable Interface.
261 virtual bool Init();
262 virtual uint32 Run();
263 virtual void Stop();
264 //~ End FRunnable Interface
265};
266
267/** Suspends heartbeat measuring for the current thread in the current scope */
269{
270private:
273public:
274 FORCEINLINE FSlowHeartBeatScope(bool bAllThreads = false)
275 : bSuspendedAllThreads(bAllThreads)
276 , bSuspended(false)
277 {
279 {
280 bSuspended = true;
282 }
283 }
285 {
286 if (bSuspended)
287 {
289 {
291 }
292 }
293 }
294};
295
296/** Simple scope object to put at the top of a function to monitor it completes in a timely fashion */
298{
299private:
301public:
303 : bStartedMonitor(false)
304 {
306 {
307 bStartedMonitor = true;
309 }
310 }
312 {
313 if (bStartedMonitor)
314 {
316 {
318 }
319 }
320 }
321};
322
323
324// When 1, performs a full symbol lookup in hitch call stacks, otherwise only
325// a backtrace is performed and the raw addresses are written to the log.
326#ifndef LOOKUP_SYMBOLS_IN_HITCH_STACK_WALK
327#define LOOKUP_SYMBOLS_IN_HITCH_STACK_WALK 0
328#endif
329
330#ifndef WALK_STACK_ON_HITCH_DETECTED
331#define WALK_STACK_ON_HITCH_DETECTED 0
332#endif
333
335{
337
338 /** Thread to run the worker FRunnable on */
340 /** Stops this thread */
342 /** Synch object for the heartbeat */
344
346 float HangDuration = -1.0f;
348 double FirstStartTime;
349 double FrameStartTime;
351
352#if WALK_STACK_ON_HITCH_DETECTED
353#if LOOKUP_SYMBOLS_IN_HITCH_STACK_WALK
354 static constexpr SIZE_T StackTraceSize = 65535;
356#else
357 static constexpr uint32 MaxStackDepth = 128;
359#endif // LOOKUP_SYMBOLS_IN_HITCH_STACK_WALK
360#endif // WALK_STACK_ON_HITCH_DETECTED
361#endif // USE_HITCH_DETECTION
362
364
366
369
370public:
371
373 {
374 /** Invalid thread Id used by CheckHeartBeat */
375 InvalidThreadId = (uint32)-1
376 };
377
378 /** Gets the heartbeat singleton */
381
382 /**
383 * Called at the start of a frame to register the time we are looking to detect a hitch
384 */
385 void FrameStart(bool bSkipThisFrame = false);
386
388 double GetCurrentTime();
389
390 /**
391 * Suspend heartbeat hitch detection. Must call ResumeHeartBeat later to resume.
392 */
394
395 /**
396 * Resume heartbeat hitch detection. Call only after first calling SuspendHeartBeat.
397 */
399
400 // No-op, used in FUnixSignalGameHitchHeartBeat
401 void Restart() {}
402
403 //~ Begin FRunnable Interface.
404 virtual bool Init();
405 virtual uint32 Run();
406 virtual void Stop();
407 //~ End FRunnable Interface
408};
409
411typedef FUnixSignalGameHitchHeartBeat FGameThreadHitchHeartBeat;
412#else
414#endif
415
416/** Suspends hitch detection in the current scope */
418{
420 {
422 }
424 {
426 }
427};
#define check(expr)
#define UE_BUILD_SHIPPING
Definition Build.h:4
#define USE_HITCH_DETECTION
Definition Build.h:421
#define DECLARE_DELEGATE_OneParam(DelegateName, Param1Type)
#define FORCENOINLINE
Definition Platform.h:647
#define FORCEINLINE
Definition Platform.h:644
#define PLATFORM_UNIX
Definition Platform.h:59
FGameThreadHitchHeartBeatThreaded FGameThreadHitchHeartBeat
FWindowsCriticalSection FCriticalSection
static FGameThreadHitchHeartBeatThreaded * Singleton
void FrameStart(bool bSkipThisFrame=false)
static FGameThreadHitchHeartBeatThreaded * GetNoInit()
static FGameThreadHitchHeartBeatThreaded & Get()
FORCEINLINE FName()
Definition NameTypes.h:974
const uint64 MaxTimeStepCycles
FThreadHeartBeatClock(double InMaxTimeStep)
virtual void Stop()
static FThreadHeartBeat * Singleton
FThreadSafeBool bReadyToCheckHeartbeat
void SetDurationMultiplier(double NewMultiplier)
void MonitorCheckpointEnd(FName CheckPoint)
uint32 CheckFunctionHeartBeat(double &OutHangDuration)
uint32 GetLastHungThreadId() const
void HeartBeat(bool bReadConfig=false)
FOnThreadStuck & GetOnThreadStuck()
FOnThreadUnstuck & GetOnThreadUnstuck()
virtual ~FThreadHeartBeat()
static FThreadHeartBeat & Get()
uint32 CheckCheckpointHeartBeat(double &OutHangDuration)
void MonitorFunctionStart()
void FORCENOINLINE OnPresentHang(double HangDuration)
FThreadSafeCounter GlobalSuspendCount
FThreadHeartBeatClock Clock
TMap< FName, FHeartBeatInfo > CheckpointHeartBeat
double GetHangDuration() const
virtual bool Init()
FRunnableThread * Thread
FHeartBeatInfo PresentHeartBeat
virtual uint32 Run()
void MonitorCheckpointStart(FName EndCheckPoint, double TimeToReachCheckpoint)
TMap< uint32, FHeartBeatInfo > FunctionHeartBeat
void SuspendHeartBeat(bool bAllThreads=false)
TMap< uint32, FHeartBeatInfo > ThreadHeartBeat
FOnThreadStuck OnStuck
void FORCENOINLINE OnHang(double HangDuration, uint32 ThreadThatHung)
void MonitorFunctionEnd()
FCriticalSection CheckpointHeartBeatCritical
FCriticalSection FunctionHeartBeatCritical
void ResumeHeartBeat(bool bAllThreads=false)
uint32 GetLastStuckThreadId() const
FThreadSafeCounter StopTaskCounter
FCriticalSection HeartBeatCritical
static FThreadHeartBeat * GetNoInit()
FOnThreadUnstuck OnUnstuck
uint32 CheckHeartBeat(double &OutHangDuration)
FORCEINLINE FDisableHitchDetectorScope()
FORCEINLINE ~FDisableHitchDetectorScope()
FORCEINLINE ~FFunctionHeartBeatScope()
FORCEINLINE FFunctionHeartBeatScope()
FORCEINLINE ~FSlowHeartBeatScope()
FORCEINLINE FSlowHeartBeatScope(bool bAllThreads=false)
void Resume(double CurrentTime)