2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Memory Management
23 * \author Mark Spencer <markster@digium.com>
28 #ifdef __AST_DEBUG_MALLOC
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
36 #include "asterisk/cli.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/strings.h"
39 #include "asterisk/unaligned.h"
41 #define SOME_PRIME 563
53 /* Undefine all our macros */
63 #define FENCE_MAGIC 0xdeadbeef
67 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
68 must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
69 the structure in memory (and thus immediately before the allocated region the fence is
70 supposed to be used to monitor). In other words, we cannot allow the compiler to insert
71 any padding between this structure and anything following it, so add up the sizes of all the
72 fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
73 is padding the structure and either the fields need to be rearranged to eliminate internal
74 padding, or a dummy field will need to be inserted before the 'fence' field to push it to
75 the end of the actual space it will consume. Note that this must be checked for both 32-bit
76 and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
79 static struct ast_region {
80 struct ast_region *next;
86 unsigned int cache; /* region was allocated as part of a cache pool */
88 unsigned char data[0];
89 } *regions[SOME_PRIME];
92 (((unsigned long)(a)) % SOME_PRIME)
94 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
95 * code allocates memory */
96 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
98 #define astmm_log(...) \
100 fprintf(stderr, __VA_ARGS__); \
102 fprintf(mmlog, __VA_ARGS__); \
107 static inline void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
109 struct ast_region *reg;
114 if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
115 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
116 "at line %d of %s\n", (int) size, func, lineno, file);
120 ast_copy_string(reg->file, file, sizeof(reg->file));
121 ast_copy_string(reg->func, func, sizeof(reg->func));
122 reg->lineno = lineno;
128 reg->fence = FENCE_MAGIC;
129 fence = (ptr + reg->len);
130 put_unaligned_uint32(fence, FENCE_MAGIC);
132 ast_mutex_lock(®lock);
133 reg->next = regions[hash];
135 ast_mutex_unlock(®lock);
140 static inline size_t __ast_sizeof_region(void *ptr)
142 int hash = HASH(ptr);
143 struct ast_region *reg;
146 ast_mutex_lock(®lock);
147 for (reg = regions[hash]; reg; reg = reg->next) {
148 if (reg->data == ptr) {
153 ast_mutex_unlock(®lock);
158 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
161 struct ast_region *reg, *prev = NULL;
169 ast_mutex_lock(®lock);
170 for (reg = regions[hash]; reg; reg = reg->next) {
171 if (reg->data == ptr) {
173 prev->next = reg->next;
175 regions[hash] = reg->next;
180 ast_mutex_unlock(®lock);
183 fence = (unsigned int *)(reg->data + reg->len);
184 if (reg->fence != FENCE_MAGIC) {
185 astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
186 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
188 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
189 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
190 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
194 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
195 ptr, func, file, lineno);
199 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
203 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
204 memset(ptr, 0, size * nmemb);
209 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
213 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
214 memset(ptr, 0, size * nmemb);
219 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
221 return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
224 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
226 __ast_free_region(ptr, file, lineno, func);
229 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
234 if (ptr && !(len = __ast_sizeof_region(ptr))) {
235 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
236 "line %d\n", ptr, func, file, lineno);
240 if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
246 memcpy(tmp, ptr, len);
247 __ast_free_region(ptr, file, lineno, func);
253 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
262 if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
268 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
279 if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
285 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
294 size = vsnprintf(&s, 1, fmt, ap2);
296 if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
300 vsnprintf(*strp, size + 1, fmt, ap);
306 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
314 size = vsnprintf(&s, 1, fmt, ap2);
316 if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
320 vsnprintf(*strp, size + 1, fmt, ap);
325 static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
327 const char *fn = NULL;
328 struct ast_region *reg;
330 unsigned int len = 0;
331 unsigned int cache_len = 0;
332 unsigned int count = 0;
337 e->command = "memory show allocations";
339 "Usage: memory show allocations [<file>]\n"
340 " Dumps a list of all segments of allocated memory, optionally\n"
341 " limited to those from a specific file\n";
351 ast_mutex_lock(®lock);
352 for (x = 0; x < SOME_PRIME; x++) {
353 for (reg = regions[x]; reg; reg = reg->next) {
354 if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
355 fence = (unsigned int *)(reg->data + reg->len);
356 if (reg->fence != FENCE_MAGIC) {
357 astmm_log("WARNING: Low fence violation at %p, "
358 "in %s of %s, line %d\n", reg->data,
359 reg->func, reg->file, reg->lineno);
361 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
362 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
363 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
366 if (!fn || !strcasecmp(fn, reg->file)) {
367 ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
368 (int) reg->len, reg->cache ? " (cache)" : "",
369 reg->func, reg->lineno, reg->file);
372 cache_len += reg->len;
377 ast_mutex_unlock(®lock);
380 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
382 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
387 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
389 const char *fn = NULL;
391 struct ast_region *reg;
392 unsigned int len = 0;
393 unsigned int cache_len = 0;
395 struct file_summary {
400 struct file_summary *next;
401 } *list = NULL, *cur;
405 e->command = "memory show summary";
407 "Usage: memory show summary [<file>]\n"
408 " Summarizes heap memory allocations by file, or optionally\n"
409 "by function, if a file is specified\n";
418 ast_mutex_lock(®lock);
419 for (x = 0; x < SOME_PRIME; x++) {
420 for (reg = regions[x]; reg; reg = reg->next) {
421 if (fn && strcasecmp(fn, reg->file))
424 for (cur = list; cur; cur = cur->next) {
425 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
429 cur = alloca(sizeof(*cur));
430 memset(cur, 0, sizeof(*cur));
431 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
436 cur->len += reg->len;
438 cur->cache_len += reg->len;
442 ast_mutex_unlock(®lock);
444 /* Dump the whole list */
445 for (cur = list; cur; cur = cur->next) {
447 cache_len += cur->cache_len;
449 if (cur->cache_len) {
451 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
452 cur->len, cur->cache_len, cur->count, cur->fn, fn);
454 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
455 cur->len, cur->cache_len, cur->count, cur->fn);
459 ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
460 cur->len, cur->count, cur->fn, fn);
462 ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n",
463 cur->len, cur->count, cur->fn);
469 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
471 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
476 static struct ast_cli_entry cli_memory[] = {
477 AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
478 AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
481 void __ast_mm_init(void)
483 char filename[PATH_MAX];
484 size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
487 ast_log(LOG_ERROR, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad);
490 ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
492 snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
494 ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
496 if ((mmlog = fopen(filename, "a+"))) {
497 fprintf(mmlog, "%ld - New session\n", (long)time(NULL));