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