Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

372 lines
11 KiB
C

/*H********************************************************************************/
/*!
\File zmemtrack.c
\Description
Routines for tracking memory allocations.
\Copyright
Copyright (c) 2005-2017 Electronic Arts Inc.
\Version 02/15/2005 (jbrookes) First Version, based on jfrank's implementation.
*/
/********************************************************************************H*/
/*** Include files ****************************************************************/
#include <stdio.h>
#include <ctype.h>
#include "DirtySDK/platform.h"
#include "zlib.h"
#include "zmem.h"
#include "zmemtrack.h"
/*** Defines **********************************************************************/
#define ZMEMTRACK_MEMDUMPBYTES (64) //!< number of bytes to print around the leak
#define ZMEMTRACK_MAXALLOCATIONS (1024*8) //!< maximum list allocation size
/*** Type Definitions *************************************************************/
typedef struct ZMemtrackElemT
{
void *pMem;
uint32_t uMemSize;
uint32_t uTag;
} ZMemtrackElemT;
typedef struct ZMemtrackRefT
{
uint32_t uNumAllocations;
uint32_t uMaxAllocations;
uint32_t uTotalAllocations;
uint32_t uTotalMemory;
uint32_t uMaxMemory;
uint8_t bOverflow;
uint8_t bStarted;
uint8_t _pad[2];
ZMemtrackLogCbT *pLoggingCb;
void *pUserData;
ZMemtrackElemT MemList[ZMEMTRACK_MAXALLOCATIONS];
} ZMemtrackRefT;
/*** Variables ********************************************************************/
static ZMemtrackRefT _ZMemtrack_Ref;
/*** Private Functions ************************************************************/
/*F********************************************************************************/
/*!
\Function _ZMemtrackLogPrintf
\Description
Logs the information from the module
\Input *pFormat - information to log
\Input ... - additional parameters
\Version 09/18/2017 (eesponda)
*/
/********************************************************************************F*/
static void _ZMemtrackLogPrintf(const char *pFormat, ...)
{
char strText[2048];
int32_t iOffset = 0;
va_list Args;
ZMemtrackRefT *pRef = &_ZMemtrack_Ref;
// format output
va_start(Args, pFormat);
iOffset += ds_vsnprintf(strText+iOffset, sizeof(strText)-iOffset, pFormat, Args);
va_end(Args);
// forward to callback, or print if not callback installed
if (pRef->pLoggingCb != NULL)
{
pRef->pLoggingCb(strText, pRef->pUserData);
}
else
{
ZPrintf("zmemtrack: %s", strText);
}
}
/*F********************************************************************************/
/*!
\Function _ZMemtrackPrintLeak
\Description
Print a memory leak to debug output.
\Input *pElem - pointer to allocation that was leaked
\Version 02/15/2005 (jbrookes)
*/
/********************************************************************************F*/
static void _ZMemtrackPrintLeak(ZMemtrackElemT *pElem)
{
static const char _hex[] = "0123456789ABCDEF";
uint32_t uBytes;
char strOutput[128];
int32_t iOutput = 2;
_ZMemtrackLogPrintf("allocated: [%d] bytes at [%p] with tag '%c%c%c%c'\n", pElem->uMemSize, pElem->pMem,
(uint8_t)(pElem->uTag>>24), (uint8_t)(pElem->uTag>>16), (uint8_t)(pElem->uTag>>8), (uint8_t)(pElem->uTag));
ds_memset(strOutput, ' ', sizeof(strOutput)-1);
strOutput[sizeof(strOutput)-1] = '\0';
for (uBytes = 0; (uBytes < pElem->uMemSize) && (uBytes < ZMEMTRACK_MEMDUMPBYTES); uBytes++, iOutput += 2)
{
unsigned char cByte = ((unsigned char *)(pElem->pMem))[uBytes];
strOutput[iOutput] = _hex[cByte>>4];
strOutput[iOutput+1] = _hex[cByte&0xf];
strOutput[(iOutput/2)+40] = isprint(cByte) ? cByte : '.';
if (uBytes > 0)
{
if (((uBytes+1) % 16) == 0)
{
strOutput[(iOutput/2)+40+1] = '\0';
_ZMemtrackLogPrintf("%s\n", strOutput);
ds_memset(strOutput, ' ', sizeof(strOutput)-1);
strOutput[sizeof(strOutput)-1] = '\0';
iOutput = 0;
}
else if (((uBytes+1) % 4) == 0)
{
iOutput++;
}
}
}
if (((uBytes > ZMEMTRACK_MEMDUMPBYTES) && (uBytes % ZMEMTRACK_MEMDUMPBYTES) != 0) || (pElem->uMemSize < 16))
{
strOutput[(iOutput/2)+40+1] = '\0';
_ZMemtrackLogPrintf("%s\n", strOutput);
}
}
/*** Public functions *************************************************************/
/*F********************************************************************************/
/*!
\Function ZMemtrackStartup
\Description
Start up the ZMemtracking module.
\Version 02/15/2005 (jbrookes)
*/
/********************************************************************************F*/
void ZMemtrackStartup(void)
{
ZMemtrackRefT *pRef = &_ZMemtrack_Ref;
ds_memclr(pRef, sizeof(*pRef));
pRef->bStarted = TRUE;
}
/*F********************************************************************************/
/*!
\Function ZMemtrackShutdown
\Description
Shut down the ZMemtracking module.
\Version 02/15/2005 (jbrookes)
*/
/********************************************************************************F*/
void ZMemtrackShutdown(void)
{
// dump the current status of the entire module
ZMemtrackPrint(ZMEMTRACK_PRINTFLAG_TRACKING, 0, NULL);
_ZMemtrack_Ref.bStarted = FALSE;
}
/*F********************************************************************************/
/*!
\Function ZMemtrackCallback
\Description
Set the logging callback
\Input *pLoggingCb - logging function pointer
\Input *pUserData - additional data to pass along
\Version 09/18/2017 (eesponda)
*/
/********************************************************************************F*/
void ZMemtrackCallback(ZMemtrackLogCbT *pLoggingCb, void *pUserData)
{
ZMemtrackRefT *pRef = &_ZMemtrack_Ref;
pRef->pLoggingCb = pLoggingCb;
pRef->pUserData = pUserData;
}
/*F********************************************************************************/
/*!
\Function ZMemtrackAlloc
\Description
Track an allocation.
\Input *pMem - pointer to allocated memory block
\Input uSize - size of allocated memory block
\Input uTag - allocation tag
\Version 02/15/2005 (jbrookes)
*/
/********************************************************************************F*/
void ZMemtrackAlloc(void *pMem, uint32_t uSize, uint32_t uTag)
{
ZMemtrackRefT *pRef = &_ZMemtrack_Ref;
uint32_t uMemEntry;
// now if we got the memory, add to the list
if ((pMem == NULL) || (pRef->bStarted == FALSE))
{
return;
}
// find a clear spot
for (uMemEntry = 0; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++)
{
if (pRef->MemList[uMemEntry].pMem == NULL)
{
// get the memory location
pRef->uTotalMemory += uSize;
pRef->uNumAllocations += 1;
pRef->uTotalAllocations += 1;
// update high-water tracking
if (pRef->uMaxAllocations < pRef->uNumAllocations)
{
pRef->uMaxAllocations = pRef->uNumAllocations;
}
if (pRef->uMaxMemory < pRef->uTotalMemory)
{
pRef->uMaxMemory = pRef->uTotalMemory;
}
// store the info
pRef->MemList[uMemEntry].pMem = pMem;
pRef->MemList[uMemEntry].uMemSize = uSize;
pRef->MemList[uMemEntry].uTag = uTag;
break;
}
}
// check to see if we ran out of room to store this stuff
if (uMemEntry == ZMEMTRACK_MAXALLOCATIONS)
{
pRef->bOverflow = 1;
}
}
/*F********************************************************************************/
/*!
\Function ZMemtrackFree
\Description
Track a free operation.
\Input *pMem - pointer to allocated memory block
\Input *pSize - [out] storage for memory block size
\Version 02/15/2005 (jbrookes)
*/
/********************************************************************************F*/
void ZMemtrackFree(void *pMem, uint32_t *pSize)
{
ZMemtrackRefT *pRef = &_ZMemtrack_Ref;
uint32_t uMemEntry;
if ((pMem == NULL) || (pRef->bStarted == FALSE))
{
*pSize = 0;
return;
}
for (uMemEntry = 0, *pSize = 0; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++)
{
if (pRef->MemList[uMemEntry].pMem == pMem)
{
pRef->uTotalMemory -= pRef->MemList[uMemEntry].uMemSize;
pRef->uNumAllocations -= 1;
*pSize = pRef->MemList[uMemEntry].uMemSize;
ds_memclr(&pRef->MemList[uMemEntry], sizeof(pRef->MemList[uMemEntry]));
break;
}
}
}
/*F********************************************************************************/
/*!
\Function ZMemtrackPrint
\Description
Print overall memory info.
\Input uFlags - ZMemtrack_PRINTFLAG_*
\Input uTag - [optional] if non-zero, only display memory leaks stamped with this tag
\Input *pModuleName - [optional] pointer to module name
\Version 02/15/2005 (jbrookes)
*/
/********************************************************************************F*/
void ZMemtrackPrint(uint32_t uFlags, uint32_t uTag, const char *pModuleName)
{
ZMemtrackRefT *pRef = &_ZMemtrack_Ref;
uint32_t uMemEntry;
if (uFlags & ZMEMTRACK_PRINTFLAG_TRACKING)
{
_ZMemtrackLogPrintf("memory report\n");
_ZMemtrackLogPrintf(" maximum number of allocations at once: [%u]\n", pRef->uMaxAllocations);
_ZMemtrackLogPrintf(" current number of allocations : [%u]\n", pRef->uNumAllocations);
_ZMemtrackLogPrintf(" total number of allocations ever : [%u]\n", pRef->uTotalAllocations);
_ZMemtrackLogPrintf(" maximum memory allocated : [%u] bytes\n", pRef->uMaxMemory);
_ZMemtrackLogPrintf(" current memory allocated : [%u] bytes\n", pRef->uTotalMemory);
_ZMemtrackLogPrintf("\n");
}
if (pRef->bOverflow)
{
_ZMemtrackLogPrintf("WARNING: Allocation watcher overflowed!");
}
// see if there were any leaks
for (uMemEntry = 0; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++)
{
ZMemtrackElemT *pElem = &pRef->MemList[uMemEntry];
if ((pElem->pMem != NULL) && ((uTag == 0) || (pElem->uTag == uTag)))
{
break;
}
}
// if there were leaks, display them
if (uMemEntry != ZMEMTRACK_MAXALLOCATIONS)
{
_ZMemtrackLogPrintf("detected %s memory leaks!\n", pModuleName != NULL ? pModuleName : "");
for ( ; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++)
{
ZMemtrackElemT *pElem = &pRef->MemList[uMemEntry];
if ((pElem->pMem != NULL) && ((uTag == 0) || (pElem->uTag == uTag)))
{
_ZMemtrackPrintLeak(pElem);
}
}
}
}