simplify calculation
[asterisk-bristuff.git] / main / astmm.c
blobf5ce26f0acc109a09330998e8bc24f3ab1bb5c55
1 /*
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.
19 /*! \file
21 * \brief Memory Management
23 * \author Mark Spencer <markster@digium.com>
26 #ifdef __AST_DEBUG_MALLOC
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <stdio.h>
33 #include <string.h>
34 #include <stddef.h>
35 #include <time.h>
37 #include "asterisk/cli.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/options.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/strings.h"
42 #include "asterisk/unaligned.h"
44 #define SOME_PRIME 563
46 enum func_type {
47 FUNC_CALLOC = 1,
48 FUNC_MALLOC,
49 FUNC_REALLOC,
50 FUNC_STRDUP,
51 FUNC_STRNDUP,
52 FUNC_VASPRINTF,
53 FUNC_ASPRINTF
56 /* Undefine all our macros */
57 #undef malloc
58 #undef calloc
59 #undef realloc
60 #undef strdup
61 #undef strndup
62 #undef free
63 #undef vasprintf
64 #undef asprintf
66 #define FENCE_MAGIC 0xdeadbeef
68 static FILE *mmlog;
70 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
71 must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
72 the structure in memory (and thus immediately before the allocated region the fence is
73 supposed to be used to monitor). In other words, we cannot allow the compiler to insert
74 any padding between this structure and anything following it, so add up the sizes of all the
75 fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
76 is padding the structure and either the fields need to be rearranged to eliminate internal
77 padding, or a dummy field will need to be inserted before the 'fence' field to push it to
78 the end of the actual space it will consume. Note that this must be checked for both 32-bit
79 and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
82 static struct ast_region {
83 struct ast_region *next;
84 size_t len;
85 char file[40];
86 char func[40];
87 unsigned int lineno;
88 enum func_type which;
89 unsigned int cache; /* region was allocated as part of a cache pool */
90 unsigned int fence;
91 unsigned char data[0];
92 } *regions[SOME_PRIME];
94 #define HASH(a) \
95 (((unsigned long)(a)) % SOME_PRIME)
97 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
99 #define astmm_log(...) \
100 do { \
101 fprintf(stderr, __VA_ARGS__); \
102 if (mmlog) { \
103 fprintf(mmlog, __VA_ARGS__); \
104 fflush(mmlog); \
106 } while (0)
108 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)
110 struct ast_region *reg;
111 void *ptr = NULL;
112 unsigned int *fence;
113 int hash;
115 if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
116 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
117 "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;
123 reg->len = size;
124 reg->which = which;
125 reg->cache = cache;
126 ptr = reg->data;
127 hash = HASH(ptr);
128 reg->fence = FENCE_MAGIC;
129 fence = (ptr + reg->len);
130 put_unaligned_uint32(fence, FENCE_MAGIC);
132 ast_mutex_lock(&reglock);
133 reg->next = regions[hash];
134 regions[hash] = reg;
135 ast_mutex_unlock(&reglock);
137 return ptr;
140 static inline size_t __ast_sizeof_region(void *ptr)
142 int hash = HASH(ptr);
143 struct ast_region *reg;
144 size_t len = 0;
146 ast_mutex_lock(&reglock);
147 for (reg = regions[hash]; reg; reg = reg->next) {
148 if (reg->data == ptr) {
149 len = reg->len;
150 break;
153 ast_mutex_unlock(&reglock);
155 return len;
158 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
160 int hash = HASH(ptr);
161 struct ast_region *reg, *prev = NULL;
162 unsigned int *fence;
164 ast_mutex_lock(&reglock);
165 for (reg = regions[hash]; reg; reg = reg->next) {
166 if (reg->data == ptr) {
167 if (prev)
168 prev->next = reg->next;
169 else
170 regions[hash] = reg->next;
171 break;
173 prev = reg;
175 ast_mutex_unlock(&reglock);
177 if (reg) {
178 fence = (unsigned int *)(reg->data + reg->len);
179 if (reg->fence != FENCE_MAGIC) {
180 astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
181 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
183 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
184 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
185 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
187 free(reg);
188 } else {
189 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
190 ptr, func, file, lineno);
194 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
196 void *ptr;
198 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
199 memset(ptr, 0, size * nmemb);
201 return ptr;
204 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
206 void *ptr;
208 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
209 memset(ptr, 0, size * nmemb);
211 return ptr;
214 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
216 return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
219 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
221 __ast_free_region(ptr, file, lineno, func);
224 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
226 void *tmp;
227 size_t len = 0;
229 if (ptr && !(len = __ast_sizeof_region(ptr))) {
230 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
231 "line %d\n", ptr, func, file, lineno);
232 return NULL;
235 if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
236 return NULL;
238 if (len > size)
239 len = size;
240 if (ptr) {
241 memcpy(tmp, ptr, len);
242 __ast_free_region(ptr, file, lineno, func);
245 return tmp;
248 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
250 size_t len;
251 void *ptr;
253 if (!s)
254 return NULL;
256 len = strlen(s) + 1;
257 if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
258 strcpy(ptr, s);
260 return ptr;
263 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
265 size_t len;
266 void *ptr;
268 if (!s)
269 return NULL;
271 len = strlen(s) + 1;
272 if (len > n)
273 len = n;
274 if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
275 strcpy(ptr, s);
277 return ptr;
280 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
282 int size;
283 va_list ap, ap2;
284 char s;
286 *strp = NULL;
287 va_start(ap, fmt);
288 va_copy(ap2, ap);
289 size = vsnprintf(&s, 1, fmt, ap2);
290 va_end(ap2);
291 if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
292 va_end(ap);
293 return -1;
295 vsnprintf(*strp, size + 1, fmt, ap);
296 va_end(ap);
298 return size;
301 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
303 int size;
304 va_list ap2;
305 char s;
307 *strp = NULL;
308 va_copy(ap2, ap);
309 size = vsnprintf(&s, 1, fmt, ap2);
310 va_end(ap2);
311 if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
312 va_end(ap);
313 return -1;
315 vsnprintf(*strp, size + 1, fmt, ap);
317 return size;
320 static int handle_show_memory(int fd, int argc, char *argv[])
322 char *fn = NULL;
323 struct ast_region *reg;
324 unsigned int x;
325 unsigned int len = 0;
326 unsigned int cache_len = 0;
327 unsigned int count = 0;
328 unsigned int *fence;
330 if (argc > 3)
331 fn = argv[3];
333 ast_mutex_lock(&reglock);
334 for (x = 0; x < SOME_PRIME; x++) {
335 for (reg = regions[x]; reg; reg = reg->next) {
336 if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
337 fence = (unsigned int *)(reg->data + reg->len);
338 if (reg->fence != FENCE_MAGIC) {
339 astmm_log("WARNING: Low fence violation at %p, "
340 "in %s of %s, line %d\n", reg->data,
341 reg->func, reg->file, reg->lineno);
343 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
344 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
345 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
348 if (!fn || !strcasecmp(fn, reg->file)) {
349 ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
350 (int) reg->len, reg->cache ? " (cache)" : "",
351 reg->func, reg->lineno, reg->file);
352 len += reg->len;
353 if (reg->cache)
354 cache_len += reg->len;
355 count++;
359 ast_mutex_unlock(&reglock);
361 if (cache_len)
362 ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
363 else
364 ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
366 return RESULT_SUCCESS;
369 static int handle_show_memory_summary(int fd, int argc, char *argv[])
371 char *fn = NULL;
372 int x;
373 struct ast_region *reg;
374 unsigned int len = 0;
375 unsigned int cache_len = 0;
376 int count = 0;
377 struct file_summary {
378 char fn[80];
379 int len;
380 int cache_len;
381 int count;
382 struct file_summary *next;
383 } *list = NULL, *cur;
385 if (argc > 3)
386 fn = argv[3];
388 ast_mutex_lock(&reglock);
389 for (x = 0; x < SOME_PRIME; x++) {
390 for (reg = regions[x]; reg; reg = reg->next) {
391 if (fn && strcasecmp(fn, reg->file))
392 continue;
394 for (cur = list; cur; cur = cur->next) {
395 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
396 break;
398 if (!cur) {
399 cur = alloca(sizeof(*cur));
400 memset(cur, 0, sizeof(*cur));
401 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
402 cur->next = list;
403 list = cur;
406 cur->len += reg->len;
407 if (reg->cache)
408 cur->cache_len += reg->len;
409 cur->count++;
412 ast_mutex_unlock(&reglock);
414 /* Dump the whole list */
415 for (cur = list; cur; cur = cur->next) {
416 len += cur->len;
417 cache_len += cur->cache_len;
418 count += cur->count;
419 if (cur->cache_len) {
420 if (fn) {
421 ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
422 cur->len, cur->cache_len, cur->count, cur->fn, fn);
423 } else {
424 ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
425 cur->len, cur->cache_len, cur->count, cur->fn);
427 } else {
428 if (fn) {
429 ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
430 cur->len, cur->count, cur->fn, fn);
431 } else {
432 ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n",
433 cur->len, cur->count, cur->fn);
438 if (cache_len)
439 ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
440 else
441 ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
443 return RESULT_SUCCESS;
446 static char show_memory_help[] =
447 "Usage: memory show allocations [<file>]\n"
448 " Dumps a list of all segments of allocated memory, optionally\n"
449 "limited to those from a specific file\n";
451 static char show_memory_summary_help[] =
452 "Usage: memory show summary [<file>]\n"
453 " Summarizes heap memory allocations by file, or optionally\n"
454 "by function, if a file is specified\n";
456 static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
457 { "show", "memory", "allocations", NULL },
458 handle_show_memory, NULL,
459 NULL };
461 static struct ast_cli_entry cli_show_memory_summary_deprecated = {
462 { "show", "memory", "summary", NULL },
463 handle_show_memory_summary, NULL,
464 NULL };
466 static struct ast_cli_entry cli_memory[] = {
467 { { "memory", "show", "allocations", NULL },
468 handle_show_memory, "Display outstanding memory allocations",
469 show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
471 { { "memory", "show", "summary", NULL },
472 handle_show_memory_summary, "Summarize outstanding memory allocations",
473 show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
476 void __ast_mm_init(void)
478 char filename[PATH_MAX];
479 size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
481 if (pad) {
482 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);
485 ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
487 snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
489 if (option_verbose)
490 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
492 if ((mmlog = fopen(filename, "a+"))) {
493 fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
494 fflush(mmlog);
498 #endif