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