1/*---------------------------------------------------------------------------*
2 *  pmemory.c  *
3 *                                                                           *
4 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
5 *                                                                           *
6 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7 *  you may not use this file except in compliance with the License.         *
8 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
10 *      http://www.apache.org/licenses/LICENSE-2.0                           *
11 *                                                                           *
12 *  Unless required by applicable law or agreed to in writing, software      *
13 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 *  See the License for the specific language governing permissions and      *
16 *  limitations under the License.                                           *
17 *                                                                           *
18 *---------------------------------------------------------------------------*/
19
20
21
22
23#include "passert.h"
24#include "pcrc.h"
25#include "pmemory.h"
26#include "PFileSystem.h"
27#include "PStackTrace.h"
28#include "passert.h"
29#include "pmemory_ext.h"
30#include "pmutex.h"
31
32#ifndef USE_STDLIB_MALLOC
33
34#undef malloc
35#undef calloc
36#undef realloc
37#undef free
38
39static unsigned int gNbInit = 0;
40static PFile* gFile = NULL;
41static ESR_BOOL isLogEnabled = ESR_TRUE;
42
43#ifdef PMEM_MAP_TRACE
44static asr_uint32_t gMaxAlloc = -1;
45static asr_uint32_t gCurAlloc = -1;
46#endif
47
48#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
49static size_t   gMemPoolSize = (3*1024*1024); /* default value: 3M */
50#endif
51
52#ifdef USE_THREAD
53static MUTEX memMutex;
54#endif
55
56#define MAX_MEM_TAG 256
57
58/* Only PMEM_MAP_TRACE has been defined, could do other memory logging/debugging */
59#ifdef PMEM_MAP_TRACE
60
61#define PMEM_STACKTRACE 0
62/* If enabled, logs individual memory allocation, reallocation, free operations */
63#define PMEM_LOG_LOWLEVEL 0
64#elif defined(WIN32)
65#pragma message("No PMEM_MAP_TRACE")
66#endif
67
68typedef struct MemoryData_t
69{
70#ifdef PMEM_MAP_TRACE
71  int index;
72#endif
73  size_t size;
74#if PMEM_STACKTRACE
75  /**
76   * Stacktrace of where the memory was allocated from.
77   */
78  const LCHAR* stackTrace;
79  /**
80   * Pointer to next memory allocation associated with the same tag.
81   */
82  struct MemoryData_t* next;
83  /**
84   * Pointer to last memory allocation associated with the same tag.
85   */
86  struct MemoryData_t* last;
87#endif
88}
89MemoryData;
90
91#ifdef PMEM_MAP_TRACE
92typedef struct MemMapEntry_t
93{
94  /**
95   * Memory tag/ID associated with allocation.
96   */
97  const LCHAR* tag;
98  asr_uint32_t curAlloc;
99  asr_uint32_t maxAlloc;
100  unsigned int crc;
101  /**
102   * First memory allocation associated with this tag.
103   * Memory that has been deallocated will not show up on this list.
104   */
105  MemoryData* first;
106  /**
107   * Last memory allocation associated with this tag.
108   * Memory that has been deallocated will not show up on this list.
109   */
110  MemoryData* last;
111}
112MemMapEntry;
113
114static MemMapEntry gMemoryMap[MAX_MEM_TAG];
115#endif
116
117#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
118extern ESR_ReturnCode memory_pool_creation_status; /* Verify that memory pool actually was created */
119#define malloc PortNew
120#define free PortDelete
121#endif
122
123
124#if PMEM_STACKTRACE
125static ESR_ReturnCode getStackTrace(LCHAR* stackTrace, size_t* len)
126{
127  ESR_BOOL isInit;
128  ESR_ReturnCode rc;
129
130  rc = PStackTraceIsInitialized(&isInit);
131  if (rc == ESR_SUCCESS && isInit)
132  {
133    LCHAR* index;
134    size_t bufferLen = *len;
135    size_t i;
136
137    rc = PStackTraceGetValue(stackTrace, &bufferLen);
138    if (rc == ESR_SUCCESS)
139    {
140      for (i = 0; i < 2; ++i)
141      {
142        rc = PStackTracePopLevel(stackTrace);
143        if (rc != ESR_SUCCESS)
144        {
145          pfprintf(PSTDERR, "[%s:%d] PStackTracePopLevel failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
146          goto CLEANUP;
147        }
148      }
149      index = stackTrace;
150      while (index)
151      {
152        index = LSTRSTR(index, L(" at\n"));
153        if (index != NULL)
154          *(index + 3) = L(' ');
155      }
156    }
157    else if (rc == ESR_NOT_SUPPORTED)
158      LSTRCPY(stackTrace, L(""));
159    else if (rc != ESR_SUCCESS)
160    {
161      pfprintf(PSTDERR, "[%s:%d] PStackTraceGetValue failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
162      goto CLEANUP;
163    }
164  }
165  else
166    LSTRCPY(stackTrace, L("(null)"));
167  *len = LSTRLEN(stackTrace);
168  return ESR_SUCCESS;
169CLEANUP:
170  return rc;
171}
172#endif /* PMEM_STACKTRACE */
173
174#ifdef PMEM_MAP_TRACE
175static int getIndex(const LCHAR *key)
176{
177  unsigned int crc = ~pcrcComputeString(key);
178  int initialIdx = (int)(crc % MAX_MEM_TAG);
179  int idx = initialIdx;
180
181  for (;;)
182  {
183    if (gMemoryMap[idx].tag == NULL)
184    {
185      /* found an empty slot, use it. */
186      gMemoryMap[idx].tag = key;
187      gMemoryMap[idx].curAlloc = 0;
188      gMemoryMap[idx].maxAlloc = 0;
189      gMemoryMap[idx].crc = crc;
190      gMemoryMap[idx].first = NULL;
191      gMemoryMap[idx].last = NULL;
192#if PMEM_LOG_LOWLEVEL
193      if (gFile != NULL)
194        pfprintf(gFile, L("pmem|newtag|%s|%d|\n"), key, idx);
195#endif
196      return idx;
197    }
198
199    if (gMemoryMap[idx].crc == crc &&
200        LSTRCMP(gMemoryMap[idx].tag, key) == 0)
201    {
202      /* found a matching slot, return it */
203      return idx;
204    }
205
206    if (++idx == MAX_MEM_TAG)
207    {
208      /* Look at next slot and wrap around. */
209      idx = 0;
210    }
211    if (idx == initialIdx)
212      return -1;
213  }
214}
215#endif
216
217/* Not thread-safe. But do not expect user calls this function on different threads simultaneously */
218ESR_ReturnCode PMemorySetPoolSize(size_t size)
219{
220#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
221  if (gNbInit > 0)
222    return ESR_INVALID_STATE;
223
224  gMemPoolSize = size;
225  return ESR_SUCCESS;
226#else
227  return ESR_NOT_SUPPORTED;
228#endif
229}
230
231ESR_ReturnCode PMemoryGetPoolSize(size_t *size)
232{
233#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
234  *size = gMemPoolSize;
235  return ESR_SUCCESS;
236#else
237  return ESR_NOT_SUPPORTED;
238#endif
239}
240
241/* it is not thread safe: hard to protect the createMutex()
242  * could fix it by using static mutex initialization in some OS,
243  * but does not work with our own pthread implementation for vxworks
244  * SUPPOSE the user just calls this function once
245  */
246ESR_ReturnCode PMemInit(void)
247{
248  ESR_ReturnCode init_status;
249
250  if (gNbInit > 0)
251    return ESR_INVALID_STATE;
252
253  init_status = createMutex(&memMutex, ESR_FALSE);
254
255  if (init_status == ESR_SUCCESS)
256  {
257    ++gNbInit;
258#ifdef PMEM_MAP_TRACE
259    memset(gMemoryMap, 0, sizeof(gMemoryMap));
260#endif
261#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
262    PortMemSetPoolSize(gMemPoolSize);
263    PortMemoryInit();
264    /* There is no friggin' way to pass the status of the memory initialization, because of the damn macros and all the other crap */
265    /* So I am checking the value of an external variable, this sucks, but I can't ignore something this important */
266    if (memory_pool_creation_status == ESR_SUCCESS)
267    {
268      /* Reset this because with all the layers of crap, I can't guarantee we'll get to the bottom layer on a re-init */
269      memory_pool_creation_status = ESR_FATAL_ERROR;
270    }
271    else
272    {
273      pfprintf(PSTDERR, L("ESR_INVALID_STATE: Memory Pool Could Not Be Created\n"));
274      PortMemoryTerm();
275      unlockMutex(&memMutex);
276      deleteMutex(&memMutex);
277      init_status = ESR_INVALID_STATE;
278    }
279#endif
280  }
281  else
282  {
283    deleteMutex(&memMutex);
284  }
285
286#ifdef PMEM_MAP_TRACE
287  // Initialize global static variables
288  gCurAlloc = 0;
289  gMaxAlloc = 0;
290#endif
291
292  return (init_status);
293}
294
295/* it is not thread safe: hard to protect the deleteMutex()
296  * could fix it by using static mutex initialization in some OS,
297  * but does not work with our own pthread implementation for vxworks
298  * SUPPOSE the user just calls this function once
299  */
300ESR_ReturnCode PMemShutdown(void)
301{
302#ifdef PMEM_MAP_TRACE
303  size_t i;
304#endif
305
306  if (gNbInit == 0)
307    return ESR_INVALID_STATE;
308  if (gNbInit == 1)
309  {
310#ifdef PMEM_MAP_TRACE
311    for (i = 0; i < MAX_MEM_TAG; ++i)
312    {
313      free((LCHAR*) gMemoryMap[i].tag);
314      gMemoryMap[i].tag = NULL;
315    }
316#endif
317#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
318    PortMemoryTerm();
319#endif
320    deleteMutex(&memMutex);
321  }
322  gNbInit--;
323
324  return ESR_SUCCESS;
325}
326
327ESR_ReturnCode PMemSetLogFile(PFile* file)
328{
329  if (gNbInit == 0)
330    return ESR_INVALID_STATE;
331
332  lockMutex(&memMutex);
333  gFile = file;
334  unlockMutex(&memMutex);
335
336  return ESR_SUCCESS;
337}
338
339ESR_ReturnCode PMemDumpLogFile(void)
340{
341  ESR_ReturnCode rc;
342
343  if (gNbInit == 0)
344    return ESR_INVALID_STATE;
345
346  lockMutex(&memMutex);
347  if (gFile != NULL)
348  {
349    /* Hide gFile from memory report */
350/*    CHK(rc, gFile->hideMemoryAllocation(gFile));*/
351
352    rc = PMemReport(gFile);
353    if (rc != ESR_SUCCESS)
354    {
355      pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
356      goto CLEANUP;
357    }
358    if (gFile != PSTDIN && gFile != PSTDOUT && gFile != PSTDERR)
359    {
360/*      rc = gFile->destroy(gFile);
361      if (rc != ESR_SUCCESS)
362      {
363        pfprintf(PSTDERR, L("%s: PMemDumpLogFile() at %s:%d"), ESR_rc2str(rc), __FILE__, __LINE__);
364        goto CLEANUP;
365      }*/
366      pfclose ( gFile );
367    }
368    gFile = NULL;
369  }
370  unlockMutex(&memMutex);
371  return ESR_SUCCESS;
372CLEANUP:
373  unlockMutex(&memMutex);
374  return rc;
375}
376
377ESR_ReturnCode PMemSetLogEnabled(ESR_BOOL value)
378{
379  lockMutex(&memMutex);
380  isLogEnabled = value;
381  unlockMutex(&memMutex);
382
383  return ESR_SUCCESS;
384}
385
386ESR_ReturnCode PMemLogFree(void* ptr)
387{
388  MemoryData* data;
389#ifdef PMEM_MAP_TRACE
390  MemMapEntry* e;
391#endif
392#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
393  ESR_ReturnCode rc;
394#endif
395
396  if (ptr == NULL || gNbInit == 0)
397    return ESR_SUCCESS;
398
399  lockMutex(&memMutex);
400
401  data = (MemoryData*)(((unsigned char*) ptr) - sizeof(MemoryData));
402#ifdef PMEM_MAP_TRACE
403  e = gMemoryMap + data->index;
404  passert(data->index >= 0 && data->index <= MAX_MEM_TAG);
405  if (isLogEnabled)
406  {
407    passert(e->curAlloc >= data->size);
408    e->curAlloc -= data->size;
409
410    passert(gCurAlloc >= data->size);
411    gCurAlloc -= data->size;
412
413    data->size = 0;
414  }
415#if PMEM_STACKTRACE
416  if (e->first != NULL && e->first == data)
417    e->first = data->next;
418  if (e->last != NULL && e->last == data)
419    e->last = data->last;
420  if (data->last != NULL)
421    data->last->next = data->next;
422  if (data->next != NULL)
423  {
424    data->next->last = data->last;
425    data->next = NULL;
426  }
427  data->last = NULL;
428#endif
429#if PMEM_LOG_LOWLEVEL
430  if (gFile != NULL && isLogEnabled)
431  {
432#if PMEM_STACKTRACE
433    LCHAR stackTrace[P_MAX_STACKTRACE];
434    size_t len = P_MAX_STACKTRACE;
435
436    rc = getStackTrace(stackTrace, &len);
437    if (rc != ESR_SUCCESS)
438    {
439      pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
440      goto CLEANUP;
441    }
442    pfprintf(gFile, L("pmem|free|%s|%s|%d|0x%x|%s|\n"), e->tag, data->stackTrace, data->size, ptr, stackTrace);
443#else
444    pfprintf(gFile, L("pmem|free|%s|%d|0x%x\n"), e->tag, data->size, ptr);
445#endif /* PMEM_STACKTRACE */
446  }
447#endif /* PMEM_LOG_LOWLEVEL */
448#endif /* PMEM_MAP_TRACE */
449
450  unlockMutex(&memMutex);
451  return ESR_SUCCESS;
452#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
453CLEANUP:
454  unlockMutex(&memMutex);
455  return rc;
456#endif
457}
458
459ESR_ReturnCode PMemReport(PFile* file)
460{
461#define TAG_SIZE 52
462#ifdef PMEM_MAP_TRACE
463  asr_uint32_t totalAlloc = 0;
464  size_t i;
465  MemMapEntry* e;
466  unsigned int crc;
467  LCHAR truncatedTag[TAG_SIZE];
468  size_t len;
469  LCHAR TAG_PREFIX[] = L("...");
470  const size_t TAG_PREFIX_SIZE = LSTRLEN(TAG_PREFIX);
471  const size_t countToCopy = (TAG_SIZE - 1) - TAG_PREFIX_SIZE;
472#endif
473#if PMEM_STACKTRACE
474  MemoryData* data;
475#endif
476
477  if (gNbInit == 0)
478    return ESR_INVALID_STATE;
479  if (file == NULL)
480  {
481    file = gFile;
482    if (file == NULL)
483      return ESR_SUCCESS;
484  }
485
486  lockMutex(&memMutex);
487#ifdef PMEM_MAP_TRACE
488  if (gFile != NULL)
489  {
490    for (i = 0, e = gMemoryMap; i < MAX_MEM_TAG; ++i, ++e)
491    {
492      if (e->tag == NULL)
493        continue;
494      crc = ~pcrcComputeString(e->tag);
495      if (crc != e->crc)
496        pfprintf(gFile, L("pmem|-|0|corrupt|%d|\n"), i);
497    }
498  }
499
500  pfprintf(file, L("%-52s %10s %15s\n"), L("Memory tag"), L("Cur. Alloc"), L("Max. Alloc"));
501
502  for (i = 0, e = gMemoryMap; i < MAX_MEM_TAG; ++i, ++e)
503  {
504    if (e->tag == NULL)
505      continue;
506    crc = ~pcrcComputeString(e->tag);
507    if (crc != e->crc)
508      pfprintf(file, L("**********%04d********** %38u %15u\n"), i, e->curAlloc, e->maxAlloc);
509    else
510    {
511      len = LSTRLEN(e->tag);
512
513      if (len > TAG_SIZE - 1)
514      {
515        LSTRCPY(truncatedTag, TAG_PREFIX);
516        LSTRCPY(truncatedTag + TAG_PREFIX_SIZE, e->tag + (len - countToCopy));
517        passert(LSTRLEN(truncatedTag) == TAG_SIZE - 1);
518      }
519      else
520        LSTRCPY(truncatedTag, e->tag);
521      pfprintf(file, L("%-52s %10u %15u\n"), truncatedTag, e->curAlloc, e->maxAlloc);
522    }
523#if PMEM_STACKTRACE
524    data = gMemoryMap[i].first;
525    while (data)
526    {
527      if (data->size != 0 && data->stackTrace != NULL)
528      {
529        LCHAR stackTrace[P_MAX_STACKTRACE];
530        LCHAR* index;
531
532        LSTRCPY(stackTrace, data->stackTrace);
533        index = stackTrace;
534        while (index)
535        {
536          index = LSTRSTR(index, L(" at "));
537          if (index != NULL)
538            *(index + 3) = L('\n');
539        }
540        pfprintf(file, L("StackTrace:\n%s\n\n"), stackTrace);
541      }
542      data = data->next;
543    }
544#endif
545    passert(e->curAlloc >= 0);
546    totalAlloc += e->curAlloc;
547  }
548  pfprintf(file, L("%-52s %10u %15u\n"), L("Total"), totalAlloc, gMaxAlloc);
549  passert(totalAlloc == gCurAlloc);
550#else
551  /* not support */
552#endif /* PMEM_MAP_TRACE */
553  unlockMutex(&memMutex);
554
555  return ESR_SUCCESS;
556}
557/*
558DESCRIPTION
559  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.
560The malloc() function shall allocate unused space for an object whose size in bytes is specified by size and whose value is unspecified.
561
562The 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.
563
564RETURN VALUE
565Upon 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.
566*/
567#ifdef PMEM_MAP_TRACE
568void *pmalloc(size_t nbBytes, const LCHAR* tag, const LCHAR* file, int line)
569#else
570void *pmalloc(size_t nbBytes)
571#endif
572{
573  MemoryData* data;
574  void* result = NULL;
575  size_t actualSize;
576#ifdef PMEM_MAP_TRACE
577  int idx;
578  MemMapEntry* e;
579#endif
580#if PMEM_STACKTRACE
581  size_t stackTraceSize = P_MAX_STACKTRACE;
582  LCHAR* stackTrace;
583  ESR_BOOL isInit;
584  ESR_ReturnCode rc;
585#endif
586
587  if (gNbInit == 0)
588    return NULL;
589
590  lockMutex(&memMutex);
591
592#ifdef PMEM_MAP_TRACE
593  if (tag == NULL)
594    tag = file;
595  passert(tag != NULL);
596
597  idx = getIndex(tag);
598  if (idx == -1)
599  {
600    pfprintf(PSTDERR, L("ESR_INVALID_STATE: pmalloc() ran out of slots"));
601    goto CLEANUP;
602  }
603  if (gMemoryMap[idx].tag == tag)
604  {
605    /* This is a new key, allocate memory for it */
606    gMemoryMap[idx].tag = malloc(sizeof(LCHAR) * (LSTRLEN(tag) + 1));
607    if (gMemoryMap[idx].tag == NULL)
608      goto CLEANUP;
609    LSTRCPY((LCHAR*) gMemoryMap[idx].tag, tag);
610  }
611#endif
612  actualSize = sizeof(MemoryData) + nbBytes;
613
614  data = (MemoryData *) malloc(actualSize);
615  if (data == NULL)
616  {
617    /*
618     * printf("no space when alloc %d from file %s line %d\nmem usage: %d\n",
619     * nbBytes, file, line, PortMallocGetMaxMemUsed());
620     */
621    goto CLEANUP;
622  }
623
624#ifdef PMEM_MAP_TRACE
625  data->index = idx;
626#if PMEM_STACKTRACE
627  rc = PStackTraceIsInitialized(&isInit);
628  if (rc != ESR_SUCCESS)
629    goto CLEANUP;
630  if (isInit)
631  {
632    stackTrace = malloc(sizeof(LCHAR) * (stackTraceSize + 1));
633    if (stackTrace == NULL)
634      goto CLEANUP;
635    rc = getStackTrace(stackTrace, &stackTraceSize);
636    if (rc != ESR_SUCCESS)
637      goto CLEANUP;
638    /* Shrink stackTrace buffer */
639    passert(LSTRLEN(stackTrace) < P_MAX_STACKTRACE);
640    data->stackTrace = realloc(stackTrace, sizeof(LCHAR) * (LSTRLEN(stackTrace) + 1));
641    if (data->stackTrace == NULL)
642    {
643      free(stackTrace);
644      goto CLEANUP;
645    }
646  }
647  else
648    data->stackTrace = NULL;
649#endif
650
651  e = gMemoryMap + idx;
652
653#if PMEM_STACKTRACE
654  if (e->last != NULL)
655    e->last->next = data;
656  data->last = e->last;
657  data->next = NULL;
658  e->last = data;
659  if (e->first == NULL)
660    e->first = data;
661#endif
662#endif
663
664  if (isLogEnabled)
665  {
666    data->size = actualSize;
667#ifdef PMEM_MAP_TRACE
668    e->curAlloc += actualSize;
669    if (e->maxAlloc < e->curAlloc)
670      e->maxAlloc = e->curAlloc;
671
672    gCurAlloc += actualSize;
673    if (gMaxAlloc < gCurAlloc)
674      gMaxAlloc = gCurAlloc;
675#endif
676  }
677  else
678    data->size = 0;
679
680  result = (void*)(data + 1);
681
682#if PMEM_LOG_LOWLEVEL
683  if (gFile != NULL && isLogEnabled)
684
685    if (gFile != NULL)
686    {
687#if PMEM_STACKTRACE
688      pfprintf(gFile, L("pmem|alloc|%s|%d|0x%x|%s|\n"), tag, actualSize, result, data->stackTrace);
689#else
690      pfprintf(gFile, L("pmem|alloc|%s|%d|0x%x|\n"), tag, actualSize, result);
691#endif /* PMEM_STACKTRACE */
692    }
693#endif /* PMEM_LOG_LOWLEVEL */
694
695CLEANUP:
696  unlockMutex(&memMutex);
697  return result;
698}
699
700#ifdef PMEM_MAP_TRACE
701void *pcalloc(size_t nbItems, size_t itemSize, const LCHAR* tag, const LCHAR* file, int line)
702#else
703void *pcalloc(size_t nbItems, size_t itemSize)
704#endif
705{
706  void* result = NULL;
707
708  if (gNbInit == 1)
709  {
710#ifdef PMEM_MAP_TRACE
711    result = (MemoryData *)pmalloc(nbItems * itemSize, tag, file, line);
712#else
713    result = (MemoryData *)pmalloc(nbItems * itemSize);
714#endif
715    if (result != NULL)
716      memset(result, 0, nbItems * itemSize);
717  }
718  return (result);
719}
720
721/*
722DESCRIPTION
723The 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.
724If ptr is a null pointer, realloc() behaves like malloc() for the specified size.
725
726If 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.
727
728The 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.
729
730 RETURN VALUE
731Upon 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
732*/
733#ifdef PMEM_MAP_TRACE
734void *prealloc(void *ptr, size_t newSize, const LCHAR *file, int line)
735#else
736void *prealloc(void *ptr, size_t newSize)
737#endif
738{
739  MemoryData* oldData;
740  MemoryData* newData;
741  void *result = NULL;
742  size_t actualSize;
743#ifdef PMEM_MAP_TRACE
744  MemMapEntry* e;
745#endif
746  size_t oldSize;
747#if PMEM_STACKTRACE
748  const LCHAR* oldStackTrace;
749  MemoryData* oldNext;
750  MemoryData* oldLast;
751#endif
752  ESR_BOOL bMalloc = ESR_FALSE;
753
754  if (gNbInit == 0)
755    return NULL;
756
757  if (newSize == 0 && ptr != NULL)
758  {
759#ifdef PMEM_MAP_TRACE
760    pfree(ptr, file, line);
761#else
762    pfree(ptr);
763#endif
764    return NULL;
765  }
766  else if (ptr == NULL)
767  {
768#ifdef PMEM_MAP_TRACE
769    return pmalloc(newSize, NULL, file, line);
770#else
771    return pmalloc(newSize);
772#endif
773  }
774
775  lockMutex(&memMutex);
776
777  oldData = (MemoryData *)(((unsigned char *) ptr) - sizeof(MemoryData));
778  oldSize = oldData->size;
779  passert(oldSize >= 0);
780#if PMEM_STACKTRACE
781  oldStackTrace = oldData->stackTrace;
782  oldNext = oldData->next;
783  oldLast = oldData->last;
784#endif
785#ifdef PMEM_MAP_TRACE
786  e = gMemoryMap + oldData->index;
787#endif
788
789  actualSize = newSize + sizeof(MemoryData);
790  if (oldSize != actualSize)
791  {
792#if defined(PORTABLE_DINKUM_MEM_MGR) || defined(PORTABLE_FIXED_SIZE_MEM_BLOCK_SCHEME)
793    newData = (MemoryData *) PortNew(actualSize);
794    if (newData == NULL)
795    {
796      pfprintf(PSTDERR, L("OUT_OF_MEMORY: prealloc() failed at %s:%d"), __FILE__, __LINE__);
797      return NULL;
798    }
799    bMalloc = ESR_TRUE;
800    if (oldSize >= actualSize)
801    {
802      memcpy(newData, oldData, actualSize);
803    }
804    else
805    {
806      memcpy(newData, oldData, oldSize);
807    }
808    PortDelete(oldData);
809#else
810    newData = (MemoryData *) realloc(oldData, actualSize);
811    bMalloc = ESR_TRUE;
812#endif
813  }
814  else /* No change */
815  {
816    newData = oldData;
817  }
818
819#ifdef PMEM_MAP_TRACE
820  if (newData != NULL && bMalloc)
821  {
822    if (isLogEnabled)
823    {
824      e->curAlloc += actualSize - oldSize;
825      if (e->maxAlloc < e->curAlloc)
826        e->maxAlloc = e->curAlloc;
827
828      gCurAlloc += actualSize - oldSize;
829      if (gMaxAlloc < gCurAlloc)
830        gMaxAlloc = gCurAlloc;
831    }
832
833#if PMEM_STACKTRACE
834    newData->stackTrace = oldStackTrace;
835    newData->next = oldNext;
836    newData->last = oldLast;
837    if (newData->last != NULL)
838      newData->last->next = newData;
839    if (newData->next != NULL)
840      newData->next->last = newData;
841    if (e->first == oldData)
842      e->first = newData;
843    if (e->last == oldData)
844      e->last = newData;
845#endif
846  }
847#endif
848
849  if (newData != NULL)
850  {
851    newData->size = actualSize;
852    result = (void*)(newData + 1);
853  }
854
855#if PMEM_LOG_LOWLEVEL
856  if (gFile != NULL && isLogEnabled)
857  {
858#if PMEM_STACKTRACE
859    LCHAR stackTrace[P_MAX_STACKTRACE];
860    size_t len = P_MAX_STACKTRACE;
861    ESR_ReturnCode rc;
862
863    rc = getStackTrace(stackTrace, &len);
864    if (rc != ESR_SUCCESS)
865    {
866      pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
867      goto CLEANUP;
868    }
869    pfprintf(gFile, L("pmem|%s|%d|realloc|%d|0x%x|%s|\n"), e->tag, oldSize, actualSize, ptr, stackTrace);
870#else
871    pfprintf(gFile, L("pmem|%s|%d|realloc|%d|0x%x|\n"), e->tag, oldSize, actualSize, ptr);
872#endif /* PMEM_STACKTRACE */
873  }
874#endif /* PMEM_LOG_LOWLEVEL */
875
876  unlockMutex(&memMutex);
877  return result;
878#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
879CLEANUP:
880  unlockMutex(&memMutex);
881  return NULL;
882#endif
883}
884
885#ifdef PMEM_MAP_TRACE
886void pfree(void* ptr, const LCHAR* file, int line)
887#else
888void pfree(void* ptr)
889#endif
890{
891  MemoryData* data;
892#ifdef PMEM_MAP_TRACE
893  MemMapEntry* e;
894#endif
895  if (ptr == NULL || gNbInit == 0)
896    return;
897
898  lockMutex(&memMutex);
899
900  data = (MemoryData*)(((unsigned char*) ptr) - sizeof(MemoryData));
901#ifdef PMEM_MAP_TRACE
902  passert(data->index >= 0 && data->index <= MAX_MEM_TAG);
903  e = gMemoryMap + data->index;
904  if (isLogEnabled)
905  {
906    passert(e->curAlloc >= data->size);
907    e->curAlloc -= data->size;
908
909    passert(gCurAlloc >= data->size);
910    gCurAlloc -= data->size;
911  }
912#if PMEM_STACKTRACE
913  if (e->first != NULL && e->first == data)
914    e->first = data->next;
915  if (e->last != NULL && e->last == data)
916    e->last = data->last;
917  if (data->last != NULL)
918    data->last->next = data->next;
919  if (data->next != NULL)
920  {
921    data->next->last = data->last;
922    data->next = NULL;
923  }
924  data->last = NULL;
925#endif /* PMEM_STACKTRACE */
926#if PMEM_LOG_LOWLEVEL
927  if (gFile != NULL && isLogEnabled)
928  {
929#if PMEM_STACKTRACE
930    LCHAR stackTrace[P_MAX_STACKTRACE];
931    size_t len = P_MAX_STACKTRACE;
932    ESR_ReturnCode rc;
933
934    rc = getStackTrace(stackTrace, &len);
935    if (rc != ESR_SUCCESS)
936    {
937      pfprintf(PSTDERR, "[%s:%d] getStackTrace failed with %s\n", __FILE__, __LINE__, ESR_rc2str(rc));
938      goto CLEANUP;
939    }
940    pfprintf(gFile, L("pmem|free|%s|%s|%d|0x%x|%s|\n"), e->tag, data->stackTrace, data->size, ptr, stackTrace);
941#else
942    pfprintf(gFile, L("pmem|free|%s|%d|0x%x\n"), e->tag, data->size, ptr);
943#endif /* PMEM_STACKTRACE */
944  }
945#endif /* PMEM_LOG_LOWLEVEL */
946#if PMEM_STACKTRACE
947  free((LCHAR*) data->stackTrace);
948  data->stackTrace = NULL;
949#endif /* PMEM_STACKTRACE */
950#endif
951
952  free(data);
953  unlockMutex(&memMutex);
954#if PMEM_STACKTRACE && PMEM_LOG_LOWLEVEL
955CLEANUP:
956  unlockMutex(&memMutex);
957  return;
958#endif
959
960}
961
962#endif
963