1<html> 2<head> 3 <title>libsm : Memory Allocation</title> 4</head> 5<body> 6 7<a href="index.html">Back to libsm overview</a> 8 9<center> 10 <h1> libsm : Memory Allocation </h1> 11 <br> $Id: heap.html,v 1.1.1.1 2002/02/17 21:56:43 gshapiro Exp $ 12</center> 13 14<h2> Introduction </h2> 15 16The heap package provides a layer of abstraction on top of 17<tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt> 18that provides optional error checking and memory leak detection, 19and which optionally raises an exception when an allocation request 20cannot be satisfied. 21 22<h2> Synopsis </h2> 23 24<pre> 25#include <sm/heap.h> 26 27/* 28** Wrappers for malloc, realloc, free 29*/ 30void *sm_malloc(size_t size); 31void *sm_realloc(void *ptr, size_t size); 32void sm_free(void *ptr); 33 34/* 35** Wrappers for malloc, realloc that raise an exception instead of 36** returning NULL on heap exhaustion. 37*/ 38void *sm_malloc_x(size_t size); 39void *sm_realloc_x(void *ptr, size_t size); 40 41/* 42** Print a list of currently allocated blocks, 43** used to diagnose memory leaks. 44*/ 45void sm_heap_report(FILE *stream, int verbosity); 46 47/* 48** Low level interfaces. 49*/ 50int sm_heap_group(); 51int sm_heap_setgroup(int g); 52int sm_heap_newgroup(); 53void *sm_malloc_tagged(size_t size, char *file, int line, int group); 54void *sm_malloc_tagged_x(size_t size, char *file, int line, int group); 55bool sm_heap_register(void *ptr, size_t size, char *file, int line); 56</pre> 57 58<h2> How to allocate and free memory </h2> 59 60 <tt>sm_malloc</tt>, <tt>sm_realloc</tt> and <tt>sm_free</tt> 61 are portable plug in replacements 62 for <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt> that provide 63 error checking and memory leak detection. 64 <tt>sm_malloc_x</tt> and <tt>sm_realloc_x</tt> 65 are variants of 66 <tt>sm_malloc</tt> and <tt>sm_realloc</tt> 67 that raise an exception on error. 68 To use the package effectively, 69 all calls to <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt> 70 should be replaced by calls 71 to the corresponding <tt>sm_</tt>* routines. 72 73<dl> 74<dt> 75<tt> void *sm_malloc(size_t size) </tt> 76<dd> 77 This function is a plug-in replacement for <tt>malloc</tt>. 78 It allocates <tt>size</tt> bytes of memory on the heap 79 and returns a pointer to it, 80 or it returns <tt>NULL</tt> on failure. 81 <p> 82 83 The C standard says that <tt>malloc(0)</tt> may return 84 either <tt>NULL</tt> or a non-<tt>NULL</tt> value. 85 To ensure consistent behaviour on all platforms, 86 <tt>sm_malloc(0)</tt> is equivalent to <tt>sm_malloc(1)</tt>. 87 <p> 88 89 In addition, if heap checking is enabled, then <tt>sm_malloc</tt> 90 maintains a hash table describing all currently allocated 91 memory blocks. This table is used for argument validity 92 checking in <tt>sm_realloc</tt> and <tt>sm_free</tt>, 93 and it can be printed using <tt>sm_heap_report</tt> 94 as an aid to finding memory leaks. 95 <p> 96 97<dt> 98<tt> void *sm_malloc_x(size_t size) </tt> 99<dd> 100 This function is just like <tt>sm_malloc</tt> 101 except that it raises the <tt>SmHeapOutOfMemory</tt> exception 102 instead of returning <tt>NULL</tt> on error. 103 <p> 104 105<dt> 106<tt> void *sm_realloc(void *ptr, size_t size) </tt> 107<dd> 108 This function is a plug-in replacement for <tt>realloc</tt>. 109 If <tt>ptr</tt> is null then this call is equivalent 110 to <tt>sm_malloc(size)</tt>. 111 Otherwise, the size of the object pointed to by <tt>ptr</tt> 112 is changed to <tt>size</tt> bytes, and a pointer to the 113 (possibly moved) object is returned. 114 If the space cannot be allocated, then the object pointed to 115 by <tt>ptr</tt> is unchanged and <tt>NULL</tt> is returned. 116 <p> 117 118 If <tt>size</tt> is 0 then we pretend that <tt>size</tt> is 1. 119 This may be a mistake. 120 <p> 121 122 If ptr is not NULL and heap checking is enabled, 123 then ptr is required to be a value that was 124 previously returned by sm_malloc or sm_realloc, and which 125 has not yet been freed by sm_free. If this condition is not 126 met, then the program is aborted using sm_abort. 127 <p> 128 129<dt> 130<tt> void *sm_realloc_x(void *ptr, size_t size) </tt> 131<dd> 132 This function is just like <tt>sm_realloc</tt> 133 except that it raises the SmHeapOutOfMemory exception 134 instead of returning <tt>NULL</tt> on error. 135 <p> 136 137<dt> 138<tt> void sm_free(void *ptr) </tt> 139<dd> 140 This function is a plug-in replacement for free. 141 If heap checking is disabled, then this function is equivalent 142 to a call to free. Otherwise, the following additional semantics 143 apply. 144 <p> 145 146 If ptr is NULL, this function has no effect. 147 <p> 148 149 Otherwise, ptr is required to be a value that was 150 previously returned by sm_malloc or sm_realloc, and which 151 has not yet been freed by sm_free. If this condition is not 152 met, then the program is aborted using sm_abort. 153 <p> 154 155 Otherwise, if there is no error, then the block pointed to by ptr 156 will be set to all zeros before free() is called. This is intended 157 to assist in detecting the use of dangling pointers. 158</dl> 159 160<h2> How to control tag information </h2> 161 162When heap checking is enabled, 163the heap package maintains a hash table which associates the 164following values with each currently allocated block: 165 166<dl> 167<dt> 168<tt> size_t size </tt> 169<dd> 170 The size of the block. 171<dt> 172<tt> char *tag </tt> 173<dd> 174 By default, this is the name of the source file from which 175 the block was allocated, but you can specify an arbitrary 176 string pointer, or <tt>NULL</tt>. 177<dt> 178<tt> int num </tt> 179<dd> 180 By default, this is the line number from which the block was 181 allocated. 182<dt> 183<tt> int group </tt> 184<dd> 185 By convention, group==0 indicates that the block is permanently 186 allocated and will never be freed. The meanings of other group 187 numbers are defined by the application developer. 188 Unless you take special action, all blocks allocated by 189 <tt>sm_malloc</tt> and <tt>sm_malloc_x</tt> will be assigned 190 to group 1. 191</dl> 192 193These tag values are printed by <tt>sm_heap_report</tt>, 194and are used to help analyze memory allocation behaviour 195and to find memory leaks. 196The following functions give you precise control over the 197tag values associated with each allocated block. 198 199<dl> 200<dt> 201<tt> void *sm_malloc_tagged(size_t size, int tag, int num, int group) </tt> 202<dd> 203 Just like <tt>sm_malloc</tt>, except you directly specify 204 all of the tag values. 205 If heap checking is disabled at compile time, then a call 206 to <tt>sm_malloc_tagged</tt> is macro expanded to 207 a call to <tt>malloc</tt>. 208 <p> 209 210 Note that the expression <tt>sm_malloc(size)</tt> is macro expanded to 211 212<blockquote><pre> 213sm_malloc_tagged(size, __FILE__, __LINE__, sm_heap_group()) 214</pre></blockquote> 215 216<dt> 217<tt> void *sm_malloc_tagged_x(size_t size, int tag, int num, int group) </tt> 218<dd> 219 A variant of <tt>sm_malloc_tagged</tt> 220 that raises an exception on error. 221 A call to <tt>sm_malloc_x</tt> is macro expanded 222 to a call to <tt>sm_malloc_tagged_x</tt>. 223 <p> 224 225<dt> 226<tt> int sm_heap_group() </tt> 227<dd> 228 The heap package maintains a thread-local variable containing 229 the current group number. 230 This is the group that <tt>sm_malloc</tt> and <tt>sm_malloc_x</tt> 231 will assign a newly allocated block to. 232 The initial value of this variable is 1. 233 The current value of this variable is returned by 234 <tt>sm_heap_group()</tt>. 235 <p> 236 237<dt> 238<tt> int sm_heap_setgroup(int g) </tt> 239<dd> 240 Set the current group to the specified value. 241</dl> 242 243Here are two examples of how you might use these interfaces. 244 245<ol> 246<li> 247One way to detect memory leaks is to turn on heap checking 248and call <tt>sm_heap_report(stdout,2)</tt> 249when the program exits. 250This prints a list of all allocated blocks that do not belong to group 0. 251(Blocks in group 0 are assumed to be permanently allocated, 252and so their existence at program exit does not indicate a leak.) 253If you want to allocate a block and assign it to group 0, 254you have two choices: 255 256<blockquote><pre> 257int g = sm_heap_group(); 258sm_heap_setgroup(0); 259p = sm_malloc_x(size); 260sm_heap_setgroup(g); 261</pre></blockquote> 262 263or 264 265<blockquote><pre> 266p = sm_malloc_tagged_x(size, __FILE__, __LINE__, 0); 267</pre></blockquote> 268 269<li> 270Suppose you have a utility function foo_alloc which allocates 271and initializes a 'foo' object. When sm_heap_report is called, 272all unfreed 'foo' objects will be reported to have the same 273source code file name and line number. 274That might make it difficult to determine where a memory leak is. 275<p> 276 277Here is how you can arrange for more precise reporting for 278unfreed foo objects: 279 280<blockquote><pre> 281#include <sm/heap.h> 282 283#if SM_HEAP_CHECK 284# define foo_alloc_x() foo_alloc_tagged_x(__FILE__,__LINE) 285 FOO *foo_alloc_tagged_x(char *, int); 286#else 287 FOO *foo_alloc_x(void); 288# define foo_alloc_tagged_x(file,line) foo_alloc_x() 289#endif 290 291... 292 293#if SM_HEAP_CHECK 294FOO * 295foo_alloc_tagged_x(char *file, int line) 296#else 297FOO * 298foo_alloc_x(void) 299#endif 300{ 301 FOO *p; 302 303 p = sm_malloc_tagged_x(sizeof(FOO), file, line, sm_heap_group()); 304 ... 305 return p; 306} 307</pre></blockquote> 308</ol> 309 310<h2> How to dump the block list </h2> 311 312To perform memory leak detection, you need to arrange for your 313program to call sm_heap_report at appropriate times. 314 315<dl> 316<dt> 317<tt> void sm_heap_report(FILE *stream, int verbosity) </tt> 318<dd> 319 If heap checking is disabled, this function does nothing. 320 If verbosity <= 0, this function does nothing. 321 <p> 322 323 If verbosity >= 1, then sm_heap_report prints a single line 324 to stream giving the total number of bytes currently allocated. 325 If you call sm_heap_report each time the program has reached a 326 "ground state", and the reported amount of heap storage is 327 monotonically increasing, that indicates a leak. 328 <p> 329 330 If verbosity >= 2, then sm_heap_report additionally prints one line 331 for each block of memory currently allocated, providing that 332 the group != 0. 333 (Such blocks are assumed to be permanently allocated storage, and 334 are not reported to cut down the level of noise.) 335 <p> 336 337 If verbosity >= 3, then sm_heap_report prints one line for each 338 allocated block, regardless of the group. 339</dl> 340 341<h2> How to enable heap checking </h2> 342 343The overhead of using the package can be made as small as you want. 344You have three options: 345 346<ol> 347<li> 348 If you compile your software with -DSM_HEAP_CHECK=0 then 349 sm_malloc, sm_realloc and sm_free will be redefined 350 as macros that call malloc, realloc, and free. In this case, 351 there is zero overhead. 352<li> 353 If you do not define -DSM_HEAP_CHECK=0, and you do not explicitly 354 turn on heap checking at run time, then your program will run 355 without error checking and memory leak detection, and the additional 356 cost of calling sm_malloc, sm_realloc and sm_free is a 357 function call and test. That overhead is sufficiently low that 358 the checking code can be left compiled in a production environment. 359<li> 360 If you do not define -DSM_HEAP_CHECK=0, and you explicitly turn on 361 heap checking at run time, then the additional cost of calling 362 sm_malloc, sm_realloc and sm_free is a hash table lookup. 363</ol> 364 365 Here's how to modify your application to use the heap package. 366 First, change all calls to malloc, realloc and free to sm_malloc, 367 sm_realloc and sm_free. 368 Make sure that there is a -d command line option that 369 uses the libsm debug package to enable named debug options. 370 Add the following code to your program just before it calls exit, 371 or register an atexit handler function containing the following code: 372 373<blockquote><pre> 374#if SM_HEAP_CHECK 375 /* dump the heap, if we are checking for memory leaks */ 376 if (sm_debug_active(&SmHeapCheck, 2)) 377 sm_heap_report(stdout, sm_debug_level(&SmHeapCheck) - 1); 378#endif 379</pre></blockquote> 380 381 To turn on heap checking, use the command line option "-dsm_check_heap.1". 382 This will cause a table of all currently allocated blocks to be 383 maintained. The table is used by sm_realloc and sm_free to perform 384 validity checking on the first argument. 385 386 <p> 387 The command line option "-dsm_check_heap.2" will cause your application 388 to invoke sm_heap_report with verbosity=1 just before exit. 389 That will print a single line reporting total storage allocation. 390 391 <p> 392 The command line option "-dsm_check_heap.3" will cause your application 393 to invoke sm_heap_report with verbosity=2 just before exit. 394 This will print a list of all leaked blocks. 395 396 <p> 397 The command line option "-dsm_check_heap.4" will cause your application 398 to invoke sm_heap_report with verbosity=3 just before exit. 399 This will print a list of all allocated blocks. 400 401<h2> Using sm_heap_register </h2> 402 403 Suppose you call a library routine foo that allocates a block of storage 404 for you using malloc, and expects you to free the block later using 405 free. Because the storage was not allocated using sm_malloc, you 406 will normally get an abort if you try to pass the pointer to 407 sm_free. The way to fix this problem is to 'register' the pointer 408 returned by foo with the heap package, by calling sm_heap_register: 409 410<blockquote><pre> 411bool sm_heap_register(ptr, size, file, line, group) 412</pre></blockquote> 413 414 The 'ptr' argument is the pointer returned by foo. The 'size' argument 415 can be smaller than the actual size of the allocated block, but it must 416 not be larger. The file and line arguments indicate at which line of 417 source code the block was allocated, and is printed by sm_heap_report. 418 For group, you probably want to pass sm_heap_group(). 419 <p> 420 This function returns <tt>true</tt> on success, 421 or <tt>false</tt> if it failed due to heap exhaustion. 422 423</body> 424</html> 425