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