summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libsm/heap.html
blob: bc32b012e7570fb8a58002c06e9570c9d0e648e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
<html>
<head>
    <title>libsm : Memory Allocation</title>
</head>
<body>

<a href="index.html">Back to libsm overview</a>

<center>
    <h1> libsm : Memory Allocation </h1>
    <br> $Id: heap.html,v 1.9 2000/12/08 21:41:42 ca Exp $
</center>

<h2> Introduction </h2>

The heap package provides a layer of abstraction on top of
<tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt>
that provides optional error checking and memory leak detection,
and which optionally raises an exception when an allocation request
cannot be satisfied.

<h2> Synopsis </h2>

<pre>
#include &lt;sm/heap.h&gt;

/*
**  Wrappers for malloc, realloc, free
*/
void *sm_malloc(size_t size);
void *sm_realloc(void *ptr, size_t size);
void  sm_free(void *ptr);

/*
**  Wrappers for malloc, realloc that raise an exception instead of
**  returning NULL on heap exhaustion.
*/
void *sm_malloc_x(size_t size);
void *sm_realloc_x(void *ptr, size_t size);

/*
**  Print a list of currently allocated blocks,
**  used to diagnose memory leaks.
*/
void  sm_heap_report(FILE *stream, int verbosity);

/*
**  Low level interfaces.
*/
int sm_heap_group();
int sm_heap_setgroup(int g);
int sm_heap_newgroup();
void *sm_malloc_tagged(size_t size, char *file, int line, int group);
void *sm_malloc_tagged_x(size_t size, char *file, int line, int group);
bool  sm_heap_register(void *ptr, size_t size, char *file, int line);
</pre>

<h2> How to allocate and free memory </h2>

    <tt>sm_malloc</tt>, <tt>sm_realloc</tt> and <tt>sm_free</tt>
    are portable plug in replacements
    for <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt> that provide
    error checking and memory leak detection.
    <tt>sm_malloc_x</tt> and <tt>sm_realloc_x</tt>
    are variants of
    <tt>sm_malloc</tt> and <tt>sm_realloc</tt>
    that raise an exception on error.
    To use the package effectively,
    all calls to <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt>
    should be replaced by calls
    to the corresponding <tt>sm_</tt>* routines.

<dl>
<dt>
<tt> void *sm_malloc(size_t size) </tt>
<dd>
	This function is a plug-in replacement for <tt>malloc</tt>.
	It allocates <tt>size</tt> bytes of memory on the heap
	and returns a pointer to it,
	or it returns <tt>NULL</tt> on failure.
	<p>

	The C standard says that <tt>malloc(0)</tt> may return
	either <tt>NULL</tt> or a non-<tt>NULL</tt> value.
	To ensure consistent behaviour on all platforms,
	<tt>sm_malloc(0)</tt> is equivalent to <tt>sm_malloc(1)</tt>.
	<p>

	In addition, if heap checking is enabled, then <tt>sm_malloc</tt>
	maintains a hash table describing all currently allocated
	memory blocks.  This table is used for argument validity
	checking in <tt>sm_realloc</tt> and <tt>sm_free</tt>,
	and it can be printed using <tt>sm_heap_report</tt>
	as an aid to finding memory leaks.
	<p>

<dt>
<tt> void *sm_malloc_x(size_t size) </tt>
<dd>
    This function is just like <tt>sm_malloc</tt>
    except that it raises the <tt>SmHeapOutOfMemory</tt> exception
    instead of returning <tt>NULL</tt> on error.
    <p>
    
<dt>
<tt> void *sm_realloc(void *ptr, size_t size) </tt>
<dd>
	This function is a plug-in replacement for <tt>realloc</tt>.
	If <tt>ptr</tt> is null then this call is equivalent
	to <tt>sm_malloc(size)</tt>.
	Otherwise, the size of the object pointed to by <tt>ptr</tt>
	is changed to <tt>size</tt> bytes, and a pointer to the
	(possibly moved) object is returned.
	If the space cannot be allocated, then the object pointed to
	by <tt>ptr</tt> is unchanged and <tt>NULL</tt> is returned.
	<p>

	If <tt>size</tt> is 0 then we pretend that <tt>size</tt> is 1.
	This may be a mistake.
	<p>

	If ptr is not NULL and heap checking is enabled,
	then ptr is required to be a value that was
	previously returned by sm_malloc or sm_realloc, and which
	has not yet been freed by sm_free.  If this condition is not
	met, then the program is aborted using sm_abort.
	<p>

<dt>
<tt> void *sm_realloc_x(void *ptr, size_t size) </tt>
<dd>
    This function is just like <tt>sm_realloc</tt>
    except that it raises the SmHeapOutOfMemory exception
    instead of returning <tt>NULL</tt> on error.
    <p>

<dt>
<tt> void sm_free(void *ptr) </tt>
<dd>
	This function is a plug-in replacement for free.
	If heap checking is disabled, then this function is equivalent
	to a call to free.  Otherwise, the following additional semantics
	apply.
	<p>

	If ptr is NULL, this function has no effect.
	<p>

	Otherwise, ptr is required to be a value that was
	previously returned by sm_malloc or sm_realloc, and which
	has not yet been freed by sm_free.  If this condition is not
	met, then the program is aborted using sm_abort.
	<p>

	Otherwise, if there is no error, then the block pointed to by ptr
	will be set to all zeros before free() is called.  This is intended
	to assist in detecting the use of dangling pointers.
</dl>

<h2> How to control tag information </h2>

When heap checking is enabled,
the heap package maintains a hash table which associates the
following values with each currently allocated block:

<dl>
<dt>
<tt> size_t size </tt>
<dd>
	The size of the block.
<dt>
<tt> char *tag </tt>
<dd>
	By default, this is the name of the source file from which
	the block was allocated, but you can specify an arbitrary
	string pointer, or <tt>NULL</tt>.
<dt>
<tt> int num </tt>
<dd>
	By default, this is the line number from which the block was
	allocated.
<dt>
<tt> int group </tt>
<dd>
	By convention, group==0 indicates that the block is permanently
	allocated and will never be freed.  The meanings of other group
	numbers are defined by the application developer.
	Unless you take special action, all blocks allocated by
	<tt>sm_malloc</tt> and <tt>sm_malloc_x</tt> will be assigned
	to group 1.
</dl>

These tag values are printed by <tt>sm_heap_report</tt>,
and are used to help analyze memory allocation behaviour
and to find memory leaks.
The following functions give you precise control over the
tag values associated with each allocated block.

<dl>
<dt>
<tt> void *sm_malloc_tagged(size_t size, int tag, int num, int group) </tt>
<dd>
	Just like <tt>sm_malloc</tt>, except you directly specify
	all of the tag values.
	If heap checking is disabled at compile time, then a call
	to <tt>sm_malloc_tagged</tt> is macro expanded to
	a call to <tt>malloc</tt>.
	<p>

	Note that the expression <tt>sm_malloc(size)</tt> is macro expanded to

<blockquote><pre>
sm_malloc_tagged(size, __FILE__, __LINE__, sm_heap_group())
</pre></blockquote>

<dt>
<tt> void *sm_malloc_tagged_x(size_t size, int tag, int num, int group) </tt>
<dd>
	A variant of <tt>sm_malloc_tagged</tt>
	that raises an exception on error.
	A call to <tt>sm_malloc_x</tt> is macro expanded
	to a call to <tt>sm_malloc_tagged_x</tt>.
	<p>

<dt>
<tt> int sm_heap_group() </tt>
<dd>
	The heap package maintains a thread-local variable containing
	the current group number.
	This is the group that <tt>sm_malloc</tt> and <tt>sm_malloc_x</tt>
	will assign a newly allocated block to.
	The initial value of this variable is 1.
	The current value of this variable is returned by
	<tt>sm_heap_group()</tt>.
	<p>

<dt>
<tt> int sm_heap_setgroup(int g) </tt>
<dd>
	Set the current group to the specified value.
</dl>

Here are two examples of how you might use these interfaces.

<ol>
<li>
One way to detect memory leaks is to turn on heap checking
and call <tt>sm_heap_report(stdout,2)</tt>
when the program exits.
This prints a list of all allocated blocks that do not belong to group 0.
(Blocks in group 0 are assumed to be permanently allocated,
and so their existence at program exit does not indicate a leak.)
If you want to allocate a block and assign it to group 0,
you have two choices:

<blockquote><pre>
int g = sm_heap_group();
sm_heap_setgroup(0);
p = sm_malloc_x(size);
sm_heap_setgroup(g);
</pre></blockquote>

or

<blockquote><pre>
p = sm_malloc_tagged_x(size, __FILE__, __LINE__, 0);
</pre></blockquote>

<li>
Suppose you have a utility function foo_alloc which allocates
and initializes a 'foo' object.  When sm_heap_report is called,
all unfreed 'foo' objects will be reported to have the same
source code file name and line number.
That might make it difficult to determine where a memory leak is.
<p>

Here is how you can arrange for more precise reporting for
unfreed foo objects:

<blockquote><pre>
#include &lt;sm/heap.h&gt;

#if SM_HEAP_CHECK
#  define foo_alloc_x() foo_alloc_tagged_x(__FILE__,__LINE)
   FOO *foo_alloc_tagged_x(char *, int);
#else
   FOO *foo_alloc_x(void);
#  define foo_alloc_tagged_x(file,line) foo_alloc_x()
#endif

...

#if SM_HEAP_CHECK
FOO *
foo_alloc_tagged_x(char *file, int line)
#else
FOO *
foo_alloc_x(void)
#endif
{
	FOO *p;

	p = sm_malloc_tagged_x(sizeof(FOO), file, line, sm_heap_group());
	...
	return p;
}
</pre></blockquote>
</ol>

<h2> How to dump the block list </h2>

To perform memory leak detection, you need to arrange for your
program to call sm_heap_report at appropriate times.

<dl>
<dt>
<tt> void sm_heap_report(FILE *stream, int verbosity) </tt>
<dd>
	If heap checking is disabled, this function does nothing.
	If verbosity &lt;= 0, this function does nothing.
	<p>

	If verbosity &gt;= 1, then sm_heap_report prints a single line
	to stream giving the total number of bytes currently allocated.
	If you call sm_heap_report each time the program has reached a
	"ground state", and the reported amount of heap storage is
	monotonically increasing, that indicates a leak.
	<p>

	If verbosity &gt;= 2, then sm_heap_report additionally prints one line
	for each block of memory currently allocated, providing that
	the group != 0.
	(Such blocks are assumed to be permanently allocated storage, and
	are not reported to cut down the level of noise.)
	<p>

	If verbosity &gt;= 3, then sm_heap_report prints one line for each
	allocated block, regardless of the group.
</dl>

<h2> How to enable heap checking </h2>

The overhead of using the package can be made as small as you want.
You have three options:

<ol>
<li>
        If you compile your software with -DSM_HEAP_CHECK=0 then
	sm_malloc, sm_realloc and sm_free will be redefined
	as macros that call malloc, realloc, and free.  In this case,
	there is zero overhead.
<li>
        If you do not define -DSM_HEAP_CHECK=0, and you do not explicitly
	turn on heap checking at run time, then your program will run
	without error checking and memory leak detection, and the additional
	cost of calling sm_malloc, sm_realloc and sm_free is a
	function call and test.  That overhead is sufficiently low that
	the checking code can be left compiled in a production environment.
<li>
        If you do not define -DSM_HEAP_CHECK=0, and you explicitly turn on
	heap checking at run time, then the additional cost of calling
	sm_malloc, sm_realloc and sm_free is a hash table lookup.
</ol>

    Here's how to modify your application to use the heap package.
    First, change all calls to malloc, realloc and free to sm_malloc,
    sm_realloc and sm_free.
    Make sure that there is a -d command line option that
    uses the libsm debug package to enable named debug options.
    Add the following code to your program just before it calls exit,
    or register an atexit handler function containing the following code:

<blockquote><pre>
#if SM_HEAP_CHECK
	/* dump the heap, if we are checking for memory leaks */
	if (sm_debug_active(&SmHeapCheck, 2))
		sm_heap_report(stdout, sm_debug_level(&SmHeapCheck) - 1);
#endif
</pre></blockquote>

    To turn on heap checking, use the command line option "-dsm_check_heap.1".
    This will cause a table of all currently allocated blocks to be
    maintained.  The table is used by sm_realloc and sm_free to perform
    validity checking on the first argument.

    <p>
    The command line option "-dsm_check_heap.2" will cause your application
    to invoke sm_heap_report with verbosity=1 just before exit.
    That will print a single line reporting total storage allocation.

    <p>
    The command line option "-dsm_check_heap.3" will cause your application
    to invoke sm_heap_report with verbosity=2 just before exit.
    This will print a list of all leaked blocks.

    <p>
    The command line option "-dsm_check_heap.4" will cause your application
    to invoke sm_heap_report with verbosity=3 just before exit.
    This will print a list of all allocated blocks.

<h2> Using sm_heap_register </h2>

    Suppose you call a library routine foo that allocates a block of storage
    for you using malloc, and expects you to free the block later using
    free.  Because the storage was not allocated using sm_malloc, you
    will normally get an abort if you try to pass the pointer to
    sm_free.  The way to fix this problem is to 'register' the pointer
    returned by foo with the heap package, by calling sm_heap_register:

<blockquote><pre>
bool sm_heap_register(ptr, size, file, line, group)
</pre></blockquote>

    The 'ptr' argument is the pointer returned by foo.  The 'size' argument
    can be smaller than the actual size of the allocated block, but it must
    not be larger.  The file and line arguments indicate at which line of
    source code the block was allocated, and is printed by sm_heap_report.
    For group, you probably want to pass sm_heap_group().
    <p>
    This function returns <tt>true</tt> on success,
    or <tt>false</tt> if it failed due to heap exhaustion.

</body>
</html>
OpenPOWER on IntegriCloud