Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
MallocLeakDetection.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 MallocLeakDetection.h: Helper class to track memory allocations
5=============================================================================*/
6
7#pragma once
8
9#include "Containers/Array.h"
10#include "Containers/Map.h"
11#include "Containers/Set.h"
12#include "Containers/UnrealString.h"
13#include "CoreTypes.h"
14#include "HAL/MemoryBase.h"
15#include "HAL/ThreadSafeBool.h"
16#include "HAL/UnrealMemory.h"
17#include "Misc/Crc.h"
18#include "Misc/ScopeLock.h"
19
20#ifndef MALLOC_LEAKDETECTION
21 #define MALLOC_LEAKDETECTION 0
22#endif
23
24/**
25 * Options that can be supplied when calling FMallocLeakDetection::DumpOpenCallstacks
26 */
28{
29 enum class ESortOption
30 {
34 };
35
37 {
39 }
40
41 /** If >0 only report allocations greater than this size */
42 uint32 SizeFilter;
43
44 /** If >0 only report allocations at a greater bytes/frame than this */
46
47 /** Restrict report to allocations that have no history of being deleted */
49
50 /** Only show allocations after this frame */
51 uint32 FrameStart;
52
53 /** Only show allocations from before this frame */
54 uint32 FrameEnd;
55
56 /** Sort allocations by this (default - size) */
58};
59
61
62/**
63 * Maintains a list of all pointers to currently allocated memory.
64 */
65class FMallocLeakDetection
66{
67 struct FCallstackTrack
68 {
69 FCallstackTrack()
70 {
71 FMemory::Memzero(this, sizeof(FCallstackTrack));
72 }
73 static constexpr int32 Depth = 32;
74 uint64 CallStack[Depth];
75 uint32 FirstFrame;
76 uint32 LastFrame;
77 uint64 Size;
78 uint32 Count;
79 uint32 CachedHash;
80
81 // least square line fit stuff
82 uint32 NumCheckPoints;
83 float SumOfFramesNumbers;
84 float SumOfFramesNumbersSquared;
85 float SumOfMemory;
86 float SumOfMemoryTimesFrameNumber;
87
88 // least square line results
89 float Baseline;
90 float BytesPerFrame;
91
92 bool operator==(const FCallstackTrack& Other) const
93 {
94 bool bEqual = true;
95 for (int32 i = 0; i < Depth; ++i)
96 {
97 if (CallStack[i] != Other.CallStack[i])
98 {
99 bEqual = false;
100 break;
101 }
102 }
103 return bEqual;
104 }
105
106 bool operator!=(const FCallstackTrack& Other) const
107 {
108 return !(*this == Other);
109 }
110
111 void GetLinearFit();
112
113 uint32 GetHash()
114 {
115 CachedHash = FCrc::MemCrc32(CallStack, sizeof(CallStack), 0);
116 return CachedHash;
117 }
118 };
119
120private:
121
122 FMallocLeakDetection();
123 ~FMallocLeakDetection();
124
125 /** Track a callstack */
126
127 /** Stop tracking a callstack */
128 void AddCallstack(FCallstackTrack& Callstack);
129 void RemoveCallstack(FCallstackTrack& Callstack);
130
131 /** List of all currently allocated pointers */
132 TMap<void*, FCallstackTrack> OpenPointers;
133
134 /** List of all unique callstacks with allocated memory */
135 TMap<uint32, FCallstackTrack> UniqueCallstacks;
136
137 /** Set of callstacks that are known to delete memory (not reset on ClearData()) */
138 TSet<uint32> KnownDeleters;
139
140 /** Set of callstacks that are known to resize memory (not reset on ClearData()) */
141 TSet<uint32> KnownTrimmers;
142
143 /** Contexts that are associated with allocations */
144 TMap<void*, FString> PointerContexts;
145
146 /** Stack of contexts */
147 struct FContextString { TCHAR Buffer[64]; };
148 TArray<FContextString> Contexts;
149
150 /** Critical section for mutating internal data */
151 FCriticalSection AllocatedPointersCritical;
152
153 /** Set during mutating operations to prevent internal allocations from recursing */
154 bool bRecursive;
155
156 /** Is allocation capture enabled? */
157 bool bCaptureAllocs;
158
159 /** Minimal size to capture? */
160 int32 MinAllocationSize;
161
162 /** Size of all tracked callstacks */
163 SIZE_T TotalTracked;
164
165 /** How long since we compacted things? */
166 int32 AllocsWithoutCompact;
167
168public:
169
170 static FMallocLeakDetection& Get();
171 static void HandleMallocLeakCommand(const TArray< FString >& Args);
172
173 /** Enable/disable collection of allocation with an optional filter on allocation size */
174 void SetAllocationCollection(bool bEnabled, int32 Size = 0);
175
176 /** Returns state of allocation collection */
177 bool IsAllocationCollectionEnabled(void) const { return bCaptureAllocs; }
178
179 /** Clear currently accumulated data */
180 void ClearData();
181
182 /** Dumps currently open callstacks */
183 int32 DumpOpenCallstacks(const TCHAR* FileName, const FMallocLeakReportOptions& Options = FMallocLeakReportOptions());
184
185 /** Perform a linear fit checkpoint of all open callstacks */
186 void CheckpointLinearFit();
187
188 /** Handles new allocated pointer */
189 void Malloc(void* Ptr, SIZE_T Size);
190
191 /** Handles reallocation */
192 void Realloc(void* OldPtr, SIZE_T OldSize, void* NewPtr, SIZE_T NewSize);
193
194 /** Removes allocated pointer from list */
195 void Free(void* Ptr);
196
197 /** Disabled allocation tracking for this thread, @see MALLOCLEAK_IGNORE_SCOPE and FMallocLeakDetectionIgnoreScope. */
198 void SetDisabledForThisThread(const bool Disabled);
199
200 /** Returns true of allocation tracking for this thread is */
201 bool IsDisabledForThisThread() const;
202
203 /** Pushes context that will be associated with allocations. All open contexts will be displayed alongside
204 callstacks in a report. */
205 void PushContext(const FString& Context)
206 {
207 this->PushContext(*Context);
208 }
209 void PushContext(const TCHAR* Context);
210
211 /** Pops a context from the above */
212 void PopContext();
213
214 /** Returns */
215 void GetOpenCallstacks(TArray<uint32>& OutCallstacks, SIZE_T& OutTotalSize, const FMallocLeakReportOptions& Options = FMallocLeakReportOptions());
216};
217
218/**
219 * Helper class that can be used to ignore allocations from a specific scope for leak detection.
220 * Use this carefully and only if you know that a portion of code is throwing up either false
221 * positives or can be ignored. (e.g., one example is the FName table which never shrinks
222 * and eventually reaches a max that is relatively inconsequential).
223 */
224class FMallocLeakDetectionIgnoreScope
225{
226public:
227 FMallocLeakDetectionIgnoreScope()
228 {
229 FMallocLeakDetection::Get().SetDisabledForThisThread(true);
230 }
231
232 ~FMallocLeakDetectionIgnoreScope()
233 {
234 FMallocLeakDetection::Get().SetDisabledForThisThread(false);
235 }
236
237 // Non-copyable
238 FMallocLeakDetectionIgnoreScope(const FMallocLeakDetectionIgnoreScope&) = delete;
239 FMallocLeakDetectionIgnoreScope& operator=(const FMallocLeakDetectionIgnoreScope&) = delete;
240};
241
242class FMallocLeakScopedContext
243{
244public:
245 template <typename ArgType>
246 explicit FMallocLeakScopedContext(ArgType&& Context)
247 {
248 FMallocLeakDetection::Get().PushContext(Forward<ArgType>(Context));
249 }
250
251 ~FMallocLeakScopedContext()
252 {
253 FMallocLeakDetection::Get().PopContext();
254 }
255
256 // Non-copyable
257 FMallocLeakScopedContext(const FMallocLeakScopedContext&) = delete;
258 FMallocLeakScopedContext& operator=(const FMallocLeakScopedContext&) = delete;
259};
260
261#define MALLOCLEAK_IGNORE_SCOPE()
262 FMallocLeakDetectionIgnoreScope ANONYMOUS_VARIABLE(DetectionShouldIgnoreScope)
263
264#define MALLOCLEAK_SCOPED_CONTEXT(Context)
265 FMallocLeakScopedContext ANONYMOUS_VARIABLE(ScopedContext)(Context)
266
267#else // MALLOC_LEAKDETECTION 0
268
269#define MALLOCLEAK_IGNORE_SCOPE()
270#define MALLOCLEAK_SCOPED_CONTEXT(Context)
271
272#endif // MALLOC_LEAKDETECTION
#define MALLOC_LEAKDETECTION
static FORCEINLINE void * Memzero(void *Dest, SIZE_T Count)