1 /*
2 * alloc.c - memory allocation module implementation
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2001, 2003 Stefan Jahn <stefan@lkcc.org>
6 * Copyright (C) 2000 Raimund Jacob <raimi@lkcc.org>
7 * Copyright (C) 1999 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
8 *
9 * This is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3, or (at your option)
12 * any later version.
13 *
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this package. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include "timidity.h"
26 #include "unused.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "libserveez/alloc.h"
32 #include "libserveez/util.h"
33
34 #if DEBUG_MEMORY_LEAKS
35 # include "libserveez/hash.h"
36 # include "le-u32-hash.h"
37 #endif /* DEBUG_MEMORY_LEAKS */
38
39 #if ENABLE_DEBUG
40 /* The overall number of bytes allocated by libserveez. */
41 static size_t allocated_bytes = 0;
42 /* The number of memory blocks reserved by libserveez. */
43 static size_t allocated_blocks = 0;
44 #endif /* ENABLE_DEBUG */
45
46 /* Default memory management functions. */
47 svz_malloc_func_t svz_malloc_func = malloc;
48 svz_realloc_func_t svz_realloc_func = realloc;
49 svz_free_func_t svz_free_func = free;
50
51 /**
52 * Set the internal memory management functions to @var{cus_malloc},
53 * @var{cus_realloc} and @var{cus_free}, respectively.
54 * The default internal values are @code{malloc}, @code{realloc}
55 * and @code{free}.
56 */
57 void
svz_set_mm_funcs(svz_malloc_func_t cus_malloc,svz_realloc_func_t cus_realloc,svz_free_func_t cus_free)58 svz_set_mm_funcs (svz_malloc_func_t cus_malloc,
59 svz_realloc_func_t cus_realloc,
60 svz_free_func_t cus_free)
61 {
62 svz_malloc_func = cus_malloc;
63 svz_realloc_func = cus_realloc;
64 svz_free_func = cus_free;
65 }
66
67 #if DEBUG_MEMORY_LEAKS
68
69 /* heap hash table */
70 static svz_hash_t *heap = NULL;
71
72 /* return static heap hash code key length */
73 static size_t
heap_hash_keylen(UNUSED const char * id)74 heap_hash_keylen (UNUSED const char *id)
75 {
76 return SIZEOF_VOID_P;
77 }
78
79 /* compare two heap hash values */
80 static int
heap_hash_equals(const char * id1,const char * id2)81 heap_hash_equals (const char *id1, const char *id2)
82 {
83 return memcmp (id1, id2, SIZEOF_VOID_P);
84 }
85
86 /* calculate heap hash code */
87 static unsigned long
heap_hash_code(const char * id)88 heap_hash_code (const char *id)
89 {
90 unsigned long code = le_u32_hash (id);
91 code >>= 3;
92 return code;
93 }
94
95 /* structure for heap management */
96 typedef struct
97 {
98 void *ptr; /* memory pointer */
99 size_t size; /* memory block's size */
100 void *caller; /* the caller */
101 }
102 heap_block_t;
103
104 /* add another heap block to the heap management */
105 static void
heap_add(heap_block_t * block)106 heap_add (heap_block_t *block)
107 {
108 if (heap == NULL)
109 heap = svz_hash_configure (svz_hash_create (4, NULL),
110 heap_hash_keylen,
111 heap_hash_code,
112 heap_hash_equals);
113 svz_hash_put (heap, (char *) &block->ptr, block);
114 }
115
116 #ifdef _MSC_VER
117 # include <windows.h>
118 # include <imagehlp.h>
119 # define __builtin_return_address(no) ((void *) (stack.AddrReturn.Offset))
120 # define heap_caller() \
121 STACKFRAME stack; \
122 StackWalk (IMAGE_FILE_MACHINE_I386, GetCurrentProcess (), \
123 GetCurrentThread (), &stack, NULL, NULL, NULL, NULL, NULL)
124 #else
125 # ifndef __GNUC__
126 # define __builtin_return_address(no) 0
127 # endif
128 # define heap_caller()
129 #endif
130
131 #else /* !DEBUG_MEMORY_LEAKS */
132 # define heap_caller()
133 #endif /* !DEBUG_MEMORY_LEAKS */
134
135 /* FIXME: Make ‘static inline’ func w/ attribute ‘SVZ_EXITING’. */
136 #define oom(who) do \
137 { \
138 svz_log (SVZ_LOG_FATAL, who ": virtual memory exhausted\n"); \
139 exit (EXIT_FAILURE); \
140 } \
141 while (0)
142
143 /**
144 * Allocate @var{size} bytes of memory and return a pointer to this block.
145 */
146 void *
svz_malloc(size_t size)147 svz_malloc (size_t size)
148 {
149 void *ptr;
150 #if ENABLE_DEBUG
151 size_t *p;
152 #if DEBUG_MEMORY_LEAKS
153 heap_block_t *block;
154 #endif /* DEBUG_MEMORY_LEAKS */
155 #endif /* ENABLE_DEBUG */
156
157 heap_caller ();
158 assert (size);
159
160 #if ENABLE_DEBUG
161 if ((ptr = (void *) svz_malloc_func (size + 2 *
162 sizeof (size_t))) != NULL)
163 {
164 #if ENABLE_HEAP_COUNT
165 /* save size at the beginning of the block */
166 p = (size_t *) ptr;
167 *p = size;
168 p += 2;
169 ptr = (void *) p;
170 #if DEBUG_MEMORY_LEAKS
171 /* put heap pointer into special heap hash */
172 block = svz_malloc_func (sizeof (heap_block_t));
173 block->ptr = ptr;
174 block->size = size;
175 block->caller = __builtin_return_address (0);
176 heap_add (block);
177 #endif /* DEBUG_MEMORY_LEAKS */
178 allocated_bytes += size;
179 #endif /* ENABLE_HEAP_COUNT */
180 allocated_blocks++;
181 return ptr;
182 }
183 #else /* not ENABLE_DEBUG */
184 if ((ptr = (void *) svz_malloc_func (size)) != NULL)
185 {
186 return ptr;
187 }
188 #endif /* not ENABLE_DEBUG */
189 else
190 oom ("malloc");
191 }
192
193 /**
194 * Allocate @var{size} bytes of memory and return a pointer to this block.
195 * The memory is cleared (filled with zeros).
196 */
197 void *
svz_calloc(size_t size)198 svz_calloc (size_t size)
199 {
200 void *ptr = svz_malloc (size);
201 memset (ptr, 0, size);
202 return ptr;
203 }
204
205 /**
206 * Change the size of a block of memory at @var{ptr}, previously
207 * returned by @code{svz_malloc}, to @var{size} bytes. If @var{ptr}
208 * is @code{NULL}, allocate a new block.
209 */
210 void *
svz_realloc(void * ptr,size_t size)211 svz_realloc (void *ptr, size_t size)
212 {
213 #if ENABLE_DEBUG
214 size_t old_size, *p;
215 #endif /* ENABLE_DEBUG */
216 #if DEBUG_MEMORY_LEAKS
217 heap_block_t *block;
218 #endif /* DEBUG_MEMORY_LEAKS */
219
220 heap_caller ();
221 assert (size);
222
223 if (ptr)
224 {
225 #if ENABLE_DEBUG
226 #if ENABLE_HEAP_COUNT
227 #if DEBUG_MEMORY_LEAKS
228 if ((block = svz_hash_delete (heap, (char *) &ptr)) == NULL ||
229 block->ptr != ptr)
230 {
231 fprintf (stdout, "realloc: %p not found in heap (caller: %p)\n",
232 ptr, __builtin_return_address (0));
233 assert (0);
234 }
235 svz_free_func (block);
236 #endif /* DEBUG_MEMORY_LEAKS */
237
238 /* get previous blocksize */
239 p = (size_t *) ptr;
240 p -= 2;
241 old_size = *p;
242 ptr = (void *) p;
243 #endif /* ENABLE_HEAP_COUNT */
244
245 if ((ptr = (void *) svz_realloc_func (ptr, size + 2 *
246 sizeof (size_t))) != NULL)
247 {
248 #if ENABLE_HEAP_COUNT
249 /* save block size */
250 p = (size_t *) ptr;
251 *p = size;
252 p += 2;
253 ptr = (void *) p;
254
255 #if DEBUG_MEMORY_LEAKS
256 block = svz_malloc_func (sizeof (heap_block_t));
257 block->ptr = ptr;
258 block->size = size;
259 block->caller = __builtin_return_address (0);
260 heap_add (block);
261 #endif /* DEBUG_MEMORY_LEAKS */
262
263 allocated_bytes += size - old_size;
264 #endif /* ENABLE_HEAP_COUNT */
265
266 return ptr;
267 }
268 #else /* not ENABLE_DEBUG */
269 if ((ptr = (void *) svz_realloc_func (ptr, size)) != NULL)
270 {
271 return ptr;
272 }
273 #endif /* not ENABLE_DEBUG */
274 else
275 oom ("realloc");
276 }
277 else
278 {
279 ptr = svz_malloc (size);
280 return ptr;
281 }
282 }
283
284 /**
285 * Free a block of memory at @var{ptr}, previously returned by
286 * @code{svz_malloc} or @code{svz_realloc}. If @var{ptr} is
287 * @code{NULL}, do nothing.
288 */
289 void
svz_free(void * ptr)290 svz_free (void *ptr)
291 {
292 #if ENABLE_DEBUG
293 #if ENABLE_HEAP_COUNT
294 size_t size, *p;
295 #if DEBUG_MEMORY_LEAKS
296 heap_block_t *block;
297 #endif /* DEBUG_MEMORY_LEAKS */
298 #endif /* ENABLE_HEAP_COUNT */
299 #endif /* ENABLE_DEBUG */
300
301 heap_caller ();
302
303 if (ptr)
304 {
305 #if ENABLE_DEBUG
306 #if ENABLE_HEAP_COUNT
307 #if DEBUG_MEMORY_LEAKS
308 if ((block = svz_hash_delete (heap, (char *) &ptr)) == NULL ||
309 block->ptr != ptr)
310 {
311 fprintf (stdout, "free: %p not found in heap (caller: %p)\n",
312 ptr, __builtin_return_address (0));
313 assert (0);
314 }
315 svz_free_func (block);
316 #endif /* DEBUG_MEMORY_LEAKS */
317
318 /* get blocksize */
319 p = (size_t *) ptr;
320 p -= 2;
321 size = *p;
322 ptr = (void *) p;
323 assert (size);
324 allocated_bytes -= size;
325 #endif /* ENABLE_HEAP_COUNT */
326
327 allocated_blocks--;
328 #endif /* ENABLE_DEBUG */
329 svz_free_func (ptr);
330 }
331 }
332
333 #if DEBUG_MEMORY_LEAKS
334 static void
heap_internal(UNUSED void * k,void * v,UNUSED void * closure)335 heap_internal (UNUSED void *k, void *v, UNUSED void *closure)
336 {
337 heap_block_t *block = v;
338 size_t *p = (size_t *) block->ptr;
339
340 p -= 2;
341 printf ("heap: caller = %p, ptr = %p, size = %zu\n",
342 block->caller, block->ptr, block->size);
343 svz_hexdump (stdout, "unreleased heap",
344 (int) block->ptr, block->ptr, *p, 256);
345 svz_free_func (block);
346 }
347
348 /*
349 * Print a list of non-released memory blocks. This is for debugging only
350 * and should never occur in final software releases. The function goes
351 * through the heap hash and states each blocks address, size and caller.
352 */
353 void
svz_heap(void)354 svz_heap (void)
355 {
356 if (svz_hash_size (heap))
357 {
358 svz_hash_foreach (heap_internal, heap, NULL);
359 }
360 else
361 {
362 fprintf (stdout, "heap: no unreleased heap blocks\n");
363 }
364 svz_hash_destroy (heap);
365 heap = NULL;
366 }
367 #endif /* DEBUG_MEMORY_LEAKS */
368
369 /**
370 * Duplicate the given string @var{src} if it is not @code{NULL} and has
371 * non-zero length. Return the new string.
372 */
373 char *
svz_strdup(const char * src)374 svz_strdup (const char *src)
375 {
376 char *dst;
377 int len;
378
379 if (src == NULL || (len = strlen (src)) == 0)
380 return NULL;
381
382 dst = svz_malloc (len + 1);
383 memcpy (dst, src, len + 1);
384 return dst;
385 }
386
387 /*
388 * Resize the memory block pointed to by @var{ptr} to @var{size} bytes.
389 * This routine also allocates memory permanently.
390 */
391 void *
svz_prealloc(void * ptr,size_t size)392 svz_prealloc (void *ptr, size_t size)
393 {
394 void *dst = svz_realloc_func (ptr, size);
395 if (dst == NULL)
396 oom ("realloc");
397 return dst;
398 }
399
400 /*
401 * Duplicate the given character string @var{src} permanently.
402 */
403 char *
svz_pstrdup(const char * src)404 svz_pstrdup (const char *src)
405 {
406 char *dst;
407 size_t count;
408
409 assert (src);
410 count = 1 + strlen (src);
411 if (! (dst = svz_malloc_func (count)))
412 oom ("malloc");
413 memcpy (dst, src, count);
414
415 return dst;
416 }
417
418 /**
419 * Write values to @code{to[0]} and @code{to[1]} representing the
420 * number of currently allocated bytes and blocks, respectively.
421 * If Serveez was not configured with @samp{--enable-debug},
422 * the values are always 0.
423 */
424 void
svz_get_curalloc(size_t * to)425 svz_get_curalloc (size_t *to)
426 {
427 #ifndef ENABLE_DEBUG
428 to[0] = to[1] = 0;
429 #else
430 to[0] = allocated_bytes;
431 to[1] = allocated_blocks;
432 #endif
433 }
434