Main Page | Modules | Data Structures | Directories | File List | Data Fields | Related Pages

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 int 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 
00242 dbus_bool_t
00243 _dbus_decrement_fail_alloc_counter (void)
00244 {
00245   _dbus_initialize_malloc_debug ();
00246   
00247   if (fail_alloc_counter <= 0)
00248     {
00249       if (backtrace_on_fail_alloc)
00250         _dbus_print_backtrace ();
00251 
00252       _dbus_verbose ("failure %d\n", n_failures_this_failure);
00253       
00254       n_failures_this_failure += 1;
00255       if (n_failures_this_failure >= n_failures_per_failure)
00256         {
00257           if (fail_nth >= 0)
00258             fail_alloc_counter = fail_nth;
00259           else
00260             fail_alloc_counter = _DBUS_INT_MAX;
00261 
00262           n_failures_this_failure = 0;
00263 
00264           _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
00265         }
00266       
00267       return TRUE;
00268     }
00269   else
00270     {
00271       fail_alloc_counter -= 1;
00272       return FALSE;
00273     }
00274 }
00275 
00281 int
00282 _dbus_get_malloc_blocks_outstanding (void)
00283 {
00284   return n_blocks_outstanding;
00285 }
00286 
00290 typedef enum
00291 {
00292   SOURCE_UNKNOWN,
00293   SOURCE_MALLOC,
00294   SOURCE_REALLOC,
00295   SOURCE_MALLOC_ZERO,
00296   SOURCE_REALLOC_NULL
00297 } BlockSource;
00298 
00299 static const char*
00300 source_string (BlockSource source)
00301 {
00302   switch (source)
00303     {
00304     case SOURCE_UNKNOWN:
00305       return "unknown";
00306     case SOURCE_MALLOC:
00307       return "malloc";
00308     case SOURCE_REALLOC:
00309       return "realloc";
00310     case SOURCE_MALLOC_ZERO:
00311       return "malloc0";
00312     case SOURCE_REALLOC_NULL:
00313       return "realloc(NULL)";
00314     }
00315   _dbus_assert_not_reached ("Invalid malloc block source ID");
00316   return "invalid!";
00317 }
00318 
00319 static void
00320 check_guards (void *free_block)
00321 {
00322   if (free_block != NULL)
00323     {
00324       unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
00325       size_t requested_bytes = *(dbus_uint32_t*)block;
00326       BlockSource source = *(dbus_uint32_t*)(block + 4);
00327       unsigned int i;
00328       dbus_bool_t failed;
00329 
00330       failed = FALSE;
00331 
00332 #if 0
00333       _dbus_verbose ("Checking %d bytes request from source %s\n",
00334                      requested_bytes, source_string (source));
00335 #endif
00336       
00337       i = GUARD_INFO_SIZE;
00338       while (i < GUARD_START_OFFSET)
00339         {
00340           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00341           if (value != GUARD_VALUE)
00342             {
00343               _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n",
00344                           (long) requested_bytes, source_string (source),
00345                           value, i, GUARD_VALUE);
00346               failed = TRUE;
00347             }
00348           
00349           i += 4;
00350         }
00351 
00352       i = GUARD_START_OFFSET + requested_bytes;
00353       while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00354         {
00355           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00356           if (value != GUARD_VALUE)
00357             {
00358               _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n",
00359                           (long) requested_bytes, source_string (source),
00360                           value, i, GUARD_VALUE);
00361               failed = TRUE;
00362             }
00363           
00364           i += 4;
00365         }
00366 
00367       if (failed)
00368         _dbus_assert_not_reached ("guard value corruption");
00369     }
00370 }
00371 
00372 static void*
00373 set_guards (void       *real_block,
00374             size_t      requested_bytes,
00375             BlockSource source)
00376 {
00377   unsigned char *block = real_block;
00378   unsigned int i;
00379   
00380   if (block == NULL)
00381     return NULL;
00382 
00383   _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
00384   
00385   *((dbus_uint32_t*)block) = requested_bytes;
00386   *((dbus_uint32_t*)(block + 4)) = source;
00387 
00388   i = GUARD_INFO_SIZE;
00389   while (i < GUARD_START_OFFSET)
00390     {
00391       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00392       
00393       i += 4;
00394     }
00395 
00396   i = GUARD_START_OFFSET + requested_bytes;
00397   while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00398     {
00399       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00400       
00401       i += 4;
00402     }
00403   
00404   check_guards (block + GUARD_START_OFFSET);
00405   
00406   return block + GUARD_START_OFFSET;
00407 }
00408 
00409 #endif
00410  /* End of internals docs */
00412 
00413 
00429 void*
00430 dbus_malloc (size_t bytes)
00431 {
00432 #ifdef DBUS_BUILD_TESTS
00433   _dbus_initialize_malloc_debug ();
00434   
00435   if (_dbus_decrement_fail_alloc_counter ())
00436     {
00437       _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
00438       
00439       return NULL;
00440     }
00441 #endif
00442   
00443   if (bytes == 0) /* some system mallocs handle this, some don't */
00444     return NULL;
00445 #ifdef DBUS_BUILD_TESTS
00446   else if (fail_size != 0 && bytes > fail_size)
00447     return NULL;
00448   else if (guards)
00449     {
00450       void *block;
00451 
00452       block = malloc (bytes + GUARD_EXTRA_SIZE);
00453       if (block)
00454         n_blocks_outstanding += 1;
00455       
00456       return set_guards (block, bytes, SOURCE_MALLOC);
00457     }
00458 #endif
00459   else
00460     {
00461       void *mem;
00462       mem = malloc (bytes);
00463 #ifdef DBUS_BUILD_TESTS
00464       if (mem)
00465         n_blocks_outstanding += 1;
00466 #endif
00467       return mem;
00468     }
00469 }
00470 
00480 void*
00481 dbus_malloc0 (size_t bytes)
00482 {
00483 #ifdef DBUS_BUILD_TESTS
00484   _dbus_initialize_malloc_debug ();
00485   
00486   if (_dbus_decrement_fail_alloc_counter ())
00487     {
00488       _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
00489       
00490       return NULL;
00491     }
00492 #endif
00493 
00494   if (bytes == 0)
00495     return NULL;
00496 #ifdef DBUS_BUILD_TESTS
00497   else if (fail_size != 0 && bytes > fail_size)
00498     return NULL;
00499   else if (guards)
00500     {
00501       void *block;
00502 
00503       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
00504       if (block)
00505         n_blocks_outstanding += 1;
00506       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
00507     }
00508 #endif
00509   else
00510     {
00511       void *mem;
00512       mem = calloc (bytes, 1);
00513 #ifdef DBUS_BUILD_TESTS
00514       if (mem)
00515         n_blocks_outstanding += 1;
00516 #endif
00517       return mem;
00518     }
00519 }
00520 
00531 void*
00532 dbus_realloc (void  *memory,
00533               size_t bytes)
00534 {
00535 #ifdef DBUS_BUILD_TESTS
00536   _dbus_initialize_malloc_debug ();
00537   
00538   if (_dbus_decrement_fail_alloc_counter ())
00539     {
00540       _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
00541       
00542       return NULL;
00543     }
00544 #endif
00545   
00546   if (bytes == 0) /* guarantee this is safe */
00547     {
00548       dbus_free (memory);
00549       return NULL;
00550     }
00551 #ifdef DBUS_BUILD_TESTS
00552   else if (fail_size != 0 && bytes > fail_size)
00553     return NULL;
00554   else if (guards)
00555     {
00556       if (memory)
00557         {
00558           size_t old_bytes;
00559           void *block;
00560           
00561           check_guards (memory);
00562           
00563           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
00564                            bytes + GUARD_EXTRA_SIZE);
00565 
00566           old_bytes = *(dbus_uint32_t*)block;
00567           if (block && bytes >= old_bytes)
00568             /* old guards shouldn't have moved */
00569             check_guards (((unsigned char*)block) + GUARD_START_OFFSET);
00570           
00571           return set_guards (block, bytes, SOURCE_REALLOC);
00572         }
00573       else
00574         {
00575           void *block;
00576           
00577           block = malloc (bytes + GUARD_EXTRA_SIZE);
00578 
00579           if (block)
00580             n_blocks_outstanding += 1;
00581           
00582           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
00583         }
00584     }
00585 #endif
00586   else
00587     {
00588       void *mem;
00589       mem = realloc (memory, bytes);
00590 #ifdef DBUS_BUILD_TESTS
00591       if (memory == NULL && mem != NULL)
00592         n_blocks_outstanding += 1;
00593 #endif
00594       return mem;
00595     }
00596 }
00597 
00604 void
00605 dbus_free (void  *memory)
00606 {
00607 #ifdef DBUS_BUILD_TESTS
00608   if (guards)
00609     {
00610       check_guards (memory);
00611       if (memory)
00612         {
00613           n_blocks_outstanding -= 1;
00614           
00615           _dbus_assert (n_blocks_outstanding >= 0);
00616           
00617           free (((unsigned char*)memory) - GUARD_START_OFFSET);
00618         }
00619       
00620       return;
00621     }
00622 #endif
00623     
00624   if (memory) /* we guarantee it's safe to free (NULL) */
00625     {
00626 #ifdef DBUS_BUILD_TESTS
00627       n_blocks_outstanding -= 1;
00628       
00629       _dbus_assert (n_blocks_outstanding >= 0);
00630 #endif
00631 
00632       free (memory);
00633     }
00634 }
00635 
00642 void
00643 dbus_free_string_array (char **str_array)
00644 {
00645   if (str_array)
00646     {
00647       int i;
00648 
00649       i = 0;
00650       while (str_array[i])
00651         {
00652           dbus_free (str_array[i]);
00653           i++;
00654         }
00655 
00656       dbus_free (str_array);
00657     }
00658 }
00659  /* End of public API docs block */
00661 
00662 
00675 int _dbus_current_generation = 1;
00676 
00680 typedef struct ShutdownClosure ShutdownClosure;
00681 
00685 struct ShutdownClosure
00686 {
00687   ShutdownClosure *next;     
00688   DBusShutdownFunction func; 
00689   void *data;                
00690 };
00691 
00692 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs);
00693 static ShutdownClosure *registered_globals = NULL;
00694 
00703 dbus_bool_t
00704 _dbus_register_shutdown_func (DBusShutdownFunction  func,
00705                               void                 *data)
00706 {
00707   ShutdownClosure *c;
00708 
00709   c = dbus_new (ShutdownClosure, 1);
00710 
00711   if (c == NULL)
00712     return FALSE;
00713 
00714   c->func = func;
00715   c->data = data;
00716 
00717   _DBUS_LOCK (shutdown_funcs);
00718   
00719   c->next = registered_globals;
00720   registered_globals = c;
00721 
00722   _DBUS_UNLOCK (shutdown_funcs);
00723   
00724   return TRUE;
00725 }
00726  /* End of private API docs block */
00728 
00729 
00748 void
00749 dbus_shutdown (void)
00750 {
00751   while (registered_globals != NULL)
00752     {
00753       ShutdownClosure *c;
00754 
00755       c = registered_globals;
00756       registered_globals = c->next;
00757       
00758       (* c->func) (c->data);
00759       
00760       dbus_free (c);
00761     }
00762 
00763   _dbus_current_generation += 1;
00764 }
00765  
00768 #ifdef DBUS_BUILD_TESTS
00769 #include "dbus-test.h"
00770 
00776 dbus_bool_t
00777 _dbus_memory_test (void)
00778 {
00779   dbus_bool_t old_guards;
00780   void *p;
00781   size_t size;
00782 
00783   old_guards = guards;
00784   guards = TRUE;
00785   p = dbus_malloc (4);
00786   if (p == NULL)
00787     _dbus_assert_not_reached ("no memory");
00788   for (size = 4; size < 256; size += 4)
00789     {
00790       p = dbus_realloc (p, size);
00791       if (p == NULL)
00792         _dbus_assert_not_reached ("no memory");
00793     }
00794   for (size = 256; size != 0; size -= 4)
00795     {
00796       p = dbus_realloc (p, size);
00797       if (p == NULL)
00798         _dbus_assert_not_reached ("no memory");
00799     }
00800   dbus_free (p);
00801   guards = old_guards;
00802   return TRUE;
00803 }
00804 
00805 #endif

Generated on Thu Aug 11 21:11:13 2005 for D-BUS by  doxygen 1.4.0