| /*---------------------------------------------------------------------------* |
| * pmemory.c * |
| * * |
| * Copyright 2007, 2008 Nuance Communciations, Inc. * |
| * * |
| * 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. * |
| * * |
| *---------------------------------------------------------------------------*/ |
| |
| |
| |
| |
| #include "passert.h" |
| #include "pcrc.h" |
| #include "pmemory.h" |
| #include "PFileSystem.h" |
| #include "PStackTrace.h" |
| #include "passert.h" |
| #include "pmemory_ext.h" |
| #include "pmutex.h" |
| |
| #ifndef USE_STDLIB_MALLOC |
| |
| #undef malloc |
| #undef calloc |
| #undef realloc |
| #undef free |
| |
| static unsigned int gNbInit = 0; |
| static PFile* gFile = NULL; |
| static ESR_BOOL isLogEnabled = ESR_TRUE; |
| |
| #ifdef PMEM_MAP_TRACE |
| static asr_uint32_t gMaxAlloc = -1; |
| static asr_uint32_t gCurAlloc = -1; |
| #endif |
| |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| static size_t gMemPoolSize = (3*1024*1024); /* default value: 3M */ |
| #endif |
| |
| #ifdef USE_THREAD |
| static MUTEX memMutex; |
| #endif |
| |
| #define MAX_MEM_TAG 256 |
| |
| /* Only PMEM_MAP_TRACE has been defined, could do other memory logging/debugging */ |
| #ifdef PMEM_MAP_TRACE |
| |
| #define PMEM_STACKTRACE 0 |
| /* If enabled, logs individual memory allocation, reallocation, free operations */ |
| #define PMEM_LOG_LOWLEVEL 0 |
| #elif defined(WIN32) |
| #pragma message("No PMEM_MAP_TRACE") |
| #endif |
| |
| typedef struct MemoryData_t |
| { |
| #ifdef PMEM_MAP_TRACE |
| int index; |
| #endif |
| size_t size; |
| #if PMEM_STACKTRACE |
| /** |
| * Stacktrace of where the memory was allocated from. |
| */ |
| const LCHAR* stackTrace; |
| /** |
| * Pointer to next memory allocation associated with the same tag. |
| */ |
| struct MemoryData_t* next; |
| /** |
| * Pointer to last memory allocation associated with the same tag. |
| */ |
| struct MemoryData_t* last; |
| #endif |
| } |
| MemoryData; |
| |
| #ifdef PMEM_MAP_TRACE |
| typedef struct MemMapEntry_t |
| { |
| /** |
| * Memory tag/ID associated with allocation. |
| */ |
| const LCHAR* tag; |
| asr_uint32_t curAlloc; |
| asr_uint32_t maxAlloc; |
| unsigned int crc; |
| /** |
| * First memory allocation associated with this tag. |
| * Memory that has been deallocated will not show up on this list. |
| */ |
| MemoryData* first; |
| /** |
| * Last memory allocation associated with this tag. |
| * Memory that has been deallocated will not show up on this list. |
| */ |
| MemoryData* last; |
| } |
| MemMapEntry; |
| |
| static MemMapEntry gMemoryMap[MAX_MEM_TAG]; |
| #endif |
| |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| extern ESR_ReturnCode memory_pool_creation_status; /* Verify that memory pool actually was created */ |
| #define malloc PortNew |
| #define free PortDelete |
| #endif |
| |
| |
| #if PMEM_STACKTRACE |
| static ESR_ReturnCode getStackTrace(LCHAR* stackTrace, size_t* len) |
| { |
| ESR_BOOL isInit; |
| ESR_ReturnCode rc; |
| |
| rc = PStackTraceIsInitialized(&isInit); |
| if (rc == ESR_SUCCESS && isInit) |
| { |
| LCHAR* index; |
| size_t bufferLen = *len; |
| size_t i; |
| |
| rc = PStackTraceGetValue(stackTrace, &bufferLen); |
| if (rc == ESR_SUCCESS) |
| { |
| for (i = 0; i < 2; ++i) |
| { |
| rc = PStackTracePopLevel(stackTrace); |
| if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, "[%s:%d] PStackTracePopLevel failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc)); |
| goto CLEANUP; |
| } |
| } |
| index = stackTrace; |
| while (index) |
| { |
| index = LSTRSTR(index, L(" at\n")); |
| if (index != NULL) |
| *(index + 3) = L(' '); |
| } |
| } |
| else if (rc == ESR_NOT_SUPPORTED) |
| LSTRCPY(stackTrace, L("")); |
| else if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, "[%s:%d] PStackTraceGetValue failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc)); |
| goto CLEANUP; |
| } |
| } |
| else |
| LSTRCPY(stackTrace, L("(null)")); |
| *len = LSTRLEN(stackTrace); |
| return ESR_SUCCESS; |
| CLEANUP: |
| return rc; |
| } |
| #endif /* PMEM_STACKTRACE */ |
| |
| #ifdef PMEM_MAP_TRACE |
| static int getIndex(const LCHAR *key) |
| { |
| unsigned int crc = ~pcrcComputeString(key); |
| int initialIdx = (int)(crc % MAX_MEM_TAG); |
| int idx = initialIdx; |
| |
| for (;;) |
| { |
| if (gMemoryMap[idx].tag == NULL) |
| { |
| /* found an empty slot, use it. */ |
| gMemoryMap[idx].tag = key; |
| gMemoryMap[idx].curAlloc = 0; |
| gMemoryMap[idx].maxAlloc = 0; |
| gMemoryMap[idx].crc = crc; |
| gMemoryMap[idx].first = NULL; |
| gMemoryMap[idx].last = NULL; |
| #if PMEM_LOG_LOWLEVEL |
| if (gFile != NULL) |
| pfprintf(gFile, L("pmem|newtag|%s|%d|\n"), key, idx); |
| #endif |
| return idx; |
| } |
| |
| if (gMemoryMap[idx].crc == crc && |
| LSTRCMP(gMemoryMap[idx].tag, key) == 0) |
| { |
| /* found a matching slot, return it */ |
| return idx; |
| } |
| |
| if (++idx == MAX_MEM_TAG) |
| { |
| /* Look at next slot and wrap around. */ |
| idx = 0; |
| } |
| if (idx == initialIdx) |
| return -1; |
| } |
| } |
| #endif |
| |
| /* Not thread-safe. But do not expect user calls this function on different threads simultaneously */ |
| ESR_ReturnCode PMemorySetPoolSize(size_t size) |
| { |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| if (gNbInit > 0) |
| return ESR_INVALID_STATE; |
| |
| gMemPoolSize = size; |
| return ESR_SUCCESS; |
| #else |
| return ESR_NOT_SUPPORTED; |
| #endif |
| } |
| |
| ESR_ReturnCode PMemoryGetPoolSize(size_t *size) |
| { |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| *size = gMemPoolSize; |
| return ESR_SUCCESS; |
| #else |
| return ESR_NOT_SUPPORTED; |
| #endif |
| } |
| |
| /* it is not thread safe: hard to protect the createMutex() |
| * could fix it by using static mutex initialization in some OS, |
| * but does not work with our own pthread implementation for vxworks |
| * SUPPOSE the user just calls this function once |
| */ |
| ESR_ReturnCode PMemInit(void) |
| { |
| ESR_ReturnCode init_status; |
| |
| if (gNbInit > 0) |
| return ESR_INVALID_STATE; |
| |
| init_status = createMutex(&memMutex, ESR_FALSE); |
| |
| if (init_status == ESR_SUCCESS) |
| { |
| ++gNbInit; |
| #ifdef PMEM_MAP_TRACE |
| memset(gMemoryMap, 0, sizeof(gMemoryMap)); |
| #endif |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| PortMemSetPoolSize(gMemPoolSize); |
| PortMemoryInit(); |
| /* There is no friggin' way to pass the status of the memory initialization, because of the damn macros and all the other crap */ |
| /* So I am checking the value of an external variable, this sucks, but I can't ignore something this important */ |
| if (memory_pool_creation_status == ESR_SUCCESS) |
| { |
| /* Reset this because with all the layers of crap, I can't guarantee we'll get to the bottom layer on a re-init */ |
| memory_pool_creation_status = ESR_FATAL_ERROR; |
| } |
| else |
| { |
| pfprintf(PSTDERR, L("ESR_INVALID_STATE: Memory Pool Could Not Be Created\n")); |
| PortMemoryTerm(); |
| unlockMutex(&memMutex); |
| deleteMutex(&memMutex); |
| init_status = ESR_INVALID_STATE; |
| } |
| #endif |
| } |
| else |
| { |
| deleteMutex(&memMutex); |
| } |
| |
| #ifdef PMEM_MAP_TRACE |
| // Initialize global static variables |
| gCurAlloc = 0; |
| gMaxAlloc = 0; |
| #endif |
| |
| return (init_status); |
| } |
| |
| /* it is not thread safe: hard to protect the deleteMutex() |
| * could fix it by using static mutex initialization in some OS, |
| * but does not work with our own pthread implementation for vxworks |
| * SUPPOSE the user just calls this function once |
| */ |
| ESR_ReturnCode PMemShutdown(void) |
| { |
| #ifdef PMEM_MAP_TRACE |
| size_t i; |
| #endif |
| |
| if (gNbInit == 0) |
| return ESR_INVALID_STATE; |
| if (gNbInit == 1) |
| { |
| #ifdef PMEM_MAP_TRACE |
| for (i = 0; i < MAX_MEM_TAG; ++i) |
| { |
| free((LCHAR*) gMemoryMap[i].tag); |
| gMemoryMap[i].tag = NULL; |
| } |
| #endif |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| PortMemoryTerm(); |
| #endif |
| deleteMutex(&memMutex); |
| } |
| gNbInit--; |
| |
| return ESR_SUCCESS; |
| } |
| |
| ESR_ReturnCode PMemSetLogFile(PFile* file) |
| { |
| if (gNbInit == 0) |
| return ESR_INVALID_STATE; |
| |
| lockMutex(&memMutex); |
| gFile = file; |
| unlockMutex(&memMutex); |
| |
| return ESR_SUCCESS; |
| } |
| |
| ESR_ReturnCode PMemDumpLogFile(void) |
| { |
| ESR_ReturnCode rc; |
| |
| if (gNbInit == 0) |
| return ESR_INVALID_STATE; |
| |
| lockMutex(&memMutex); |
| if (gFile != NULL) |
| { |
| /* Hide gFile from memory report */ |
| /* CHK(rc, gFile->hideMemoryAllocation(gFile));*/ |
| |
| rc = PMemReport(gFile); |
| if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__); |
| goto CLEANUP; |
| } |
| if (gFile != PSTDIN && gFile != PSTDOUT && gFile != PSTDERR) |
| { |
| /* rc = gFile->destroy(gFile); |
| if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__); |
| goto CLEANUP; |
| }*/ |
| pfclose ( gFile ); |
| } |
| gFile = NULL; |
| } |
| unlockMutex(&memMutex); |
| return ESR_SUCCESS; |
| CLEANUP: |
| unlockMutex(&memMutex); |
| return rc; |
| } |
| |
| ESR_ReturnCode PMemSetLogEnabled(ESR_BOOL value) |
| { |
| lockMutex(&memMutex); |
| isLogEnabled = value; |
| unlockMutex(&memMutex); |
| |
| return ESR_SUCCESS; |
| } |
| |
| ESR_ReturnCode PMemLogFree(void* ptr) |
| { |
| MemoryData* data; |
| #ifdef PMEM_MAP_TRACE |
| MemMapEntry* e; |
| #endif |
| #if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL |
| ESR_ReturnCode rc; |
| #endif |
| |
| if (ptr == NULL || gNbInit == 0) |
| return ESR_SUCCESS; |
| |
| lockMutex(&memMutex); |
| |
| data = (MemoryData*)(((unsigned char*) ptr) - sizeof(MemoryData)); |
| #ifdef PMEM_MAP_TRACE |
| e = gMemoryMap + data->index; |
| passert(data->index >= 0 && data->index <= MAX_MEM_TAG); |
| if (isLogEnabled) |
| { |
| passert(e->curAlloc >= data->size); |
| e->curAlloc -= data->size; |
| |
| passert(gCurAlloc >= data->size); |
| gCurAlloc -= data->size; |
| |
| data->size = 0; |
| } |
| #if PMEM_STACKTRACE |
| if (e->first != NULL && e->first == data) |
| e->first = data->next; |
| if (e->last != NULL && e->last == data) |
| e->last = data->last; |
| if (data->last != NULL) |
| data->last->next = data->next; |
| if (data->next != NULL) |
| { |
| data->next->last = data->last; |
| data->next = NULL; |
| } |
| data->last = NULL; |
| #endif |
| #if PMEM_LOG_LOWLEVEL |
| if (gFile != NULL && isLogEnabled) |
| { |
| #if PMEM_STACKTRACE |
| LCHAR stackTrace[P_MAX_STACKTRACE]; |
| size_t len = P_MAX_STACKTRACE; |
| |
| rc = getStackTrace(stackTrace, &len); |
| if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc)); |
| goto CLEANUP; |
| } |
| pfprintf(gFile, L("pmem|free|%s|%s|%d|0x%x|%s|\n"), e->tag, data->stackTrace, data->size, ptr, stackTrace); |
| #else |
| pfprintf(gFile, L("pmem|free|%s|%d|0x%x\n"), e->tag, data->size, ptr); |
| #endif /* PMEM_STACKTRACE */ |
| } |
| #endif /* PMEM_LOG_LOWLEVEL */ |
| #endif /* PMEM_MAP_TRACE */ |
| |
| unlockMutex(&memMutex); |
| return ESR_SUCCESS; |
| #if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL |
| CLEANUP: |
| unlockMutex(&memMutex); |
| return rc; |
| #endif |
| } |
| |
| ESR_ReturnCode PMemReport(PFile* file) |
| { |
| #define TAG_SIZE 52 |
| #ifdef PMEM_MAP_TRACE |
| asr_uint32_t totalAlloc = 0; |
| size_t i; |
| MemMapEntry* e; |
| unsigned int crc; |
| LCHAR truncatedTag[TAG_SIZE]; |
| size_t len; |
| LCHAR TAG_PREFIX[] = L("..."); |
| const size_t TAG_PREFIX_SIZE = LSTRLEN(TAG_PREFIX); |
| const size_t countToCopy = (TAG_SIZE - 1) - TAG_PREFIX_SIZE; |
| #endif |
| #if PMEM_STACKTRACE |
| MemoryData* data; |
| #endif |
| |
| if (gNbInit == 0) |
| return ESR_INVALID_STATE; |
| if (file == NULL) |
| { |
| file = gFile; |
| if (file == NULL) |
| return ESR_SUCCESS; |
| } |
| |
| lockMutex(&memMutex); |
| #ifdef PMEM_MAP_TRACE |
| if (gFile != NULL) |
| { |
| for (i = 0, e = gMemoryMap; i < MAX_MEM_TAG; ++i, ++e) |
| { |
| if (e->tag == NULL) |
| continue; |
| crc = ~pcrcComputeString(e->tag); |
| if (crc != e->crc) |
| pfprintf(gFile, L("pmem|-|0|corrupt|%d|\n"), i); |
| } |
| } |
| |
| pfprintf(file, L("%-52s %10s %15s\n"), L("Memory tag"), L("Cur. Alloc"), L("Max. Alloc")); |
| |
| for (i = 0, e = gMemoryMap; i < MAX_MEM_TAG; ++i, ++e) |
| { |
| if (e->tag == NULL) |
| continue; |
| crc = ~pcrcComputeString(e->tag); |
| if (crc != e->crc) |
| pfprintf(file, L("**********%04d********** %38u %15u\n"), i, e->curAlloc, e->maxAlloc); |
| else |
| { |
| len = LSTRLEN(e->tag); |
| |
| if (len > TAG_SIZE - 1) |
| { |
| LSTRCPY(truncatedTag, TAG_PREFIX); |
| LSTRCPY(truncatedTag + TAG_PREFIX_SIZE, e->tag + (len - countToCopy)); |
| passert(LSTRLEN(truncatedTag) == TAG_SIZE - 1); |
| } |
| else |
| LSTRCPY(truncatedTag, e->tag); |
| pfprintf(file, L("%-52s %10u %15u\n"), truncatedTag, e->curAlloc, e->maxAlloc); |
| } |
| #if PMEM_STACKTRACE |
| data = gMemoryMap[i].first; |
| while (data) |
| { |
| if (data->size != 0 && data->stackTrace != NULL) |
| { |
| LCHAR stackTrace[P_MAX_STACKTRACE]; |
| LCHAR* index; |
| |
| LSTRCPY(stackTrace, data->stackTrace); |
| index = stackTrace; |
| while (index) |
| { |
| index = LSTRSTR(index, L(" at ")); |
| if (index != NULL) |
| *(index + 3) = L('\n'); |
| } |
| pfprintf(file, L("StackTrace:\n%s\n\n"), stackTrace); |
| } |
| data = data->next; |
| } |
| #endif |
| passert(e->curAlloc >= 0); |
| totalAlloc += e->curAlloc; |
| } |
| pfprintf(file, L("%-52s %10u %15u\n"), L("Total"), totalAlloc, gMaxAlloc); |
| passert(totalAlloc == gCurAlloc); |
| #else |
| /* not support */ |
| #endif /* PMEM_MAP_TRACE */ |
| unlockMutex(&memMutex); |
| |
| return ESR_SUCCESS; |
| } |
| /* |
| DESCRIPTION |
| The functionality described on this reference page is aligned with the ISO C standard. Any conflict between the requirements described here and the ISO C standard is unintentional. This volume of IEEE Std 1003.1-2001 defers to the ISO C standard. |
| The malloc() function shall allocate unused space for an object whose size in bytes is specified by size and whose value is unspecified. |
| |
| The order and contiguity of storage allocated by successive calls to malloc() is unspecified. The pointer returned if the allocation succeeds shall be suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object in the space allocated (until the space is explicitly freed or reallocated). Each such allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer shall be returned. If the size of the space requested is 0, the behavior is implementation-defined: the value returned shall be either a null pointer or a unique pointer. |
| |
| RETURN VALUE |
| Upon successful completion with size not equal to 0, malloc() shall return a pointer to the allocated space. If size is 0, either a null pointer or a unique pointer that can be successfully passed to free() shall be returned. Otherwise, it shall return a null pointer and set errno to indicate the error. |
| */ |
| #ifdef PMEM_MAP_TRACE |
| void *pmalloc(size_t nbBytes, const LCHAR* tag, const LCHAR* file, int line) |
| #else |
| void *pmalloc(size_t nbBytes) |
| #endif |
| { |
| MemoryData* data; |
| void* result = NULL; |
| size_t actualSize; |
| #ifdef PMEM_MAP_TRACE |
| int idx; |
| MemMapEntry* e; |
| #endif |
| #if PMEM_STACKTRACE |
| size_t stackTraceSize = P_MAX_STACKTRACE; |
| LCHAR* stackTrace; |
| ESR_BOOL isInit; |
| ESR_ReturnCode rc; |
| #endif |
| |
| if (gNbInit == 0) |
| return NULL; |
| |
| lockMutex(&memMutex); |
| |
| #ifdef PMEM_MAP_TRACE |
| if (tag == NULL) |
| tag = file; |
| passert(tag != NULL); |
| |
| idx = getIndex(tag); |
| if (idx == -1) |
| { |
| pfprintf(PSTDERR, L("ESR_INVALID_STATE: pmalloc() ran out of slots")); |
| goto CLEANUP; |
| } |
| if (gMemoryMap[idx].tag == tag) |
| { |
| /* This is a new key, allocate memory for it */ |
| gMemoryMap[idx].tag = malloc(sizeof(LCHAR) * (LSTRLEN(tag) + 1)); |
| if (gMemoryMap[idx].tag == NULL) |
| goto CLEANUP; |
| LSTRCPY((LCHAR*) gMemoryMap[idx].tag, tag); |
| } |
| #endif |
| actualSize = sizeof(MemoryData) + nbBytes; |
| |
| data = (MemoryData *) malloc(actualSize); |
| if (data == NULL) |
| { |
| /* |
| * printf("no space when alloc %d from file %s line %d\nmem usage: %d\n", |
| * nbBytes, file, line, PortMallocGetMaxMemUsed()); |
| */ |
| goto CLEANUP; |
| } |
| |
| #ifdef PMEM_MAP_TRACE |
| data->index = idx; |
| #if PMEM_STACKTRACE |
| rc = PStackTraceIsInitialized(&isInit); |
| if (rc != ESR_SUCCESS) |
| goto CLEANUP; |
| if (isInit) |
| { |
| stackTrace = malloc(sizeof(LCHAR) * (stackTraceSize + 1)); |
| if (stackTrace == NULL) |
| goto CLEANUP; |
| rc = getStackTrace(stackTrace, &stackTraceSize); |
| if (rc != ESR_SUCCESS) |
| goto CLEANUP; |
| /* Shrink stackTrace buffer */ |
| passert(LSTRLEN(stackTrace) < P_MAX_STACKTRACE); |
| data->stackTrace = realloc(stackTrace, sizeof(LCHAR) * (LSTRLEN(stackTrace) + 1)); |
| if (data->stackTrace == NULL) |
| { |
| free(stackTrace); |
| goto CLEANUP; |
| } |
| } |
| else |
| data->stackTrace = NULL; |
| #endif |
| |
| e = gMemoryMap + idx; |
| |
| #if PMEM_STACKTRACE |
| if (e->last != NULL) |
| e->last->next = data; |
| data->last = e->last; |
| data->next = NULL; |
| e->last = data; |
| if (e->first == NULL) |
| e->first = data; |
| #endif |
| #endif |
| |
| if (isLogEnabled) |
| { |
| data->size = actualSize; |
| #ifdef PMEM_MAP_TRACE |
| e->curAlloc += actualSize; |
| if (e->maxAlloc < e->curAlloc) |
| e->maxAlloc = e->curAlloc; |
| |
| gCurAlloc += actualSize; |
| if (gMaxAlloc < gCurAlloc) |
| gMaxAlloc = gCurAlloc; |
| #endif |
| } |
| else |
| data->size = 0; |
| |
| result = (void*)(data + 1); |
| |
| #if PMEM_LOG_LOWLEVEL |
| if (gFile != NULL && isLogEnabled) |
| |
| if (gFile != NULL) |
| { |
| #if PMEM_STACKTRACE |
| pfprintf(gFile, L("pmem|alloc|%s|%d|0x%x|%s|\n"), tag, actualSize, result, data->stackTrace); |
| #else |
| pfprintf(gFile, L("pmem|alloc|%s|%d|0x%x|\n"), tag, actualSize, result); |
| #endif /* PMEM_STACKTRACE */ |
| } |
| #endif /* PMEM_LOG_LOWLEVEL */ |
| |
| CLEANUP: |
| unlockMutex(&memMutex); |
| return result; |
| } |
| |
| #ifdef PMEM_MAP_TRACE |
| void *pcalloc(size_t nbItems, size_t itemSize, const LCHAR* tag, const LCHAR* file, int line) |
| #else |
| void *pcalloc(size_t nbItems, size_t itemSize) |
| #endif |
| { |
| void* result = NULL; |
| |
| if (gNbInit == 1) |
| { |
| #ifdef PMEM_MAP_TRACE |
| result = (MemoryData *)pmalloc(nbItems * itemSize, tag, file, line); |
| #else |
| result = (MemoryData *)pmalloc(nbItems * itemSize); |
| #endif |
| if (result != NULL) |
| memset(result, 0, nbItems * itemSize); |
| } |
| return (result); |
| } |
| |
| /* |
| DESCRIPTION |
| The realloc() function changes the size of the memory object pointed to by ptr to the size specified by size. The contents of the object will remain unchanged up to the lesser of the new and old sizes. If the new size of the memory object would require movement of the object, the space for the previous instantiation of the object is freed. If the new size is larger, the contents of the newly allocated portion of the object are unspecified. If size is 0 and ptr is not a null pointer, the object pointed to is freed. If the space cannot be allocated, the object remains unchanged. |
| If ptr is a null pointer, realloc() behaves like malloc() for the specified size. |
| |
| If ptr does not match a pointer returned earlier by calloc(), malloc() or realloc() or if the space has previously been deallocated by a call to free() or realloc(), the behaviour is undefined. |
| |
| The order and contiguity of storage allocated by successive calls to realloc() is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object in the space allocated (until the space is explicitly freed or reallocated). Each such allocation will yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned. |
| |
| RETURN VALUE |
| Upon successful completion with a size not equal to 0, realloc() returns a pointer to the (possibly moved) allocated space. If size is 0, either a null pointer or a unique pointer that can be successfully passed to free() is returned. If there is not enough available memory, realloc() returns a null pointer |
| */ |
| #ifdef PMEM_MAP_TRACE |
| void *prealloc(void *ptr, size_t newSize, const LCHAR *file, int line) |
| #else |
| void *prealloc(void *ptr, size_t newSize) |
| #endif |
| { |
| MemoryData* oldData; |
| MemoryData* newData; |
| void *result = NULL; |
| size_t actualSize; |
| #ifdef PMEM_MAP_TRACE |
| MemMapEntry* e; |
| #endif |
| size_t oldSize; |
| #if PMEM_STACKTRACE |
| const LCHAR* oldStackTrace; |
| MemoryData* oldNext; |
| MemoryData* oldLast; |
| #endif |
| ESR_BOOL bMalloc = ESR_FALSE; |
| |
| if (gNbInit == 0) |
| return NULL; |
| |
| if (newSize == 0 && ptr != NULL) |
| { |
| #ifdef PMEM_MAP_TRACE |
| pfree(ptr, file, line); |
| #else |
| pfree(ptr); |
| #endif |
| return NULL; |
| } |
| else if (ptr == NULL) |
| { |
| #ifdef PMEM_MAP_TRACE |
| return pmalloc(newSize, NULL, file, line); |
| #else |
| return pmalloc(newSize); |
| #endif |
| } |
| |
| lockMutex(&memMutex); |
| |
| oldData = (MemoryData *)(((unsigned char *) ptr) - sizeof(MemoryData)); |
| oldSize = oldData->size; |
| passert(oldSize >= 0); |
| #if PMEM_STACKTRACE |
| oldStackTrace = oldData->stackTrace; |
| oldNext = oldData->next; |
| oldLast = oldData->last; |
| #endif |
| #ifdef PMEM_MAP_TRACE |
| e = gMemoryMap + oldData->index; |
| #endif |
| |
| actualSize = newSize + sizeof(MemoryData); |
| if (oldSize != actualSize) |
| { |
| #if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME) |
| newData = (MemoryData *) PortNew(actualSize); |
| if (newData == NULL) |
| { |
| pfprintf(PSTDERR, L("OUT_OF_MEMORY: prealloc() failed at %s:%d"), __FILE__, __LINE__); |
| return NULL; |
| } |
| bMalloc = ESR_TRUE; |
| if (oldSize >= actualSize) |
| { |
| memcpy(newData, oldData, actualSize); |
| } |
| else |
| { |
| memcpy(newData, oldData, oldSize); |
| } |
| PortDelete(oldData); |
| #else |
| newData = (MemoryData *) realloc(oldData, actualSize); |
| bMalloc = ESR_TRUE; |
| #endif |
| } |
| else /* No change */ |
| { |
| newData = oldData; |
| } |
| |
| #ifdef PMEM_MAP_TRACE |
| if (newData != NULL && bMalloc) |
| { |
| if (isLogEnabled) |
| { |
| e->curAlloc += actualSize - oldSize; |
| if (e->maxAlloc < e->curAlloc) |
| e->maxAlloc = e->curAlloc; |
| |
| gCurAlloc += actualSize - oldSize; |
| if (gMaxAlloc < gCurAlloc) |
| gMaxAlloc = gCurAlloc; |
| } |
| |
| #if PMEM_STACKTRACE |
| newData->stackTrace = oldStackTrace; |
| newData->next = oldNext; |
| newData->last = oldLast; |
| if (newData->last != NULL) |
| newData->last->next = newData; |
| if (newData->next != NULL) |
| newData->next->last = newData; |
| if (e->first == oldData) |
| e->first = newData; |
| if (e->last == oldData) |
| e->last = newData; |
| #endif |
| } |
| #endif |
| |
| if (newData != NULL) |
| { |
| newData->size = actualSize; |
| result = (void*)(newData + 1); |
| } |
| |
| #if PMEM_LOG_LOWLEVEL |
| if (gFile != NULL && isLogEnabled) |
| { |
| #if PMEM_STACKTRACE |
| LCHAR stackTrace[P_MAX_STACKTRACE]; |
| size_t len = P_MAX_STACKTRACE; |
| ESR_ReturnCode rc; |
| |
| rc = getStackTrace(stackTrace, &len); |
| if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc)); |
| goto CLEANUP; |
| } |
| pfprintf(gFile, L("pmem|%s|%d|realloc|%d|0x%x|%s|\n"), e->tag, oldSize, actualSize, ptr, stackTrace); |
| #else |
| pfprintf(gFile, L("pmem|%s|%d|realloc|%d|0x%x|\n"), e->tag, oldSize, actualSize, ptr); |
| #endif /* PMEM_STACKTRACE */ |
| } |
| #endif /* PMEM_LOG_LOWLEVEL */ |
| |
| unlockMutex(&memMutex); |
| return result; |
| #if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL |
| CLEANUP: |
| unlockMutex(&memMutex); |
| return NULL; |
| #endif |
| } |
| |
| #ifdef PMEM_MAP_TRACE |
| void pfree(void* ptr, const LCHAR* file, int line) |
| #else |
| void pfree(void* ptr) |
| #endif |
| { |
| MemoryData* data; |
| #ifdef PMEM_MAP_TRACE |
| MemMapEntry* e; |
| #endif |
| if (ptr == NULL || gNbInit == 0) |
| return; |
| |
| lockMutex(&memMutex); |
| |
| data = (MemoryData*)(((unsigned char*) ptr) - sizeof(MemoryData)); |
| #ifdef PMEM_MAP_TRACE |
| passert(data->index >= 0 && data->index <= MAX_MEM_TAG); |
| e = gMemoryMap + data->index; |
| if (isLogEnabled) |
| { |
| passert(e->curAlloc >= data->size); |
| e->curAlloc -= data->size; |
| |
| passert(gCurAlloc >= data->size); |
| gCurAlloc -= data->size; |
| } |
| #if PMEM_STACKTRACE |
| if (e->first != NULL && e->first == data) |
| e->first = data->next; |
| if (e->last != NULL && e->last == data) |
| e->last = data->last; |
| if (data->last != NULL) |
| data->last->next = data->next; |
| if (data->next != NULL) |
| { |
| data->next->last = data->last; |
| data->next = NULL; |
| } |
| data->last = NULL; |
| #endif /* PMEM_STACKTRACE */ |
| #if PMEM_LOG_LOWLEVEL |
| if (gFile != NULL && isLogEnabled) |
| { |
| #if PMEM_STACKTRACE |
| LCHAR stackTrace[P_MAX_STACKTRACE]; |
| size_t len = P_MAX_STACKTRACE; |
| ESR_ReturnCode rc; |
| |
| rc = getStackTrace(stackTrace, &len); |
| if (rc != ESR_SUCCESS) |
| { |
| pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc)); |
| goto CLEANUP; |
| } |
| pfprintf(gFile, L("pmem|free|%s|%s|%d|0x%x|%s|\n"), e->tag, data->stackTrace, data->size, ptr, stackTrace); |
| #else |
| pfprintf(gFile, L("pmem|free|%s|%d|0x%x\n"), e->tag, data->size, ptr); |
| #endif /* PMEM_STACKTRACE */ |
| } |
| #endif /* PMEM_LOG_LOWLEVEL */ |
| #if PMEM_STACKTRACE |
| free((LCHAR*) data->stackTrace); |
| data->stackTrace = NULL; |
| #endif /* PMEM_STACKTRACE */ |
| #endif |
| |
| free(data); |
| unlockMutex(&memMutex); |
| #if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL |
| CLEANUP: |
| unlockMutex(&memMutex); |
| return; |
| #endif |
| |
| } |
| |
| #endif |