Newer
Older
Mark Spencer
committed
/*
* Asterisk -- An open source telephony toolkit.
Mark Spencer
committed
*
* Copyright (C) 1999 - 2012, Digium, Inc.
Mark Spencer
committed
*
* Mark Spencer <markster@digium.com>
Mark Spencer
committed
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
Mark Spencer
committed
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief Memory Management
*
* \author Mark Spencer <markster@digium.com>
* \author Richard Mudgett <rmudgett@digium.com>
Mark Spencer
committed
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#define ASTMM_LIBC ASTMM_IGNORE
#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
Kevin P. Fleming
committed
#include <time.h>
Kevin P. Fleming
committed
#include "asterisk/cli.h"
#include "asterisk/lock.h"
Kevin P. Fleming
committed
#include "asterisk/strings.h"
#include "asterisk/unaligned.h"
#include "asterisk/backtrace.h"
Mark Spencer
committed
/*!
* The larger the number the faster memory can be freed.
* However, more memory then is used for the regions[] hash
* table.
*/
#define SOME_PRIME 1567
Mark Spencer
committed
enum func_type {
FUNC_CALLOC = 1,
FUNC_MALLOC,
FUNC_REALLOC,
FUNC_STRDUP,
FUNC_STRNDUP,
FUNC_VASPRINTF,
FUNC_ASPRINTF
};
Mark Spencer
committed
Richard Mudgett
committed
#define FENCE_MAGIC 0xfeedbabe /*!< Allocated memory high/low fence overwrite check. */
#define FREED_MAGIC 0xdeaddead /*!< Freed memory wipe filler. */
#define MALLOC_FILLER 0x55 /*!< Malloced memory filler. Must not be zero. */
AST_LIST_ENTRY(ast_region) node;
struct ast_bt *bt;
unsigned int cache; /* region was allocated as part of a cache pool */
char file[64];
char func[40];
/*!
* \brief Lower guard fence.
*
* \note Must be right before data[].
*
* \note Padding between fence and data[] is irrelevent because
* data[] is used to fill in the lower fence check value and not
* the fence member. The fence member is to ensure that there
* is space reserved for the fence check value.
*/
/*!
* \brief Location of the requested malloc block to return.
*
* \note Must have the same alignment that malloc returns.
* i.e., It is suitably aligned for any kind of varible.
*/
unsigned char data[0] __attribute__((aligned));
};
/*! Hash table of lists of active allocated memory regions. */
static struct ast_region *regions[SOME_PRIME];
/*! Number of freed regions to keep around to delay actually freeing them. */
#define FREED_MAX_COUNT 1500
Mark Spencer
committed
/*! Maximum size of a minnow block */
#define MINNOWS_MAX_SIZE 50
struct ast_freed_regions {
/*! Memory regions that have been freed. */
struct ast_region *regions[FREED_MAX_COUNT];
/*! Next index into freed regions[] to use. */
int index;
};
/*! Large memory blocks that have been freed. */
static struct ast_freed_regions whales;
/*! Small memory blocks that have been freed. */
static struct ast_freed_regions minnows;
enum summary_opts {
/*! No summary at exit. */
SUMMARY_OFF,
/*! Bit set if summary by line at exit. */
SUMMARY_BY_LINE = (1 << 0),
/*! Bit set if summary by function at exit. */
SUMMARY_BY_FUNC = (1 << 1),
/*! Bit set if summary by file at exit. */
SUMMARY_BY_FILE = (1 << 2),
};
/*! Summary options of unfreed regions at exit. */
static enum summary_opts atexit_summary;
/*! Nonzero if the unfreed regions are listed at exit. */
static int atexit_list;
/*! Nonzero if the memory allocation backtrace is enabled. */
static int backtrace_enabled;
#define HASH(a) (((unsigned long)(a)) % ARRAY_LEN(regions))
/*! Tracking this mutex will cause infinite recursion, as the mutex tracking
* code allocates memory */
AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
Mark Spencer
committed
#define astmm_log(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
if (mmlog) { \
fprintf(mmlog, __VA_ARGS__); \
fflush(mmlog); \
} \
} while (0)
void *ast_std_malloc(size_t size)
{
return malloc(size);
}
void *ast_std_calloc(size_t nmemb, size_t size)
{
return calloc(nmemb, size);
}
void *ast_std_realloc(void *ptr, size_t size)
{
return realloc(ptr, size);
}
void ast_std_free(void *ptr)
{
free(ptr);
}
void ast_free_ptr(void *ptr)
{
ast_free(ptr);
}
static void print_backtrace(struct ast_bt *bt)
{
int i = 0;
char **strings;
if (!bt) {
return;
}
if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
astmm_log("Memory allocation backtrace:\n");
for (i = 3; i < bt->num_frames - 2; i++) {
astmm_log("#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]);
}
ast_std_free(strings);
}
}
/*!
* \internal
*
* \note If DO_CRASH is not defined then the function returns.
*
* \return Nothing
*/
static void my_do_crash(void)
{
/*
* Give the logger a chance to get the message out, just in case
* we abort(), or Asterisk crashes due to whatever problem just
* happened.
*/
usleep(1);
ast_do_crash();
}
static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
Mark Spencer
committed
{
struct ast_region *reg;
unsigned int *fence;
Mark Spencer
committed
int hash;
if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
(int) size, file, func, lineno);
return NULL;
Mark Spencer
committed
}
reg->lineno = lineno;
reg->which = which;
reg->bt = backtrace_enabled ? ast_bt_create() : NULL;
ast_copy_string(reg->file, file, sizeof(reg->file));
ast_copy_string(reg->func, func, sizeof(reg->func));
/*
* Init lower fence.
*
* We use the bytes just preceeding reg->data and not reg->fence
* because there is likely to be padding between reg->fence and
* reg->data for reg->data alignment.
*/
fence = (unsigned int *) (reg->data - sizeof(*fence));
*fence = FENCE_MAGIC;
/* Init higher fence. */
fence = (unsigned int *) (reg->data + reg->len);
put_unaligned_uint32(fence, FENCE_MAGIC);
AST_LIST_NEXT(reg, node) = regions[hash];
ast_mutex_unlock(®lock);
Mark Spencer
committed
}
/*!
* \internal
* \brief Wipe the region payload data with a known value.
*
* \param reg Region block to be wiped.
*
* \return Nothing
*/
static void region_data_wipe(struct ast_region *reg)
Mark Spencer
committed
{
void *end;
unsigned int *pos;
/*
* Wipe the lower fence, the payload, and whatever amount of the
* higher fence that falls into alignment with the payload.
*/
end = reg->data + reg->len;
for (pos = ®->fence; (void *) pos <= end; ++pos) {
*pos = FREED_MAGIC;
}
}
/*!
* \internal
* \brief Check the region payload data for memory corruption.
*
* \param reg Region block to be checked.
*
* \return Nothing
*/
static void region_data_check(struct ast_region *reg)
{
void *end;
unsigned int *pos;
/*
* Check the lower fence, the payload, and whatever amount of
* the higher fence that falls into alignment with the payload.
*/
end = reg->data + reg->len;
for (pos = ®->fence; (void *) pos <= end; ++pos) {
if (*pos != FREED_MAGIC) {
astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
reg->data, reg->file, reg->func, reg->lineno);
print_backtrace(reg->bt);
Mark Spencer
committed
break;
}
}
}
/*!
* \internal
* \brief Flush the circular array of freed regions.
*
* \param freed Already freed region blocks storage.
*
* \return Nothing
*/
static void freed_regions_flush(struct ast_freed_regions *freed)
{
int idx;
struct ast_region *old;
ast_mutex_lock(®lock);
for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
old = freed->regions[idx];
freed->regions[idx] = NULL;
if (old) {
region_data_check(old);
free(old);
}
}
freed->index = 0;
ast_mutex_unlock(®lock);
}
/*!
* \internal
* \brief Delay freeing a region block.
*
* \param freed Already freed region blocks storage.
* \param reg Region block to be freed.
*
* \return Nothing
*/
static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
{
struct ast_region *old;
region_data_wipe(reg);
ast_mutex_lock(®lock);
old = freed->regions[freed->index];
freed->regions[freed->index] = reg;
++freed->index;
if (ARRAY_LEN(freed->regions) <= freed->index) {
freed->index = 0;
}
ast_mutex_unlock(®lock);
if (old) {
region_data_check(old);
old->bt = ast_bt_destroy(old->bt);
Mark Spencer
committed
}
/*!
* \internal
* \brief Remove a region from the active regions.
*
* \param ptr Region payload data pointer.
*
* \retval region on success.
* \retval NULL if not found.
*/
static struct ast_region *region_remove(void *ptr)
Mark Spencer
committed
{
struct ast_region *reg;
struct ast_region *prev = NULL;
hash = HASH(ptr);
ast_mutex_lock(®lock);
for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
Mark Spencer
committed
if (reg->data == ptr) {
AST_LIST_NEXT(prev, node) = AST_LIST_NEXT(reg, node);
regions[hash] = AST_LIST_NEXT(reg, node);
Mark Spencer
committed
break;
}
prev = reg;
}
ast_mutex_unlock(®lock);
return reg;
}
/*!
* \internal
* \brief Check the fences of a region.
*
* \param reg Region block to check.
*
* \return Nothing
*/
static void region_check_fences(struct ast_region *reg)
{
unsigned int *fence;
/*
* We use the bytes just preceeding reg->data and not reg->fence
* because there is likely to be padding between reg->fence and
* reg->data for reg->data alignment.
*/
fence = (unsigned int *) (reg->data - sizeof(*fence));
if (*fence != FENCE_MAGIC) {
astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
reg->data, reg->file, reg->func, reg->lineno);
print_backtrace(reg->bt);
my_do_crash();
}
fence = (unsigned int *) (reg->data + reg->len);
if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
reg->data, reg->file, reg->func, reg->lineno);
print_backtrace(reg->bt);
/*!
* \internal
* \brief Check the fences of all regions currently allocated.
*
* \return Nothing
*/
static void regions_check_all_fences(void)
{
int idx;
struct ast_region *reg;
ast_mutex_lock(®lock);
for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
region_check_fences(reg);
}
}
ast_mutex_unlock(®lock);
}
static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
{
struct ast_region *reg;
if (!ptr) {
return;
}
reg = region_remove(ptr);
Mark Spencer
committed
if (reg) {
region_check_fences(reg);
if (reg->len <= MINNOWS_MAX_SIZE) {
region_free(&minnows, reg);
} else {
region_free(&whales, reg);
/*
* This memory region is not registered. It could be because of
* a double free or the memory block was not allocated by the
* malloc debug code.
*/
astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
ptr, file, func, lineno);
my_do_crash();
Mark Spencer
committed
}
void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
Mark Spencer
committed
{
void *ptr;
ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
if (ptr) {
memset(ptr, 0, size * nmemb);
void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
if (ptr) {
Mark Spencer
committed
memset(ptr, 0, size * nmemb);
Mark Spencer
committed
return ptr;
}
void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
Mark Spencer
committed
{
void *ptr;
ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
if (ptr) {
/* Make sure that the malloced memory is not zero. */
memset(ptr, MALLOC_FILLER, size);
}
return ptr;
Mark Spencer
committed
}
void __ast_free(void *ptr, const char *file, int lineno, const char *func)
Mark Spencer
committed
{
__ast_free_region(ptr, file, lineno, func);
}
/*!
* \note reglock must be locked before calling.
*/
static struct ast_region *region_find(void *ptr)
Mark Spencer
committed
{
for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
if (reg->data == ptr) {
break;
}
Mark Spencer
committed
}
return reg;
}
void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
{
size_t len;
struct ast_region *found;
void *new_mem;
ast_mutex_lock(®lock);
found = region_find(ptr);
if (!found) {
ast_mutex_unlock(®lock);
astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
ptr, file, func, lineno);
my_do_crash();
return NULL;
}
len = found->len;
ast_mutex_unlock(®lock);
} else {
found = NULL;
len = 0;
}
if (!size) {
__ast_free_region(ptr, file, lineno, func);
return NULL;
}
new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
if (new_mem) {
if (found) {
/* Copy the old data to the new malloced memory. */
if (size <= len) {
memcpy(new_mem, ptr, size);
} else {
memcpy(new_mem, ptr, len);
/* Make sure that the added memory is not zero. */
memset(new_mem + len, MALLOC_FILLER, size - len);
}
__ast_free_region(ptr, file, lineno, func);
} else {
/* Make sure that the malloced memory is not zero. */
memset(new_mem, MALLOC_FILLER, size);
}
Mark Spencer
committed
}
Mark Spencer
committed
}
char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
Mark Spencer
committed
{
size_t len;
void *ptr;
Mark Spencer
committed
if (!s)
return NULL;
Mark Spencer
committed
len = strlen(s) + 1;
if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
Mark Spencer
committed
strcpy(ptr, s);
Mark Spencer
committed
return ptr;
}
char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
Mark Spencer
committed
{
size_t len;
Mark Spencer
committed
return NULL;
len = strnlen(s, n);
if ((ptr = __ast_alloc_region(len + 1, FUNC_STRNDUP, file, lineno, func, 0))) {
memcpy(ptr, s, len);
ptr[len] = '\0';
}
Mark Spencer
committed
return ptr;
}
int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
{
int size;
va_list ap, ap2;
char s;
*strp = NULL;
va_start(ap, fmt);
va_copy(ap2, ap);
size = vsnprintf(&s, 1, fmt, ap2);
va_end(ap2);
if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
va_end(ap);
return -1;
}
vsnprintf(*strp, size + 1, fmt, ap);
va_end(ap);
return size;
}
int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
int size;
va_list ap2;
char s;
*strp = NULL;
va_copy(ap2, ap);
size = vsnprintf(&s, 1, fmt, ap2);
va_end(ap2);
if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
va_end(ap);
vsnprintf(*strp, size + 1, fmt, ap);
return size;
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
/*!
* \internal
* \brief Count the number of bytes in the specified freed region.
*
* \param freed Already freed region blocks storage.
*
* \note reglock must be locked before calling.
*
* \return Number of bytes in freed region.
*/
static size_t freed_regions_size(struct ast_freed_regions *freed)
{
size_t total_len = 0;
int idx;
struct ast_region *old;
for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
old = freed->regions[idx];
if (old) {
total_len += old->len;
}
}
return total_len;
}
static char *handle_memory_atexit_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "memory atexit list {on|off}";
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
e->usage =
"Usage: memory atexit list {on|off}\n"
" Enable dumping a list of still allocated memory segments at exit.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
if (ast_true(a->argv[3])) {
atexit_list = 1;
} else if (ast_false(a->argv[3])) {
atexit_list = 0;
} else {
return CLI_SHOWUSAGE;
}
ast_cli(a->fd, "The atexit list is: %s\n", atexit_list ? "On" : "Off");
return CLI_SUCCESS;
}
static char *handle_memory_atexit_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
char buf[80];
switch (cmd) {
case CLI_INIT:
e->command = "memory atexit summary {off|byline|byfunc|byfile}";
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
e->usage =
"Usage: memory atexit summary {off|byline|byfunc|byfile}\n"
" Summary of still allocated memory segments at exit options.\n"
" off - Disable at exit summary.\n"
" byline - Enable at exit summary by file line number.\n"
" byfunc - Enable at exit summary by function name.\n"
" byfile - Enable at exit summary by file.\n"
"\n"
" Note: byline, byfunc, and byfile are cumulative enables.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
if (ast_false(a->argv[3])) {
atexit_summary = SUMMARY_OFF;
} else if (!strcasecmp(a->argv[3], "byline")) {
atexit_summary |= SUMMARY_BY_LINE;
} else if (!strcasecmp(a->argv[3], "byfunc")) {
atexit_summary |= SUMMARY_BY_FUNC;
} else if (!strcasecmp(a->argv[3], "byfile")) {
atexit_summary |= SUMMARY_BY_FILE;
} else {
return CLI_SHOWUSAGE;
}
if (atexit_summary) {
buf[0] = '\0';
if (atexit_summary & SUMMARY_BY_LINE) {
strcat(buf, "byline");
}
if (atexit_summary & SUMMARY_BY_FUNC) {
if (buf[0]) {
strcat(buf, " | ");
}
strcat(buf, "byfunc");
}
if (atexit_summary & SUMMARY_BY_FILE) {
if (buf[0]) {
strcat(buf, " | ");
}
strcat(buf, "byfile");
}
} else {
strcpy(buf, "Off");
}
ast_cli(a->fd, "The atexit summary is: %s\n", buf);
return CLI_SUCCESS;
}
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
/*!
* \internal
* \brief Common summary output at the end of the memory show commands.
*
* \param fd CLI output file descriptor.
* \param whales_len Accumulated size of free large allocations.
* \param minnows_len Accumulated size of free small allocations.
* \param total_len Accumulated size of all current allocations.
* \param selected_len Accumulated size of the selected allocations.
* \param cache_len Accumulated size of the allocations that are part of a cache.
* \param count Number of selected allocations.
*
* \return Nothing
*/
static void print_memory_show_common_stats(int fd,
unsigned int whales_len,
unsigned int minnows_len,
unsigned int total_len,
unsigned int selected_len,
unsigned int cache_len,
unsigned int count)
{
if (cache_len) {
ast_cli(fd, "%10u bytes allocated (%u in caches) in %u selected allocations\n\n",
selected_len, cache_len, count);
} else {
ast_cli(fd, "%10u bytes allocated in %u selected allocations\n\n",
selected_len, count);
}
ast_cli(fd, "%10u bytes in all allocations\n", total_len);
ast_cli(fd, "%10u bytes in deferred free large allocations\n", whales_len);
ast_cli(fd, "%10u bytes in deferred free small allocations\n", minnows_len);
ast_cli(fd, "%10u bytes in deferred free allocations\n",
whales_len + minnows_len);
ast_cli(fd, "%10u bytes in all allocations and deferred free allocations\n",
total_len + whales_len + minnows_len);
}
static char *handle_memory_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Spencer
committed
{
Mark Spencer
committed
struct ast_region *reg;
unsigned int idx;
unsigned int whales_len;
unsigned int minnows_len;
unsigned int total_len = 0;
unsigned int selected_len = 0;
switch (cmd) {
case CLI_INIT:
e->command = "memory show allocations";
e->usage =
"Usage: memory show allocations [<file>|anomalies]\n"
" Dumps a list of segments of allocated memory.\n"
" Defaults to listing all memory allocations.\n"
" <file> - Restricts output to memory allocated by the file.\n"
" anomalies - Only check for fence violations.\n";
Russell Bryant
committed
return NULL;
} else if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
/* Look for historical misspelled option as well. */
if (fn && (!strcasecmp(fn, "anomalies") || !strcasecmp(fn, "anomolies"))) {
regions_check_all_fences();
ast_cli(a->fd, "Anomaly check complete.\n");
return CLI_SUCCESS;
}
Mark Spencer
committed
for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
total_len += reg->len;
if (fn && strcasecmp(fn, reg->file)) {
continue;
}
region_check_fences(reg);
ast_cli(a->fd, "%10u bytes allocated%s by %20s() line %5u of %s\n",
(unsigned int) reg->len, reg->cache ? " (cache)" : "",
reg->func, reg->lineno, reg->file);
selected_len += reg->len;
if (reg->cache) {
cache_len += reg->len;
Mark Spencer
committed
}
Mark Spencer
committed
}
}
whales_len = freed_regions_size(&whales);
minnows_len = freed_regions_size(&minnows);
print_memory_show_common_stats(a->fd,
whales_len, minnows_len, total_len,
selected_len, cache_len, count);
Mark Spencer
committed
}
static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Spencer
committed
{
#define my_max(a, b) ((a) >= (b) ? (a) : (b))
Mark Spencer
committed
struct ast_region *reg;
unsigned int whales_len;
unsigned int minnows_len;
unsigned int total_len = 0;
unsigned int selected_len = 0;
struct file_summary {
struct file_summary *next;
unsigned int len;
unsigned int cache_len;
unsigned int count;
unsigned int lineno;
char name[my_max(sizeof(reg->file), sizeof(reg->func))];
} *list = NULL, *cur, **prev;
switch (cmd) {
case CLI_INIT:
e->command = "memory show summary";
e->usage =
"Usage: memory show summary [<file>]\n"
" Summarizes heap memory allocations by file, or optionally\n"
" by line if a file is specified.\n";
Russell Bryant
committed
return NULL;
} else if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
Mark Spencer
committed
ast_mutex_lock(®lock);
for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
total_len += reg->len;
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
if (fn) {
if (strcasecmp(fn, reg->file)) {
continue;
}
/* Sort list by func/lineno. Find existing or place to insert. */
for (prev = &list; (cur = *prev); prev = &cur->next) {
cmp = strcmp(cur->name, reg->func);
if (cmp < 0) {
continue;
}
if (cmp > 0) {
/* Insert before current */
cur = NULL;
break;
}
cmp = cur->lineno - reg->lineno;
if (cmp < 0) {
continue;
}
if (cmp > 0) {
/* Insert before current */
cur = NULL;
}
break;
}
} else {
/* Sort list by filename. Find existing or place to insert. */
for (prev = &list; (cur = *prev); prev = &cur->next) {
cmp = strcmp(cur->name, reg->file);
if (cmp < 0) {
continue;
}
if (cmp > 0) {
/* Insert before current */
cur = NULL;
}
cur = ast_alloca(sizeof(*cur));
cur->lineno = reg->lineno;
ast_copy_string(cur->name, fn ? reg->func : reg->file, sizeof(cur->name));
cur->next = *prev;
*prev = cur;
Mark Spencer
committed
}
Mark Spencer
committed
}
}
whales_len = freed_regions_size(&whales);
minnows_len = freed_regions_size(&minnows);
ast_mutex_unlock(®lock);