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 63 static mem_track_malloc_func g_malloc = malloc; 64 static mem_track_calloc_func g_calloc = calloc; 65 static mem_track_realloc_func g_realloc = realloc; 66 static mem_track_free_func g_free = free; 67 static mem_track_memcpy_func g_memcpy = memcpy; 68 static mem_track_memset_func g_memset = memset; 69 static 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 */ 82 static void memtrack_log(const char *fmt, ...); 83 static void memory_tracker_dump(); 84 static void memory_tracker_check_integrity(char *file, unsigned int line); 85 static void memory_tracker_add(size_t addr, unsigned int size, 86 char *file, unsigned int line, 87 int padded); 88 static int memory_tracker_remove(size_t addr); 89 static 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 95 static int memory_tracker_lock_mutex(); 96 static int memory_tracker_unlock_mutex(); 97 #endif 98 99 #ifndef VPX_NO_GLOBALS 100 struct 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 126 static struct memory_tracker memtrack; //our global memory allocation list 127 static int g_b_mem_tracker_inited = 0; //indicates whether the global list has 128 //been initialized (1:yes/0:no) 129 static 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 143 extern void *vpx_malloc(size_t size); 144 extern void vpx_free(void *memblk); 145 extern void *vpx_memcpy(void *dest, const void *src, size_t length); 146 extern 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 */ 165 int 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 */ 229 void 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 */ 276 void 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 */ 291 int 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 */ 303 struct 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 */ 322 void 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 */ 339 void 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 */ 357 int 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 */ 406 int 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 441 static 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 */ 484 static 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 */ 530 static 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 */ 588 void 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 */ 639 int 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 */ 684 static 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 */ 709 static 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 */ 744 static 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 */ 781 int 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