|
D-Bus
1.5.8
|
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00002 /* dbus-memory.c D-Bus memory handling 00003 * 00004 * Copyright (C) 2002, 2003 Red Hat Inc. 00005 * 00006 * Licensed under the Academic Free License version 2.1 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 * 00022 */ 00023 00024 #include <config.h> 00025 #include "dbus-memory.h" 00026 #include "dbus-internals.h" 00027 #include "dbus-sysdeps.h" 00028 #include "dbus-list.h" 00029 #include <stdlib.h> 00030 /* end of public API docs */ 00092 00099 #ifdef DBUS_BUILD_TESTS 00100 static dbus_bool_t debug_initialized = FALSE; 00101 static int fail_nth = -1; 00102 static size_t fail_size = 0; 00103 static int fail_alloc_counter = _DBUS_INT_MAX; 00104 static int n_failures_per_failure = 1; 00105 static int n_failures_this_failure = 0; 00106 static dbus_bool_t guards = FALSE; 00107 static dbus_bool_t disable_mem_pools = FALSE; 00108 static dbus_bool_t backtrace_on_fail_alloc = FALSE; 00109 static DBusAtomic n_blocks_outstanding = {0}; 00110 00112 #define GUARD_VALUE 0xdeadbeef 00113 00114 #define GUARD_INFO_SIZE 8 00115 00116 #define GUARD_START_PAD 16 00117 00118 #define GUARD_END_PAD 16 00119 00120 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE) 00121 00122 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD) 00123 00124 static void 00125 _dbus_initialize_malloc_debug (void) 00126 { 00127 if (!debug_initialized) 00128 { 00129 debug_initialized = TRUE; 00130 00131 if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL) 00132 { 00133 fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH")); 00134 fail_alloc_counter = fail_nth; 00135 _dbus_verbose ("Will fail malloc every %d times\n", fail_nth); 00136 } 00137 00138 if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL) 00139 { 00140 fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN")); 00141 _dbus_verbose ("Will fail mallocs over %ld bytes\n", 00142 (long) fail_size); 00143 } 00144 00145 if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL) 00146 { 00147 guards = TRUE; 00148 _dbus_verbose ("Will use malloc guards\n"); 00149 } 00150 00151 if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL) 00152 { 00153 disable_mem_pools = TRUE; 00154 _dbus_verbose ("Will disable memory pools\n"); 00155 } 00156 00157 if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL) 00158 { 00159 backtrace_on_fail_alloc = TRUE; 00160 _dbus_verbose ("Will backtrace on failing a malloc\n"); 00161 } 00162 } 00163 } 00164 00170 dbus_bool_t 00171 _dbus_disable_mem_pools (void) 00172 { 00173 _dbus_initialize_malloc_debug (); 00174 return disable_mem_pools; 00175 } 00176 00185 void 00186 _dbus_set_fail_alloc_counter (int until_next_fail) 00187 { 00188 _dbus_initialize_malloc_debug (); 00189 00190 fail_alloc_counter = until_next_fail; 00191 00192 #if 0 00193 _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter); 00194 #endif 00195 } 00196 00203 int 00204 _dbus_get_fail_alloc_counter (void) 00205 { 00206 _dbus_initialize_malloc_debug (); 00207 00208 return fail_alloc_counter; 00209 } 00210 00217 void 00218 _dbus_set_fail_alloc_failures (int failures_per_failure) 00219 { 00220 n_failures_per_failure = failures_per_failure; 00221 } 00222 00229 int 00230 _dbus_get_fail_alloc_failures (void) 00231 { 00232 return n_failures_per_failure; 00233 } 00234 00235 #ifdef DBUS_BUILD_TESTS 00236 00244 dbus_bool_t 00245 _dbus_decrement_fail_alloc_counter (void) 00246 { 00247 _dbus_initialize_malloc_debug (); 00248 #ifdef DBUS_WIN_FIXME 00249 { 00250 static dbus_bool_t called = 0; 00251 00252 if (!called) 00253 { 00254 _dbus_verbose("TODO: memory allocation testing errors disabled for now\n"); 00255 called = 1; 00256 } 00257 return FALSE; 00258 } 00259 #endif 00260 00261 if (fail_alloc_counter <= 0) 00262 { 00263 if (backtrace_on_fail_alloc) 00264 _dbus_print_backtrace (); 00265 00266 _dbus_verbose ("failure %d\n", n_failures_this_failure); 00267 00268 n_failures_this_failure += 1; 00269 if (n_failures_this_failure >= n_failures_per_failure) 00270 { 00271 if (fail_nth >= 0) 00272 fail_alloc_counter = fail_nth; 00273 else 00274 fail_alloc_counter = _DBUS_INT_MAX; 00275 00276 n_failures_this_failure = 0; 00277 00278 _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter); 00279 } 00280 00281 return TRUE; 00282 } 00283 else 00284 { 00285 fail_alloc_counter -= 1; 00286 return FALSE; 00287 } 00288 } 00289 #endif /* DBUS_BUILD_TESTS */ 00290 00296 int 00297 _dbus_get_malloc_blocks_outstanding (void) 00298 { 00299 return _dbus_atomic_get (&n_blocks_outstanding); 00300 } 00301 00305 typedef enum 00306 { 00307 SOURCE_UNKNOWN, 00308 SOURCE_MALLOC, 00309 SOURCE_REALLOC, 00310 SOURCE_MALLOC_ZERO, 00311 SOURCE_REALLOC_NULL 00312 } BlockSource; 00313 00314 static const char* 00315 source_string (BlockSource source) 00316 { 00317 switch (source) 00318 { 00319 case SOURCE_UNKNOWN: 00320 return "unknown"; 00321 case SOURCE_MALLOC: 00322 return "malloc"; 00323 case SOURCE_REALLOC: 00324 return "realloc"; 00325 case SOURCE_MALLOC_ZERO: 00326 return "malloc0"; 00327 case SOURCE_REALLOC_NULL: 00328 return "realloc(NULL)"; 00329 } 00330 _dbus_assert_not_reached ("Invalid malloc block source ID"); 00331 return "invalid!"; 00332 } 00333 00334 static void 00335 check_guards (void *free_block, 00336 dbus_bool_t overwrite) 00337 { 00338 if (free_block != NULL) 00339 { 00340 unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET; 00341 size_t requested_bytes = *(dbus_uint32_t*)block; 00342 BlockSource source = *(dbus_uint32_t*)(block + 4); 00343 unsigned int i; 00344 dbus_bool_t failed; 00345 00346 failed = FALSE; 00347 00348 #if 0 00349 _dbus_verbose ("Checking %d bytes request from source %s\n", 00350 requested_bytes, source_string (source)); 00351 #endif 00352 00353 i = GUARD_INFO_SIZE; 00354 while (i < GUARD_START_OFFSET) 00355 { 00356 dbus_uint32_t value = *(dbus_uint32_t*) &block[i]; 00357 if (value != GUARD_VALUE) 00358 { 00359 _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n", 00360 (long) requested_bytes, source_string (source), 00361 value, i, GUARD_VALUE); 00362 failed = TRUE; 00363 } 00364 00365 i += 4; 00366 } 00367 00368 i = GUARD_START_OFFSET + requested_bytes; 00369 while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD)) 00370 { 00371 dbus_uint32_t value = *(dbus_uint32_t*) &block[i]; 00372 if (value != GUARD_VALUE) 00373 { 00374 _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n", 00375 (long) requested_bytes, source_string (source), 00376 value, i, GUARD_VALUE); 00377 failed = TRUE; 00378 } 00379 00380 i += 4; 00381 } 00382 00383 /* set memory to anything but nul bytes */ 00384 if (overwrite) 00385 memset (free_block, 'g', requested_bytes); 00386 00387 if (failed) 00388 _dbus_assert_not_reached ("guard value corruption"); 00389 } 00390 } 00391 00392 static void* 00393 set_guards (void *real_block, 00394 size_t requested_bytes, 00395 BlockSource source) 00396 { 00397 unsigned char *block = real_block; 00398 unsigned int i; 00399 00400 if (block == NULL) 00401 return NULL; 00402 00403 _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE); 00404 00405 *((dbus_uint32_t*)block) = requested_bytes; 00406 *((dbus_uint32_t*)(block + 4)) = source; 00407 00408 i = GUARD_INFO_SIZE; 00409 while (i < GUARD_START_OFFSET) 00410 { 00411 (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE; 00412 00413 i += 4; 00414 } 00415 00416 i = GUARD_START_OFFSET + requested_bytes; 00417 while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD)) 00418 { 00419 (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE; 00420 00421 i += 4; 00422 } 00423 00424 check_guards (block + GUARD_START_OFFSET, FALSE); 00425 00426 return block + GUARD_START_OFFSET; 00427 } 00428 00429 #endif 00430 /* End of internals docs */ 00432 00433 00452 void* 00453 dbus_malloc (size_t bytes) 00454 { 00455 #ifdef DBUS_BUILD_TESTS 00456 _dbus_initialize_malloc_debug (); 00457 00458 if (_dbus_decrement_fail_alloc_counter ()) 00459 { 00460 _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes); 00461 return NULL; 00462 } 00463 #endif 00464 00465 if (bytes == 0) /* some system mallocs handle this, some don't */ 00466 return NULL; 00467 #ifdef DBUS_BUILD_TESTS 00468 else if (fail_size != 0 && bytes > fail_size) 00469 return NULL; 00470 else if (guards) 00471 { 00472 void *block; 00473 00474 block = malloc (bytes + GUARD_EXTRA_SIZE); 00475 if (block) 00476 _dbus_atomic_inc (&n_blocks_outstanding); 00477 00478 return set_guards (block, bytes, SOURCE_MALLOC); 00479 } 00480 #endif 00481 else 00482 { 00483 void *mem; 00484 mem = malloc (bytes); 00485 #ifdef DBUS_BUILD_TESTS 00486 if (mem) 00487 _dbus_atomic_inc (&n_blocks_outstanding); 00488 #endif 00489 return mem; 00490 } 00491 } 00492 00505 void* 00506 dbus_malloc0 (size_t bytes) 00507 { 00508 #ifdef DBUS_BUILD_TESTS 00509 _dbus_initialize_malloc_debug (); 00510 00511 if (_dbus_decrement_fail_alloc_counter ()) 00512 { 00513 _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes); 00514 00515 return NULL; 00516 } 00517 #endif 00518 00519 if (bytes == 0) 00520 return NULL; 00521 #ifdef DBUS_BUILD_TESTS 00522 else if (fail_size != 0 && bytes > fail_size) 00523 return NULL; 00524 else if (guards) 00525 { 00526 void *block; 00527 00528 block = calloc (bytes + GUARD_EXTRA_SIZE, 1); 00529 if (block) 00530 _dbus_atomic_inc (&n_blocks_outstanding); 00531 return set_guards (block, bytes, SOURCE_MALLOC_ZERO); 00532 } 00533 #endif 00534 else 00535 { 00536 void *mem; 00537 mem = calloc (bytes, 1); 00538 #ifdef DBUS_BUILD_TESTS 00539 if (mem) 00540 _dbus_atomic_inc (&n_blocks_outstanding); 00541 #endif 00542 return mem; 00543 } 00544 } 00545 00556 void* 00557 dbus_realloc (void *memory, 00558 size_t bytes) 00559 { 00560 #ifdef DBUS_BUILD_TESTS 00561 _dbus_initialize_malloc_debug (); 00562 00563 if (_dbus_decrement_fail_alloc_counter ()) 00564 { 00565 _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes); 00566 00567 return NULL; 00568 } 00569 #endif 00570 00571 if (bytes == 0) /* guarantee this is safe */ 00572 { 00573 dbus_free (memory); 00574 return NULL; 00575 } 00576 #ifdef DBUS_BUILD_TESTS 00577 else if (fail_size != 0 && bytes > fail_size) 00578 return NULL; 00579 else if (guards) 00580 { 00581 if (memory) 00582 { 00583 size_t old_bytes; 00584 void *block; 00585 00586 check_guards (memory, FALSE); 00587 00588 block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET, 00589 bytes + GUARD_EXTRA_SIZE); 00590 00591 old_bytes = *(dbus_uint32_t*)block; 00592 if (block && bytes >= old_bytes) 00593 /* old guards shouldn't have moved */ 00594 check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE); 00595 00596 return set_guards (block, bytes, SOURCE_REALLOC); 00597 } 00598 else 00599 { 00600 void *block; 00601 00602 block = malloc (bytes + GUARD_EXTRA_SIZE); 00603 00604 if (block) 00605 _dbus_atomic_inc (&n_blocks_outstanding); 00606 00607 return set_guards (block, bytes, SOURCE_REALLOC_NULL); 00608 } 00609 } 00610 #endif 00611 else 00612 { 00613 void *mem; 00614 mem = realloc (memory, bytes); 00615 #ifdef DBUS_BUILD_TESTS 00616 if (memory == NULL && mem != NULL) 00617 _dbus_atomic_inc (&n_blocks_outstanding); 00618 #endif 00619 return mem; 00620 } 00621 } 00622 00629 void 00630 dbus_free (void *memory) 00631 { 00632 #ifdef DBUS_BUILD_TESTS 00633 if (guards) 00634 { 00635 check_guards (memory, TRUE); 00636 if (memory) 00637 { 00638 #ifdef DBUS_DISABLE_ASSERT 00639 _dbus_atomic_dec (&n_blocks_outstanding); 00640 #else 00641 dbus_int32_t old_value; 00642 00643 old_value = _dbus_atomic_dec (&n_blocks_outstanding); 00644 _dbus_assert (old_value >= 1); 00645 #endif 00646 00647 free (((unsigned char*)memory) - GUARD_START_OFFSET); 00648 } 00649 00650 return; 00651 } 00652 #endif 00653 00654 if (memory) /* we guarantee it's safe to free (NULL) */ 00655 { 00656 #ifdef DBUS_BUILD_TESTS 00657 #ifdef DBUS_DISABLE_ASSERT 00658 _dbus_atomic_dec (&n_blocks_outstanding); 00659 #else 00660 dbus_int32_t old_value; 00661 00662 old_value = _dbus_atomic_dec (&n_blocks_outstanding); 00663 _dbus_assert (old_value >= 1); 00664 #endif 00665 #endif 00666 00667 free (memory); 00668 } 00669 } 00670 00677 void 00678 dbus_free_string_array (char **str_array) 00679 { 00680 if (str_array) 00681 { 00682 int i; 00683 00684 i = 0; 00685 while (str_array[i]) 00686 { 00687 dbus_free (str_array[i]); 00688 i++; 00689 } 00690 00691 dbus_free (str_array); 00692 } 00693 } 00694 /* End of public API docs block */ 00696 00697 00710 int _dbus_current_generation = 1; 00711 00715 typedef struct ShutdownClosure ShutdownClosure; 00716 00720 struct ShutdownClosure 00721 { 00722 ShutdownClosure *next; 00723 DBusShutdownFunction func; 00724 void *data; 00725 }; 00726 00727 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs); 00728 static ShutdownClosure *registered_globals = NULL; 00729 00738 dbus_bool_t 00739 _dbus_register_shutdown_func (DBusShutdownFunction func, 00740 void *data) 00741 { 00742 ShutdownClosure *c; 00743 00744 c = dbus_new (ShutdownClosure, 1); 00745 00746 if (c == NULL) 00747 return FALSE; 00748 00749 c->func = func; 00750 c->data = data; 00751 00752 _DBUS_LOCK (shutdown_funcs); 00753 00754 c->next = registered_globals; 00755 registered_globals = c; 00756 00757 _DBUS_UNLOCK (shutdown_funcs); 00758 00759 return TRUE; 00760 } 00761 /* End of private API docs block */ 00763 00764 00808 void 00809 dbus_shutdown (void) 00810 { 00811 while (registered_globals != NULL) 00812 { 00813 ShutdownClosure *c; 00814 00815 c = registered_globals; 00816 registered_globals = c->next; 00817 00818 (* c->func) (c->data); 00819 00820 dbus_free (c); 00821 } 00822 00823 _dbus_current_generation += 1; 00824 } 00825 00828 #ifdef DBUS_BUILD_TESTS 00829 #include "dbus-test.h" 00830 00836 dbus_bool_t 00837 _dbus_memory_test (void) 00838 { 00839 dbus_bool_t old_guards; 00840 void *p; 00841 size_t size; 00842 00843 old_guards = guards; 00844 guards = TRUE; 00845 p = dbus_malloc (4); 00846 if (p == NULL) 00847 _dbus_assert_not_reached ("no memory"); 00848 for (size = 4; size < 256; size += 4) 00849 { 00850 p = dbus_realloc (p, size); 00851 if (p == NULL) 00852 _dbus_assert_not_reached ("no memory"); 00853 } 00854 for (size = 256; size != 0; size -= 4) 00855 { 00856 p = dbus_realloc (p, size); 00857 if (p == NULL) 00858 _dbus_assert_not_reached ("no memory"); 00859 } 00860 dbus_free (p); 00861 guards = old_guards; 00862 return TRUE; 00863 } 00864 00865 #endif
1.7.5.1