1
#ifndef NMEMORYDEBUGHOOK_H
2
#define NMEMORYDEBUGHOOK_H
6
#define INL_NB_ALLOCATOR_MAX 127
7
/// MemDebug class is a layer that sits on memory allocators, and used to help debugging memory management
8
/*! The Memory Debugger is doing a lot of verification on every allocation/free action.
9
The idea is to allocate more than requested (32 bytes for the header + 4 trailing bytes = 36 bytes)
10
to keep information on the current memory block. This header contains:
11
- 2 Pointers for insertion in a double linked list
12
- The Size of the original block allocated
13
- The current Status of the block
14
- The Allocator ID that from which the block belong
15
- The filename & Line number in the file where the block has been allocated (and then freed)
19
The Memory Debugger keeps track of all the block allocated in a double linked list. That's the way
20
used to track leak at run-time. There is one distinct list per allocator, so it's possible to verify
21
leak for a specific allocator, or for all the system.
22
On allocation, the real size requested to the allocator is bigger to keep the header at the beginning
23
of the memory block. The size of the header is always a multiple of GEAR_HW_MEM_ALIGN.
24
The header is initialized, the checksum computed, and the buffer is filled with a known value. This will
25
be used when the block will be freed to evaluate the percentage of the block that has been used. This system
26
isn't 100% bullet proof, since there is a small percentage of cases where memory wasted will be detected
27
and will not be in reality (if the user write in the buffer the same pattern as used as filling value).
28
There is also 4 extra bytes added at the end of the buffer as a stopper, to ensure that no buffer overflow
29
occured. The pointer returned is then a value pointing right after the header, and this value is guaranteed
32
During run time, a buffer integrity can be verified (Verify alignment, header checksum, allocator matching
33
and bottom flag integrity). A leak detection can be performed, that consist of simply listing all buffers
34
currently still allocated.
36
class MemDebug : public MemoryHook
38
/// MemDebug is a Global Object
39
INL_DECLARE_GLOBAL_OBJECT(MemDebug, NGlobalSingletonInitializer);
42
typedef enum { Invalid = -1,
50
/// SetAllocFailure allows to simulate a failure in allocation.
51
/*! This function available only when the debugger is enabled, allows to simulate allocation
52
failure to test user code. The failure is set per Allocator. Any call to Alloc/Realloc/New will
53
fail for [NbFailure] allocation in [Delay] calls. Failure sequence aren't queued, so any call
54
to SetAllocFailure will change the previous one. To disable any failure simulation, just
55
call SetAllocFailure(Allocator, 0, 0) (default params).
56
\param Allocator The allocator that will simulate an alloc fail
57
\param Delay The number of call To Alloc before the failure. 0 = Next call
58
\param NbFailure The number of call to Alloc that must fail in a row.
60
\sa GEAR_ALLOC(), GEAR_REALLOC()
62
void SetAllocFailure( const NMemoryAllocatorInterface& Allocator,
64
unsigned NbFailure = 0);
66
/// Returns the number of buffers still allocated in the specified allocator
67
/*! \param AllocId The allocator we're gonna verify
68
\param Verbose When true, will send a warning on the debug device if a leak is detected
69
\return The number of buffer still allocated
70
\sa VerifyLeakAll(), GEAR_ALLOC(), GEAR_FREE()
72
unsigned VerifyLeak(NMemoryAllocatorInterface::id AllocId, bool Verbose = true);
74
/// Returns the number of buffers still allocated in all allocators
75
/*! \param Verbose When true, will send a warning on the debug device if a leak is detected
76
\return The total number of buffer still allocated in all buffers
77
\sa VerifyLeak(), GEAR_ALLOC(), GEAR_FREE()
79
unsigned VerifyLeakAll(bool Verbose = true);
81
/// Verify buffer status for the specified allocator
82
/*! Here's a list of problems detected on a problematics buffers:
83
- Pointer position (Null & Alignment)
85
- Header integrity (Overflow problem slightly before the pointer)
86
- Verify allocator matching (The allocator specified is the same as recorded when allocated)
87
- Bottom flag (To detect overflow)
88
\param AllocId The allocator we're gonna verify
89
\param Verbose When true, will send a warning on the debug device if a problem is detected
90
\return An error code (see MemDebug::Err) or 0 if the buffer is sound.
91
\sa VerifyLeakAll(), GEAR_ALLOC(), GEAR_FREE()
93
int VerifyBuffer(NMemoryAllocatorInterface::id AllocId, bool Verbose = true);
95
/// Verify buffer status for all allocators
96
/*! \param Verbose When true, will send a warning on the debug device if a problem is detected
97
\return The number of allocators that contains problematics buffers
100
unsigned VerifyBufferAll(bool Verbose = true);
102
/// Verify buffer status for the specified allocator and the specified buffer pointed on by pBuf
103
/*! Here's a list of problems detected on a problematics buffers:
104
- Pointer position (Null & Alignment)
106
- Header integrity (Overflow problem slightly before the pointer)
107
- Verify allocator matching (The allocator specified is the same as recorded when allocated)
108
- Bottom flag (To detect overflow)
109
\param pBuf Pointer to the memory block
110
\return Block status (see MemDebug::Err) or 0 if the buffer is sound.
112
int VerifyPointer(void* pBuf);
114
/// Returns the unused memory in an allocated memory block
115
/*! When allocated, a block is filled with a known pattern. Starting from the end of the block,
116
we can trace back the pattern until the first byte changed, and evaluate the number of bytes
118
\param pData A pointer on a block of data allocated
119
\return The number of bytes unused (wasted)
121
virtual unsigned GetWastedMemory(const void* pData) const;
123
/// Returns the logagent identifier for the memory debugger
124
static const char* GetLogId(void)
126
return "MemoryDebugger";
129
/// Reset the Leak Threshold value. All allocations done before will not be considered as leak
130
t_u32 ResetLeakThreshold(void)
132
t_u32 oldValue = m_LeakThresholdSeqNo;
133
m_LeakThresholdSeqNo = m_SequenceNoServer;
137
/// Sets the leak threshold value. All allocations made before this sequence number won't be considered as leaks
138
/// Set value to 0 to get all unfreed allocations
139
void SetLeakThreshold(t_u32 seqNo)
141
m_LeakThresholdSeqNo = seqNo;
144
virtual void* Alloc( int OpId,
146
NMemoryAllocatorInterface& Allocator,
149
const char* pObjType,
151
const TCHAR* pFileName);
153
virtual void* AllocAligned(int OpId,
156
NMemoryAllocatorInterface& Allocator,
159
const char* pObjType,
161
const TCHAR* pFileName);
163
virtual void* Realloc(void* pOldPtr,
164
unsigned NewMemSizeInByte,
165
NMemoryAllocatorInterface& Allocator,
167
const TCHAR* pFileName);
169
virtual void* ReallocAligned(void* pOldPtr,
170
unsigned NewMemSizeInByte,
172
NMemoryAllocatorInterface& Allocator,
174
const TCHAR* pFileName);
176
virtual void Free(int OpId,
178
NMemoryAllocatorInterface& Allocator,
180
const TCHAR* pFileName);
181
virtual void FreeMarker(void* pMarker,
182
MemStackInterface& StackAllocator,
184
const TCHAR* pFileName);
185
virtual t_u32 GetAllocatedSize(void* pData,
186
NMemoryAllocatorInterface& Allocator,
187
t_u32* pRetHeaderSize) const;
189
virtual void AllocatorDestruction(NMemoryAllocatorInterface& Allocator);
191
virtual unsigned GetDebugOverHead(unsigned alignment) const;
193
/// We use the default behavior of theses virtual functions. It's intentionally NOT implemented
194
virtual void* GetMarker(MemStackInterface& StackAllocator,
196
const TCHAR* pFileName);
199
t_u32 GetAllocationNumber(const void *pData) const;
201
// Callback used by the client for validation
202
typedef void (*OnAllocCallbackType)(void * userAddr, size_t size);
203
typedef void (*OnFreeCallbackType) (void * userAddr, size_t size);
204
void SetOnAllocCallback(OnAllocCallbackType callback);
205
void SetOnFreeCallback(OnFreeCallbackType callback);
208
void FreeBlock( int OpId,
210
NMemoryAllocatorInterface& Allocator,
212
const TCHAR* pFileName);
213
int VerifyBlock( void* pBuf,
214
NMemoryAllocatorInterface::id AllocId,
215
bool Verbose = true);
216
bool VerifyAllocFailure( NMemoryAllocatorInterface::id AllocId);
217
t_u32 CalculateDebugSize( t_u32 Size, t_u32 Alignment) const;
218
void* SecondStageAlloc( void* pBuffer,
222
NMemoryAllocatorInterface::id AllocId,
224
const TCHAR* pFileName,
226
void VerifyMemoryWasted( const void* pBuf);
231
t_u32 FrontFlag; // Flag at the beginning of the packet
232
NListNoDyn<dbg_header>::Node m_Node;
233
//INL_NLISTNODYN_NODE(dbg_header); // Double linked list header
235
t_u32 Size; // Size of the user buffer
236
t_u16 LineNo; // Line number from allocator
237
t_u8 Status; // Status of block
238
t_u8 AllocId; // Allocator Identifier
239
const TCHAR* pFileName; // Allocator's FileName
240
t_u32 SequenceNo; // Sequential number
241
t_u32 Checksum; // Header checksum
244
class data_per_allocator
247
NListNoDyn<dbg_header> AllocatedList; // List of allocated block
248
unsigned FailureDelay; // Delay before next failure
249
unsigned FailureCount; // Number of successive failure
252
data_per_allocator m_AllocatorTbl[INL_NB_ALLOCATOR_MAX];
253
NCriticalSection m_Lock;
254
t_u32 m_SequenceNoServer; // On every alloc we increase this counter
255
t_u32 m_LeakThresholdSeqNo; // All alloc before this value is not considered Leak
257
OnAllocCallbackType OnAllocCallback; // The pointer on client AllocCallback
258
OnFreeCallbackType OnFreeCallback; // The pointer on client FreeCallback
263
#endif // NMEMORYDEBUGHOOK_H