Home | History | Annotate | Download | only in vpx_mem
      1 /*
      2  *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 
     12 /*
     13   vpx_mem_tracker.c
     14 
     15   jwz 2003-09-30:
     16    Stores a list of addreses, their size, and file and line they came from.
     17    All exposed lib functions are prefaced by vpx_ and allow the global list
     18    to be thread safe.
     19    Current supported platforms are:
     20     Linux, Win32, win_ce and vx_works
     21    Further support can be added by defining the platform specific mutex
     22    in the memory_tracker struct as well as calls to create/destroy/lock/unlock
     23    the mutex in vpx_memory_tracker_init/Destroy and memory_tracker_lock_mutex/unlock_mutex
     24 */
     25 #include "./vpx_config.h"
     26 
     27 #if defined(__uClinux__)
     28 # include <lddk.h>
     29 #endif
     30 
     31 #if HAVE_PTHREAD_H
     32 # include <pthread.h>
     33 #elif defined(WIN32) || defined(_WIN32_WCE)
     34 # define WIN32_LEAN_AND_MEAN
     35 # include <windows.h>
     36 # include <winbase.h>
     37 #elif defined(VXWORKS)
     38 # include <sem_lib.h>
     39 #endif
     40 
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h> // VXWORKS doesn't have a malloc/memory.h file,
     44 // this should pull in malloc,free,etc.
     45 #include <stdarg.h>
     46 
     47 #include "include/vpx_mem_tracker.h"
     48 
     49 #undef vpx_malloc   // undefine any vpx_mem macros that may affect calls to
     50 #undef vpx_free     // memory functions in this file
     51 #undef vpx_memcpy
     52 #undef vpx_memset
     53 
     54 
     55 #ifndef USE_GLOBAL_FUNCTION_POINTERS
     56 # define USE_GLOBAL_FUNCTION_POINTERS   0  // use function pointers instead of compiled functions.
     57 #endif
     58 
     59 #if USE_GLOBAL_FUNCTION_POINTERS
     60 static mem_track_malloc_func g_malloc   = malloc;
     61 static mem_track_calloc_func g_calloc   = calloc;
     62 static mem_track_realloc_func g_realloc = realloc;
     63 static mem_track_free_func g_free       = free;
     64 static mem_track_memcpy_func g_memcpy   = memcpy;
     65 static mem_track_memset_func g_memset   = memset;
     66 static mem_track_memmove_func g_memmove = memmove;
     67 # define MEM_TRACK_MALLOC g_malloc
     68 # define MEM_TRACK_FREE   g_free
     69 # define MEM_TRACK_MEMCPY g_memcpy
     70 # define MEM_TRACK_MEMSET g_memset
     71 #else
     72 # define MEM_TRACK_MALLOC vpx_malloc
     73 # define MEM_TRACK_FREE   vpx_free
     74 # define MEM_TRACK_MEMCPY vpx_memcpy
     75 # define MEM_TRACK_MEMSET vpx_memset
     76 #endif // USE_GLOBAL_FUNCTION_POINTERS
     77 
     78 /* prototypes for internal library functions */
     79 static void memtrack_log(const char *fmt, ...);
     80 static void memory_tracker_dump();
     81 static void memory_tracker_check_integrity(char *file, unsigned int line);
     82 static void memory_tracker_add(size_t addr, unsigned int size,
     83                                char *file, unsigned int line,
     84                                int padded);
     85 static int memory_tracker_remove(size_t addr);
     86 static struct mem_block *memory_tracker_find(size_t addr);
     87 
     88 #if defined(NO_MUTEX)
     89 # define memory_tracker_lock_mutex() (!g_b_mem_tracker_inited)
     90 # define memory_tracker_unlock_mutex()
     91 #else
     92 static int memory_tracker_lock_mutex();
     93 static int memory_tracker_unlock_mutex();
     94 #endif
     95 
     96 #ifndef VPX_NO_GLOBALS
     97 struct memory_tracker {
     98   struct mem_block *head,
     99       * tail;
    100   int len,
    101       totalsize;
    102   unsigned int current_allocated,
    103            max_allocated;
    104 
    105 #if HAVE_PTHREAD_H
    106   pthread_mutex_t mutex;
    107 #elif defined(WIN32) || defined(_WIN32_WCE)
    108   HANDLE mutex;
    109 #elif defined(VXWORKS)
    110   SEM_ID mutex;
    111 #elif defined(NO_MUTEX)
    112 #else
    113 #error "No mutex type defined for this platform!"
    114 #endif
    115 
    116   int padding_size,
    117       pad_value;
    118 };
    119 
    120 static struct memory_tracker memtrack;   // our global memory allocation list
    121 static int g_b_mem_tracker_inited = 0;     // indicates whether the global list has
    122 // been initialized (1:yes/0:no)
    123 static struct {
    124   FILE *file;
    125   int type;
    126   void (*func)(void *userdata, const char *fmt, va_list args);
    127   void *userdata;
    128 } g_logging = {NULL, 0, NULL, NULL};
    129 #else
    130 # include "vpx_global_handling.h"
    131 #define g_b_mem_tracker_inited vpxglobalm(vpxmem,g_b_mem_tracker_inited)
    132 #define g_logging vpxglobalm(vpxmem,g_logging)
    133 #define memtrack vpxglobalm(vpxmem,memtrack)
    134 #endif // #ifndef VPX_NO_GLOBALS
    135 
    136 extern void *vpx_malloc(size_t size);
    137 extern void vpx_free(void *memblk);
    138 extern void *vpx_memcpy(void *dest, const void *src, size_t length);
    139 extern void *vpx_memset(void *dest, int val, size_t length);
    140 
    141 /*
    142  *
    143  * Exposed library functions
    144  *
    145 */
    146 
    147 /*
    148     vpx_memory_tracker_init(int padding_size, int pad_value)
    149       padding_size - the size of the padding before and after each mem addr.
    150                      Values > 0 indicate that integrity checks can be performed
    151                      by inspecting these areas.
    152       pad_value - the initial value within the padding area before and after
    153                   each mem addr.
    154 
    155     Initializes global memory tracker structure
    156     Allocates the head of the list
    157 */
    158 int vpx_memory_tracker_init(int padding_size, int pad_value) {
    159   if (!g_b_mem_tracker_inited) {
    160     if ((memtrack.head = (struct mem_block *)
    161                          MEM_TRACK_MALLOC(sizeof(struct mem_block)))) {
    162       int ret;
    163 
    164       MEM_TRACK_MEMSET(memtrack.head, 0, sizeof(struct mem_block));
    165 
    166       memtrack.tail = memtrack.head;
    167 
    168       memtrack.current_allocated = 0;
    169       memtrack.max_allocated     = 0;
    170 
    171       memtrack.padding_size = padding_size;
    172       memtrack.pad_value    = pad_value;
    173 
    174 #if HAVE_PTHREAD_H
    175       ret = pthread_mutex_init(&memtrack.mutex,
    176                                NULL);            /*mutex attributes (NULL=default)*/
    177 #elif defined(WIN32) || defined(_WIN32_WCE)
    178       memtrack.mutex = CreateMutex(NULL,   /*security attributes*/
    179                                    FALSE,  /*we don't want initial ownership*/
    180                                    NULL);  /*mutex name*/
    181       ret = !memtrack.mutex;
    182 #elif defined(VXWORKS)
    183       memtrack.mutex = sem_bcreate(SEM_Q_FIFO, /*SEM_Q_FIFO non-priority based mutex*/
    184                                    SEM_FULL);  /*SEM_FULL initial state is unlocked*/
    185       ret = !memtrack.mutex;
    186 #elif defined(NO_MUTEX)
    187       ret = 0;
    188 #endif
    189 
    190       if (ret) {
    191         memtrack_log("vpx_memory_tracker_init: Error creating mutex!\n");
    192 
    193         MEM_TRACK_FREE(memtrack.head);
    194         memtrack.head = NULL;
    195       } else {
    196         memtrack_log("Memory Tracker init'd, v."vpx_mem_tracker_version" pad_size:%d pad_val:0x%x %d\n"
    197 , padding_size
    198 , pad_value
    199 , pad_value);
    200         g_b_mem_tracker_inited = 1;
    201       }
    202     }
    203   }
    204 
    205   return g_b_mem_tracker_inited;
    206 }
    207 
    208 /*
    209     vpx_memory_tracker_destroy()
    210     If our global struct was initialized zeros out all its members,
    211     frees memory and destroys it's mutex
    212 */
    213 void vpx_memory_tracker_destroy() {
    214   if (!memory_tracker_lock_mutex()) {
    215     struct mem_block *p  = memtrack.head,
    216                           * p2 = memtrack.head;
    217 
    218     memory_tracker_dump();
    219 
    220     while (p) {
    221       p2 = p;
    222       p  = p->next;
    223 
    224       MEM_TRACK_FREE(p2);
    225     }
    226 
    227     memtrack.head              = NULL;
    228     memtrack.tail              = NULL;
    229     memtrack.len               = 0;
    230     memtrack.current_allocated = 0;
    231     memtrack.max_allocated     = 0;
    232 
    233     if (!g_logging.type && g_logging.file && g_logging.file != stderr) {
    234       fclose(g_logging.file);
    235       g_logging.file = NULL;
    236     }
    237 
    238     memory_tracker_unlock_mutex();
    239 
    240     g_b_mem_tracker_inited = 0;
    241   }
    242 }
    243 
    244 /*
    245     vpx_memory_tracker_add(size_t addr, unsigned int size,
    246                          char * file, unsigned int line)
    247       addr - memory address to be added to list
    248       size - size of addr
    249       file - the file addr was referenced from
    250       line - the line in file addr was referenced from
    251     Adds memory address addr, it's size, file and line it came from
    252     to the global list via the thread safe internal library function
    253 */
    254 void vpx_memory_tracker_add(size_t addr, unsigned int size,
    255                             char *file, unsigned int line,
    256                             int padded) {
    257   memory_tracker_add(addr, size, file, line, padded);
    258 }
    259 
    260 /*
    261     vpx_memory_tracker_remove(size_t addr)
    262       addr - memory address to be removed from list
    263     Removes addr from the global list via the thread safe
    264     internal remove function
    265     Return:
    266       Same as described for memory_tracker_remove
    267 */
    268 int vpx_memory_tracker_remove(size_t addr) {
    269   return memory_tracker_remove(addr);
    270 }
    271 
    272 /*
    273     vpx_memory_tracker_find(size_t addr)
    274       addr - address to be found in list
    275     Return:
    276         If found, pointer to the memory block that matches addr
    277         NULL otherwise
    278 */
    279 struct mem_block *vpx_memory_tracker_find(size_t addr) {
    280   struct mem_block *p = NULL;
    281 
    282   if (!memory_tracker_lock_mutex()) {
    283     p = memory_tracker_find(addr);
    284     memory_tracker_unlock_mutex();
    285   }
    286 
    287   return p;
    288 }
    289 
    290 /*
    291     vpx_memory_tracker_dump()
    292     Locks the memory tracker's mutex and calls the internal
    293     library function to dump the current contents of the
    294     global memory allocation list
    295 */
    296 void vpx_memory_tracker_dump() {
    297   if (!memory_tracker_lock_mutex()) {
    298     memory_tracker_dump();
    299     memory_tracker_unlock_mutex();
    300   }
    301 }
    302 
    303 /*
    304     vpx_memory_tracker_check_integrity(char* file, unsigned int line)
    305       file - The file name where the check was placed
    306       line - The line in file where the check was placed
    307     Locks the memory tracker's mutex and calls the internal
    308     integrity check function to inspect every address in the global
    309     memory allocation list
    310 */
    311 void vpx_memory_tracker_check_integrity(char *file, unsigned int line) {
    312   if (!memory_tracker_lock_mutex()) {
    313     memory_tracker_check_integrity(file, line);
    314     memory_tracker_unlock_mutex();
    315   }
    316 }
    317 
    318 /*
    319     vpx_memory_tracker_set_log_type
    320     Sets the logging type for the memory tracker. Based on the value it will
    321     direct its output to the appropriate place.
    322     Return:
    323       0: on success
    324       -1: if the logging type could not be set, because the value was invalid
    325           or because a file could not be opened
    326 */
    327 int vpx_memory_tracker_set_log_type(int type, char *option) {
    328   int ret = -1;
    329 
    330   switch (type) {
    331     case 0:
    332       g_logging.type = 0;
    333 
    334       if (!option) {
    335         g_logging.file = stderr;
    336         ret = 0;
    337       } else {
    338         if ((g_logging.file = fopen((char *)option, "w")))
    339           ret = 0;
    340       }
    341 
    342       break;
    343 #if defined(WIN32) && !defined(_WIN32_WCE)
    344     case 1:
    345       g_logging.type = type;
    346       ret = 0;
    347       break;
    348 #endif
    349     default:
    350       break;
    351   }
    352 
    353   // output the version to the new logging destination
    354   if (!ret)
    355     memtrack_log("Memory Tracker logging initialized, "
    356                  "Memory Tracker v."vpx_mem_tracker_version"\n");
    357 
    358   return ret;
    359 }
    360 
    361 /*
    362     vpx_memory_tracker_set_log_func
    363     Sets a logging function to be used by the memory tracker.
    364     Return:
    365       0: on success
    366       -1: if the logging type could not be set because logfunc was NULL
    367 */
    368 int vpx_memory_tracker_set_log_func(void *userdata,
    369                                     void(*logfunc)(void *userdata,
    370                                                    const char *fmt, va_list args)) {
    371   int ret = -1;
    372 
    373   if (logfunc) {
    374     g_logging.type     = -1;
    375     g_logging.userdata = userdata;
    376     g_logging.func     = logfunc;
    377     ret = 0;
    378   }
    379 
    380   // output the version to the new logging destination
    381   if (!ret)
    382     memtrack_log("Memory Tracker logging initialized, "
    383                  "Memory Tracker v."vpx_mem_tracker_version"\n");
    384 
    385   return ret;
    386 }
    387 
    388 /*
    389  *
    390  * END - Exposed library functions
    391  *
    392 */
    393 
    394 
    395 /*
    396  *
    397  * Internal library functions
    398  *
    399 */
    400 
    401 static void memtrack_log(const char *fmt, ...) {
    402   va_list list;
    403 
    404   va_start(list, fmt);
    405 
    406   switch (g_logging.type) {
    407     case -1:
    408 
    409       if (g_logging.func)
    410         g_logging.func(g_logging.userdata, fmt, list);
    411 
    412       break;
    413     case 0:
    414 
    415       if (g_logging.file) {
    416         vfprintf(g_logging.file, fmt, list);
    417         fflush(g_logging.file);
    418       }
    419 
    420       break;
    421 #if defined(WIN32) && !defined(_WIN32_WCE)
    422     case 1: {
    423       char temp[1024];
    424       _vsnprintf(temp, sizeof(temp) / sizeof(char) - 1, fmt, list);
    425       OutputDebugString(temp);
    426     }
    427     break;
    428 #endif
    429     default:
    430       break;
    431   }
    432 
    433   va_end(list);
    434 }
    435 
    436 /*
    437     memory_tracker_dump()
    438     Dumps the current contents of the global memory allocation list
    439 */
    440 static void memory_tracker_dump() {
    441   int i = 0;
    442   struct mem_block *p = (memtrack.head ? memtrack.head->next : NULL);
    443 
    444   memtrack_log("\n_currently Allocated= %d; Max allocated= %d\n",
    445                memtrack.current_allocated, memtrack.max_allocated);
    446 
    447   while (p) {
    448 #if defined(WIN32) && !defined(_WIN32_WCE)
    449 
    450     /*when using outputdebugstring, output filenames so they
    451       can be clicked to be opened in visual studio*/
    452     if (g_logging.type == 1)
    453       memtrack_log("memblocks[%d].addr= 0x%.8x, memblocks[%d].size= %d, file:\n"
    454                    "  %s(%d):\n", i,
    455                    p->addr, i, p->size,
    456                    p->file, p->line);
    457     else
    458 #endif
    459       memtrack_log("memblocks[%d].addr= 0x%.8x, memblocks[%d].size= %d, file: %s, line: %d\n", i,
    460                    p->addr, i, p->size,
    461                    p->file, p->line);
    462 
    463     p = p->next;
    464     ++i;
    465   }
    466 
    467   memtrack_log("\n");
    468 }
    469 
    470 /*
    471     memory_tracker_check_integrity(char* file, unsigned int file)
    472       file - the file name where the check was placed
    473       line - the line in file where the check was placed
    474     If a padding_size was supplied to vpx_memory_tracker_init()
    475     this function will check ea. addr in the list verifying that
    476     addr-padding_size and addr+padding_size is filled with pad_value
    477 */
    478 static void memory_tracker_check_integrity(char *file, unsigned int line) {
    479   if (memtrack.padding_size) {
    480     int i,
    481         index = 0;
    482     unsigned char *p_show_me,
    483              * p_show_me2;
    484     unsigned int tempme = memtrack.pad_value,
    485                  dead1,
    486                  dead2;
    487     unsigned char *x_bounds;
    488     struct mem_block *p = memtrack.head->next;
    489 
    490     while (p) {
    491       // x_bounds = (unsigned char*)p->addr;
    492       // back up VPX_BYTE_ALIGNMENT
    493       // x_bounds -= memtrack.padding_size;
    494 
    495       if (p->padded) { // can the bounds be checked?
    496         /*yes, move to the address that was actually allocated
    497         by the vpx_* calls*/
    498         x_bounds = (unsigned char *)(((size_t *)p->addr)[-1]);
    499 
    500         for (i = 0; i < memtrack.padding_size; i += sizeof(unsigned int)) {
    501           p_show_me = (x_bounds + i);
    502           p_show_me2 = (unsigned char *)(p->addr + p->size + i);
    503 
    504           MEM_TRACK_MEMCPY(&dead1, p_show_me, sizeof(unsigned int));
    505           MEM_TRACK_MEMCPY(&dead2, p_show_me2, sizeof(unsigned int));
    506 
    507           if ((dead1 != tempme) || (dead2 != tempme)) {
    508             memtrack_log("\n[vpx_mem integrity check failed]:\n"
    509                          "    index[%d,%d] {%s:%d} addr=0x%x, size=%d,"
    510                          " file: %s, line: %d c0:0x%x c1:0x%x\n",
    511                          index, i, file, line, p->addr, p->size, p->file,
    512                          p->line, dead1, dead2);
    513           }
    514         }
    515       }
    516 
    517       ++index;
    518       p = p->next;
    519     }
    520   }
    521 }
    522 
    523 /*
    524     memory_tracker_add(size_t addr, unsigned int size,
    525                      char * file, unsigned int line)
    526     Adds an address (addr), it's size, file and line number to our list.
    527     Adjusts the total bytes allocated and max bytes allocated if necessary.
    528     If memory cannot be allocated the list will be destroyed.
    529 */
    530 void memory_tracker_add(size_t addr, unsigned int size,
    531                         char *file, unsigned int line,
    532                         int padded) {
    533   if (!memory_tracker_lock_mutex()) {
    534     struct mem_block *p;
    535 
    536     p = MEM_TRACK_MALLOC(sizeof(struct mem_block));
    537 
    538     if (p) {
    539       p->prev       = memtrack.tail;
    540       p->prev->next = p;
    541       p->addr       = addr;
    542       p->size       = size;
    543       p->line       = line;
    544       p->file       = file;
    545       p->padded     = padded;
    546       p->next       = NULL;
    547 
    548       memtrack.tail = p;
    549 
    550       memtrack.current_allocated += size;
    551 
    552       if (memtrack.current_allocated > memtrack.max_allocated)
    553         memtrack.max_allocated = memtrack.current_allocated;
    554 
    555       // memtrack_log("memory_tracker_add: added addr=0x%.8x\n", addr);
    556 
    557       memory_tracker_unlock_mutex();
    558     } else {
    559       memtrack_log("memory_tracker_add: error allocating memory!\n");
    560       memory_tracker_unlock_mutex();
    561       vpx_memory_tracker_destroy();
    562     }
    563   }
    564 }
    565 
    566 /*
    567     memory_tracker_remove(size_t addr)
    568     Removes an address and its corresponding size (if they exist)
    569     from the memory tracker list and adjusts the current number
    570     of bytes allocated.
    571     Return:
    572       0: on success
    573       -1: if the mutex could not be locked
    574       -2: if the addr was not found in the list
    575 */
    576 int memory_tracker_remove(size_t addr) {
    577   int ret = -1;
    578 
    579   if (!memory_tracker_lock_mutex()) {
    580     struct mem_block *p;
    581 
    582     if ((p = memory_tracker_find(addr))) {
    583       memtrack.current_allocated -= p->size;
    584 
    585       p->prev->next = p->next;
    586 
    587       if (p->next)
    588         p->next->prev = p->prev;
    589       else
    590         memtrack.tail = p->prev;
    591 
    592       ret = 0;
    593       MEM_TRACK_FREE(p);
    594     } else {
    595       if (addr)
    596         memtrack_log("memory_tracker_remove(): addr not found in list,"
    597                      " 0x%.8x\n", addr);
    598 
    599       ret = -2;
    600     }
    601 
    602     memory_tracker_unlock_mutex();
    603   }
    604 
    605   return ret;
    606 }
    607 
    608 /*
    609     memory_tracker_find(size_t addr)
    610     Finds an address in our addrs list
    611     NOTE: the mutex MUST be locked in the other internal
    612           functions before calling this one. This avoids
    613           the need for repeated locking and unlocking as in Remove
    614     Returns: pointer to the mem block if found, NULL otherwise
    615 */
    616 static struct mem_block *memory_tracker_find(size_t addr) {
    617   struct mem_block *p = NULL;
    618 
    619   if (memtrack.head) {
    620     p = memtrack.head->next;
    621 
    622     while (p && (p->addr != addr))
    623       p = p->next;
    624   }
    625 
    626   return p;
    627 }
    628 
    629 
    630 #if !defined(NO_MUTEX)
    631 /*
    632     memory_tracker_lock_mutex()
    633     Locks the memory tracker mutex with a platform specific call
    634     Returns:
    635         0: Success
    636        <0: Failure, either the mutex was not initialized
    637            or the call to lock the mutex failed
    638 */
    639 static int memory_tracker_lock_mutex() {
    640   int ret = -1;
    641 
    642   if (g_b_mem_tracker_inited) {
    643 
    644 #if HAVE_PTHREAD_H
    645     ret = pthread_mutex_lock(&memtrack.mutex);
    646 #elif defined(WIN32) || defined(_WIN32_WCE)
    647     ret = WaitForSingleObject(memtrack.mutex, INFINITE);
    648 #elif defined(VXWORKS)
    649     ret = sem_take(memtrack.mutex, WAIT_FOREVER);
    650 #endif
    651 
    652     if (ret) {
    653       memtrack_log("memory_tracker_lock_mutex: mutex lock failed\n");
    654     }
    655   }
    656 
    657   return ret;
    658 }
    659 
    660 /*
    661     memory_tracker_unlock_mutex()
    662     Unlocks the memory tracker mutex with a platform specific call
    663     Returns:
    664         0: Success
    665        <0: Failure, either the mutex was not initialized
    666            or the call to unlock the mutex failed
    667 */
    668 static int memory_tracker_unlock_mutex() {
    669   int ret = -1;
    670 
    671   if (g_b_mem_tracker_inited) {
    672 
    673 #if HAVE_PTHREAD_H
    674     ret = pthread_mutex_unlock(&memtrack.mutex);
    675 #elif defined(WIN32) || defined(_WIN32_WCE)
    676     ret = !ReleaseMutex(memtrack.mutex);
    677 #elif defined(VXWORKS)
    678     ret = sem_give(memtrack.mutex);
    679 #endif
    680 
    681     if (ret) {
    682       memtrack_log("memory_tracker_unlock_mutex: mutex unlock failed\n");
    683     }
    684   }
    685 
    686   return ret;
    687 }
    688 #endif
    689 
    690 /*
    691     vpx_memory_tracker_set_functions
    692 
    693     Sets the function pointers for the standard library functions.
    694 
    695     Return:
    696       0: on success
    697       -1: if the use global function pointers is not set.
    698 */
    699 int vpx_memory_tracker_set_functions(mem_track_malloc_func g_malloc_l
    700 , mem_track_calloc_func g_calloc_l
    701 , mem_track_realloc_func g_realloc_l
    702 , mem_track_free_func g_free_l
    703 , mem_track_memcpy_func g_memcpy_l
    704 , mem_track_memset_func g_memset_l
    705 , mem_track_memmove_func g_memmove_l) {
    706 #if USE_GLOBAL_FUNCTION_POINTERS
    707 
    708   if (g_malloc_l)
    709     g_malloc = g_malloc_l;
    710 
    711   if (g_calloc_l)
    712     g_calloc = g_calloc_l;
    713 
    714   if (g_realloc_l)
    715     g_realloc = g_realloc_l;
    716 
    717   if (g_free_l)
    718     g_free = g_free_l;
    719 
    720   if (g_memcpy_l)
    721     g_memcpy = g_memcpy_l;
    722 
    723   if (g_memset_l)
    724     g_memset = g_memset_l;
    725 
    726   if (g_memmove_l)
    727     g_memmove = g_memmove_l;
    728 
    729   return 0;
    730 #else
    731   (void)g_malloc_l;
    732   (void)g_calloc_l;
    733   (void)g_realloc_l;
    734   (void)g_free_l;
    735   (void)g_memcpy_l;
    736   (void)g_memset_l;
    737   (void)g_memmove_l;
    738   return -1;
    739 #endif
    740 }
    741