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