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
00031 #include "asterisk.h"
00032
00033 #if defined(__AST_DEBUG_MALLOC)
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 398703 $")
00036
00037 #include "asterisk/paths.h"
00038 #include <stddef.h>
00039 #include <time.h>
00040
00041 #include "asterisk/cli.h"
00042 #include "asterisk/lock.h"
00043 #include "asterisk/strings.h"
00044 #include "asterisk/unaligned.h"
00045
00046
00047
00048
00049
00050
00051 #define SOME_PRIME 1567
00052
00053 enum func_type {
00054 FUNC_CALLOC = 1,
00055 FUNC_MALLOC,
00056 FUNC_REALLOC,
00057 FUNC_STRDUP,
00058 FUNC_STRNDUP,
00059 FUNC_VASPRINTF,
00060 FUNC_ASPRINTF
00061 };
00062
00063
00064 #undef malloc
00065 #undef calloc
00066 #undef realloc
00067 #undef strdup
00068 #undef strndup
00069 #undef free
00070 #undef vasprintf
00071 #undef asprintf
00072
00073 #define FENCE_MAGIC 0xfeedbabe
00074 #define FREED_MAGIC 0xdeaddead
00075 #define MALLOC_FILLER 0x55
00076
00077 static FILE *mmlog;
00078
00079 struct ast_region {
00080 AST_LIST_ENTRY(ast_region) node;
00081 size_t len;
00082 unsigned int cache;
00083 unsigned int lineno;
00084 enum func_type which;
00085 char file[64];
00086 char func[40];
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 unsigned int fence;
00099
00100
00101
00102
00103
00104
00105 unsigned char data[0] __attribute__((aligned));
00106 };
00107
00108
00109 static struct ast_region *regions[SOME_PRIME];
00110
00111
00112 #define FREED_MAX_COUNT 1500
00113
00114
00115 #define MINNOWS_MAX_SIZE 50
00116
00117 struct ast_freed_regions {
00118
00119 struct ast_region *regions[FREED_MAX_COUNT];
00120
00121 int index;
00122 };
00123
00124
00125 static struct ast_freed_regions whales;
00126
00127 static struct ast_freed_regions minnows;
00128
00129 enum summary_opts {
00130
00131 SUMMARY_OFF,
00132
00133 SUMMARY_BY_LINE = (1 << 0),
00134
00135 SUMMARY_BY_FUNC = (1 << 1),
00136
00137 SUMMARY_BY_FILE = (1 << 2),
00138 };
00139
00140
00141 static enum summary_opts atexit_summary;
00142
00143 static int atexit_list;
00144
00145 #define HASH(a) (((unsigned long)(a)) % ARRAY_LEN(regions))
00146
00147
00148
00149 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
00150
00151 #define astmm_log(...) \
00152 do { \
00153 fprintf(stderr, __VA_ARGS__); \
00154 if (mmlog) { \
00155 fprintf(mmlog, __VA_ARGS__); \
00156 fflush(mmlog); \
00157 } \
00158 } while (0)
00159
00160 void *ast_std_malloc(size_t size)
00161 {
00162 return malloc(size);
00163 }
00164
00165 void *ast_std_calloc(size_t nmemb, size_t size)
00166 {
00167 return calloc(nmemb, size);
00168 }
00169
00170 void *ast_std_realloc(void *ptr, size_t size)
00171 {
00172 return realloc(ptr, size);
00173 }
00174
00175 void ast_std_free(void *ptr)
00176 {
00177 free(ptr);
00178 }
00179
00180 void ast_free_ptr(void *ptr)
00181 {
00182 ast_free(ptr);
00183 }
00184
00185
00186
00187
00188
00189
00190
00191
00192 static void my_do_crash(void)
00193 {
00194
00195
00196
00197
00198
00199 usleep(1);
00200 ast_do_crash();
00201 }
00202
00203 static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
00204 {
00205 struct ast_region *reg;
00206 unsigned int *fence;
00207 int hash;
00208
00209 if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
00210 astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
00211 (int) size, file, func, lineno);
00212 return NULL;
00213 }
00214
00215 reg->len = size;
00216 reg->cache = cache;
00217 reg->lineno = lineno;
00218 reg->which = which;
00219 ast_copy_string(reg->file, file, sizeof(reg->file));
00220 ast_copy_string(reg->func, func, sizeof(reg->func));
00221
00222
00223
00224
00225
00226
00227
00228
00229 fence = (unsigned int *) (reg->data - sizeof(*fence));
00230 *fence = FENCE_MAGIC;
00231
00232
00233 fence = (unsigned int *) (reg->data + reg->len);
00234 put_unaligned_uint32(fence, FENCE_MAGIC);
00235
00236 hash = HASH(reg->data);
00237 ast_mutex_lock(®lock);
00238 AST_LIST_NEXT(reg, node) = regions[hash];
00239 regions[hash] = reg;
00240 ast_mutex_unlock(®lock);
00241
00242 return reg->data;
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 static void region_data_wipe(struct ast_region *reg)
00254 {
00255 void *end;
00256 unsigned int *pos;
00257
00258
00259
00260
00261
00262 end = reg->data + reg->len;
00263 for (pos = ®->fence; (void *) pos <= end; ++pos) {
00264 *pos = FREED_MAGIC;
00265 }
00266 }
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 static void region_data_check(struct ast_region *reg)
00277 {
00278 void *end;
00279 unsigned int *pos;
00280
00281
00282
00283
00284
00285 end = reg->data + reg->len;
00286 for (pos = ®->fence; (void *) pos <= end; ++pos) {
00287 if (*pos != FREED_MAGIC) {
00288 astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
00289 reg->data, reg->file, reg->func, reg->lineno);
00290 my_do_crash();
00291 break;
00292 }
00293 }
00294 }
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 static void freed_regions_flush(struct ast_freed_regions *freed)
00305 {
00306 int idx;
00307 struct ast_region *old;
00308
00309 ast_mutex_lock(®lock);
00310 for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
00311 old = freed->regions[idx];
00312 freed->regions[idx] = NULL;
00313 if (old) {
00314 region_data_check(old);
00315 free(old);
00316 }
00317 }
00318 freed->index = 0;
00319 ast_mutex_unlock(®lock);
00320 }
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331 static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
00332 {
00333 struct ast_region *old;
00334
00335 region_data_wipe(reg);
00336
00337 ast_mutex_lock(®lock);
00338 old = freed->regions[freed->index];
00339 freed->regions[freed->index] = reg;
00340
00341 ++freed->index;
00342 if (ARRAY_LEN(freed->regions) <= freed->index) {
00343 freed->index = 0;
00344 }
00345 ast_mutex_unlock(®lock);
00346
00347 if (old) {
00348 region_data_check(old);
00349 free(old);
00350 }
00351 }
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362 static struct ast_region *region_remove(void *ptr)
00363 {
00364 int hash;
00365 struct ast_region *reg;
00366 struct ast_region *prev = NULL;
00367
00368 hash = HASH(ptr);
00369
00370 ast_mutex_lock(®lock);
00371 for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
00372 if (reg->data == ptr) {
00373 if (prev) {
00374 AST_LIST_NEXT(prev, node) = AST_LIST_NEXT(reg, node);
00375 } else {
00376 regions[hash] = AST_LIST_NEXT(reg, node);
00377 }
00378 break;
00379 }
00380 prev = reg;
00381 }
00382 ast_mutex_unlock(®lock);
00383
00384 return reg;
00385 }
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 static void region_check_fences(struct ast_region *reg)
00396 {
00397 unsigned int *fence;
00398
00399
00400
00401
00402
00403
00404 fence = (unsigned int *) (reg->data - sizeof(*fence));
00405 if (*fence != FENCE_MAGIC) {
00406 astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
00407 reg->data, reg->file, reg->func, reg->lineno);
00408 my_do_crash();
00409 }
00410 fence = (unsigned int *) (reg->data + reg->len);
00411 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00412 astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
00413 reg->data, reg->file, reg->func, reg->lineno);
00414 my_do_crash();
00415 }
00416 }
00417
00418
00419
00420
00421
00422
00423
00424 static void regions_check_all_fences(void)
00425 {
00426 int idx;
00427 struct ast_region *reg;
00428
00429 ast_mutex_lock(®lock);
00430 for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00431 for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
00432 region_check_fences(reg);
00433 }
00434 }
00435 ast_mutex_unlock(®lock);
00436 }
00437
00438 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
00439 {
00440 struct ast_region *reg;
00441
00442 if (!ptr) {
00443 return;
00444 }
00445
00446 reg = region_remove(ptr);
00447 if (reg) {
00448 region_check_fences(reg);
00449
00450 if (reg->len <= MINNOWS_MAX_SIZE) {
00451 region_free(&minnows, reg);
00452 } else {
00453 region_free(&whales, reg);
00454 }
00455 } else {
00456
00457
00458
00459
00460
00461 astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
00462 ptr, file, func, lineno);
00463 my_do_crash();
00464 }
00465 }
00466
00467 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
00468 {
00469 void *ptr;
00470
00471 ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
00472 if (ptr) {
00473 memset(ptr, 0, size * nmemb);
00474 }
00475
00476 return ptr;
00477 }
00478
00479 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
00480 {
00481 void *ptr;
00482
00483 ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
00484 if (ptr) {
00485 memset(ptr, 0, size * nmemb);
00486 }
00487
00488 return ptr;
00489 }
00490
00491 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
00492 {
00493 void *ptr;
00494
00495 ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
00496 if (ptr) {
00497
00498 memset(ptr, MALLOC_FILLER, size);
00499 }
00500
00501 return ptr;
00502 }
00503
00504 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
00505 {
00506 __ast_free_region(ptr, file, lineno, func);
00507 }
00508
00509
00510
00511
00512 static struct ast_region *region_find(void *ptr)
00513 {
00514 int hash;
00515 struct ast_region *reg;
00516
00517 hash = HASH(ptr);
00518 for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
00519 if (reg->data == ptr) {
00520 break;
00521 }
00522 }
00523
00524 return reg;
00525 }
00526
00527 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
00528 {
00529 size_t len;
00530 struct ast_region *found;
00531 void *new_mem;
00532
00533 if (ptr) {
00534 ast_mutex_lock(®lock);
00535 found = region_find(ptr);
00536 if (!found) {
00537 ast_mutex_unlock(®lock);
00538 astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
00539 ptr, file, func, lineno);
00540 my_do_crash();
00541 return NULL;
00542 }
00543 len = found->len;
00544 ast_mutex_unlock(®lock);
00545 } else {
00546 found = NULL;
00547 len = 0;
00548 }
00549
00550 if (!size) {
00551 __ast_free_region(ptr, file, lineno, func);
00552 return NULL;
00553 }
00554
00555 new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
00556 if (new_mem) {
00557 if (found) {
00558
00559 if (size <= len) {
00560 memcpy(new_mem, ptr, size);
00561 } else {
00562 memcpy(new_mem, ptr, len);
00563
00564 memset(new_mem + len, MALLOC_FILLER, size - len);
00565 }
00566 __ast_free_region(ptr, file, lineno, func);
00567 } else {
00568
00569 memset(new_mem, MALLOC_FILLER, size);
00570 }
00571 }
00572
00573 return new_mem;
00574 }
00575
00576 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
00577 {
00578 size_t len;
00579 void *ptr;
00580
00581 if (!s)
00582 return NULL;
00583
00584 len = strlen(s) + 1;
00585 if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
00586 strcpy(ptr, s);
00587
00588 return ptr;
00589 }
00590
00591 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
00592 {
00593 size_t len;
00594 char *ptr;
00595
00596 if (!s) {
00597 return NULL;
00598 }
00599
00600 len = strnlen(s, n);
00601 if ((ptr = __ast_alloc_region(len + 1, FUNC_STRNDUP, file, lineno, func, 0))) {
00602 memcpy(ptr, s, len);
00603 ptr[len] = '\0';
00604 }
00605
00606 return ptr;
00607 }
00608
00609 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00610 {
00611 int size;
00612 va_list ap, ap2;
00613 char s;
00614
00615 *strp = NULL;
00616 va_start(ap, fmt);
00617 va_copy(ap2, ap);
00618 size = vsnprintf(&s, 1, fmt, ap2);
00619 va_end(ap2);
00620 if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00621 va_end(ap);
00622 return -1;
00623 }
00624 vsnprintf(*strp, size + 1, fmt, ap);
00625 va_end(ap);
00626
00627 return size;
00628 }
00629
00630 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
00631 {
00632 int size;
00633 va_list ap2;
00634 char s;
00635
00636 *strp = NULL;
00637 va_copy(ap2, ap);
00638 size = vsnprintf(&s, 1, fmt, ap2);
00639 va_end(ap2);
00640 if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00641 va_end(ap);
00642 return -1;
00643 }
00644 vsnprintf(*strp, size + 1, fmt, ap);
00645
00646 return size;
00647 }
00648
00649 static char *handle_memory_atexit_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00650 {
00651 switch (cmd) {
00652 case CLI_INIT:
00653 e->command = "memory atexit list";
00654 e->usage =
00655 "Usage: memory atexit list {on|off}\n"
00656 " Enable dumping a list of still allocated memory segments at exit.\n";
00657 return NULL;
00658 case CLI_GENERATE:
00659 if (a->pos == 3) {
00660 const char * const options[] = { "off", "on", NULL };
00661
00662 return ast_cli_complete(a->word, options, a->n);
00663 }
00664 return NULL;
00665 }
00666
00667 if (a->argc != 4) {
00668 return CLI_SHOWUSAGE;
00669 }
00670
00671 if (ast_true(a->argv[3])) {
00672 atexit_list = 1;
00673 } else if (ast_false(a->argv[3])) {
00674 atexit_list = 0;
00675 } else {
00676 return CLI_SHOWUSAGE;
00677 }
00678
00679 ast_cli(a->fd, "The atexit list is: %s\n", atexit_list ? "On" : "Off");
00680
00681 return CLI_SUCCESS;
00682 }
00683
00684 static char *handle_memory_atexit_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00685 {
00686 char buf[80];
00687
00688 switch (cmd) {
00689 case CLI_INIT:
00690 e->command = "memory atexit summary";
00691 e->usage =
00692 "Usage: memory atexit summary {off|byline|byfunc|byfile}\n"
00693 " Summary of still allocated memory segments at exit options.\n"
00694 " off - Disable at exit summary.\n"
00695 " byline - Enable at exit summary by file line number.\n"
00696 " byfunc - Enable at exit summary by function name.\n"
00697 " byfile - Enable at exit summary by file.\n"
00698 "\n"
00699 " Note: byline, byfunc, and byfile are cumulative enables.\n";
00700 return NULL;
00701 case CLI_GENERATE:
00702 if (a->pos == 3) {
00703 const char * const options[] = { "off", "byline", "byfunc", "byfile", NULL };
00704
00705 return ast_cli_complete(a->word, options, a->n);
00706 }
00707 return NULL;
00708 }
00709
00710 if (a->argc != 4) {
00711 return CLI_SHOWUSAGE;
00712 }
00713
00714 if (ast_false(a->argv[3])) {
00715 atexit_summary = SUMMARY_OFF;
00716 } else if (!strcasecmp(a->argv[3], "byline")) {
00717 atexit_summary |= SUMMARY_BY_LINE;
00718 } else if (!strcasecmp(a->argv[3], "byfunc")) {
00719 atexit_summary |= SUMMARY_BY_FUNC;
00720 } else if (!strcasecmp(a->argv[3], "byfile")) {
00721 atexit_summary |= SUMMARY_BY_FILE;
00722 } else {
00723 return CLI_SHOWUSAGE;
00724 }
00725
00726 if (atexit_summary) {
00727 buf[0] = '\0';
00728 if (atexit_summary & SUMMARY_BY_LINE) {
00729 strcat(buf, "byline");
00730 }
00731 if (atexit_summary & SUMMARY_BY_FUNC) {
00732 if (buf[0]) {
00733 strcat(buf, " | ");
00734 }
00735 strcat(buf, "byfunc");
00736 }
00737 if (atexit_summary & SUMMARY_BY_FILE) {
00738 if (buf[0]) {
00739 strcat(buf, " | ");
00740 }
00741 strcat(buf, "byfile");
00742 }
00743 } else {
00744 strcpy(buf, "Off");
00745 }
00746 ast_cli(a->fd, "The atexit summary is: %s\n", buf);
00747
00748 return CLI_SUCCESS;
00749 }
00750
00751 static char *handle_memory_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00752 {
00753 const char *fn = NULL;
00754 struct ast_region *reg;
00755 unsigned int idx;
00756 unsigned int len = 0;
00757 unsigned int cache_len = 0;
00758 unsigned int count = 0;
00759
00760 switch (cmd) {
00761 case CLI_INIT:
00762 e->command = "memory show allocations";
00763 e->usage =
00764 "Usage: memory show allocations [<file>|anomalies]\n"
00765 " Dumps a list of segments of allocated memory.\n"
00766 " Defaults to listing all memory allocations.\n"
00767 " <file> - Restricts output to memory allocated by the file.\n"
00768 " anomalies - Only check for fence violations.\n";
00769 return NULL;
00770 case CLI_GENERATE:
00771 return NULL;
00772 }
00773
00774 if (a->argc == 4) {
00775 fn = a->argv[3];
00776 } else if (a->argc != 3) {
00777 return CLI_SHOWUSAGE;
00778 }
00779
00780
00781 if (fn && (!strcasecmp(fn, "anomalies") || !strcasecmp(fn, "anomolies"))) {
00782 regions_check_all_fences();
00783 ast_cli(a->fd, "Anomaly check complete.\n");
00784 return CLI_SUCCESS;
00785 }
00786
00787 ast_mutex_lock(®lock);
00788 for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00789 for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
00790 if (fn && strcasecmp(fn, reg->file)) {
00791 continue;
00792 }
00793
00794 region_check_fences(reg);
00795
00796 ast_cli(a->fd, "%10u bytes allocated%s by %20s() line %5u of %s\n",
00797 (unsigned int) reg->len, reg->cache ? " (cache)" : "",
00798 reg->func, reg->lineno, reg->file);
00799
00800 len += reg->len;
00801 if (reg->cache) {
00802 cache_len += reg->len;
00803 }
00804 ++count;
00805 }
00806 }
00807 ast_mutex_unlock(®lock);
00808
00809 if (cache_len) {
00810 ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
00811 len, cache_len, count);
00812 } else {
00813 ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
00814 }
00815
00816 return CLI_SUCCESS;
00817 }
00818
00819 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00820 {
00821 #define my_max(a, b) ((a) >= (b) ? (a) : (b))
00822
00823 const char *fn = NULL;
00824 int idx;
00825 int cmp;
00826 struct ast_region *reg;
00827 unsigned int len = 0;
00828 unsigned int cache_len = 0;
00829 unsigned int count = 0;
00830 struct file_summary {
00831 struct file_summary *next;
00832 unsigned int len;
00833 unsigned int cache_len;
00834 unsigned int count;
00835 unsigned int lineno;
00836 char name[my_max(sizeof(reg->file), sizeof(reg->func))];
00837 } *list = NULL, *cur, **prev;
00838
00839 switch (cmd) {
00840 case CLI_INIT:
00841 e->command = "memory show summary";
00842 e->usage =
00843 "Usage: memory show summary [<file>]\n"
00844 " Summarizes heap memory allocations by file, or optionally\n"
00845 " by line, if a file is specified.\n";
00846 return NULL;
00847 case CLI_GENERATE:
00848 return NULL;
00849 }
00850
00851 if (a->argc == 4) {
00852 fn = a->argv[3];
00853 } else if (a->argc != 3) {
00854 return CLI_SHOWUSAGE;
00855 }
00856
00857 ast_mutex_lock(®lock);
00858 for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00859 for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
00860 if (fn) {
00861 if (strcasecmp(fn, reg->file)) {
00862 continue;
00863 }
00864
00865
00866 for (prev = &list; (cur = *prev); prev = &cur->next) {
00867 cmp = strcmp(cur->name, reg->func);
00868 if (cmp < 0) {
00869 continue;
00870 }
00871 if (cmp > 0) {
00872
00873 cur = NULL;
00874 break;
00875 }
00876 cmp = cur->lineno - reg->lineno;
00877 if (cmp < 0) {
00878 continue;
00879 }
00880 if (cmp > 0) {
00881
00882 cur = NULL;
00883 }
00884 break;
00885 }
00886 } else {
00887
00888 for (prev = &list; (cur = *prev); prev = &cur->next) {
00889 cmp = strcmp(cur->name, reg->file);
00890 if (cmp < 0) {
00891 continue;
00892 }
00893 if (cmp > 0) {
00894
00895 cur = NULL;
00896 }
00897 break;
00898 }
00899 }
00900
00901 if (!cur) {
00902 cur = ast_alloca(sizeof(*cur));
00903 memset(cur, 0, sizeof(*cur));
00904 cur->lineno = reg->lineno;
00905 ast_copy_string(cur->name, fn ? reg->func : reg->file, sizeof(cur->name));
00906
00907 cur->next = *prev;
00908 *prev = cur;
00909 }
00910
00911 cur->len += reg->len;
00912 if (reg->cache) {
00913 cur->cache_len += reg->len;
00914 }
00915 ++cur->count;
00916 }
00917 }
00918 ast_mutex_unlock(®lock);
00919
00920
00921 for (cur = list; cur; cur = cur->next) {
00922 len += cur->len;
00923 cache_len += cur->cache_len;
00924 count += cur->count;
00925 if (cur->cache_len) {
00926 if (fn) {
00927 ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations by %20s() line %5u of %s\n",
00928 cur->len, cur->cache_len, cur->count, cur->name, cur->lineno, fn);
00929 } else {
00930 ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations in file %s\n",
00931 cur->len, cur->cache_len, cur->count, cur->name);
00932 }
00933 } else {
00934 if (fn) {
00935 ast_cli(a->fd, "%10u bytes in %10u allocations by %20s() line %5u of %s\n",
00936 cur->len, cur->count, cur->name, cur->lineno, fn);
00937 } else {
00938 ast_cli(a->fd, "%10u bytes in %10u allocations in file %s\n",
00939 cur->len, cur->count, cur->name);
00940 }
00941 }
00942 }
00943
00944 if (cache_len) {
00945 ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
00946 len, cache_len, count);
00947 } else {
00948 ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
00949 }
00950
00951 return CLI_SUCCESS;
00952 }
00953
00954 static struct ast_cli_entry cli_memory[] = {
00955 AST_CLI_DEFINE(handle_memory_atexit_list, "Enable memory allocations not freed at exit list."),
00956 AST_CLI_DEFINE(handle_memory_atexit_summary, "Enable memory allocations not freed at exit summary."),
00957 AST_CLI_DEFINE(handle_memory_show_allocations, "Display outstanding memory allocations"),
00958 AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
00959 };
00960
00961 AST_LIST_HEAD_NOLOCK(region_list, ast_region);
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979 static size_t mm_atexit_hash_list(struct region_list *list)
00980 {
00981 struct ast_region *reg;
00982 size_t total_length;
00983 int idx;
00984
00985 total_length = 0;
00986 for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00987 while ((reg = regions[idx])) {
00988 regions[idx] = AST_LIST_NEXT(reg, node);
00989 AST_LIST_NEXT(reg, node) = NULL;
00990 AST_LIST_INSERT_HEAD(list, reg, node);
00991 ++total_length;
00992 }
00993 }
00994 return total_length;
00995 }
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007 static void mm_atexit_hash_restore(struct region_list *list)
01008 {
01009 struct ast_region *reg;
01010 int hash;
01011
01012 while ((reg = AST_LIST_REMOVE_HEAD(list, node))) {
01013 hash = HASH(reg->data);
01014 AST_LIST_NEXT(reg, node) = regions[hash];
01015 regions[hash] = reg;
01016 }
01017 }
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030 static int mm_atexit_cmp(struct ast_region *left, struct ast_region *right)
01031 {
01032 int cmp;
01033 ptrdiff_t cmp_ptr;
01034 ssize_t cmp_size;
01035
01036
01037 cmp = strcmp(left->file, right->file);
01038 if (cmp) {
01039 return cmp;
01040 }
01041
01042
01043 cmp = left->lineno - right->lineno;
01044 if (cmp) {
01045 return cmp;
01046 }
01047
01048
01049 cmp_size = left->len - right->len;
01050 if (cmp_size) {
01051 if (cmp_size < 0) {
01052 return -1;
01053 }
01054 return 1;
01055 }
01056
01057
01058 cmp_ptr = left->data - right->data;
01059 if (cmp_ptr) {
01060 if (cmp_ptr < 0) {
01061 return -1;
01062 }
01063 return 1;
01064 }
01065
01066 return 0;
01067 }
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079 static void mm_atexit_list_merge(struct region_list *list, struct region_list *sub1, struct region_list *sub2)
01080 {
01081 struct ast_region *reg;
01082
01083 for (;;) {
01084 if (AST_LIST_EMPTY(sub1)) {
01085
01086 AST_LIST_APPEND_LIST(list, sub2, node);
01087 break;
01088 }
01089 if (AST_LIST_EMPTY(sub2)) {
01090
01091 AST_LIST_APPEND_LIST(list, sub1, node);
01092 break;
01093 }
01094
01095 if (mm_atexit_cmp(AST_LIST_FIRST(sub1), AST_LIST_FIRST(sub2)) <= 0) {
01096 reg = AST_LIST_REMOVE_HEAD(sub1, node);
01097 } else {
01098 reg = AST_LIST_REMOVE_HEAD(sub2, node);
01099 }
01100 AST_LIST_INSERT_TAIL(list, reg, node);
01101 }
01102 }
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116 static void mm_atexit_list_split(struct region_list *list, struct region_list sub[], size_t num_lists, size_t size, size_t *remaining)
01117 {
01118 int idx;
01119
01120 for (idx = 0; idx < num_lists; ++idx) {
01121 size_t count;
01122
01123 if (*remaining < size) {
01124
01125 AST_LIST_APPEND_LIST(&sub[idx], list, node);
01126 *remaining = 0;
01127 break;
01128 }
01129
01130
01131 *remaining -= size;
01132 for (count = size; count--;) {
01133 struct ast_region *reg;
01134
01135 reg = AST_LIST_REMOVE_HEAD(list, node);
01136 AST_LIST_INSERT_TAIL(&sub[idx], reg, node);
01137 }
01138 }
01139 }
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150 static void mm_atexit_list_sort(struct region_list *list, size_t length)
01151 {
01152
01153 struct region_list merged = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
01154
01155 struct region_list sub[2] = {
01156 AST_LIST_HEAD_NOLOCK_INIT_VALUE,
01157 AST_LIST_HEAD_NOLOCK_INIT_VALUE
01158 };
01159
01160 size_t size = 1;
01161
01162 size_t remaining;
01163
01164 int passes;
01165
01166 for (;;) {
01167 remaining = length;
01168
01169 passes = 0;
01170 while (!AST_LIST_EMPTY(list)) {
01171 mm_atexit_list_split(list, sub, ARRAY_LEN(sub), size, &remaining);
01172 mm_atexit_list_merge(&merged, &sub[0], &sub[1]);
01173 ++passes;
01174 }
01175 AST_LIST_APPEND_LIST(list, &merged, node);
01176 if (passes <= 1) {
01177
01178 break;
01179 }
01180
01181
01182 size <<= 1;
01183 }
01184 }
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194 static void mm_atexit_regions_list(struct region_list *alloced)
01195 {
01196 struct ast_region *reg;
01197
01198 AST_LIST_TRAVERSE(alloced, reg, node) {
01199 astmm_log("%s %s() line %u: %u bytes%s at %p\n",
01200 reg->file, reg->func, reg->lineno,
01201 (unsigned int) reg->len, reg->cache ? " (cache)" : "", reg->data);
01202 }
01203 }
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213 static void mm_atexit_regions_summary(struct region_list *alloced)
01214 {
01215 struct ast_region *reg;
01216 struct ast_region *next;
01217 struct {
01218 unsigned int count;
01219 unsigned int len;
01220 unsigned int cache_len;
01221 } by_line, by_func, by_file, total;
01222
01223 by_line.count = 0;
01224 by_line.len = 0;
01225 by_line.cache_len = 0;
01226
01227 by_func.count = 0;
01228 by_func.len = 0;
01229 by_func.cache_len = 0;
01230
01231 by_file.count = 0;
01232 by_file.len = 0;
01233 by_file.cache_len = 0;
01234
01235 total.count = 0;
01236 total.len = 0;
01237 total.cache_len = 0;
01238
01239 AST_LIST_TRAVERSE(alloced, reg, node) {
01240 next = AST_LIST_NEXT(reg, node);
01241
01242 ++by_line.count;
01243 by_line.len += reg->len;
01244 if (reg->cache) {
01245 by_line.cache_len += reg->len;
01246 }
01247 if (next && !strcmp(reg->file, next->file) && reg->lineno == next->lineno) {
01248 continue;
01249 }
01250 if (atexit_summary & SUMMARY_BY_LINE) {
01251 if (by_line.cache_len) {
01252 astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s() line %u\n",
01253 by_line.len, by_line.cache_len, by_line.count, reg->file, reg->func, reg->lineno);
01254 } else {
01255 astmm_log("%10u bytes in %5u allocations. %s %s() line %u\n",
01256 by_line.len, by_line.count, reg->file, reg->func, reg->lineno);
01257 }
01258 }
01259
01260 by_func.count += by_line.count;
01261 by_func.len += by_line.len;
01262 by_func.cache_len += by_line.cache_len;
01263 by_line.count = 0;
01264 by_line.len = 0;
01265 by_line.cache_len = 0;
01266 if (next && !strcmp(reg->file, next->file) && !strcmp(reg->func, next->func)) {
01267 continue;
01268 }
01269 if (atexit_summary & SUMMARY_BY_FUNC) {
01270 if (by_func.cache_len) {
01271 astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s()\n",
01272 by_func.len, by_func.cache_len, by_func.count, reg->file, reg->func);
01273 } else {
01274 astmm_log("%10u bytes in %5u allocations. %s %s()\n",
01275 by_func.len, by_func.count, reg->file, reg->func);
01276 }
01277 }
01278
01279 by_file.count += by_func.count;
01280 by_file.len += by_func.len;
01281 by_file.cache_len += by_func.cache_len;
01282 by_func.count = 0;
01283 by_func.len = 0;
01284 by_func.cache_len = 0;
01285 if (next && !strcmp(reg->file, next->file)) {
01286 continue;
01287 }
01288 if (atexit_summary & SUMMARY_BY_FILE) {
01289 if (by_file.cache_len) {
01290 astmm_log("%10u bytes (%u in caches) in %u allocations. %s\n",
01291 by_file.len, by_file.cache_len, by_file.count, reg->file);
01292 } else {
01293 astmm_log("%10u bytes in %5u allocations. %s\n",
01294 by_file.len, by_file.count, reg->file);
01295 }
01296 }
01297
01298 total.count += by_file.count;
01299 total.len += by_file.len;
01300 total.cache_len += by_file.cache_len;
01301 by_file.count = 0;
01302 by_file.len = 0;
01303 by_file.cache_len = 0;
01304 }
01305
01306 if (total.cache_len) {
01307 astmm_log("%u bytes (%u in caches) in %u allocations.\n",
01308 total.len, total.cache_len, total.count);
01309 } else {
01310 astmm_log("%u bytes in %u allocations.\n", total.len, total.count);
01311 }
01312 }
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322 static void mm_atexit_dump(void)
01323 {
01324 struct region_list alloced_atexit = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
01325 size_t length;
01326
01327 length = mm_atexit_hash_list(&alloced_atexit);
01328 if (!length) {
01329
01330 astmm_log("Exiting with all memory freed.\n");
01331 return;
01332 }
01333
01334 mm_atexit_list_sort(&alloced_atexit, length);
01335
01336 astmm_log("Exiting with the following memory not freed:\n");
01337 if (atexit_list) {
01338 mm_atexit_regions_list(&alloced_atexit);
01339 }
01340 if (atexit_summary) {
01341 mm_atexit_regions_summary(&alloced_atexit);
01342 }
01343
01344
01345
01346
01347
01348
01349
01350 mm_atexit_hash_restore(&alloced_atexit);
01351 }
01352
01353
01354
01355
01356
01357 static void mm_atexit_final(void)
01358 {
01359 FILE *log;
01360
01361
01362 if (atexit_list || atexit_summary) {
01363 fprintf(stderr, "Waiting 10 seconds to let other threads die.\n");
01364 sleep(10);
01365 }
01366
01367 regions_check_all_fences();
01368
01369
01370 freed_regions_flush(&whales);
01371 freed_regions_flush(&minnows);
01372
01373
01374 if (atexit_list || atexit_summary) {
01375 ast_mutex_lock(®lock);
01376 mm_atexit_dump();
01377 ast_mutex_unlock(®lock);
01378 }
01379
01380
01381 log = mmlog;
01382 mmlog = NULL;
01383 if (log) {
01384 fclose(log);
01385 }
01386 }
01387
01388
01389
01390
01391
01392
01393
01394
01395 void __ast_mm_init_phase_1(void)
01396 {
01397 atexit(mm_atexit_final);
01398 }
01399
01400
01401
01402
01403
01404 static void mm_atexit_ast(void)
01405 {
01406 ast_cli_unregister_multiple(cli_memory, ARRAY_LEN(cli_memory));
01407 }
01408
01409
01410
01411
01412
01413
01414 void __ast_mm_init_phase_2(void)
01415 {
01416 char filename[PATH_MAX];
01417
01418 ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
01419
01420 snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
01421
01422 ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
01423
01424 mmlog = fopen(filename, "a+");
01425 if (mmlog) {
01426 fprintf(mmlog, "%ld - New session\n", (long) time(NULL));
01427 fflush(mmlog);
01428 } else {
01429 ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
01430 }
01431
01432 ast_register_atexit(mm_atexit_ast);
01433 }
01434
01435 #endif