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