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
60static mem_track_malloc_func g_malloc   = malloc;
61static mem_track_calloc_func g_calloc   = calloc;
62static mem_track_realloc_func g_realloc = realloc;
63static mem_track_free_func g_free       = free;
64static mem_track_memcpy_func g_memcpy   = memcpy;
65static mem_track_memset_func g_memset   = memset;
66static 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 */
79static void memtrack_log(const char *fmt, ...);
80static void memory_tracker_dump();
81static void memory_tracker_check_integrity(char *file, unsigned int line);
82static void memory_tracker_add(size_t addr, unsigned int size,
83                               char *file, unsigned int line,
84                               int padded);
85static int memory_tracker_remove(size_t addr);
86static 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
92static int memory_tracker_lock_mutex();
93static int memory_tracker_unlock_mutex();
94#endif
95
96#ifndef VPX_NO_GLOBALS
97struct 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
120static struct memory_tracker memtrack;   // our global memory allocation list
121static int g_b_mem_tracker_inited = 0;     // indicates whether the global list has
122// been initialized (1:yes/0:no)
123static 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
136extern void *vpx_malloc(size_t size);
137extern void vpx_free(void *memblk);
138extern void *vpx_memcpy(void *dest, const void *src, size_t length);
139extern 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*/
158int 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*/
213void 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*/
254void 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*/
268int 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*/
279struct 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*/
296void 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*/
311void 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*/
327int 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*/
368int 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
401static 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*/
440static 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*/
478static 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*/
530void 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*/
576int 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*/
616static 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*/
639static 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*/
668static 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*/
699int 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