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