00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #ifdef __AST_DEBUG_MALLOC
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00031
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <time.h>
00035
00036 #include "asterisk/cli.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/options.h"
00039 #include "asterisk/lock.h"
00040 #include "asterisk/strings.h"
00041 #include "asterisk/unaligned.h"
00042
00043 #define SOME_PRIME 563
00044
00045 enum func_type {
00046 FUNC_CALLOC = 1,
00047 FUNC_MALLOC,
00048 FUNC_REALLOC,
00049 FUNC_STRDUP,
00050 FUNC_STRNDUP,
00051 FUNC_VASPRINTF,
00052 FUNC_ASPRINTF
00053 };
00054
00055
00056 #undef malloc
00057 #undef calloc
00058 #undef realloc
00059 #undef strdup
00060 #undef strndup
00061 #undef free
00062 #undef vasprintf
00063 #undef asprintf
00064
00065 #define FENCE_MAGIC 0xdeadbeef
00066
00067 static FILE *mmlog;
00068
00069 static struct ast_region {
00070 struct ast_region *next;
00071 char file[40];
00072 char func[40];
00073 unsigned int lineno;
00074 enum func_type which;
00075 unsigned int cache;
00076 size_t len;
00077 unsigned int fence;
00078 unsigned char data[0];
00079 } *regions[SOME_PRIME];
00080
00081 #define HASH(a) \
00082 (((unsigned long)(a)) % SOME_PRIME)
00083
00084 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
00085
00086 #define astmm_log(...) \
00087 do { \
00088 fprintf(stderr, __VA_ARGS__); \
00089 if (mmlog) { \
00090 fprintf(mmlog, __VA_ARGS__); \
00091 fflush(mmlog); \
00092 } \
00093 } while (0)
00094
00095 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)
00096 {
00097 struct ast_region *reg;
00098 void *ptr = NULL;
00099 unsigned int *fence;
00100 int hash;
00101
00102 if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
00103 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
00104 "at line %d of %s\n", (int) size, func, lineno, file);
00105 }
00106
00107 ast_copy_string(reg->file, file, sizeof(reg->file));
00108 ast_copy_string(reg->func, func, sizeof(reg->func));
00109 reg->lineno = lineno;
00110 reg->len = size;
00111 reg->which = which;
00112 reg->cache = cache;
00113 ptr = reg->data;
00114 hash = HASH(ptr);
00115 reg->fence = FENCE_MAGIC;
00116 fence = (ptr + reg->len);
00117 put_unaligned_uint32(fence, FENCE_MAGIC);
00118
00119 ast_mutex_lock(®lock);
00120 reg->next = regions[hash];
00121 regions[hash] = reg;
00122 ast_mutex_unlock(®lock);
00123
00124 return ptr;
00125 }
00126
00127 static inline size_t __ast_sizeof_region(void *ptr)
00128 {
00129 int hash = HASH(ptr);
00130 struct ast_region *reg;
00131 size_t len = 0;
00132
00133 ast_mutex_lock(®lock);
00134 for (reg = regions[hash]; reg; reg = reg->next) {
00135 if (reg->data == ptr) {
00136 len = reg->len;
00137 break;
00138 }
00139 }
00140 ast_mutex_unlock(®lock);
00141
00142 return len;
00143 }
00144
00145 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
00146 {
00147 int hash = HASH(ptr);
00148 struct ast_region *reg, *prev = NULL;
00149 unsigned int *fence;
00150
00151 ast_mutex_lock(®lock);
00152 for (reg = regions[hash]; reg; reg = reg->next) {
00153 if (reg->data == ptr) {
00154 if (prev)
00155 prev->next = reg->next;
00156 else
00157 regions[hash] = reg->next;
00158 break;
00159 }
00160 prev = reg;
00161 }
00162 ast_mutex_unlock(®lock);
00163
00164 if (reg) {
00165 fence = (unsigned int *)(reg->data + reg->len);
00166 if (reg->fence != FENCE_MAGIC) {
00167 astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
00168 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00169 }
00170 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00171 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00172 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00173 }
00174 free(reg);
00175 } else {
00176 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
00177 ptr, func, file, lineno);
00178 }
00179 }
00180
00181 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
00182 {
00183 void *ptr;
00184
00185 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
00186 memset(ptr, 0, size * nmemb);
00187
00188 return ptr;
00189 }
00190
00191 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
00192 {
00193 void *ptr;
00194
00195 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
00196 memset(ptr, 0, size * nmemb);
00197
00198 return ptr;
00199 }
00200
00201 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
00202 {
00203 return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
00204 }
00205
00206 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
00207 {
00208 __ast_free_region(ptr, file, lineno, func);
00209 }
00210
00211 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
00212 {
00213 void *tmp;
00214 size_t len = 0;
00215
00216 if (ptr && !(len = __ast_sizeof_region(ptr))) {
00217 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
00218 "line %d\n", ptr, func, file, lineno);
00219 return NULL;
00220 }
00221
00222 if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
00223 return NULL;
00224
00225 if (len > size)
00226 len = size;
00227 if (ptr) {
00228 memcpy(tmp, ptr, len);
00229 __ast_free_region(ptr, file, lineno, func);
00230 }
00231
00232 return tmp;
00233 }
00234
00235 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
00236 {
00237 size_t len;
00238 void *ptr;
00239
00240 if (!s)
00241 return NULL;
00242
00243 len = strlen(s) + 1;
00244 if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
00245 strcpy(ptr, s);
00246
00247 return ptr;
00248 }
00249
00250 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
00251 {
00252 size_t len;
00253 void *ptr;
00254
00255 if (!s)
00256 return NULL;
00257
00258 len = strlen(s) + 1;
00259 if (len > n)
00260 len = n;
00261 if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
00262 strcpy(ptr, s);
00263
00264 return ptr;
00265 }
00266
00267 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00268 {
00269 int size;
00270 va_list ap, ap2;
00271 char s;
00272
00273 *strp = NULL;
00274 va_start(ap, fmt);
00275 va_copy(ap2, ap);
00276 size = vsnprintf(&s, 1, fmt, ap2);
00277 va_end(ap2);
00278 if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00279 va_end(ap);
00280 return -1;
00281 }
00282 vsnprintf(*strp, size + 1, fmt, ap);
00283 va_end(ap);
00284
00285 return size;
00286 }
00287
00288 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
00289 {
00290 int size;
00291 va_list ap2;
00292 char s;
00293
00294 *strp = NULL;
00295 va_copy(ap2, ap);
00296 size = vsnprintf(&s, 1, fmt, ap2);
00297 va_end(ap2);
00298 if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00299 va_end(ap);
00300 return -1;
00301 }
00302 vsnprintf(*strp, size + 1, fmt, ap);
00303
00304 return size;
00305 }
00306
00307 static int handle_show_memory(int fd, int argc, char *argv[])
00308 {
00309 char *fn = NULL;
00310 struct ast_region *reg;
00311 unsigned int x;
00312 unsigned int len = 0;
00313 unsigned int cache_len = 0;
00314 unsigned int count = 0;
00315 unsigned int *fence;
00316
00317 if (argc > 3)
00318 fn = argv[3];
00319
00320 ast_mutex_lock(®lock);
00321 for (x = 0; x < SOME_PRIME; x++) {
00322 for (reg = regions[x]; reg; reg = reg->next) {
00323 if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
00324 fence = (unsigned int *)(reg->data + reg->len);
00325 if (reg->fence != FENCE_MAGIC) {
00326 astmm_log("WARNING: Low fence violation at %p, "
00327 "in %s of %s, line %d\n", reg->data,
00328 reg->func, reg->file, reg->lineno);
00329 }
00330 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00331 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00332 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00333 }
00334 }
00335 if (!fn || !strcasecmp(fn, reg->file)) {
00336 ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
00337 (int) reg->len, reg->cache ? " (cache)" : "",
00338 reg->func, reg->lineno, reg->file);
00339 len += reg->len;
00340 if (reg->cache)
00341 cache_len += reg->len;
00342 count++;
00343 }
00344 }
00345 }
00346 ast_mutex_unlock(®lock);
00347
00348 if (cache_len)
00349 ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00350 else
00351 ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00352
00353 return RESULT_SUCCESS;
00354 }
00355
00356 static int handle_show_memory_summary(int fd, int argc, char *argv[])
00357 {
00358 char *fn = NULL;
00359 int x;
00360 struct ast_region *reg;
00361 unsigned int len = 0;
00362 unsigned int cache_len = 0;
00363 int count = 0;
00364 struct file_summary {
00365 char fn[80];
00366 int len;
00367 int cache_len;
00368 int count;
00369 struct file_summary *next;
00370 } *list = NULL, *cur;
00371
00372 if (argc > 3)
00373 fn = argv[3];
00374
00375 ast_mutex_lock(®lock);
00376 for (x = 0; x < SOME_PRIME; x++) {
00377 for (reg = regions[x]; reg; reg = reg->next) {
00378 if (fn && strcasecmp(fn, reg->file))
00379 continue;
00380
00381 for (cur = list; cur; cur = cur->next) {
00382 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
00383 break;
00384 }
00385 if (!cur) {
00386 cur = alloca(sizeof(*cur));
00387 memset(cur, 0, sizeof(*cur));
00388 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
00389 cur->next = list;
00390 list = cur;
00391 }
00392
00393 cur->len += reg->len;
00394 if (reg->cache)
00395 cur->cache_len += reg->len;
00396 cur->count++;
00397 }
00398 }
00399 ast_mutex_unlock(®lock);
00400
00401
00402 for (cur = list; cur; cur = cur->next) {
00403 len += cur->len;
00404 cache_len += cur->cache_len;
00405 count += cur->count;
00406 if (cur->cache_len) {
00407 if (fn) {
00408 ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
00409 cur->len, cur->cache_len, cur->count, cur->fn, fn);
00410 } else {
00411 ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
00412 cur->len, cur->cache_len, cur->count, cur->fn);
00413 }
00414 } else {
00415 if (fn) {
00416 ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
00417 cur->len, cur->count, cur->fn, fn);
00418 } else {
00419 ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n",
00420 cur->len, cur->count, cur->fn);
00421 }
00422 }
00423 }
00424
00425 if (cache_len)
00426 ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00427 else
00428 ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00429
00430 return RESULT_SUCCESS;
00431 }
00432
00433 static char show_memory_help[] =
00434 "Usage: memory show allocations [<file>]\n"
00435 " Dumps a list of all segments of allocated memory, optionally\n"
00436 "limited to those from a specific file\n";
00437
00438 static char show_memory_summary_help[] =
00439 "Usage: memory show summary [<file>]\n"
00440 " Summarizes heap memory allocations by file, or optionally\n"
00441 "by function, if a file is specified\n";
00442
00443 static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
00444 { "show", "memory", "allocations", NULL },
00445 handle_show_memory, NULL,
00446 NULL };
00447
00448 static struct ast_cli_entry cli_show_memory_summary_deprecated = {
00449 { "show", "memory", "summary", NULL },
00450 handle_show_memory_summary, NULL,
00451 NULL };
00452
00453 static struct ast_cli_entry cli_memory[] = {
00454 { { "memory", "show", "allocations", NULL },
00455 handle_show_memory, "Display outstanding memory allocations",
00456 show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
00457
00458 { { "memory", "show", "summary", NULL },
00459 handle_show_memory_summary, "Summarize outstanding memory allocations",
00460 show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
00461 };
00462
00463 void __ast_mm_init(void)
00464 {
00465 char filename[PATH_MAX];
00466
00467 ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
00468
00469 snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
00470
00471 if (option_verbose)
00472 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
00473
00474 if ((mmlog = fopen(filename, "a+"))) {
00475 fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
00476 fflush(mmlog);
00477 }
00478 }
00479
00480 #endif