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 2003-04-08 15:11:43 zacheiss Exp $ |
---|
12 | </center> |
---|
13 | |
---|
14 | <h2> Introduction </h2> |
---|
15 | |
---|
16 | The heap package provides a layer of abstraction on top of |
---|
17 | <tt>malloc</tt>, <tt>realloc</tt> and <tt>free</tt> |
---|
18 | that provides optional error checking and memory leak detection, |
---|
19 | and which optionally raises an exception when an allocation request |
---|
20 | cannot be satisfied. |
---|
21 | |
---|
22 | <h2> Synopsis </h2> |
---|
23 | |
---|
24 | <pre> |
---|
25 | #include <sm/heap.h> |
---|
26 | |
---|
27 | /* |
---|
28 | ** Wrappers for malloc, realloc, free |
---|
29 | */ |
---|
30 | void *sm_malloc(size_t size); |
---|
31 | void *sm_realloc(void *ptr, size_t size); |
---|
32 | void sm_free(void *ptr); |
---|
33 | |
---|
34 | /* |
---|
35 | ** Wrappers for malloc, realloc that raise an exception instead of |
---|
36 | ** returning NULL on heap exhaustion. |
---|
37 | */ |
---|
38 | void *sm_malloc_x(size_t size); |
---|
39 | void *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 | */ |
---|
45 | void sm_heap_report(FILE *stream, int verbosity); |
---|
46 | |
---|
47 | /* |
---|
48 | ** Low level interfaces. |
---|
49 | */ |
---|
50 | int sm_heap_group(); |
---|
51 | int sm_heap_setgroup(int g); |
---|
52 | int sm_heap_newgroup(); |
---|
53 | void *sm_malloc_tagged(size_t size, char *file, int line, int group); |
---|
54 | void *sm_malloc_tagged_x(size_t size, char *file, int line, int group); |
---|
55 | bool 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 | |
---|
162 | When heap checking is enabled, |
---|
163 | the heap package maintains a hash table which associates the |
---|
164 | following 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 | |
---|
193 | These tag values are printed by <tt>sm_heap_report</tt>, |
---|
194 | and are used to help analyze memory allocation behaviour |
---|
195 | and to find memory leaks. |
---|
196 | The following functions give you precise control over the |
---|
197 | tag 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> |
---|
213 | sm_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 | |
---|
243 | Here are two examples of how you might use these interfaces. |
---|
244 | |
---|
245 | <ol> |
---|
246 | <li> |
---|
247 | One way to detect memory leaks is to turn on heap checking |
---|
248 | and call <tt>sm_heap_report(stdout,2)</tt> |
---|
249 | when the program exits. |
---|
250 | This 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, |
---|
252 | and so their existence at program exit does not indicate a leak.) |
---|
253 | If you want to allocate a block and assign it to group 0, |
---|
254 | you have two choices: |
---|
255 | |
---|
256 | <blockquote><pre> |
---|
257 | int g = sm_heap_group(); |
---|
258 | sm_heap_setgroup(0); |
---|
259 | p = sm_malloc_x(size); |
---|
260 | sm_heap_setgroup(g); |
---|
261 | </pre></blockquote> |
---|
262 | |
---|
263 | or |
---|
264 | |
---|
265 | <blockquote><pre> |
---|
266 | p = sm_malloc_tagged_x(size, __FILE__, __LINE__, 0); |
---|
267 | </pre></blockquote> |
---|
268 | |
---|
269 | <li> |
---|
270 | Suppose you have a utility function foo_alloc which allocates |
---|
271 | and initializes a 'foo' object. When sm_heap_report is called, |
---|
272 | all unfreed 'foo' objects will be reported to have the same |
---|
273 | source code file name and line number. |
---|
274 | That might make it difficult to determine where a memory leak is. |
---|
275 | <p> |
---|
276 | |
---|
277 | Here is how you can arrange for more precise reporting for |
---|
278 | unfreed 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 |
---|
294 | FOO * |
---|
295 | foo_alloc_tagged_x(char *file, int line) |
---|
296 | #else |
---|
297 | FOO * |
---|
298 | foo_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 | |
---|
312 | To perform memory leak detection, you need to arrange for your |
---|
313 | program 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 | |
---|
343 | The overhead of using the package can be made as small as you want. |
---|
344 | You 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> |
---|
411 | bool 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> |
---|