blob: 21e85eabf281dd356bb4e96ac0519d4633f0593d [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 2008 PacketVideo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
* -------------------------------------------------------------------
*/
/*! \file oscl_mem_audit.cpp
\brief This file contains the implementation of MM_Audit class
*/
#include "osclconfig_memory.h"
#if (!OSCL_BYPASS_MEMMGT)
#include "oscl_types.h"
#include "oscl_mem.h"
#include "oscl_mem_audit.h"
#include "oscl_mem_audit_internals.h"
#include "oscl_assert.h"
#include "oscl_stdstring.h"
#if MM_AUDIT_FENCE_SUPPORT
const uint32 PRE_PAD_SIZE = sizeof(MM_AllocBlockFence);
const uint32 POST_FENCE_SIZE = sizeof(MM_AllocBlockFence);
#else
const uint32 PRE_PAD_SIZE = 0;
const uint32 POST_FENCE_SIZE = 0;
#endif
const uint32 CONTROL_HEADER_SIZE = sizeof(MM_AllocBlockHdr);
const uint32 BLOCK_HDR_SIZE = CONTROL_HEADER_SIZE + PRE_PAD_SIZE;
const uint32 BLOCK_OVERHEAD_SIZE = BLOCK_HDR_SIZE + POST_FENCE_SIZE;
/**
* MM_Audit_Imp constructor and descontructor
*/
/* ======================================================================== */
/* Function : MM_Audit() */
/* Date : 10/08/2002 */
/* Purpose : Constructor, create the root node in statistics table */
/* In/out : */
/* Return : */
/* Modified : */
/* ======================================================================== */
const char root_tag[] = "";
MM_Audit_Imp::MM_Audit_Imp()
{
mpCurrAllocNode = mpAllocNode = NULL;
mNumAllocNodes = 0;
prefill_pattern = DEFAULT_PREFILL_PATTERN;
postfill_pattern = DEFAULT_POSTFILL_PATTERN;
mpStatsNode = NULL;
mnAllocNum = 0;
mnMaxTagLevel = 10;
mm_audit_per_block_overhead = 0;
mm_audit_stats_overhead = 0;
mode = DEFAULT_MM_AUDIT_MODE;
/* create root node in memory statistics table */
mpStatsNode = createStatsNode(root_tag);
}
/* ======================================================================== */
/* Function : ~MM_Audit() */
/* Date : 10/08/2002 */
/* Purpose : destructor, remove all the nodes in allocation and */
/* statistics table */
/* In/out : */
/* Return : */
/* Modified : */
/* ======================================================================== */
MM_Audit_Imp::~MM_Audit_Imp()
{
#if MM_AUDIT_ALLOC_NODE_SUPPORT
/**
* Delete all the allocation nodes
*/
removeALLAllocNodes();
mpAllocNode = mpCurrAllocNode = NULL;
mnAllocNum = 0;
#endif
/**
* Delete all the statistics nodes
*/
mpStatsNode = NULL;
mTagTree.clear();
}
/*
* ===================== API FUNCTIONS: MM_xxx() ==================================
*/
/* ============================================================================ */
/* Function : MM_allocate() */
/* Date : 10/08/2002 */
/* Purpose : API for a memory allocation attempt: create a new node in */
/* allocation table and update/create the corresponding node in */
/* statistics table */
/* In/out : all parameters are input */
/* Return : memory pointer, if something is wrong, the pointer will be NULL */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF void* MM_Audit_Imp::MM_allocate(const OsclMemStatsNode* pInStatsNode,
uint32 sizeIn,
const char *pFileName,
uint32 lineNumber,
bool allocNodeTracking)
{
#if defined(DEBUG_ENABLE)
PVUStackTrace();
if (pFileName)
{
printf("In MM_allocate, nBytes %d, file_name is %s, line_num is %d\n", sizeIn, pFileName, lineNumber);
}
else
{
printf("In MM_allocate with tag %s operator, nBytes %d\n", sizeIn);
}
#endif
MMAuditUint8AutoPtr pMem;
void *pMem_out = NULL;
#if (MM_AUDIT_ALLOC_NODE_SUPPORT && MM_AUDIT_INCLUDE_ALL_HEAP_VALIDATION)
if (mode & MM_AUDIT_VALIDATE_ALL_HEAP_FLAG)
{
validate_all_heap();
}
#endif
OsclMemStatsNode* pStatsNode;
if (pInStatsNode)
{
// remove "constness" of the stats node so it can be updated.
pStatsNode = const_cast<OsclMemStatsNode*>(pInStatsNode);
}
else
{
pStatsNode = mpStatsNode; // set to the root node.
}
uint32 full_size = sizeIn + BLOCK_OVERHEAD_SIZE;
#if MM_AUDIT_FAILURE_SIMULATION_SUPPORT
if (!isSetFailure(pStatsNode))
{
pMem.allocate(full_size);
}
#else
if (full_size > sizeIn)
{
pMem.allocate(full_size);
}
#endif
if (!pMem.get())
{
updateStatsNodeInFailure(pStatsNode);
return NULL;
}
MM_AllocNode* pAllocNode = NULL;
#if MM_AUDIT_ALLOC_NODE_SUPPORT
if ((mode & MM_AUDIT_ALLOC_NODE_ENABLE_FLAG) || allocNodeTracking ||
(sizeIn & MM_AllocBlockHdr::ALLOC_NODE_FLAG))
{
if ((pAllocNode = addAllocNode((void *)pMem.get(), sizeIn, pStatsNode, pFileName, lineNumber)) == NULL)
{
updateStatsNodeInFailure(pStatsNode);
return NULL;
}
}
#endif
// now write the header to the block
MM_AllocBlockHdr *block_hdr =
static_cast<MM_AllocBlockHdr *>(static_cast<void*>(pMem.get()));
block_hdr->size = sizeIn;
if (pAllocNode)
{
block_hdr->setAllocNodeFlag();
block_hdr->pNode = pAllocNode;
}
else
{
block_hdr->pNode = pStatsNode;
}
/**
* Save the root audit node pointer in the header.
* This pointer will be used when the block is deallocated.
*/
OsclAuditCB audit;
OsclMemInit(audit);
block_hdr->pRootNode = audit.pAudit;
#if MM_AUDIT_FILL_SUPPORT
if ((mode & MM_AUDIT_PREFILL_FLAG))
{
oscl_memset(pMem.get() + BLOCK_HDR_SIZE, prefill_pattern, sizeIn);
}
#endif
#if MM_AUDIT_FENCE_SUPPORT
/* fill the pre-fence */
MM_AllocBlockFence *pFence =
static_cast<MM_AllocBlockFence*>(static_cast<void*>(pMem.get() +
CONTROL_HEADER_SIZE));
pFence->fill_fence();
/* fill the post-fence */
uint32 post_fence_offset = sizeIn + BLOCK_HDR_SIZE;
pFence =
static_cast<MM_AllocBlockFence*>(static_cast<void*>(pMem.get() +
post_fence_offset));
pFence->fill_fence();
#endif
/* 8-byte alignment */
pMem_out = pMem.get() + BLOCK_HDR_SIZE;
MM_Stats_t delta;
delta.numBytes = sizeIn;
delta.numAllocs = 1;
delta.numAllocFails = 0;
updateStatsNode(pStatsNode, delta, true);
pMem.release(); // release so it doesn't free up memory on exit
// account for the per-block overhead
mm_audit_per_block_overhead += BLOCK_OVERHEAD_SIZE;
return pMem_out;
}
/* ============================================================================ */
/* Function : MM_deallocate() */
/* Date : 10/08/2002 */
/* Purpose : API for a memory de-allocation attempt: remove a node in */
/* allocation table and update the corresponding node in */
/* statistics table */
/* In/out : all parameters are input */
/* Return : */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF bool MM_Audit_Imp::MM_deallocate(void *pMemBlockIn)
{
if (!pMemBlockIn) return false;
if (mpStatsNode == NULL ||
(mpStatsNode && mpStatsNode->pMMStats && mpStatsNode->pMMStats->peakNumBytes == 0)) /* No actual allocation happens */
{
return true;
}
#if (MM_AUDIT_ALLOC_NODE_SUPPORT && MM_AUDIT_INCLUDE_ALL_HEAP_VALIDATION)
if (mode & MM_AUDIT_VALIDATE_ALL_HEAP_FLAG)
{
validate_all_heap();
}
#endif
bool status = true;
#if MM_AUDIT_VALIDATE_BLOCK
if (mode & MM_AUDIT_VALIDATE_ON_FREE_FLAG)
{
if (!validate(pMemBlockIn))
{
return false;
}
}
#endif
uint8 *pMem = static_cast<uint8 *>(pMemBlockIn);
pMem -= BLOCK_HDR_SIZE;
OsclMemStatsNode *pStatsNode = NULL;
MM_AllocBlockHdr *pMemBlockHdr =
static_cast<MM_AllocBlockHdr*>(static_cast<void*>(pMem));
uint32 size = pMemBlockHdr->size;
#if MM_AUDIT_ALLOC_NODE_SUPPORT
if (size & MM_AllocBlockHdr::ALLOC_NODE_FLAG)
{
pStatsNode = removeAllocNode((void *)pMem, size);
OSCL_ASSERT(pStatsNode);
}
#endif
if (!pStatsNode)
{
pStatsNode = static_cast<OsclMemStatsNode*>(pMemBlockHdr->pNode);
}
/**
* 3/1. update the node in memory statistics table
*/
MM_Stats_t delta;
delta.numAllocs = 1;
delta.numBytes = size;
delta.numAllocFails = 0;
status = updateStatsNode(pStatsNode, delta, false);
#if MM_AUDIT_FILL_SUPPORT
if ((mode & MM_AUDIT_POSTFILL_FLAG))
{
oscl_memset(pMem + BLOCK_HDR_SIZE, postfill_pattern, size);
}
#endif
MMAuditUint8AutoPtr::deallocate(pMem);
// account for the per-block overhead
mm_audit_per_block_overhead -= BLOCK_OVERHEAD_SIZE;
return status;
}
/* ============================================================================ */
/* Function : MM_GetTreeNodes() */
/* Date : 10/31/2002 */
/* Purpose : API to get the number of tree nodes including the tag node and */
/* its subtree */
/* In/out : all parameters are input */
/* Return : 0 means no tag node ; >0 means the number of tree nodes */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF uint32 MM_Audit_Imp::MM_GetTreeNodes(const char * tagIn)
{
int32 count = 0;
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator iter = mTagTree.find(tag.get());
if (iter != mTagTree.end())
{
count++; //to account for the parent
count = iter->children.size();
}
return ((uint32)count);
}
/* ================================================================================ */
/* Function : MM_GetStats() */
/* Date : 10/08/2002 */
/* Purpose : API to get memory statistics through context string(tag) */
/* In/out : all parameters are input */
/* Return : the statistics (pointer) for the current tag node */
/* if something is wrong, return NULL */
/* Modified : */
/* ================================================================================ */
OSCL_EXPORT_REF MM_Stats_t* MM_Audit_Imp::MM_GetStats(const char * const tagIn)
{
MM_Stats_t *pMMStats = NULL;
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator iter = mTagTree.find(tag.get());
if (iter != mTagTree.end())
{
/* Got it! */
OsclMemStatsNode *pStatsNode = (iter->value).get();
if (pStatsNode)
{
pMMStats = pStatsNode->pMMStats;
}
}
return pMMStats;
}
OSCL_EXPORT_REF uint32 MM_Audit_Imp::MM_GetStatsInDepth(const char *tagIn,
MM_Stats_CB *array_ptr,
uint32 max_nodes)
{
uint32 curr_array_index = 0;
populateChildren(tagIn, array_ptr, curr_array_index, max_nodes);
return (curr_array_index);
}
/* ============================================================================ */
/* Function : getNodeChildren() */
/* Date : 10/08/2002 */
/* Purpose : recursive function to go throught each child node for the */
/* current node and counter it */
/* In/out : all parameters are input */
/* Return : */
/* Modified : */
/* ============================================================================ */
void MM_Audit_Imp::populateChildren(const char *tagIn,
MM_Stats_CB *array_ptr,
uint32 &curr_array_index,
uint32 max_nodes)
{
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
if (curr_array_index > max_nodes)
{
return;
}
if (miter != mTagTree.end() && max_nodes > 0)
{
uint32 num_children = miter->children.size();
miter->sort_children();
array_ptr[curr_array_index].num_child_nodes = num_children;
for (uint32 i = 0; i < num_children; i++)
{
array_ptr[curr_array_index].tag = (miter->tag).tag;
OsclMemStatsNode *pStatsNode = (miter->value).get();
if (pStatsNode)
{
array_ptr[curr_array_index].pStats = pStatsNode->pMMStats;
}
curr_array_index++;
if (curr_array_index > max_nodes)
{
return;
}
}
/* access its children nodes */
if (!miter->children.empty())
{
/* recursive search */
for (uint32 i = 0; i < num_children; i++)
{
populateChildren((miter->children[i]->tag).tag,
array_ptr,
curr_array_index,
max_nodes);
}
}
}
return;
}
/* ============================================================================ */
/* Function : MM_GetTag() */
/* Date : 10/08/2002 */
/* Purpose : API to get an StatsNode or creates one if it doesn't exist */
/* In/out : all parameters are input */
/* Return : pointer to OsclMemStatsNode if operation succeeds, NULL otherwise */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF const OsclMemStatsNode* MM_Audit_Imp::MM_GetTagNode(const char * tagIn)
{
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator iter = mTagTree.find(tag.get());
if (iter != mTagTree.end())
return ((iter->value).get());
else
return createStatsNode(tag.get()); /* create a new empty node */
}
/* ============================================================================ */
/* Function : MM_GetTag() */
/* Date : 10/08/2002 */
/* Purpose : API to get an existing node */
/* In/out : all parameters are input */
/* Return : pointer to OsclMemStatsNode if operation succeeds, NULL otherwise */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF const OsclMemStatsNode* MM_Audit_Imp::MM_GetExistingTag(const char * tagIn)
{
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator iter = mTagTree.find(tag.get());
if (iter != mTagTree.end())
return ((iter->value).get());
else
return NULL;
}
/* ============================================================================ */
/* Function : MM_Validate() */
/* Date : 10/08/2002 */
/* Purpose : API to check the input pointer is a valid pointer to a chunk of */
/* memory */
/* In/out : all parameters are input */
/* Return : */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF bool MM_Audit_Imp::MM_Validate(const void *ptrIn)
{
bool status = validate(const_cast<void*>(ptrIn));
return status;
}
/* ============================================================================ */
/* Function : MM_SetTagLevel() */
/* Date : 10/08/2002 */
/* Purpose : API to set the maximum tag level,i.e. tag level for a.b.c.d = 4 */
/* In/out : all parameters are input */
/* Return : */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF void MM_Audit_Imp::MM_SetTagLevel(uint32 level)
{
if (level >= 1) mnMaxTagLevel = level;
}
OSCL_EXPORT_REF void MM_Audit_Imp::MM_SetMode(uint32 in_mode)
{
mode = in_mode;
}
OSCL_EXPORT_REF void MM_Audit_Imp::MM_SetPrefillPattern(uint8 pattern)
{
prefill_pattern = pattern;
}
OSCL_EXPORT_REF void MM_Audit_Imp::MM_SetPostfillPattern(uint8 pattern)
{
postfill_pattern = pattern;
}
OSCL_EXPORT_REF MM_AllocQueryInfo* MM_Audit_Imp::MM_CreateAllocNodeInfo(uint32 array_size)
{
return(MM_AllocQueryInfo*)_oscl_malloc(array_size*sizeof(MM_AllocQueryInfo));
}
OSCL_EXPORT_REF void MM_Audit_Imp::MM_ReleaseAllocNodeInfo(MM_AllocQueryInfo* output_array)
{
_oscl_free(output_array);
}
OSCL_EXPORT_REF uint32 MM_Audit_Imp::MM_GetAllocNodeInfo(MM_AllocQueryInfo* output_array,
uint32 max_array_size, uint32 offset)
{
uint32 num_nodes = 0;
if (!output_array)
{
return 0;
};
if (offset >= mNumAllocNodes)
{
return 0;
}
MM_AllocNode *pAllocNode = mpAllocNode;
// skip the leading nodes
uint32 ii;
for (ii = 0; pAllocNode && ii < offset; ++ii, pAllocNode = pAllocNode->pNext)
{
}
if (ii != offset)
{
return 0;
}
MM_AllocQueryInfo* pOutNode = output_array;
while (pAllocNode && (num_nodes < max_array_size))
{
pOutNode->allocNum = pAllocNode->pAllocInfo->allocNum;
pOutNode->lineNo = pAllocNode->pAllocInfo->lineNo;
pOutNode->size = pAllocNode->pAllocInfo->size;
pOutNode->pMemBlock = pAllocNode->pAllocInfo->pMemBlock;
pOutNode->size = pAllocNode->pAllocInfo->size;
if (pAllocNode->pAllocInfo->pFileName)
{
oscl_strncpy(pOutNode->fileName, pAllocNode->pAllocInfo->pFileName,
oscl_strlen(pAllocNode->pAllocInfo->pFileName) + 1);
pOutNode->fileName[MM_ALLOC_MAX_QUERY_FILENAME_LEN-1] = '\0';
}
else
{
pOutNode->fileName[0] = '\0';
}
if (pAllocNode->pAllocInfo->pStatsNode->tag)
{
oscl_strncpy(pOutNode->tag, pAllocNode->pAllocInfo->pStatsNode->tag,
oscl_strlen(pAllocNode->pAllocInfo->pStatsNode->tag) + 1);
pOutNode->tag[MM_ALLOC_MAX_QUERY_TAG_LEN-1] = '\0';
}
else
{
pOutNode->tag[0] = '\0';
}
++num_nodes;
++pOutNode;
pAllocNode = pAllocNode->pNext;
}
return num_nodes;
}
/* ============================================================================ */
/* Function : MM_SetFailurePoint() */
/* Date : 11/05/2002 */
/* Purpose : API to insert allocation failure deterministically according to */
/* allocation number associated with tag */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF bool MM_Audit_Imp::MM_SetFailurePoint(const char * tagIn, uint32 alloc_number)
{
if (alloc_number == 0) return false;
bool status = true;
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
if (miter != mTagTree.end())
{
/* found */
OsclMemStatsNode *pStatsNode = (miter->value).get();
if (pStatsNode == NULL || pStatsNode->pMMFIParam == NULL)
{
return false;
}
pStatsNode->pMMFIParam->nAllocNum = alloc_number;
}
else
{
/* Needs to create a new node */
OsclMemStatsNode *pStatsNode = createStatsNode(tag.get());
if (pStatsNode == NULL || pStatsNode->pMMFIParam == NULL)
{
return false;
}
pStatsNode->pMMFIParam->nAllocNum = alloc_number;
}
return status;
}
/* ============================================================================ */
/* Function : MM_UnsetFailurePoint() */
/* Date : 11/05/2002 */
/* Purpose : API to cancel the allocation failure point associated with tag */
/* In/out : all parameters are input */
/* Return : */
/* Modified : */
/* ============================================================================ */
OSCL_EXPORT_REF void MM_Audit_Imp::MM_UnsetFailurePoint(const char * tagIn)
{
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag); //tagIn = NULL means root tag
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
if (miter != mTagTree.end())
{
/* found */
OsclMemStatsNode *pStatsNode = (miter->value).get();
if (pStatsNode && pStatsNode->pMMFIParam)
{
pStatsNode->pMMFIParam->nAllocNum = 0;
}
}
}
/*
* ===================== PRIVATE (SUPPORTING) FUNCTIONS ===========================
*/
#if MM_AUDIT_ALLOC_NODE_SUPPORT
/* ============================================================================ */
/* Function : addAllocNode() */
/* Date : 10/08/2002 */
/* Purpose : add a node in memory allocation table triggered by an allocation */
/* attempt, i.e. MM_allocate(); then update/creat the corresponding */
/* node in memory statistics table */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
MM_AllocNode* MM_Audit_Imp::addAllocNode(void *pMemBlockIn, uint32 sizeIn,
OsclMemStatsNode *pStatsNode,
const char *pFileName, uint32 lineNumber
)
{
uint32 tmp_overhead_size = 0;
/**
* 1. allocate memory for a new node
*/
MM_AllocNodeAutoPtr currAllocAutoPtr(new MM_AllocNode);
if (! currAllocAutoPtr.get())
{
return NULL;
}
tmp_overhead_size += sizeof(MM_AllocNode);
MM_AllocInfo* pAllocInfo = currAllocAutoPtr->pAllocInfo = new MM_AllocInfo;
if (pAllocInfo == NULL)
{
return NULL;
}
tmp_overhead_size += sizeof(MM_AllocInfo);
// allocate space for the filename in AllocInfo
if (pFileName)
{
int len = oscl_strlen(pFileName);
Oscl_TAlloc<char, OsclMemBasicAllocator> charAlloc;
if ((pAllocInfo->pFileName =
charAlloc.allocate(len + 1)) == NULL)
{
return NULL;
}
oscl_strncpy(const_cast<char*>(pAllocInfo->pFileName), pFileName, oscl_strlen(pFileName) + 1);
tmp_overhead_size += len + 1;
}
pAllocInfo->allocNum = mnAllocNum;
//To find memory leaks by allocation number, put a debug breakpoint here, like this:
//if(mnAllocNum==113)
//{
// mnAllocNum++;
// mnAllocNum--;
//}
//To find allocations with a NULL filename, put a debug breakpoint here, like this:
//if(pFileName==NULL)
//{
// mnAllocNum++;
// mnAllocNum--;
//}
mnAllocNum++;
pAllocInfo->pMemBlock = pMemBlockIn;
pAllocInfo->size = sizeIn;
pAllocInfo->lineNo = lineNumber;
pAllocInfo->pStatsNode = pStatsNode;
if (!mpCurrAllocNode) /* First node */
{
/* mpAllocNode always represents the very first node */
mpAllocNode = mpCurrAllocNode = currAllocAutoPtr.release();
}
else
{
OSCL_ASSERT(!mpCurrAllocNode->pNext);
mpCurrAllocNode->pNext = currAllocAutoPtr.release();
currAllocAutoPtr->pPrev = mpCurrAllocNode;
mpCurrAllocNode = mpCurrAllocNode->pNext;
}
#if 0
//for debug & analysis-- print all allocations as they occur...
fprintf(stderr, "Alloc Info:\n");
fprintf(stderr, " allocNum %d\n", pAllocInfo->allocNum);
fprintf(stderr, " fileName %s\n", pAllocInfo->pFileName);
fprintf(stderr, " lineNo %d\n", pAllocInfo->lineNo);
fprintf(stderr, " size %d\n", pAllocInfo->size);
fprintf(stderr, " pMemBlock 0x%x\n", pAllocInfo->pMemBlock);
fprintf(stderr, " tag %s\n", pAllocInfo->pStatsNode->tag);
#endif
/* increment the list counter */
++mNumAllocNodes;
mm_audit_per_block_overhead += tmp_overhead_size;
return mpCurrAllocNode;
}
/* ============================================================================ */
/* Function : removeAllocNode() */
/* Date : 10/08/2002 */
/* Purpose : remove a node in memory allocation table triggered by an */
/* de-allocationattempt, i.e. MM_deallocate() */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
OsclMemStatsNode* MM_Audit_Imp::removeAllocNode(void *pMemBlockIn, uint32& size)
{
if (pMemBlockIn == NULL)
return NULL;
OsclMemStatsNode *pStatsNode = NULL;
OSCL_ASSERT(mpAllocNode);
OSCL_ASSERT(mpCurrAllocNode);
OSCL_ASSERT(mNumAllocNodes);
/* take the allocation node pointer from the control info header */
MM_AllocBlockHdr *pMemBlockHdr = static_cast<MM_AllocBlockHdr*>(pMemBlockIn);
MM_AllocNode *pAllocNode = static_cast<MM_AllocNode*>(pMemBlockHdr->pNode);
if (!pAllocNode) return NULL;
/*
* 1. remove the current node from the list
*/
if (pAllocNode == mpAllocNode)
{
/* head of the list*/
mpAllocNode = mpAllocNode->pNext;
if (mpAllocNode)
{
mpAllocNode->pPrev = NULL;
}
else
{
// list is empty
mpCurrAllocNode = mpAllocNode = NULL;
}
}
else if (pAllocNode == mpCurrAllocNode) /* end of the list */
{
mpCurrAllocNode = mpCurrAllocNode->pPrev;
if (mpCurrAllocNode)
{
mpCurrAllocNode->pNext = NULL;
}
else
{
// list is empty
mpCurrAllocNode = mpAllocNode = NULL;
}
}
else /* somewhere in the list */
{
MM_AllocNode *pPrevNode = pAllocNode->pPrev;
MM_AllocNode *pNextNode = pAllocNode->pNext;
OSCL_ASSERT(pPrevNode && pNextNode);
pPrevNode->pNext = pNextNode;
pNextNode->pPrev = pPrevNode;
}
/**
* 2. free the memory for the current node
*/
OSCL_ASSERT(pAllocNode->pAllocInfo);
pStatsNode = pAllocNode->pAllocInfo->pStatsNode;
size = pAllocNode->pAllocInfo->size;
// adjust the overhead accounting
uint32 filename_len = (pAllocNode->pAllocInfo->pFileName) ? oscl_strlen(pAllocNode->pAllocInfo->pFileName) + 1 : 0;
mm_audit_per_block_overhead -= sizeof(MM_AllocNode) + sizeof(MM_AllocInfo) +
filename_len;
OSCL_DELETE(pAllocNode);
/*
* decrement the list counter
*/
--mNumAllocNodes;
return pStatsNode;
}
/* ============================================================================ */
/* Function : removeALLAllocNodes() */
/* Date : 10/08/2002 */
/* Purpose : remove all the nodes in memory allocation table needed in */
/* destructor */
/* In/out : */
/* Return : */
/* Modified : */
/* ============================================================================ */
void MM_Audit_Imp::removeALLAllocNodes()
{
while (mpCurrAllocNode)
{
MM_AllocNode *pTmpNode = mpCurrAllocNode;
mpCurrAllocNode = mpCurrAllocNode->pPrev;
if (mpCurrAllocNode)
mpCurrAllocNode->pNext = NULL;
--mNumAllocNodes;
OSCL_DELETE(pTmpNode);
}
}
bool MM_Audit_Imp::validate_all_heap()
{
// walk the list of allocated nodes
MM_AllocNode *ptr = mpAllocNode;
if (!ptr)
{
OSCL_ASSERT(mNumAllocNodes == 0);
return true;
}
uint32 counter;
OSCL_ASSERT(ptr->pPrev == NULL);
for (counter = 0; ptr; ptr = ptr->pNext, ++counter)
{
MM_AllocBlockHdr *pMemBlockHdr = static_cast<MM_AllocBlockHdr*>(ptr->pAllocInfo->pMemBlock);
OSCL_ASSERT(pMemBlockHdr->pNode == (void *)ptr);
if (ptr->pNext)
{
OSCL_ASSERT(ptr->pNext->pPrev == ptr);
}
else
{
OSCL_ASSERT(ptr == mpCurrAllocNode);
}
uint8* pMem = static_cast<uint8*>(static_cast<void*>(pMemBlockHdr));
pMem += BLOCK_HDR_SIZE;
bool status = validate(pMem);
OSCL_ASSERT(status);
if (!status)
{
return false;
}
}
OSCL_ASSERT(counter == mNumAllocNodes);
return true;
}
#endif // #if MM_AUDIT_ALLOC_NODE_SUPPORT
/* ============================================================================ */
/* Function : validate() */
/* Date : 11/05/2002 */
/* Purpose : validate the input pointer to a chunk of memory */
/* In/out : input the memory pointer */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
bool MM_Audit_Imp::validate(void *ptrIn)
{
if (!ptrIn) return false;
uint8 *pMem = static_cast<uint8*>(const_cast<void*>(ptrIn));
pMem -= BLOCK_HDR_SIZE;
MM_AllocBlockHdr *pMemBlockHdr =
static_cast<MM_AllocBlockHdr*>((void *)pMem);
OSCL_ASSERT(pMemBlockHdr->pNode);
if (!pMemBlockHdr->pNode)
{
return false;
}
OsclMemStatsNode *pStatsNode = NULL;
#if MM_AUDIT_ALLOC_NODE_SUPPORT
#if MM_AUDIT_FENCE_SUPPORT
uint32 size = pMemBlockHdr->size;
#endif
if (pMemBlockHdr->isAllocNodePtr())
{
MM_AllocNode *pAllocNode = NULL;
pAllocNode = static_cast<MM_AllocNode*>(pMemBlockHdr->pNode);
OSCL_ASSERT(pAllocNode->pAllocInfo);
#if MM_AUDIT_FENCE_SUPPORT
OSCL_ASSERT(((size ^ pAllocNode->pAllocInfo->size) & (~MM_AllocBlockHdr::ALLOC_NODE_FLAG)) == 0);
size = pAllocNode->pAllocInfo->size;
#endif
pStatsNode = pAllocNode->pAllocInfo->pStatsNode;
}
#endif
if (!pStatsNode)
{
// must be a stats node
pStatsNode = static_cast<OsclMemStatsNode*>(pMemBlockHdr->pNode);
}
OSCL_ASSERT(pStatsNode->pMMStats);
#if MM_AUDIT_FENCE_SUPPORT
bool status;
// check the pre-fence
MM_AllocBlockFence *fence =
static_cast<MM_AllocBlockFence*>(static_cast<void*>(pMem +
CONTROL_HEADER_SIZE));
status = fence->check_fence();
OSCL_ASSERT(status);
if (!status)
{
return status;
}
// check the post fence
fence =
static_cast<MM_AllocBlockFence*>(static_cast<void*>(pMem +
BLOCK_HDR_SIZE
+ size));
status = fence->check_fence();
OSCL_ASSERT(status);
if (!status)
{
return status;
}
#endif
return true;
}
/** Retrieve the audit root pointer from within an allocation block*/
OsclMemAudit * MM_Audit_Imp::getAuditRoot(void *ptrIn)
{
if (!ptrIn) return false;
uint8 *pMem = static_cast<uint8*>(const_cast<void*>(ptrIn));
pMem -= BLOCK_HDR_SIZE;
MM_AllocBlockHdr *pMemBlockHdr =
static_cast<MM_AllocBlockHdr*>((void *)pMem);
return (OsclMemAudit*)pMemBlockHdr->pRootNode;
}
uint32 MM_Audit_Imp::getSize(void *ptrIn)
{//get original allocation size.
if (!ptrIn)
return 0;
uint8 *pMem = static_cast<uint8*>(const_cast<void*>(ptrIn));
pMem -= BLOCK_HDR_SIZE;
MM_AllocBlockHdr *pMemBlockHdr = static_cast<MM_AllocBlockHdr*>((void *)pMem);
//if it's an alloc node we have to strip out the alloc node bit
//from the size field.
if (pMemBlockHdr->isAllocNodePtr())
{
return pMemBlockHdr->size & ~MM_AllocBlockHdr::ALLOC_NODE_FLAG;
}
else
{
return pMemBlockHdr->size;
}
}
/* ============================================================================ */
/* Function : createStatsNode() */
/* Date : 10/08/2002 */
/* Purpose : creat a new node in memory statistics table triggered by an */
/* allocation attempt, */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
OsclMemStatsNode* MM_Audit_Imp::createStatsNode(const char * tagIn)
{
OsclTagTreeType::iterator iter;
MMAuditCharAutoPtr currentTag;
OsclMemStatsNode* statsNode;
/* If the input tag already exists in the tagtree, it should have a pointer value of NULL */
OSCL_ASSERT((mTagTree.find(const_cast<char* const&>(tagIn)) == mTagTree.end()) ||
(mTagTree.find(const_cast<char* const&>(tagIn)))->value.get() == 0);
#if 1
statsNode = new OsclMemStatsNode;
if (statsNode == NULL) return NULL;
currentTag.allocate(oscl_strlen(tagIn) + 1);
if (!currentTag.get()) return NULL;
oscl_strncpy(currentTag.get(), tagIn, oscl_strlen(tagIn) + 1);
statsNode->tag = currentTag.release();
if ((statsNode->pMMStats = new MM_Stats_t) == NULL) return NULL;
if ((statsNode->pMMFIParam = new MM_FailInsertParam) == NULL) return NULL;
OsclMemStatsNodeAutoPtr statsNodeAutoPtr(statsNode);
// reassign ownership of the StatsNode to the tag tree
mTagTree[statsNodeAutoPtr->tag] = statsNodeAutoPtr;
// account for the overhead memory
mm_audit_stats_overhead += sizeof(MM_Stats_t) +
sizeof(MM_FailInsertParam) +
sizeof(OsclMemStatsNode) +
oscl_strlen(currentTag.get()) + 1;
// how many levels deep is the node we just inserted?
iter = mTagTree.find(statsNodeAutoPtr->tag);
uint32 depth = iter->depth();
// the tag tree will automatically create the parent, grandparent, etc.
// make sure each ancestor's stats node is initialized, i.e. initialize each ancestor
// until you reach one that is already initialized.
OsclTagTreeType::node_ptr parent = iter->parent;
for (uint32 ii = 0; ii < depth; ii++)
{
OSCL_ASSERT(parent != 0);
// if initialized then we're done
OsclMemStatsNode* tmpStatsNode = (parent->value).get();
if (tmpStatsNode != NULL) break;
// create new stats node
tmpStatsNode = new OsclMemStatsNode;
if (tmpStatsNode == NULL) return NULL;
// copy tag already created by the tag tree
currentTag.allocate(oscl_strlen(parent->tag.tag) + 1);
if (!currentTag.get()) return NULL;
oscl_strncpy(currentTag.get(), parent->tag.tag, oscl_strlen(parent->tag.tag) + 1);
tmpStatsNode->tag = currentTag.release();
if ((tmpStatsNode->pMMStats = new MM_Stats_t) == NULL) return NULL;
if ((tmpStatsNode->pMMFIParam = new MM_FailInsertParam) == NULL) return NULL;
// set the new stats node to be held by the tag tree
parent->value.takeOwnership(tmpStatsNode);
// account for the overhead memory
mm_audit_stats_overhead += sizeof(OsclMemStatsNode) +
oscl_strlen(currentTag.get()) + 1 +
sizeof(MM_Stats_t) +
sizeof(MM_FailInsertParam);
parent = parent->parent;
}
return statsNode;
#else
OsclMemStatsNodeAutoPtr pCurrentStatsAutoPtr;
OsclMemStatsNode* pStatsNodePtr = NULL;
uint32 len = oscl_strlen(tagIn) + 1;
do
{
if ((iter = mTagTree.find(currentTag.get())) == mTagTree.end())
{
// printf("Didn't find tag %s. Creating a new stats node\n", currentTag.get());
OsclMemStatsNodeAutoPtr statsNodeAutoPtr(new OsclMemStatsNode);
if (statsNodeAutoPtr.get() == NULL)
{
return NULL;
}
// statsNodeAutoPtr will now "own" the tag string. It will
// be responsible for freeing it.
statsNodeAutoPtr->tag = currentTag.release();
if ((statsNodeAutoPtr->pMMStats = new MM_Stats_t) == NULL)
{
return NULL;
}
if ((statsNodeAutoPtr->pMMFIParam = new MM_FailInsertParam) == NULL)
{
return NULL;
}
// reassign ownership of the StatsNode to the map class
mTagTree[statsNodeAutoPtr->tag] = statsNodeAutoPtr;
// account for the overhead memory
mm_audit_stats_overhead += sizeof(MM_Stats_t) +
sizeof(MM_FailInsertParam) + sizeof(OsclMemStatsNode) +
oscl_strlen(currentTag.get()) + 1;
pStatsNodePtr = statsNodeAutoPtr.get();
// update the current ptr
pCurrentStatsAutoPtr = statsNodeAutoPtr;
// Get the parent tag length
len = retrieveParentTagLength(tagIn, len - 1);
if (len > 0)
{
currentTag.allocate(len);
if (! currentTag.get())
{
return NULL;
}
strncpy(currentTag.get(), tagIn, len);
*(currentTag.get() + (len - 1)) = '\0';
}
}
else
{
break;
}
}
while (len > 0);
return pStatsNodePtr;
#endif
}
/* ============================================================================ */
/* Function : updateStatsNode() */
/* Date : 10/08/2002 */
/* Purpose : update the node in memory statistics table triggered by an */
/* allocation/de-alocation attempt, */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
bool MM_Audit_Imp::updateStatsNode(OsclMemStatsNode *pCurrStatsNode, const MM_Stats_t& delta, bool add)
{
MMAuditCharAutoPtr tag;
makeValidTag((const char*)(pCurrStatsNode->tag), tag);
//Update
if (!pCurrStatsNode->pMMStats) return false;
pCurrStatsNode->pMMStats->update(delta, add);
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
uint32 depth = miter->depth();
OsclTagTreeType::node_ptr parent = miter->parent;
for (uint32 i = 0; i < depth; i++)
{
if (!parent->value->pMMStats) return false;
parent->value->pMMStats->update(delta, add);
parent = miter->parent;
}
return true;
}
/* ============================================================================ */
/* Function : updateStatsNodeInFailure() */
/* Date : 11/05/2002 */
/* Purpose : update "pMMStats->numAllocFails" for the current tag node with */
/* its parent tag nodes */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
bool MM_Audit_Imp::updateStatsNodeInFailure(const char * tagIn)
{
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag);
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
OsclMemStatsNode *pStatsNode;
if (miter != mTagTree.end())
{/* found */
pStatsNode = (miter->value).get();
}
else
{
pStatsNode = mpStatsNode; // update the root node
}
return updateStatsNodeInFailure(pStatsNode);
}
bool MM_Audit_Imp::updateStatsNodeInFailure(OsclMemStatsNode * pStatsNode)
{
MMAuditCharAutoPtr tag;
if (!pStatsNode) return false;
makeValidTag((const char*)(pStatsNode->tag), tag);
if (!pStatsNode->pMMStats) return false;
pStatsNode->pMMStats->numAllocFails++;
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
uint32 depth = miter->depth();
OsclTagTreeType::node_ptr parent = miter->parent;
for (uint32 i = 0; i < depth; i++)
{
if (!parent->value->pMMStats) return false;
parent->value->pMMStats->numAllocFails++;
parent = miter->parent;
}
return true;
}
/* ============================================================================ */
/* Function : isSetFailure() */
/* Date : 11/05/2002 */
/* Purpose : do allocation failure check */
/* In/out : all parameters are input */
/* Return : true if operation succeeds */
/* Modified : */
/* ============================================================================ */
bool MM_Audit_Imp::isSetFailure(const char * tagIn)
{
MMAuditCharAutoPtr tag;
makeValidTag(tagIn, tag);
OsclTagTreeType::iterator miter = mTagTree.find(tag.get());
if (miter != mTagTree.end()) /* found */
{
OsclMemStatsNode *pStatsNode = (miter->value).get();
return isSetFailure(pStatsNode);
}
return false;
}
bool MM_Audit_Imp::isSetFailure(OsclMemStatsNode* pStatsNode)
{
if (pStatsNode == NULL) return false;
if (pStatsNode->pMMFIParam == NULL) return false;
/* decision for deterministic failure insertion */
if (pStatsNode->pMMFIParam->nAllocNum > 0)
{
/* the "+ 1" term in the computation of total_allocs is to
* count the current allocation
*/
uint32 total_allocs = pStatsNode->pMMStats->peakNumAllocs + 1 +
pStatsNode->pMMStats->numAllocFails ;
if (total_allocs == pStatsNode->pMMFIParam->nAllocNum)
return true;
}
return false;
}
/* ========================================================================================== */
/* Function : retrieveParentTagLength() */
/* Date : 10/16/2002 */
/* Purpose : get the length of the parent tag(i.e."a.b.c") of the input tag(i.e. "a.b.c.d") */
/* current node and counter it */
/* In/out : all parameters are input */
/* Return : length of the parent tag(sub-string) of an input tag */
/* Modified : */
/* ========================================================================================== */
int32 MM_Audit_Imp::retrieveParentTagLength(const char *tag, int32 bound)
{
if (!tag) return 0;
#define PV_MIN(a,b) ((a)<(b)? (a):(b))
int count = 0;
int len = PV_MIN((int32)(oscl_strlen(tag)), bound);
if (len <= 0)
return 0;
else
{
bool bFound = false;
for (count = len - 1; count >= 0; count--)
{
if (tag[count] == '.')
{
bFound = true;
break;
}
}
if (!bFound) count = 0;
}
return count + 1;
}
void MM_Audit_Imp::retrieveParentTag(char *tag)
{
if (!tag) return;
int32 len = oscl_strlen(tag);
if (len == 1)
tag[0] = '\0';
else
{
bool bFound = false;
for (int32 i = len - 1; i >= 0; i--)
{
if (tag[i] == '.')
{
tag[i] = '\0';
bFound = true;
break;
}
}
if (!bFound)
tag[0] = '\0';
}
}
/* ========================================================================================== */
/* Function : makeValidTag() */
/* Date : 10/25/2002 */
/* Purpose : check the input tag and make sure its level would be bounded in maximum tag */
/* level, if its level is larger, then truncate it */
/* Note that level of "a.b.c.d" = 4 */
/* In/out : In: tagIn ; Out: *bFree */
/* Return : a valid tag within the maximum tag level constraint */
/* Modified : */
/* ========================================================================================== */
void MM_Audit_Imp::makeValidTag(const char * tagIn, MMAuditCharAutoPtr& autoptr)
{
if (tagIn == NULL)
{
//tagIn = NULL means root tag
autoptr.setWithoutOwnership(const_cast<char*>(root_tag));
}
else
{
uint32 len = getTagActualSize(tagIn);
if (len == 0)
{
/* len = 0 meaning no need of truncation */
autoptr.setWithoutOwnership(const_cast<char*>(tagIn));
}
else
{
autoptr.allocate(len + 1);
oscl_strncpy(autoptr.get(), tagIn, len);
*(autoptr.get() + len) = '\0';
}
}
}
/* ========================================================================================== */
/* Function : getTagActualSize() */
/* Date : 10/25/2002 */
/* Purpose : get the actual size of an input tag within the maximum tag level constraint */
/* Note that level of "a.b.c.d" = 4 */
/* In/out : In: tagIn ; Out: *bFree */
/* Return : 0 means no truncation ; >0 means truncated size */
/* Modified : */
/* ========================================================================================== */
uint32 MM_Audit_Imp::getTagActualSize(const char * tagIn)
{
uint32 i, len;
uint32 level = 0, count = 0;
len = oscl_strlen(tagIn);
if (len <= 2*mnMaxTagLevel - 1)
return 0; /* no truncation */
for (i = 0; i < len; i++)
{
if (tagIn[i] == '.')
{
if (++level == mnMaxTagLevel)
{
count = i;
break;
}
}
}
return count;
}
#endif //if OSCL_BYPASS_MEMMGT