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