[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(&reglock);
-	reg->next = regions[hash];
-	regions[hash] = reg;
-	ast_mutex_unlock(&reglock);
-
-	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(&reglock);
-	for (reg = regions[hash]; reg; reg = reg->next) {
-		if (reg->data == ptr) {
-			len = reg->len;
-			break;
-		}
-	}
-	ast_mutex_unlock(&reglock);
-
-	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(&reglock);
-	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(&reglock);
-#else
-	ast_mutex_lock(&reglock);
-	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(&reglock);
-
-	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(&reglock);
-	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(&reglock);
-	
-	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(&reglock);
-	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(&reglock);
-	
-	/* 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(&reglock);
-		/* 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(&reglock);
 			sleep(sleep_time);
 		} else {
 			ast_mutex_unlock(&reglock);
-			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(&reglock);
-	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(&reglock);
 }
 
+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(&reglock);
+	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(&reglock);
+
+	/* 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(&reglock);
+	reg->next = regions[hash];
+	regions[hash] = reg;
+	ast_mutex_unlock(&reglock);
+
+	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(&reglock);
+	for (reg = regions[hash]; reg; reg = reg->next) {
+		if (reg->data == ptr) {
+			len = reg->len;
+			break;
+		}
+	}
+	ast_mutex_unlock(&reglock);
+
+	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(&reglock);
+	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(&reglock);
+#else
+	ast_mutex_lock(&reglock);
+	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(&reglock);
+
+	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(&reglock);
+	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(&reglock);
+	
+	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(&reglock);
+	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(&reglock);
+	
+	/* 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(&notused, NULL, memory_destructor, NULL);
+	ast_pthread_create_background(&notused, NULL, memory_reporter, NULL);
 #endif
 
 	if (pad) {




More information about the asterisk-commits mailing list