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