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