[asterisk-commits] tilghman: branch tilghman/malloc_hold r209446 - /team/tilghman/malloc_hold/main/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Jul 28 20:30:43 CDT 2009
Author: tilghman
Date: Tue Jul 28 20:30:39 2009
New Revision: 209446
URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=209446
Log:
Woot, stability achieved!
Modified:
team/tilghman/malloc_hold/main/astmm.c
Modified: team/tilghman/malloc_hold/main/astmm.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/tilghman/malloc_hold/main/astmm.c?view=diff&rev=209446&r1=209445&r2=209446
==============================================================================
--- team/tilghman/malloc_hold/main/astmm.c (original)
+++ team/tilghman/malloc_hold/main/astmm.c Tue Jul 28 20:30:39 2009
@@ -37,6 +37,7 @@
#include <signal.h>
#ifdef MALLOC_HOLD
#include <malloc.h>
+#include <errno.h>
#endif /* MALLOC_HOLD */
#include "asterisk/cli.h"
@@ -50,6 +51,13 @@
#endif
#define SOME_PRIME 563
+#define FREE_LIST SOME_PRIME
+#define POLLUTED_LIST SOME_PRIME + 1
+#define AVAILABLE_LIST SOME_PRIME + 2
+
+#ifdef MALLOC_HOLD
+#define HOLD_LENGTH 90
+#endif
/* Cannot include utils.h, as it defines things that this file implements,
* but could eventually put these defines in a separate header file. */
@@ -94,12 +102,11 @@
#define FENCE_MAGIC 0xdeadbeef
-static FILE *mmlog;
+static FILE *mmlog = NULL;
#ifdef MALLOC_HOLD
static ssize_t pagesize = -1;
static struct sigaction default_sigsegv;
-#endif
static struct violations {
void *base_addr;
@@ -107,7 +114,9 @@
int error;
time_t when;
} violations[100] = { { NULL, } };
+
static int last_violation = -1;
+#endif
/* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
@@ -141,7 +150,7 @@
unsigned int cache; /* region was allocated as part of a cache pool */
unsigned int fence;
unsigned char data[0];
-} *regions[SOME_PRIME + 1];
+} *regions[SOME_PRIME + 3];
#ifdef MALLOC_HOLD
static struct ast_region *lastfree = NULL;
@@ -161,484 +170,32 @@
} \
} while (0)
-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)
-{
- struct ast_region *reg;
- void *ptr = NULL;
- unsigned int *fence;
- int hash;
-#ifdef MALLOC_HOLD
- size_t fullsize = size + sizeof(*reg) + sizeof(*fence);
-
- if (pagesize == -1 && (pagesize = sysconf(_SC_PAGESIZE)) == -1) {
- ast_log(LOG_ERROR, "Cannot retrieve system memory page size?");
- abort();
- }
-
- /*!
- * What, expand the size to cover the full page? Doesn't that eat memory?
- * Yes, but consider that we aren't the only consumers of memory in this
- * process. Supposing we only allocated half a page. Malloc would be free
- * to allocate the remaining space to something else, like perhaps a libc
- * internal structure. If we then protect that memory, and libc tries to
- * access it, we flag a memory error where none existed. Thus, we must
- * only allocate full memory pages.
- */
- fullsize = (fullsize / pagesize) * pagesize == fullsize ? fullsize : ((fullsize / pagesize) + 1) * pagesize;
- if (!(reg = memalign(pagesize, fullsize))) {
-#else /* !MALLOC_HOLD */
- if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
-#endif
- astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
- "at line %d of %s\n", (int) size, func, lineno, file);
- }
-
- ast_copy_string(reg->alloc_file, file, sizeof(reg->alloc_file));
- ast_copy_string(reg->alloc_func, func, sizeof(reg->alloc_func));
- reg->alloc_lineno = lineno;
- reg->len = size;
- reg->fullsize = fullsize;
- reg->which = which;
- reg->cache = cache;
- ptr = reg->data;
- hash = HASH(ptr);
- reg->fence = FENCE_MAGIC;
- fence = (ptr + reg->len);
- put_unaligned_uint32(fence, FENCE_MAGIC);
-
- ast_mutex_lock(®lock);
- reg->next = regions[hash];
- regions[hash] = reg;
- ast_mutex_unlock(®lock);
-
- return ptr;
-}
-
-static inline size_t __ast_sizeof_region(void *ptr)
-{
- int hash = HASH(ptr);
- struct ast_region *reg;
- size_t len = 0;
-
- ast_mutex_lock(®lock);
- for (reg = regions[hash]; reg; reg = reg->next) {
- if (reg->data == ptr) {
- len = reg->len;
- break;
- }
- }
- ast_mutex_unlock(®lock);
-
- return len;
-}
-
-void __ast_free_region(void *ptr, const char *file, int lineno, const char *func);
-void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
-{
- int hash;
- struct ast_region *reg, *prev = NULL;
- unsigned int *fence;
-#ifdef MALLOC_HOLD
- time_t now;
-#endif
-
- if (!ptr) {
- return;
- }
-
- hash = HASH(ptr);
-
-#ifdef MALLOC_HOLD
- now = time(NULL);
-
- /* Mark this segment as freed */
- ast_mutex_lock(®lock);
- for (reg = regions[hash]; reg; reg = reg->next) {
- if (reg->data == ptr) {
- fence = (unsigned int *)(reg->data + reg->len);
- if (reg->fence != FENCE_MAGIC) {
- astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
- "line %d, freed in %s of %s, line %d\n", reg->data,
- reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
- reg->free_func, reg->free_file, reg->free_lineno);
- /* Restore FENCE_MAGIC, as it's needed for extended checking */
- reg->fence = FENCE_MAGIC;
- }
- if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
- astmm_log("WARNING: High fence violation at %p, in %s of %s, "
- "line %d, freed in %s of %s, line %d\n", reg->data,
- reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
- reg->free_func, reg->free_file, reg->free_lineno);
- }
-
- /* Remove from "allocated" list */
- if (prev) {
- prev->next = reg->next;
- } else {
- regions[hash] = reg->next;
- }
- reg->next = NULL;
-
- reg->mperm = PROT_NONE;
- reg->operation = 0;
- reg->vlocation = NULL;
- reg->freetime = now;
- ast_copy_string(reg->free_file, file, sizeof(reg->free_file));
- ast_copy_string(reg->free_func, func, sizeof(reg->free_func));
- reg->free_lineno = lineno;
-
- /* By inserting at the end of the list, we ensure that we won't need
- * to search the list for entries to delete, but merely prune the
- * head of the list until the head no longer has a time over 120
- * seconds old. */
- if (lastfree == NULL) {
- lastfree = reg;
- regions[SOME_PRIME] = reg;
- } else {
- mprotect(lastfree, sizeof(*lastfree), PROT_READ | PROT_WRITE);
- lastfree->next = reg;
- mprotect(lastfree, sizeof(*lastfree), PROT_NONE);
- lastfree = reg;
- }
- mprotect(reg, reg->fullsize, PROT_NONE);
- break;
- }
- prev = reg;
- }
-
- if (!reg) {
- astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
- ptr, func, file, lineno);
- }
- ast_mutex_unlock(®lock);
-#else
- ast_mutex_lock(®lock);
- for (reg = regions[hash]; reg; reg = reg->next) {
- if (reg->data == ptr) {
- if (prev)
- prev->next = reg->next;
- else
- regions[hash] = reg->next;
- break;
- }
- prev = reg;
- }
- ast_mutex_unlock(®lock);
-
- if (reg) {
- fence = (unsigned int *)(reg->data + reg->len);
- if (reg->fence != FENCE_MAGIC) {
- astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
- "line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
- }
- if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
- astmm_log("WARNING: High fence violation at %p, in %s of %s, "
- "line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
- }
- free(reg);
- } else {
- astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
- ptr, func, file, lineno);
- }
-#endif
-}
-
-void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
-{
- void *ptr;
-
- if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
- memset(ptr, 0, size * nmemb);
-
- return ptr;
-}
-
-void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
-{
- void *ptr;
-
- if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
- memset(ptr, 0, size * nmemb);
-
- return ptr;
-}
-
-void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
-{
- return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
-}
-
-void __ast_free(void *ptr, const char *file, int lineno, const char *func)
-{
- __ast_free_region(ptr, file, lineno, func);
-}
-
-void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
-{
- void *tmp;
- size_t len = 0;
-
- if (ptr && !(len = __ast_sizeof_region(ptr))) {
- astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
- "line %d\n", ptr, func, file, lineno);
- return NULL;
- }
-
- if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
- return NULL;
-
- if (len > size)
- len = size;
- if (ptr) {
- memcpy(tmp, ptr, len);
- __ast_free_region(ptr, file, lineno, func);
- }
-
- return tmp;
-}
-
-char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
-{
- size_t len;
- void *ptr;
-
- if (!s)
- return NULL;
-
- len = strlen(s) + 1;
- if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
- strcpy(ptr, s);
-
- return ptr;
-}
-
-char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
-{
- size_t len;
- void *ptr;
-
- if (!s)
- return NULL;
-
- len = strlen(s) + 1;
- if (len > n)
- len = n;
- if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
- strcpy(ptr, s);
-
- 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);
- return -1;
- }
- vsnprintf(*strp, size + 1, fmt, ap);
-
- return size;
-}
-
-static int handle_show_memory(int fd, int argc, char *argv[])
-{
- char *fn = NULL;
- struct ast_region *reg;
- unsigned int x;
- unsigned int len = 0;
- unsigned int cache_len = 0;
- unsigned int count = 0;
- unsigned int *fence;
-
- if (argc > 3)
- fn = argv[3];
-
- ast_mutex_lock(®lock);
- for (x = 0; x < SOME_PRIME; x++) {
- for (reg = regions[x]; reg; reg = reg->next) {
- if (!fn || !strcasecmp(fn, reg->alloc_file) || !strcasecmp(fn, "anomolies")) {
- fence = (unsigned int *)(reg->data + reg->len);
- if (reg->fence != FENCE_MAGIC) {
- astmm_log("WARNING: Low fence violation at %p, "
- "in %s of %s, line %d\n", reg->data,
- reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
- }
- if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
- astmm_log("WARNING: High fence violation at %p, in %s of %s, "
- "line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
- }
- }
- if (!fn || !strcasecmp(fn, reg->alloc_file)) {
- ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
- (int) reg->len, reg->cache ? " (cache)" : "",
- reg->alloc_func, reg->alloc_lineno, reg->alloc_file);
- len += reg->len;
- if (reg->cache)
- cache_len += reg->len;
- count++;
- }
- }
- }
- ast_mutex_unlock(®lock);
-
- if (cache_len)
- ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
- else
- ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
-
- return RESULT_SUCCESS;
-}
-
-static int handle_show_memory_summary(int fd, int argc, char *argv[])
-{
- char *fn = NULL;
- int x;
- struct ast_region *reg;
- unsigned int len = 0;
- unsigned int cache_len = 0;
- int count = 0;
- struct file_summary {
- char fn[80];
- int len;
- int cache_len;
- int count;
- struct file_summary *next;
- } *list = NULL, *cur;
-
- if (argc > 3)
- fn = argv[3];
-
- ast_mutex_lock(®lock);
- for (x = 0; x < SOME_PRIME; x++) {
- for (reg = regions[x]; reg; reg = reg->next) {
- if (fn && strcasecmp(fn, reg->alloc_file))
- continue;
-
- for (cur = list; cur; cur = cur->next) {
- if ((!fn && !strcmp(cur->fn, reg->alloc_file)) || (fn && !strcmp(cur->fn, reg->alloc_func)))
- break;
- }
- if (!cur) {
- cur = alloca(sizeof(*cur));
- memset(cur, 0, sizeof(*cur));
- ast_copy_string(cur->fn, fn ? reg->alloc_func : reg->alloc_file, sizeof(cur->fn));
- cur->next = list;
- list = cur;
- }
-
- cur->len += reg->len;
- if (reg->cache)
- cur->cache_len += reg->len;
- cur->count++;
- }
- }
- ast_mutex_unlock(®lock);
-
- /* Dump the whole list */
- for (cur = list; cur; cur = cur->next) {
- len += cur->len;
- cache_len += cur->cache_len;
- count += cur->count;
- if (cur->cache_len) {
- if (fn) {
- ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
- cur->len, cur->cache_len, cur->count, cur->fn, fn);
- } else {
- ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
- cur->len, cur->cache_len, cur->count, cur->fn);
- }
- } else {
- if (fn) {
- ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
- cur->len, cur->count, cur->fn, fn);
- } else {
- ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n",
- cur->len, cur->count, cur->fn);
- }
- }
- }
-
- if (cache_len)
- ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
- else
- ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
-
- return RESULT_SUCCESS;
-}
-
-static char show_memory_help[] =
-"Usage: memory show allocations [<file>]\n"
-" Dumps a list of all segments of allocated memory, optionally\n"
-"limited to those from a specific file\n";
-
-static char show_memory_summary_help[] =
-"Usage: memory show summary [<file>]\n"
-" Summarizes heap memory allocations by file, or optionally\n"
-"by function, if a file is specified\n";
-
-static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
- { "show", "memory", "allocations", NULL },
- handle_show_memory, NULL,
- NULL };
-
-static struct ast_cli_entry cli_show_memory_summary_deprecated = {
- { "show", "memory", "summary", NULL },
- handle_show_memory_summary, NULL,
- NULL };
-
-static struct ast_cli_entry cli_memory[] = {
- { { "memory", "show", "allocations", NULL },
- handle_show_memory, "Display outstanding memory allocations",
- show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
-
- { { "memory", "show", "summary", NULL },
- handle_show_memory_summary, "Summarize outstanding memory allocations",
- show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
-};
-
-#ifdef MALLOC_HOLD
-static void *memory_destructor(void *notused)
+#ifdef MALLOC_HOLD
+#define MPROTECT(a,b,c) \
+ do { \
+ int __p = c; \
+ if (mprotect(a,b,c)) { \
+ fprintf(stderr, "Cannot set memory protections '%s' (%d) on %p: %s\n", \
+ __p == PROT_NONE ? "PROT_NONE" : __p == PROT_READ ? "PROT_READ" : __p == (PROT_READ | PROT_WRITE) ? "PROT_READ|PROT_WRITE" : "unknown", \
+ __p, a, strerror(errno)); \
+ abort(); \
+ } \
+ } while (0)
+
+static void *memory_reporter(void *notused)
{
struct ast_region *cur;
- int last_seen_violation = -1;
+ int last_seen_violation = 0;
for (;;) {
/* Any errors to report? */
- if (last_seen_violation != last_violation) {
- for (last_seen_violation++; last_seen_violation != last_violation;
+ if (last_violation != -1) {
+ for (; last_seen_violation != last_violation;
last_seen_violation = (last_seen_violation == ARRAY_LEN(violations) - 1) ? 0 : last_seen_violation + 1) {
if ((cur = violations[last_seen_violation].base_addr) == NULL) {
memset(&violations[last_seen_violation], 0, sizeof(violations[0]));
continue;
}
+
astmm_log("Intercepted invalid %s to %p (%p+%d), originally allocated in %s at %s:%d and freed in %s at %s:%d\n",
violations[last_seen_violation].error == PROT_READ ? "read" : "write",
violations[last_seen_violation].violation, cur->data,
@@ -646,25 +203,30 @@
cur->alloc_func, cur->alloc_file, cur->alloc_lineno,
cur->free_func, cur->free_file, cur->free_lineno);
memset(&violations[last_seen_violation], 0, sizeof(violations[0]));
+ MPROTECT(cur, cur->fullsize, PROT_READ | PROT_WRITE);
cur->operation = 0;
cur->mperm = PROT_NONE;
- mprotect(cur, cur->fullsize, PROT_NONE);
- }
- last_seen_violation--;
- }
+ MPROTECT(cur, cur->fullsize, PROT_NONE);
+ }
+ }
+ sleep(5);
+ }
+ return NULL;
+}
+
+static void *memory_destructor(void *notused)
+{
+ struct ast_region *cur;
+ for (;;) {
ast_mutex_lock(®lock);
- /* Delete any segment which is free for at least 120 seconds */
- while ((cur = regions[SOME_PRIME]) && !mprotect(cur, sizeof(*cur), PROT_READ) && cur->freetime < time(NULL) - 120) {
+ /* Delete any segment which is free for at least HOLD_LENGTH seconds */
+ while ((cur = regions[FREE_LIST]) && !mprotect(cur, sizeof(*cur), PROT_READ) && cur->freetime < time(NULL) - HOLD_LENGTH) {
int i;
- if (mprotect(cur, cur->fullsize, PROT_READ | PROT_WRITE | PROT_EXEC)) {
- astmm_log("ERROR: cannot access freed memory at %p, (originally in %s of %s, line %d, freed in %s of %s, line %d)\n",
- cur->data, cur->alloc_func, cur->alloc_file, cur->alloc_lineno, cur->free_func, cur->free_file, cur->free_lineno);
- /* XXX crash, perhaps? XXX (should not ever happen) */
- }
-
- cur->mperm = PROT_READ;
- regions[SOME_PRIME] = cur->next;
+ MPROTECT(cur, cur->fullsize, PROT_READ | PROT_WRITE);
+
+ cur->mperm = PROT_READ | PROT_WRITE;
+ regions[FREE_LIST] = cur->next;
if (cur == lastfree) {
lastfree = NULL;
}
@@ -685,20 +247,29 @@
}
}
}
- free(cur);
- }
- if ((cur = regions[SOME_PRIME])) {
- int sleep_time = 120 - (time(NULL) - cur->freetime);
-
- /* Re-protect region opened to reads while we examined cur->freetime */
- mprotect(cur, sizeof(*cur), PROT_NONE);
+
+ /* If memory was read/written incorrectly, move it to the "polluted" list;
+ * otherwise, move it to the available list. */
+ if (cur->vlocation) {
+ cur->next = regions[POLLUTED_LIST];
+ regions[POLLUTED_LIST] = cur;
+ } else {
+ cur->next = regions[AVAILABLE_LIST];
+ regions[AVAILABLE_LIST] = cur;
+ }
+ }
+ if ((cur = regions[FREE_LIST])) {
+ int sleep_time = HOLD_LENGTH - (time(NULL) - cur->freetime);
+
+ /* Re-protect region opened to reads while we examined cur->freetime (in previous loop) */
+ MPROTECT(cur, sizeof(*cur), PROT_NONE);
ast_mutex_unlock(®lock);
sleep(sleep_time);
} else {
ast_mutex_unlock(®lock);
- astmm_log("Nothing in free list. Sleeping for 120 seconds.\n");
- sleep(120);
+ astmm_log("Nothing in free list. Sleeping for %d seconds.\n", HOLD_LENGTH);
+ sleep(HOLD_LENGTH);
}
}
/* Never reached */
@@ -709,8 +280,9 @@
{
struct ast_region *cur;
- if (signal != SIGSEGV || info->si_code == SI_SIGIO) {
- return;
+ if (info->si_code == SEGV_MAPERR) {
+ fprintf(stderr, "Stepped on invalid page: %p\n", info->si_addr);
+ abort();
}
/* Access violation errors are the only things I handle;
@@ -727,8 +299,8 @@
}
/* Find base address */
- cur = (void *) ((size_t) info->si_addr / pagesize * pagesize);
- mprotect(cur, sizeof(*cur), PROT_READ);
+ cur = (void *) ((size_t) info->si_addr - ((size_t) info->si_addr % pagesize));
+ MPROTECT(cur, sizeof(*cur), PROT_READ);
while (cur->fence != FENCE_MAGIC && cur->len > cur->fullsize) {
/*!
* This loop is kind of weird. By calling this routine, we expect that
@@ -740,7 +312,7 @@
* segment, so all is good.
*/
cur -= pagesize;
- mprotect(cur, sizeof(*cur), PROT_READ);
+ MPROTECT(cur, sizeof(*cur), PROT_READ);
}
/*!
@@ -751,8 +323,9 @@
* hairs.
*/
if (cur->mperm == PROT_READ) {
- int newperm = PROT_READ | PROT_WRITE | PROT_EXEC;
- mprotect(cur, cur->fullsize, newperm);
+ int newperm = PROT_READ | PROT_WRITE;
+ /* Need to modify cur, so let us write */
+ MPROTECT(cur, cur->fullsize, newperm);
cur->mperm = newperm;
if (cur->vlocation == info->si_addr) {
cur->operation = PROT_WRITE;
@@ -761,14 +334,18 @@
}
}
} else {
+ /* Error in astmm code */
+ if ((void *) cur->data > info->si_addr) {
+ abort();
+ }
/* We're about to modify, so let us write */
- mprotect(cur, cur->fullsize, PROT_READ | PROT_WRITE);
+ MPROTECT(cur, cur->fullsize, PROT_READ | PROT_WRITE);
cur->mperm = PROT_READ;
cur->vlocation = info->si_addr;
cur->operation = PROT_READ;
- mprotect(cur, cur->fullsize, cur->mperm);
-
- if (++last_violation > ARRAY_LEN(violations)) {
+ MPROTECT(cur, cur->fullsize, cur->mperm);
+
+ if (++last_violation >= ARRAY_LEN(violations)) {
last_violation = 0;
}
violations[last_violation].base_addr = cur;
@@ -803,16 +380,532 @@
{
struct ast_region *reg;
ast_mutex_lock(®lock);
- while ((reg = regions[SOME_PRIME])) {
- mprotect(reg, sizeof(*reg), PROT_READ | PROT_WRITE);
- mprotect(reg, reg->fullsize, PROT_READ | PROT_WRITE);
+ while ((reg = regions[FREE_LIST])) {
+ MPROTECT(reg, sizeof(*reg), PROT_READ | PROT_WRITE);
+ MPROTECT(reg, reg->fullsize, PROT_READ | PROT_WRITE);
reg->operation = 0;
- regions[SOME_PRIME] = reg->next;
+ regions[FREE_LIST] = reg->next;
free(reg);
}
ast_mutex_unlock(®lock);
}
+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)
+{
+ struct ast_region *reg = NULL, *prev = NULL, *best_prev = NULL, *best_reg = NULL;
+ void *ptr = NULL;
+ unsigned int *fence;
+ int hash;
+#ifdef MALLOC_HOLD
+ size_t fullsize = size + sizeof(*reg) + sizeof(*fence);
+
+ if (pagesize == -1 && (pagesize = sysconf(_SC_PAGESIZE)) == -1) {
+ ast_log(LOG_ERROR, "Cannot retrieve system memory page size?");
+ abort();
+ }
+
+ /*!
+ * What, expand the size to cover the full page? Doesn't that eat memory?
+ * Yes, but consider that we aren't the only consumers of memory in this
+ * process. Supposing we only allocated half a page. Malloc would be free
+ * to allocate the remaining space to something else, like perhaps a libc
+ * internal structure. If we then protect that memory, and libc tries to
+ * access it, we flag a memory error where none existed. Thus, we must
+ * only allocate full memory pages.
+ */
+ fullsize = (fullsize / pagesize) * pagesize == fullsize ? fullsize : ((fullsize / pagesize) + 1) * pagesize;
+ /* Look for memory in our available list, first */
+ ast_mutex_lock(®lock);
+ for (reg = regions[AVAILABLE_LIST]; reg; prev = reg, reg = reg->next) {
+ if (fullsize < reg->fullsize) {
+ /* Not big enough */
+ continue;
+ }
+
+ if (reg->fullsize == fullsize) {
+ if (prev) {
+ prev->next = reg->next;
+ } else {
+ regions[AVAILABLE_LIST] = reg->next;
+ }
+ /* Don't let the indicated size shrink to below the actual size. */
+ fullsize = reg->fullsize;
+ break;
+ }
+
+ /* If I don't find an exact match, then find me the smallest available
+ * block that matches my needs. */
+ if (best_reg->fullsize > reg->fullsize) {
+ best_reg = reg;
+ best_prev = prev;
+ }
+ }
+
+ if (!reg && best_reg) {
+ reg = best_reg;
+ /* Remove it from the available list */
+ if (best_prev) {
+ best_prev->next = reg->next;
+ } else {
+ regions[AVAILABLE_LIST] = reg->next;
+ }
+ /* Don't let the indicated size shrink to below the actual size. */
+ fullsize = reg->fullsize;
+ }
+ ast_mutex_unlock(®lock);
+
+ /* Do I need a new allocation? */
+ if (!reg && !(reg = memalign(pagesize, fullsize))) {
+#else /* !MALLOC_HOLD */
+ if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
+#endif
+ astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
+ "at line %d of %s\n", (int) size, func, lineno, file);
+ return NULL;
+ }
+
+ ast_copy_string(reg->alloc_file, file, sizeof(reg->alloc_file));
+ ast_copy_string(reg->alloc_func, func, sizeof(reg->alloc_func));
+ reg->alloc_lineno = lineno;
+ reg->len = size;
+ reg->fullsize = fullsize;
+ reg->which = which;
+ reg->cache = cache;
+ ptr = reg->data;
+ hash = HASH(ptr);
+ reg->fence = FENCE_MAGIC;
+ fence = (ptr + reg->len);
+ put_unaligned_uint32(fence, FENCE_MAGIC);
+
+ ast_mutex_lock(®lock);
+ reg->next = regions[hash];
+ regions[hash] = reg;
+ ast_mutex_unlock(®lock);
+
+ return ptr;
+}
+
+static inline size_t __ast_sizeof_region(void *ptr)
+{
+ int hash = HASH(ptr);
+ struct ast_region *reg;
+ size_t len = 0;
+
+ ast_mutex_lock(®lock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ len = reg->len;
+ break;
+ }
+ }
+ ast_mutex_unlock(®lock);
+
+ return len;
+}
+
+#ifdef MALLOC_HOLD
+/* Not strictly necessary to make this a global symbol, but it helps when debugging */
+void __ast_free_region(void *ptr, const char *file, int lineno, const char *func);
+#else
+static
+#endif
+void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
+{
+ int hash;
+ struct ast_region *reg, *prev = NULL;
+ unsigned int *fence;
+#ifdef MALLOC_HOLD
+ time_t now;
+#endif
+
+ if (!ptr) {
+ return;
+ }
+
+ hash = HASH(ptr);
+
+#ifdef MALLOC_HOLD
+ now = time(NULL);
+
+ /* Mark this segment as freed */
+ ast_mutex_lock(®lock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
+ "line %d, freed in %s of %s, line %d\n", reg->data,
+ reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
+ reg->free_func, reg->free_file, reg->free_lineno);
+ /* Restore FENCE_MAGIC, as it's needed for extended checking */
+ reg->fence = FENCE_MAGIC;
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d, freed in %s of %s, line %d\n", reg->data,
+ reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
+ reg->free_func, reg->free_file, reg->free_lineno);
+ }
+
+ /* Remove from "allocated" list */
+ if (prev) {
+ prev->next = reg->next;
+ } else {
+ regions[hash] = reg->next;
+ }
+ reg->next = NULL;
+
+ reg->mperm = PROT_NONE;
+ reg->operation = 0;
+ reg->vlocation = NULL;
+ reg->freetime = now;
+ ast_copy_string(reg->free_file, file, sizeof(reg->free_file));
+ ast_copy_string(reg->free_func, func, sizeof(reg->free_func));
+ reg->free_lineno = lineno;
+
+ /* By inserting at the end of the list, we ensure that we won't need
+ * to search the list for entries to delete, but merely prune the
+ * head of the list until the head no longer has a time over HOLD_LENGTH
+ * seconds old. */
+ if (lastfree == NULL) {
+ lastfree = reg;
+ regions[FREE_LIST] = reg;
+ } else {
+ MPROTECT(lastfree, sizeof(*lastfree), PROT_READ | PROT_WRITE);
+ lastfree->next = reg;
+ MPROTECT(lastfree, sizeof(*lastfree), PROT_NONE);
+ lastfree = reg;
+ }
+ MPROTECT(reg, reg->fullsize, PROT_NONE);
+ break;
+ }
+ prev = reg;
+ }
+
+ if (!reg) {
+ astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
+ ptr, func, file, lineno);
+ }
+ ast_mutex_unlock(®lock);
+#else
+ ast_mutex_lock(®lock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ if (prev)
+ prev->next = reg->next;
+ else
+ regions[hash] = reg->next;
+ break;
+ }
+ prev = reg;
+ }
+ ast_mutex_unlock(®lock);
+
+ if (reg) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
+ }
+ free(reg);
+ } else {
+ astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
+ ptr, func, file, lineno);
+ }
+#endif
+}
+
+void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
+ memset(ptr, 0, size * nmemb);
+
+ return ptr;
+}
+
+void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
+ memset(ptr, 0, size * nmemb);
+
+ return ptr;
+}
+
+void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
+{
+ return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
+}
+
+void __ast_free(void *ptr, const char *file, int lineno, const char *func)
+{
+ __ast_free_region(ptr, file, lineno, func);
+}
+
+void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
+{
+ void *tmp;
+ size_t len = 0;
+
+ if (ptr && !(len = __ast_sizeof_region(ptr))) {
+ astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
+ "line %d\n", ptr, func, file, lineno);
+ return NULL;
+ }
+
+ if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
+ return NULL;
+
+ if (len > size)
+ len = size;
+ if (ptr) {
+ memcpy(tmp, ptr, len);
+ __ast_free_region(ptr, file, lineno, func);
+ }
+
+ return tmp;
+}
+
+char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
+{
+ size_t len;
+ void *ptr;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s) + 1;
+ if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
+ strcpy(ptr, s);
+
+ return ptr;
+}
+
+char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
+{
+ size_t len;
+ void *ptr;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s) + 1;
+ if (len > n)
+ len = n;
+ if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
+ strcpy(ptr, s);
+
+ 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);
+ return -1;
+ }
+ vsnprintf(*strp, size + 1, fmt, ap);
+
+ return size;
+}
+
+static int handle_show_memory(int fd, int argc, char *argv[])
+{
+ char *fn = NULL;
+ struct ast_region *reg;
+ unsigned int x;
+ unsigned int len = 0;
+ unsigned int cache_len = 0;
+ unsigned int count = 0;
+ unsigned int *fence;
+
+ if (argc > 3)
+ fn = argv[3];
+
+ ast_mutex_lock(®lock);
+ for (x = 0; x < SOME_PRIME; x++) {
+ for (reg = regions[x]; reg; reg = reg->next) {
+ if (!fn || !strcasecmp(fn, reg->alloc_file) || !strcasecmp(fn, "anomolies")) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, "
+ "in %s of %s, line %d\n", reg->data,
+ reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
+ }
+ }
+ if (!fn || !strcasecmp(fn, reg->alloc_file)) {
+ ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
+ (int) reg->len, reg->cache ? " (cache)" : "",
+ reg->alloc_func, reg->alloc_lineno, reg->alloc_file);
+ len += reg->len;
+ if (reg->cache)
+ cache_len += reg->len;
+ count++;
+ }
+ }
+ }
+ ast_mutex_unlock(®lock);
+
+ if (cache_len)
+ ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_show_memory_summary(int fd, int argc, char *argv[])
+{
+ char *fn = NULL;
+ int x;
+ struct ast_region *reg;
+ unsigned int len = 0;
+ unsigned int cache_len = 0;
+ int count = 0;
+ struct file_summary {
+ char fn[80];
+ int len;
+ int cache_len;
+ int count;
+ struct file_summary *next;
+ } *list = NULL, *cur;
+
+ if (argc > 3)
+ fn = argv[3];
+
+ ast_mutex_lock(®lock);
+ for (x = 0; x < SOME_PRIME; x++) {
+ for (reg = regions[x]; reg; reg = reg->next) {
+ if (fn && strcasecmp(fn, reg->alloc_file))
+ continue;
+
+ for (cur = list; cur; cur = cur->next) {
+ if ((!fn && !strcmp(cur->fn, reg->alloc_file)) || (fn && !strcmp(cur->fn, reg->alloc_func)))
+ break;
+ }
+ if (!cur) {
+ cur = alloca(sizeof(*cur));
+ memset(cur, 0, sizeof(*cur));
+ ast_copy_string(cur->fn, fn ? reg->alloc_func : reg->alloc_file, sizeof(cur->fn));
+ cur->next = list;
+ list = cur;
+ }
+
+ cur->len += reg->len;
+ if (reg->cache)
+ cur->cache_len += reg->len;
+ cur->count++;
+ }
+ }
+ ast_mutex_unlock(®lock);
+
+ /* Dump the whole list */
+ for (cur = list; cur; cur = cur->next) {
+ len += cur->len;
+ cache_len += cur->cache_len;
+ count += cur->count;
+ if (cur->cache_len) {
+ if (fn) {
+ ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->cache_len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
+ cur->len, cur->cache_len, cur->count, cur->fn);
+ }
+ } else {
+ if (fn) {
+ ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n",
+ cur->len, cur->count, cur->fn);
+ }
+ }
+ }
+
+ if (cache_len)
+ ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return RESULT_SUCCESS;
+}
+
+static char show_memory_help[] =
+"Usage: memory show allocations [<file>]\n"
+" Dumps a list of all segments of allocated memory, optionally\n"
+"limited to those from a specific file\n";
+
+static char show_memory_summary_help[] =
+"Usage: memory show summary [<file>]\n"
+" Summarizes heap memory allocations by file, or optionally\n"
+"by function, if a file is specified\n";
+
+static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
+ { "show", "memory", "allocations", NULL },
+ handle_show_memory, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_show_memory_summary_deprecated = {
+ { "show", "memory", "summary", NULL },
+ handle_show_memory_summary, NULL,
+ NULL };
+
+static struct ast_cli_entry cli_memory[] = {
+ { { "memory", "show", "allocations", NULL },
+ handle_show_memory, "Display outstanding memory allocations",
+ show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
+
+ { { "memory", "show", "summary", NULL },
+ handle_show_memory_summary, "Summarize outstanding memory allocations",
+ show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
+};
+
+/*!
+ * It's probably not completely obvious, but it's possible (well, it's happened)
+ * that memory is allocated PRIOR to this initialization routine being called.
+ * Be forewarned.
+ */
void __ast_mm_init(void)
{
char filename[PATH_MAX];
@@ -836,6 +929,7 @@
ast_register_atexit(mm_cleanup);
ast_pthread_create_background(¬used, NULL, memory_destructor, NULL);
+ ast_pthread_create_background(¬used, NULL, memory_reporter, NULL);
#endif
if (pad) {
More information about the asterisk-commits
mailing list