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