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 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 64 static mem_track_malloc_func g_malloc = malloc; 65 static mem_track_calloc_func g_calloc = calloc; 66 static mem_track_realloc_func g_realloc = realloc; 67 static mem_track_free_func g_free = free; 68 static mem_track_memcpy_func g_memcpy = memcpy; 69 static mem_track_memset_func g_memset = memset; 70 static 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 83 struct 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 */ 110 static void memtrack_log(const char *fmt, ...); 111 static void memory_tracker_dump(); 112 static void memory_tracker_check_integrity(char *file, unsigned int line); 113 static void memory_tracker_add(size_t addr, unsigned int size, 114 char *file, unsigned int line, 115 int padded); 116 static int memory_tracker_remove(size_t addr); 117 static 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 123 static int memory_tracker_lock_mutex(); 124 static int memory_tracker_unlock_mutex(); 125 #endif 126 127 static struct memory_tracker memtrack; //our global memory allocation list 128 static int g_b_mem_tracker_inited = 0; //indicates whether the global list has 129 //been initialized (1:yes/0:no) 130 static 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 138 extern void *vpx_malloc(size_t size); 139 extern void vpx_free(void *memblk); 140 extern void *vpx_memcpy(void *dest, const void *src, size_t length); 141 extern 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 */ 160 int 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 */ 223 void 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 */ 273 void 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 */ 288 int 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 */ 300 struct 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 */ 319 void 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 */ 336 void 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 */ 354 int 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 */ 404 int 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 439 static 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 */ 482 static 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 */ 522 static 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 */ 580 void 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 */ 631 int 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 */ 673 static 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 */ 698 static 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 */ 733 static 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 */ 770 int 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 } 813