dbus-memory.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00024 #include "dbus-memory.h"
00025 #include "dbus-internals.h"
00026 #include "dbus-sysdeps.h"
00027 #include "dbus-list.h"
00028 #include <stdlib.h>
00029  /* end of public API docs */
00091 
00098 #ifdef DBUS_BUILD_TESTS
00099 static dbus_bool_t debug_initialized = FALSE;
00100 static int fail_nth = -1;
00101 static size_t fail_size = 0;
00102 static int fail_alloc_counter = _DBUS_INT_MAX;
00103 static int n_failures_per_failure = 1;
00104 static int n_failures_this_failure = 0;
00105 static dbus_bool_t guards = FALSE;
00106 static dbus_bool_t disable_mem_pools = FALSE;
00107 static dbus_bool_t backtrace_on_fail_alloc = FALSE;
00108 static DBusAtomic n_blocks_outstanding = {0};
00109 
00111 #define GUARD_VALUE 0xdeadbeef
00112 
00113 #define GUARD_INFO_SIZE 8
00114 
00115 #define GUARD_START_PAD 16
00116 
00117 #define GUARD_END_PAD 16
00118 
00119 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
00120 
00121 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
00122 
00123 static void
00124 _dbus_initialize_malloc_debug (void)
00125 {
00126   if (!debug_initialized)
00127     {
00128       debug_initialized = TRUE;
00129       
00130       if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
00131         {
00132           fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
00133           fail_alloc_counter = fail_nth;
00134           _dbus_verbose ("Will fail malloc every %d times\n", fail_nth);
00135         }
00136       
00137       if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
00138         {
00139           fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
00140           _dbus_verbose ("Will fail mallocs over %ld bytes\n",
00141                          (long) fail_size);
00142         }
00143 
00144       if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
00145         {
00146           guards = TRUE;
00147           _dbus_verbose ("Will use malloc guards\n");
00148         }
00149 
00150       if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
00151         {
00152           disable_mem_pools = TRUE;
00153           _dbus_verbose ("Will disable memory pools\n");
00154         }
00155 
00156       if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
00157         {
00158           backtrace_on_fail_alloc = TRUE;
00159           _dbus_verbose ("Will backtrace on failing a malloc\n");
00160         }
00161     }
00162 }
00163 
00169 dbus_bool_t
00170 _dbus_disable_mem_pools (void)
00171 {
00172   _dbus_initialize_malloc_debug ();
00173   return disable_mem_pools;
00174 }
00175 
00184 void
00185 _dbus_set_fail_alloc_counter (int until_next_fail)
00186 {
00187   _dbus_initialize_malloc_debug ();
00188 
00189   fail_alloc_counter = until_next_fail;
00190 
00191 #if 0
00192   _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
00193 #endif
00194 }
00195 
00202 int
00203 _dbus_get_fail_alloc_counter (void)
00204 {
00205   _dbus_initialize_malloc_debug ();
00206 
00207   return fail_alloc_counter;
00208 }
00209 
00216 void
00217 _dbus_set_fail_alloc_failures (int failures_per_failure)
00218 {
00219   n_failures_per_failure = failures_per_failure;
00220 }
00221 
00228 int
00229 _dbus_get_fail_alloc_failures (void)
00230 {
00231   return n_failures_per_failure;
00232 }
00233 
00234 #ifdef DBUS_BUILD_TESTS
00235 
00243 dbus_bool_t
00244 _dbus_decrement_fail_alloc_counter (void)
00245 {
00246   _dbus_initialize_malloc_debug ();
00247   
00248   if (fail_alloc_counter <= 0)
00249     {
00250       if (backtrace_on_fail_alloc)
00251         _dbus_print_backtrace ();
00252 
00253       _dbus_verbose ("failure %d\n", n_failures_this_failure);
00254       
00255       n_failures_this_failure += 1;
00256       if (n_failures_this_failure >= n_failures_per_failure)
00257         {
00258           if (fail_nth >= 0)
00259             fail_alloc_counter = fail_nth;
00260           else
00261             fail_alloc_counter = _DBUS_INT_MAX;
00262 
00263           n_failures_this_failure = 0;
00264 
00265           _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
00266         }
00267       
00268       return TRUE;
00269     }
00270   else
00271     {
00272       fail_alloc_counter -= 1;
00273       return FALSE;
00274     }
00275 }
00276 #endif /* DBUS_BUILD_TESTS */
00277 
00283 int
00284 _dbus_get_malloc_blocks_outstanding (void)
00285 {
00286   return n_blocks_outstanding.value;
00287 }
00288 
00292 typedef enum
00293 {
00294   SOURCE_UNKNOWN,
00295   SOURCE_MALLOC,
00296   SOURCE_REALLOC,
00297   SOURCE_MALLOC_ZERO,
00298   SOURCE_REALLOC_NULL
00299 } BlockSource;
00300 
00301 static const char*
00302 source_string (BlockSource source)
00303 {
00304   switch (source)
00305     {
00306     case SOURCE_UNKNOWN:
00307       return "unknown";
00308     case SOURCE_MALLOC:
00309       return "malloc";
00310     case SOURCE_REALLOC:
00311       return "realloc";
00312     case SOURCE_MALLOC_ZERO:
00313       return "malloc0";
00314     case SOURCE_REALLOC_NULL:
00315       return "realloc(NULL)";
00316     }
00317   _dbus_assert_not_reached ("Invalid malloc block source ID");
00318   return "invalid!";
00319 }
00320 
00321 static void
00322 check_guards (void       *free_block,
00323               dbus_bool_t overwrite)
00324 {
00325   if (free_block != NULL)
00326     {
00327       unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
00328       size_t requested_bytes = *(dbus_uint32_t*)block;
00329       BlockSource source = *(dbus_uint32_t*)(block + 4);
00330       unsigned int i;
00331       dbus_bool_t failed;
00332 
00333       failed = FALSE;
00334 
00335 #if 0
00336       _dbus_verbose ("Checking %d bytes request from source %s\n",
00337                      requested_bytes, source_string (source));
00338 #endif
00339       
00340       i = GUARD_INFO_SIZE;
00341       while (i < GUARD_START_OFFSET)
00342         {
00343           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00344           if (value != GUARD_VALUE)
00345             {
00346               _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n",
00347                           (long) requested_bytes, source_string (source),
00348                           value, i, GUARD_VALUE);
00349               failed = TRUE;
00350             }
00351           
00352           i += 4;
00353         }
00354 
00355       i = GUARD_START_OFFSET + requested_bytes;
00356       while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00357         {
00358           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00359           if (value != GUARD_VALUE)
00360             {
00361               _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n",
00362                           (long) requested_bytes, source_string (source),
00363                           value, i, GUARD_VALUE);
00364               failed = TRUE;
00365             }
00366           
00367           i += 4;
00368         }
00369 
00370       /* set memory to anything but nul bytes */
00371       if (overwrite)
00372         memset (free_block, 'g', requested_bytes);
00373       
00374       if (failed)
00375         _dbus_assert_not_reached ("guard value corruption");
00376     }
00377 }
00378 
00379 static void*
00380 set_guards (void       *real_block,
00381             size_t      requested_bytes,
00382             BlockSource source)
00383 {
00384   unsigned char *block = real_block;
00385   unsigned int i;
00386   
00387   if (block == NULL)
00388     return NULL;
00389 
00390   _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
00391   
00392   *((dbus_uint32_t*)block) = requested_bytes;
00393   *((dbus_uint32_t*)(block + 4)) = source;
00394 
00395   i = GUARD_INFO_SIZE;
00396   while (i < GUARD_START_OFFSET)
00397     {
00398       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00399       
00400       i += 4;
00401     }
00402 
00403   i = GUARD_START_OFFSET + requested_bytes;
00404   while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00405     {
00406       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00407       
00408       i += 4;
00409     }
00410   
00411   check_guards (block + GUARD_START_OFFSET, FALSE);
00412   
00413   return block + GUARD_START_OFFSET;
00414 }
00415 
00416 #endif
00417  /* End of internals docs */
00419 
00420 
00439 void*
00440 dbus_malloc (size_t bytes)
00441 {
00442 #ifdef DBUS_BUILD_TESTS
00443   _dbus_initialize_malloc_debug ();
00444   
00445   if (_dbus_decrement_fail_alloc_counter ())
00446     {
00447       _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
00448       return NULL;
00449     }
00450 #endif
00451 
00452   if (bytes == 0) /* some system mallocs handle this, some don't */
00453     return NULL;
00454 #ifdef DBUS_BUILD_TESTS
00455   else if (fail_size != 0 && bytes > fail_size)
00456     return NULL;
00457   else if (guards)
00458     {
00459       void *block;
00460 
00461       block = malloc (bytes + GUARD_EXTRA_SIZE);
00462       if (block)
00463         _dbus_atomic_inc (&n_blocks_outstanding);
00464       
00465       return set_guards (block, bytes, SOURCE_MALLOC);
00466     }
00467 #endif
00468   else
00469     {
00470       void *mem;
00471       mem = malloc (bytes);
00472 #ifdef DBUS_BUILD_TESTS
00473       if (mem)
00474         _dbus_atomic_inc (&n_blocks_outstanding);
00475 #endif
00476       return mem;
00477     }
00478 }
00479 
00492 void*
00493 dbus_malloc0 (size_t bytes)
00494 {
00495 #ifdef DBUS_BUILD_TESTS
00496   _dbus_initialize_malloc_debug ();
00497   
00498   if (_dbus_decrement_fail_alloc_counter ())
00499     {
00500       _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
00501       
00502       return NULL;
00503     }
00504 #endif
00505   
00506   if (bytes == 0)
00507     return NULL;
00508 #ifdef DBUS_BUILD_TESTS
00509   else if (fail_size != 0 && bytes > fail_size)
00510     return NULL;
00511   else if (guards)
00512     {
00513       void *block;
00514 
00515       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
00516       if (block)
00517         _dbus_atomic_inc (&n_blocks_outstanding);
00518       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
00519     }
00520 #endif
00521   else
00522     {
00523       void *mem;
00524       mem = calloc (bytes, 1);
00525 #ifdef DBUS_BUILD_TESTS
00526       if (mem)
00527         _dbus_atomic_inc (&n_blocks_outstanding);
00528 #endif
00529       return mem;
00530     }
00531 }
00532 
00543 void*
00544 dbus_realloc (void  *memory,
00545               size_t bytes)
00546 {
00547 #ifdef DBUS_BUILD_TESTS
00548   _dbus_initialize_malloc_debug ();
00549   
00550   if (_dbus_decrement_fail_alloc_counter ())
00551     {
00552       _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
00553       
00554       return NULL;
00555     }
00556 #endif
00557   
00558   if (bytes == 0) /* guarantee this is safe */
00559     {
00560       dbus_free (memory);
00561       return NULL;
00562     }
00563 #ifdef DBUS_BUILD_TESTS
00564   else if (fail_size != 0 && bytes > fail_size)
00565     return NULL;
00566   else if (guards)
00567     {
00568       if (memory)
00569         {
00570           size_t old_bytes;
00571           void *block;
00572           
00573           check_guards (memory, FALSE);
00574           
00575           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
00576                            bytes + GUARD_EXTRA_SIZE);
00577 
00578           old_bytes = *(dbus_uint32_t*)block;
00579           if (block && bytes >= old_bytes)
00580             /* old guards shouldn't have moved */
00581             check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE);
00582           
00583           return set_guards (block, bytes, SOURCE_REALLOC);
00584         }
00585       else
00586         {
00587           void *block;
00588           
00589           block = malloc (bytes + GUARD_EXTRA_SIZE);
00590 
00591           if (block)
00592             _dbus_atomic_inc (&n_blocks_outstanding);
00593           
00594           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
00595         }
00596     }
00597 #endif
00598   else
00599     {
00600       void *mem;
00601       mem = realloc (memory, bytes);
00602 #ifdef DBUS_BUILD_TESTS
00603       if (memory == NULL && mem != NULL)
00604             _dbus_atomic_inc (&n_blocks_outstanding);
00605 #endif
00606       return mem;
00607     }
00608 }
00609 
00616 void
00617 dbus_free (void  *memory)
00618 {
00619 #ifdef DBUS_BUILD_TESTS
00620   if (guards)
00621     {
00622       check_guards (memory, TRUE);
00623       if (memory)
00624         {
00625           _dbus_atomic_dec (&n_blocks_outstanding);
00626           
00627           _dbus_assert (n_blocks_outstanding.value >= 0);
00628           
00629           free (((unsigned char*)memory) - GUARD_START_OFFSET);
00630         }
00631       
00632       return;
00633     }
00634 #endif
00635     
00636   if (memory) /* we guarantee it's safe to free (NULL) */
00637     {
00638 #ifdef DBUS_BUILD_TESTS
00639       _dbus_atomic_dec (&n_blocks_outstanding);
00640       
00641       _dbus_assert (n_blocks_outstanding.value >= 0);
00642 #endif
00643 
00644       free (memory);
00645     }
00646 }
00647 
00654 void
00655 dbus_free_string_array (char **str_array)
00656 {
00657   if (str_array)
00658     {
00659       int i;
00660 
00661       i = 0;
00662       while (str_array[i])
00663         {
00664           dbus_free (str_array[i]);
00665           i++;
00666         }
00667 
00668       dbus_free (str_array);
00669     }
00670 }
00671  /* End of public API docs block */
00673 
00674 
00687 int _dbus_current_generation = 1;
00688 
00692 typedef struct ShutdownClosure ShutdownClosure;
00693 
00697 struct ShutdownClosure
00698 {
00699   ShutdownClosure *next;     
00700   DBusShutdownFunction func; 
00701   void *data;                
00702 };
00703 
00704 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs);
00705 static ShutdownClosure *registered_globals = NULL;
00706 
00715 dbus_bool_t
00716 _dbus_register_shutdown_func (DBusShutdownFunction  func,
00717                               void                 *data)
00718 {
00719   ShutdownClosure *c;
00720 
00721   c = dbus_new (ShutdownClosure, 1);
00722 
00723   if (c == NULL)
00724     return FALSE;
00725 
00726   c->func = func;
00727   c->data = data;
00728 
00729   _DBUS_LOCK (shutdown_funcs);
00730   
00731   c->next = registered_globals;
00732   registered_globals = c;
00733 
00734   _DBUS_UNLOCK (shutdown_funcs);
00735   
00736   return TRUE;
00737 }
00738  /* End of private API docs block */
00740 
00741 
00785 void
00786 dbus_shutdown (void)
00787 {
00788   while (registered_globals != NULL)
00789     {
00790       ShutdownClosure *c;
00791 
00792       c = registered_globals;
00793       registered_globals = c->next;
00794       
00795       (* c->func) (c->data);
00796       
00797       dbus_free (c);
00798     }
00799 
00800   _dbus_current_generation += 1;
00801 }
00802  
00805 #ifdef DBUS_BUILD_TESTS
00806 #include "dbus-test.h"
00807 
00813 dbus_bool_t
00814 _dbus_memory_test (void)
00815 {
00816   dbus_bool_t old_guards;
00817   void *p;
00818   size_t size;
00819 
00820   old_guards = guards;
00821   guards = TRUE;
00822   p = dbus_malloc (4);
00823   if (p == NULL)
00824     _dbus_assert_not_reached ("no memory");
00825   for (size = 4; size < 256; size += 4)
00826     {
00827       p = dbus_realloc (p, size);
00828       if (p == NULL)
00829         _dbus_assert_not_reached ("no memory");
00830     }
00831   for (size = 256; size != 0; size -= 4)
00832     {
00833       p = dbus_realloc (p, size);
00834       if (p == NULL)
00835         _dbus_assert_not_reached ("no memory");
00836     }
00837   dbus_free (p);
00838   guards = old_guards;
00839   return TRUE;
00840 }
00841 
00842 #endif

Generated on Fri Sep 21 18:12:12 2007 for D-Bus by  doxygen 1.5.1