2023-11-18 11:04:54 +01:00
/*
* MemoryTracker . cpp
*
* Created on : Nov 9 , 2023
* Author : jief
*/
# include <Platform.h>
# include "MemoryTracker.h"
# include "../cpp_foundation/XString.h"
# ifndef DEBUG_ALL
# define DEBUG_MT 1
# else
# define DEBUG_MT DEBUG_ALL
# endif
# if DEBUG_MT == 0
# define DBG(...)
# else
# define DBG(...) DebugLog(DEBUG_MT, __VA_ARGS__)
# endif
# ifdef MEMORY_TRACKER_ENABLED
2023-11-20 20:06:35 +01:00
# define MAGIC_BEGINNING 0xDEADBEEFBBBBBBBB
# define MAGIC_END 0xEEEEEEEEDEADBEEF
2023-11-18 11:04:54 +01:00
uint64_t MT_alloc_count = 0 ;
bool MT_recording = false ;
2023-11-20 20:06:35 +01:00
uint64_t MT_count_to_break_into = 0 ;
2024-01-07 15:52:11 +01:00
bool MT_report_deleting_non_recoded_ptr = false ;
2023-11-18 11:04:54 +01:00
MTArray < uintptr_t > allocatedPtrArray ;
MTArray < uint64_t > allocatedPtrInfoArray ;
2023-11-20 20:06:35 +01:00
# if defined(IS_UEFI_MODULE)
void * real_malloc ( EFI_MEMORY_TYPE MemoryType , size_t size ) ;
# define _real_malloc(MemoryType, size) real_malloc(MemoryType, size)
# else
# define _real_malloc(MemoryType, size) real_malloc(size)
# endif
void * real_malloc ( size_t size ) ;
void real_free ( void * ) ;
2023-11-18 11:04:54 +01:00
void MT_import ( const void * p )
{
// printf("MT_import ptr %llx\n", uintptr_t(p));
MT_recording = false ;
auto count = MT_alloc_count ;
allocatedPtrArray . Add ( uintptr_t ( p ) ) ; // this can allocate memory, so don't record that.
allocatedPtrInfoArray . Add ( count ) ; // this can allocate memory, so don't record that.
MT_recording = true ;
}
extern " C " {
# if defined(IS_UEFI_MODULE)
VOID *
2023-12-31 13:43:34 +01:00
PhaseAllocatePool (
2023-11-18 11:04:54 +01:00
IN EFI_MEMORY_TYPE MemoryType ,
IN UINTN AllocationSize
)
# else
void * my_malloc ( size_t AllocationSize )
# endif
{
VOID * Memory = NULL ;
# ifdef JIEF_DEBUG
if ( MT_alloc_count = = 4028 | | MT_alloc_count = = MT_count_to_break_into ) {
NOP ;
}
# endif
if ( MT_recording )
{
2023-11-20 20:06:35 +01:00
Memory = _real_malloc ( MemoryType , AllocationSize + 24 ) ;
2023-11-18 11:04:54 +01:00
if ( ! Memory ) return NULL ;
* ( uint64_t * ) Memory = AllocationSize ;
// void* p = ((uint64_t*)Memory + 1) ;
* ( ( uint64_t * ) Memory + 1 ) = MAGIC_BEGINNING ;
* ( uint64_t * ) ( ( ( uint8_t * ) Memory ) + 16 + AllocationSize ) = MAGIC_END ;
if ( MT_recording ) {
MT_import ( ( ( uint8_t * ) Memory ) + 16 ) ;
+ + MT_alloc_count ; // do this only if !MT_recording because MT_import is doing an allocation.
}
return ( ( uint8_t * ) Memory ) + 16 ;
} else {
2023-11-20 20:06:35 +01:00
Memory = _real_malloc ( MemoryType , AllocationSize ) ;
2023-11-18 11:04:54 +01:00
return Memory ;
}
}
# if defined(IS_UEFI_MODULE)
2023-12-31 13:43:34 +01:00
//void (EFIAPI FreePool)(IN void *MemoryPlus16)
VOID
EFIAPI
PhaseFreePool ( IN VOID * MemoryPlus16 )
2023-11-18 11:04:54 +01:00
# else
void my_free ( IN void * MemoryPlus16 )
# endif
{
if ( ! MemoryPlus16 ) return ;
uintptr_t ref2 = uintptr_t ( MemoryPlus16 ) ;
auto idx = allocatedPtrArray . indexOf ( ref2 ) ;
if ( idx = = MAX_XSIZE ) {
2024-01-07 15:52:11 +01:00
if ( MT_report_deleting_non_recoded_ptr ) {
DBG ( " ERROR : Delete non recorded ptr %llx \n " , uintptr_t ( MemoryPlus16 ) ) ;
}
2023-11-18 11:04:54 +01:00
real_free ( MemoryPlus16 ) ;
return ;
}
uint8_t * Memory = ( ( uint8_t * ) MemoryPlus16 ) - 16 ;
uint64_t AllocationSize = * ( uint64_t * ) Memory ;
if ( * ( ( uint64_t * ) Memory + 1 ) ! = MAGIC_BEGINNING ) {
2024-01-07 15:52:11 +01:00
DBG ( " ERROR : Buffer underrun for ptr %llx count %lld \n " , uintptr_t ( MemoryPlus16 ) , allocatedPtrInfoArray [ idx ] ) ;
2023-11-18 11:04:54 +01:00
}
if ( * ( uint64_t * ) ( ( ( uint8_t * ) Memory ) + 16 + AllocationSize ) ! = MAGIC_END ) {
2024-01-07 15:52:11 +01:00
DBG ( " ERROR : Buffer overrun for ptr %llx count %lld \n " , uintptr_t ( MemoryPlus16 ) , allocatedPtrInfoArray [ idx ] ) ;
2023-11-18 11:04:54 +01:00
}
allocatedPtrArray . RemoveAtIndex ( idx ) ;
allocatedPtrInfoArray . RemoveAtIndex ( idx ) ;
real_free ( Memory ) ;
}
} // extern "C"
void MemoryTrackerCheck ( )
{
size_t nb_ptr = allocatedPtrArray . length ( ) ;
DBG ( " -- %zu pointers : \n " , nb_ptr ) ;
for ( size_t idx = 0 ; idx < nb_ptr ; + + idx )
{
uint8_t * Memory = ( ( uint8_t * ) allocatedPtrArray [ idx ] ) - 16 ;
uint64_t AllocationSize = * ( uint64_t * ) Memory ;
if ( * ( ( uint64_t * ) Memory + 1 ) ! = MAGIC_BEGINNING ) {
DBG ( " Buffer underrun for ptr %llx count %lld \n " , uintptr_t ( Memory + 16 ) , allocatedPtrInfoArray [ idx ] ) ;
}
if ( * ( uint64_t * ) ( ( ( uint8_t * ) Memory ) + 16 + AllocationSize ) ! = MAGIC_END ) {
DBG ( " Buffer overrun for ptr %llx count %lld \n " , uintptr_t ( Memory + 16 ) , allocatedPtrInfoArray [ idx ] ) ;
}
}
}
void MemoryTrackerInit ( )
{
MT_alloc_count = 0 ;
MT_recording = false ;
2024-01-07 15:52:11 +01:00
MT_report_deleting_non_recoded_ptr = false ;
2023-11-18 11:04:54 +01:00
}
/*
* In fact , the hook is already in place . We just have to activate it now .
*/
void MemoryTrackerInstallHook ( )
{
MT_recording = true ;
2024-01-07 15:52:11 +01:00
MT_report_deleting_non_recoded_ptr = true ;
2023-11-18 11:04:54 +01:00
}
void MT_outputDanglingPtr ( )
{
size_t nb_ptr = allocatedPtrArray . length ( ) ;
DBG ( " -- %zu lost pointer : \n " , nb_ptr ) ;
for ( size_t idx = 0 ; idx < nb_ptr ; + + idx ) {
DBG ( " Dangling ptr %llx count=%lld \n " , allocatedPtrArray [ idx ] , allocatedPtrInfoArray [ idx ] ) ;
}
}
uint64_t MT_getAllocCount ( ) { return MT_alloc_count ; } ;
uint64_t MT_getDanglingPtrCount ( ) { return allocatedPtrArray . length ( ) ; } ;
MemoryStopRecord : : MemoryStopRecord ( ) : recording ( MT_recording ) { MT_recording = false ; } ;
MemoryStopRecord : : ~ MemoryStopRecord ( ) { MT_recording = recording ; } ;
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//
// MTArray
//
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
template < class TYPE >
MTArray < TYPE > : : MTArray ( ) : m_data ( 0 ) , m_len ( 0 ) , m_allocatedSize ( 4096 )
{
m_data = ( TYPE * ) real_malloc ( 4096 * sizeof ( TYPE ) ) ;
if ( ! m_data ) {
# ifdef JIEF_DEBUG
panic ( " MTArray<TYPE>:: MTArray() : OldAllocatePool(%zu) returned NULL. System halted \n " , 4096 * sizeof ( TYPE ) ) ;
# endif
}
}
template < class TYPE >
2024-01-07 15:52:11 +01:00
size_t MTArray < TYPE > : : indexOf ( TYPE e ) const
2023-11-18 11:04:54 +01:00
{
size_t i ;
2024-01-07 15:52:11 +01:00
for ( i = 0 ; i < length ( ) ; i + = 1 ) {
if ( m_data [ i ] = = e ) return i ;
}
return MAX_XSIZE ;
2023-11-18 11:04:54 +01:00
}
/* CheckSize() // nNewSize is number of TYPE, not in bytes */
template < class TYPE >
void MTArray < TYPE > : : CheckSize ( size_t nNewSize , size_t nGrowBy )
{
//MTArray_DBG("CheckSize: m_len=%d, m_size=%d, nGrowBy=%d, nNewSize=%d\n", m_len, m_size, nGrowBy, nNewSize);
if ( nNewSize > m_allocatedSize ) {
nNewSize + = nGrowBy ;
TYPE * new_data ;
new_data = ( TYPE * ) real_malloc ( nNewSize * sizeof ( TYPE ) ) ;
if ( ! new_data ) {
# ifdef JIEF_DEBUG
panic ( " MTArray<TYPE>::CheckSize(nNewSize=%zu, nGrowBy=%zu) : OldAllocatePool(%zu, %lu, % " PRIuPTR " ) returned NULL. System halted \n " , nNewSize , nGrowBy , m_allocatedSize , nNewSize * sizeof ( TYPE ) , ( uintptr_t ) m_data ) ;
# endif
}
memcpy ( new_data , m_data , m_allocatedSize * sizeof ( TYPE ) ) ;
real_free ( m_data ) ;
m_data = new_data ;
// memset(&_Data[_Size], 0, (nNewSize-_Size) * sizeof(TYPE)); // Could help for debugging, but zeroing in not needed.
m_allocatedSize = nNewSize ;
}
}
/* CheckSize() */
template < class TYPE >
void MTArray < TYPE > : : CheckSize ( size_t nNewSize )
{
CheckSize ( nNewSize , 4096 ) ;
}
/* Add(TYPE, size_t) */
template < class TYPE >
size_t MTArray < TYPE > : : Add ( const TYPE newElement , size_t count )
{
// MTArray_DBG("size_t MTArray<TYPE>::Add(const TYPE newElement, size_t count) -> Enter. count=%d _Len=%d _Size=%d\n", count, m_len, m_size);
size_t i ;
CheckSize ( m_len + count ) ;
for ( i = 0 ; i < count ; i + + ) {
m_data [ m_len + i ] = newElement ;
}
m_len + = count ;
return m_len - count ;
}
/* RemoveAtIndex(size_t) */
template < class TYPE >
void MTArray < TYPE > : : RemoveAtIndex ( size_t nIndex )
{
if ( nIndex < m_len ) {
if ( nIndex < m_len - 1 ) memmove ( & m_data [ nIndex ] , & m_data [ nIndex + 1 ] , ( m_len - nIndex - 1 ) * sizeof ( TYPE ) ) ;
m_len - = 1 ;
return ;
}
# if defined(_DEBUG) && defined(TRACE)
TRACE ( " MTArray::Remove(size_t) -> nIndex > m_len \n " ) ;
# endif
}
/* FreeAndRemoveAtIndex(size_t) */
template < class TYPE >
void MTArray < TYPE > : : FreeAndRemoveAtIndex ( size_t nIndex )
{
if ( nIndex < m_len ) {
# ifdef UNIT_TESTS_MACOS // won't be needed soon as I'll improve the EFI mock
free ( m_data [ nIndex ] ) ;
# else
OldFreePool ( m_data [ nIndex ] ) ;
# endif
if ( nIndex < m_len - 1 ) memmove ( & m_data [ nIndex ] , & m_data [ nIndex + 1 ] , ( m_len - nIndex - 1 ) * sizeof ( TYPE ) ) ;
m_len - = 1 ;
return ;
}
# if defined(_DEBUG) && defined(TRACE)
TRACE ( " MTArray::FreeAndRemoveAtIndex(size_t) -> nIndex > m_len \n " ) ;
# endif
}
# undef malloc
# undef free
2023-11-20 20:06:35 +01:00
void * real_malloc ( EFI_MEMORY_TYPE MemoryType , size_t size )
2023-11-18 11:04:54 +01:00
{
# if defined(IS_UEFI_MODULE)
void * Memory ;
EFI_STATUS Status = gBS - > AllocatePool ( MemoryType , size , & Memory ) ; // AllocatePool in MemoryAllocationLib uses EfiBootServicesData, not EfiConventionalMemory
if ( EFI_ERROR ( Status ) ) return NULL ;
return Memory ;
# else
2023-11-20 20:06:35 +01:00
return malloc ( size ) ;
2023-11-18 11:04:54 +01:00
# endif
}
2023-11-20 20:06:35 +01:00
void * real_malloc ( size_t size ) { return real_malloc ( EfiBootServicesData , size ) ; }
2023-11-18 11:04:54 +01:00
void real_free ( void * p )
{
# if defined(IS_UEFI_MODULE)
EFI_STATUS Status = gBS - > FreePool ( p ) ;
if ( EFI_ERROR ( Status ) ) {
// What to do ?
}
# else
free ( p ) ;
# endif
}
2023-11-20 20:06:35 +01:00
# if defined(IS_UEFI_MODULE)
2023-12-31 13:43:34 +01:00
void * my_malloc ( size_t size ) { return PhaseAllocatePool ( EfiBootServicesData , size ) ; }
2023-11-20 20:06:35 +01:00
void my_free ( IN void * p ) { FreePool ( p ) ; }
2023-11-18 11:04:54 +01:00
# endif
2023-11-20 20:06:35 +01:00
//#if !defined(IS_UEFI_MODULE)
//extern "C" {
//void* malloc(size_t size);
//void free(void* p);
//} //extern C
//#endif // !defined(IS_UEFI_MODULE)
2023-11-18 11:04:54 +01:00
# else // MEMORY_TRACKER_ENABLED
void MemoryTrackerInit ( ) { } ;
void MemoryTrackerInstallHook ( ) { } ;
void MT_outputDanglingPtr ( ) { } ;
uint64_t MT_getAllocCount ( ) { return 0 ; } ;
uint64_t MT_getDanglingPtrCount ( ) { return 0 ; } ;
MemoryStopRecord : : MemoryStopRecord ( ) { } ;
MemoryStopRecord : : ~ MemoryStopRecord ( ) { } ;
void MemoryTrackerCheck ( ) { } ;
# undef malloc
# undef free
2023-11-20 20:06:35 +01:00
extern " C " {
// we don't use MemoryTracker. Let's define passthrough function to avoid having to have to remove function hooks.
2023-11-18 11:04:54 +01:00
void * my_malloc ( size_t size )
{
return malloc ( size ) ;
}
void my_free ( void * p )
{
free ( p ) ;
}
2023-11-20 20:06:35 +01:00
} // extern "C"
2023-11-18 11:04:54 +01:00
# endif // MEMORY_TRACKER_ENABLED