D-Bus  1.5.8
dbus-memory.c
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