190792Sgshapiro<html>
290792Sgshapiro<head>
390792Sgshapiro    <title>libsm : Memory Allocation</title>
490792Sgshapiro</head>
590792Sgshapiro<body>
690792Sgshapiro
790792Sgshapiro<a href="index.html">Back to libsm overview</a>
890792Sgshapiro
990792Sgshapiro<center>
1090792Sgshapiro    <h1> libsm : Memory Allocation </h1>
11266527Sgshapiro    <br> $Id: heap.html,v 1.9 2000-12-08 21:41:42 ca Exp $
1290792Sgshapiro</center>
1390792Sgshapiro
1490792Sgshapiro<h2> Introduction </h2>
1590792Sgshapiro
1690792SgshapiroThe heap package provides a layer of abstraction on top of
1790792Sgshapiro<tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt>
1890792Sgshapirothat provides optional error checking and memory leak detection,
1990792Sgshapiroand which optionally raises an exception when an allocation request
2090792Sgshapirocannot be satisfied.
2190792Sgshapiro
2290792Sgshapiro<h2> Synopsis </h2>
2390792Sgshapiro
2490792Sgshapiro<pre>
2590792Sgshapiro#include &lt;sm/heap.h&gt;
2690792Sgshapiro
2790792Sgshapiro/*
2890792Sgshapiro**  Wrappers for malloc, realloc, free
2990792Sgshapiro*/
3090792Sgshapirovoid *sm_malloc(size_t size);
3190792Sgshapirovoid *sm_realloc(void *ptr, size_t size);
3290792Sgshapirovoid  sm_free(void *ptr);
3390792Sgshapiro
3490792Sgshapiro/*
3590792Sgshapiro**  Wrappers for malloc, realloc that raise an exception instead of
3690792Sgshapiro**  returning NULL on heap exhaustion.
3790792Sgshapiro*/
3890792Sgshapirovoid *sm_malloc_x(size_t size);
3990792Sgshapirovoid *sm_realloc_x(void *ptr, size_t size);
4090792Sgshapiro
4190792Sgshapiro/*
4290792Sgshapiro**  Print a list of currently allocated blocks,
4390792Sgshapiro**  used to diagnose memory leaks.
4490792Sgshapiro*/
4590792Sgshapirovoid  sm_heap_report(FILE *stream, int verbosity);
4690792Sgshapiro
4790792Sgshapiro/*
4890792Sgshapiro**  Low level interfaces.
4990792Sgshapiro*/
5090792Sgshapiroint sm_heap_group();
5190792Sgshapiroint sm_heap_setgroup(int g);
5290792Sgshapiroint sm_heap_newgroup();
5390792Sgshapirovoid *sm_malloc_tagged(size_t size, char *file, int line, int group);
5490792Sgshapirovoid *sm_malloc_tagged_x(size_t size, char *file, int line, int group);
5590792Sgshapirobool  sm_heap_register(void *ptr, size_t size, char *file, int line);
5690792Sgshapiro</pre>
5790792Sgshapiro
5890792Sgshapiro<h2> How to allocate and free memory </h2>
5990792Sgshapiro
6090792Sgshapiro    <tt>sm_malloc</tt>, <tt>sm_realloc</tt> and <tt>sm_free</tt>
6190792Sgshapiro    are portable plug in replacements
6290792Sgshapiro    for <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt> that provide
6390792Sgshapiro    error checking and memory leak detection.
6490792Sgshapiro    <tt>sm_malloc_x</tt> and <tt>sm_realloc_x</tt>
6590792Sgshapiro    are variants of
6690792Sgshapiro    <tt>sm_malloc</tt> and <tt>sm_realloc</tt>
6790792Sgshapiro    that raise an exception on error.
6890792Sgshapiro    To use the package effectively,
6990792Sgshapiro    all calls to <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt>
7090792Sgshapiro    should be replaced by calls
7190792Sgshapiro    to the corresponding <tt>sm_</tt>* routines.
7290792Sgshapiro
7390792Sgshapiro<dl>
7490792Sgshapiro<dt>
7590792Sgshapiro<tt> void *sm_malloc(size_t size) </tt>
7690792Sgshapiro<dd>
7790792Sgshapiro	This function is a plug-in replacement for <tt>malloc</tt>.
7890792Sgshapiro	It allocates <tt>size</tt> bytes of memory on the heap
7990792Sgshapiro	and returns a pointer to it,
8090792Sgshapiro	or it returns <tt>NULL</tt> on failure.
8190792Sgshapiro	<p>
8290792Sgshapiro
8390792Sgshapiro	The C standard says that <tt>malloc(0)</tt> may return
8490792Sgshapiro	either <tt>NULL</tt> or a non-<tt>NULL</tt> value.
8590792Sgshapiro	To ensure consistent behaviour on all platforms,
8690792Sgshapiro	<tt>sm_malloc(0)</tt> is equivalent to <tt>sm_malloc(1)</tt>.
8790792Sgshapiro	<p>
8890792Sgshapiro
89266527Sgshapiro	In addition, if heap checking is enabled, then <tt>sm_malloc</tt>
9090792Sgshapiro	maintains a hash table describing all currently allocated
9190792Sgshapiro	memory blocks.  This table is used for argument validity
9290792Sgshapiro	checking in <tt>sm_realloc</tt> and <tt>sm_free</tt>,
9390792Sgshapiro	and it can be printed using <tt>sm_heap_report</tt>
9490792Sgshapiro	as an aid to finding memory leaks.
9590792Sgshapiro	<p>
9690792Sgshapiro
9790792Sgshapiro<dt>
9890792Sgshapiro<tt> void *sm_malloc_x(size_t size) </tt>
9990792Sgshapiro<dd>
10090792Sgshapiro    This function is just like <tt>sm_malloc</tt>
10190792Sgshapiro    except that it raises the <tt>SmHeapOutOfMemory</tt> exception
10290792Sgshapiro    instead of returning <tt>NULL</tt> on error.
10390792Sgshapiro    <p>
10490792Sgshapiro    
10590792Sgshapiro<dt>
10690792Sgshapiro<tt> void *sm_realloc(void *ptr, size_t size) </tt>
10790792Sgshapiro<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 &lt;sm/heap.h&gt;
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 &lt;= 0, this function does nothing.
321	<p>
322
323	If verbosity &gt;= 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 &gt;= 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 &gt;= 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