[asterisk-commits] tilghman: branch tilghman/malloc_hold-1.6.2 r259359 - in /team/tilghman/mallo...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Apr 27 15:14:04 CDT 2010
Author: tilghman
Date: Tue Apr 27 15:14:00 2010
New Revision: 259359
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=259359
Log:
Initialized merge tracking via "svnmerge" with revisions "1-259357" from
https://origsvn.digium.com/svn/asterisk/branches/1.6.2
Modified:
team/tilghman/malloc_hold-1.6.2/ (props changed)
team/tilghman/malloc_hold-1.6.2/configure
team/tilghman/malloc_hold-1.6.2/include/asterisk/astmm.h
team/tilghman/malloc_hold-1.6.2/main/astmm.c
team/tilghman/malloc_hold-1.6.2/main/bridging.c
Propchange: team/tilghman/malloc_hold-1.6.2/
------------------------------------------------------------------------------
automerge = *
Propchange: team/tilghman/malloc_hold-1.6.2/
------------------------------------------------------------------------------
automerge-email = tilghman at mail.jeffandtilghman.com
Propchange: team/tilghman/malloc_hold-1.6.2/
------------------------------------------------------------------------------
svnmerge-integrated = /branches/1.6.2:1-259357
Modified: team/tilghman/malloc_hold-1.6.2/include/asterisk/astmm.h
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/malloc_hold-1.6.2/include/asterisk/astmm.h?view=diff&rev=259359&r1=259358&r2=259359
==============================================================================
--- team/tilghman/malloc_hold-1.6.2/include/asterisk/astmm.h (original)
+++ team/tilghman/malloc_hold-1.6.2/include/asterisk/astmm.h Tue Apr 27 15:14:00 2010
@@ -54,6 +54,7 @@
void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
void *__ast_malloc(size_t size, const char *file, int lineno, const char *func);
+void *__ast_malloc_cache(size_t size, const char *file, int lineno, const char *func);
void __ast_free(void *ptr, const char *file, int lineno, const char *func);
void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func);
char *__ast_strdup(const char *s, const char *file, int lineno, const char *func);
@@ -62,10 +63,78 @@
__attribute__((format(printf, 5, 6)));
int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *file, int lineno, const char *func)
__attribute__((format(printf, 2, 0)));
+void *__ast_hold_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
+void *__ast_hold_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
+void *__ast_hold_malloc(size_t size, const char *file, int lineno, const char *func);
+void *__ast_hold_malloc_cache(size_t size, const char *file, int lineno, const char *func);
+void *__ast_hold_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func);
+char *__ast_hold_strdup(const char *s, const char *file, int lineno, const char *func);
+char *__ast_hold_strndup(const char *s, size_t n, const char *file, int lineno, const char *func);
+int __ast_hold_asprintf(const char *file, int lineno, const char *func, char **strp, const char *format, ...) __attribute__((format(printf, 5, 6)));
+int __ast_hold_vasprintf(char **strp, const char *format, va_list ap, const char *file, int lineno, const char *func) __attribute__((format(printf, 2, 0)));
+
void __ast_mm_init(void);
/* Provide our own definitions */
+#ifdef MALLOC_HOLD
+
+#define calloc(a,b) \
+ __ast_hold_calloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_calloc(a,b) \
+ __ast_hold_calloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_calloc_cache(a,b) \
+ __ast_hold_calloc_cache(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define malloc(a) \
+ __ast_hold_malloc(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_malloc(a) \
+ __ast_hold_malloc(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_malloc_cache(a) \
+ __ast_hold_malloc_cache(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define free(a) \
+ __ast_free(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_free(a) \
+ __ast_free(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define realloc(a,b) \
+ __ast_hold_realloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_realloc(a,b) \
+ __ast_hold_realloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define strdup(a) \
+ __ast_hold_strdup(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_strdup(a) \
+ __ast_hold_strdup(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define strndup(a,b) \
+ __ast_hold_strndup(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_strndup(a,b) \
+ __ast_hold_strndup(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define asprintf(a, b, c...) \
+ __ast_hold_asprintf(__FILE__, __LINE__, __PRETTY_FUNCTION__, a, b, c)
+
+#define ast_asprintf(a, b, c...) \
+ __ast_hold_asprintf(__FILE__, __LINE__, __PRETTY_FUNCTION__, a, b, c)
+
+#define vasprintf(a,b,c) \
+ __ast_hold_vasprintf(a,b,c,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_vasprintf(a,b,c) \
+ __ast_hold_vasprintf(a,b,c,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#else /* !defined(MALLOC_HOLD) */
+
#define calloc(a,b) \
__ast_calloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
@@ -81,6 +150,9 @@
#define ast_malloc(a) \
__ast_malloc(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_malloc_cache(a) \
+ __ast_malloc_cache(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
#define free(a) \
__ast_free(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
@@ -117,6 +189,7 @@
#define ast_vasprintf(a,b,c) \
__ast_vasprintf(a,b,c,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+#endif /* defined(MALLOC_HOLD) */
#endif /* !STANDALONE */
#else
Modified: team/tilghman/malloc_hold-1.6.2/main/astmm.c
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/malloc_hold-1.6.2/main/astmm.c?view=diff&rev=259359&r1=259358&r2=259359
==============================================================================
--- team/tilghman/malloc_hold-1.6.2/main/astmm.c (original)
+++ team/tilghman/malloc_hold-1.6.2/main/astmm.c Tue Apr 27 15:14:00 2010
@@ -32,6 +32,12 @@
#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
#include <stddef.h>
#include <time.h>
+#include <sys/mman.h>
+#include <signal.h>
+#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__Darwin__)
+#include <malloc.h>
+#endif
+#include <errno.h>
#include "asterisk/cli.h"
#include "asterisk/lock.h"
@@ -39,6 +45,30 @@
#include "asterisk/unaligned.h"
#define SOME_PRIME 563
+
+#define FREE_LIST SOME_PRIME
+#define POLLUTED_LIST SOME_PRIME + 1
+#define AVAILABLE_LIST SOME_PRIME + 2
+
+#define HOLD_LENGTH 90
+
+/* Cannot include utils.h, as it defines things that this file implements,
+ * but could eventually put these defines in a separate header file. */
+int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn);
+#define AST_STACKSIZE (((sizeof(void *) * 8 * 8) - 16) * 1024)
+
+#if defined(LOW_MEMORY)
+#define AST_BACKGROUND_STACKSIZE (((sizeof(void *) * 8 * 2) - 16) * 1024)
+#else
+#define AST_BACKGROUND_STACKSIZE AST_STACKSIZE
+#endif
+
+#define ast_pthread_create_background(a, b, c, d) ast_pthread_create_stack(a, b, c, d, \
+ AST_BACKGROUND_STACKSIZE, \
+ __FILE__, __FUNCTION__, \
+ __LINE__, #c)
enum func_type {
FUNC_CALLOC = 1,
@@ -62,7 +92,19 @@
#define FENCE_MAGIC 0xdeadbeef
-static FILE *mmlog;
+static FILE *mmlog = NULL;
+
+static ssize_t pagesize = -1;
+static struct sigaction default_sigsegv;
+
+static struct violations {
+ void *base_addr;
+ void *violation;
+ int error;
+ time_t when;
+} violations[100] = { { NULL, } };
+
+static int last_violation = -1;
/* 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
@@ -79,14 +121,24 @@
static struct ast_region {
struct ast_region *next;
size_t len;
- char file[64];
- char func[40];
- unsigned int lineno;
+ size_t fullsize;
+ char free_file[64];
+ char free_func[40];
+ unsigned int free_lineno;
+ unsigned int freetime;
+ int mperm;
+ int operation;
+ void *vlocation;
+ char alloc_file[64];
+ char alloc_func[40];
+ unsigned int alloc_lineno;
enum func_type which;
unsigned int cache; /* region was allocated as part of a cache pool */
unsigned int fence;
unsigned char data[0];
-} *regions[SOME_PRIME];
+} *regions[SOME_PRIME + 3];
+
+static struct ast_region *lastfree = NULL;
#define HASH(a) \
(((unsigned long)(a)) % SOME_PRIME)
@@ -104,23 +156,321 @@
} \
} 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)
+#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 = 0;
+ for (;;) {
+ /* Any errors to report? */
+ 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:%u and freed in %s at %s:%u\n",
+ violations[last_seen_violation].error == PROT_READ ? "read" : "write",
+ violations[last_seen_violation].violation, cur->data,
+ (int) (violations[last_seen_violation].violation - (void *) cur->data),
+ 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);
+ }
+ }
+ 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 HOLD_LENGTH seconds */
+ while ((cur = regions[FREE_LIST]) && !mprotect(cur, sizeof(*cur), PROT_READ) && cur->freetime < time(NULL) - HOLD_LENGTH) {
+ int i;
+
+ MPROTECT(cur, cur->fullsize, PROT_READ | PROT_WRITE);
+
+ cur->mperm = PROT_READ | PROT_WRITE;
+ regions[FREE_LIST] = cur->next;
+ if (cur == lastfree) {
+ lastfree = NULL;
+ }
+#if 0 /* Verification that this thread is running only */
+ astmm_log("Freeing memory at %p (size %d, originally allocated in %s of %s, line %d)\n", cur->data, cur->len, cur->alloc_func, cur->alloc_file, cur->alloc_lineno);
+#endif
+ if (cur->operation) {
+ for (i = 0; i < ARRAY_LEN(violations); i++) {
+ if (cur == violations[i].base_addr) {
+ astmm_log("Intercepted invalid %s to %p (%p+%d), originally allocated in %s at %s:%u and freed in %s at %s:%u\n",
+ violations[i].error == PROT_READ ? "read" : "write",
+ violations[i].violation, cur->data,
+ (int) (violations[i].violation - (void *) cur->data),
+ cur->alloc_func, cur->alloc_file, cur->alloc_lineno,
+ cur->free_func, cur->free_file, cur->free_lineno);
+ memset(&violations[i], 0, sizeof(violations[i]));
+ break;
+ }
+ }
+ }
+
+ /* 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);
+ if (sleep_time > 0) {
+ sleep(sleep_time);
+ } else {
+ sched_yield();
+ }
+ } else {
+ ast_mutex_unlock(®lock);
+ astmm_log("Nothing in free list. Sleeping for %d seconds.\n", HOLD_LENGTH);
+ sleep(HOLD_LENGTH);
+ }
+ }
+ /* Never reached */
+ return NULL;
+}
+
+static void handle_sigsegv(int signal, siginfo_t *info, void *unused)
+{
+ struct ast_region *cur;
+
+ 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;
+ * anything else goes to the default handler. */
+ if (info->si_code != SEGV_ACCERR || info->si_addr == NULL) {
+ if (default_sigsegv.sa_flags & SA_SIGINFO && default_sigsegv.sa_sigaction != NULL) {
+ (default_sigsegv.sa_sigaction)(signal, info, unused);
+ } else if (default_sigsegv.sa_handler != NULL) {
+ (default_sigsegv.sa_handler)(signal);
+ } else {
+ abort();
+ }
+ return;
+ }
+
+ /* Find base address */
+ 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
+ * the memory segment is one allocated by us, so this should terminate
+ * when we find FENCE_MAGIC. OTOH, it's possible that this wasn't us,
+ * and we'll eventually step on something that wasn't an access problem,
+ * the signal handler will fire again, call the default handler, and we'll
+ * crash. That is what we'd expect to happen, anyway, if this wasn't our
+ * segment, so all is good.
+ */
+ cur -= pagesize;
+ MPROTECT(cur, sizeof(*cur), PROT_READ);
+ }
+
+ /*!
+ * The general rule of thumb is don't try to allocate memory or output to a
+ * file descriptor in a signal handler. Therefore, we must otherwise raise
+ * flags for later reporting. This might not be completely true, but
+ * consider what signal handler this is. This isn't the place to split
+ * hairs.
+ */
+ if (cur->mperm == PROT_READ) {
+ 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;
+ if (violations[last_violation].base_addr == cur && violations[last_violation].violation == info->si_addr) {
+ violations[last_violation].error = PROT_WRITE;
+ }
+ }
+ } else {
+ /* Error in astmm code */
+ if ((void *) cur->data > (void *) info->si_addr) {
+ /* Extra cast above due to OpenBSD */
+ abort();
+ }
+ /* We're about to modify, so let us 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)) {
+ last_violation = 0;
+ }
+ violations[last_violation].base_addr = cur;
+ violations[last_violation].violation = info->si_addr;
+ violations[last_violation].when = time(NULL);
+ violations[last_violation].error = cur->operation;
+ }
+
+ /*!
+ * Okay, if we've done our job right, we now know that the user
+ * stepped on memory that was freed, we've intercepted that access,
+ * allowed the memory to be read/written, and return. This will allow
+ * the (previously illegal) operation to continue, as if the segfault
+ * never actually occurred. Yes, that seems weird, but that's the way
+ * this signal works.
+ *
+ * Now here's the interesting part. This signal handler will get
+ * re-entered immediately if we didn't clear the right protection, which
+ * is what we designed, when we differentiate between an attempted read
+ * (which won't come back) and a write (which will). We can therefore
+ * determine which type of access occurred to free'd memory.
+ *
+ * The rub is that once we unprotect this memory, we cannot easily
+ * re-protect the memory, since returning releases the control from the
+ * signal handler. Thus, we only will detect the FIRST illegal access
+ * of (this segment of) free'd memory, not any subsequent. This hopefully
+ * is sufficient for our purposes.
+ */
+}
+
+static void mm_cleanup(void)
{
struct ast_region *reg;
+ ast_mutex_lock(®lock);
+ 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[FREE_LIST] = reg->next;
+#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Darwin__)
+ munmap(reg, reg->fullsize);
+#else
+ free(reg);
+#endif
+ }
+ ast_mutex_unlock(®lock);
+}
+
+static inline void *__ast_hold_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;
void *ptr = NULL;
unsigned int *fence;
int hash;
-
- if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
+ struct ast_region *prev = NULL, *best_prev = NULL, *best_reg = NULL;
+ 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();
+ }
+
+ if (!cache) {
+ /*!
+ * 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 || 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 ((cache && !(reg = malloc(fullsize))) ||
+#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Darwin__)
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+ (!cache && !reg && (reg = mmap(NULL, fullsize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0)) == MAP_FAILED)
+#else
+ (!cache && !reg && !(reg = memalign(pagesize, fullsize)))
+#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->file, file, sizeof(reg->file));
- ast_copy_string(reg->func, func, sizeof(reg->func));
- reg->lineno = lineno;
+ ast_copy_string(reg->alloc_file, file + (strlen(file) > sizeof(reg->alloc_file) - 1 ? strlen(file) - sizeof(reg->alloc_file) + 1 : 0), 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;
@@ -137,22 +487,52 @@
return ptr;
}
-static inline size_t __ast_sizeof_region(void *ptr)
+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;
+ void *ptr = NULL;
+ unsigned int *fence;
+ int hash;
+ if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
+ 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 = 1;
+ 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 struct ast_region *ptr2region(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;
+ return reg;
}
static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
@@ -160,78 +540,191 @@
int hash;
struct ast_region *reg, *prev = NULL;
unsigned int *fence;
+ time_t now;
if (!ptr)
return;
hash = HASH(ptr);
+ now = time(NULL);
+
+ /* Mark this segment as freed */
ast_mutex_lock(®lock);
for (reg = regions[hash]; reg; reg = reg->next) {
if (reg->data == ptr) {
- if (prev)
+ 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,
+ func, file, 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 (%x) at %p, in %s of %s, "
+ "line %d, freed in %s of %s, line %d\n", get_unaligned_uint32(fence), reg->data,
+ reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
+ func, file, lineno);
+ }
+
+ /* Remove from "allocated" list */
+ if (prev) {
prev->next = reg->next;
- else
+ } else {
regions[hash] = reg->next;
+ }
+ reg->next = NULL;
+
+ if (reg->fullsize % pagesize == 0) {
+ 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);
+ } else {
+ /* Not an even multiple of pagesize, so not a candidate for MALLOC_HOLD treatment */
+ free(reg);
+ }
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);
-
- 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->func, reg->file, reg->lineno);
- }
+}
+
+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_hold_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_hold_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_hold_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_hold_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) {
+ memset(ptr, 0, size * nmemb);
+ }
+
+ return ptr;
+}
+
+void *__ast_hold_malloc(size_t size, const char *file, int lineno, const char *func)
+{
+ return __ast_hold_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
+}
+
+void *__ast_hold_malloc_cache(size_t size, const char *file, int lineno, const char *func)
+{
+ return __ast_hold_alloc_region(size, FUNC_MALLOC, file, lineno, func, 1);
+}
+
+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_malloc_cache(size_t size, const char *file, int lineno, const char *func)
+{
+ return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 1);
+}
+
+void __ast_free(void *ptr, const char *file, int lineno, const char *func)
+{
+ __ast_free_region(ptr, file, lineno, func);
+}
+
+void *__ast_hold_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
+{
+ void *tmp;
+ struct ast_region *reg = NULL;
+
+ if (ptr && !(reg = ptr2region(ptr))) {
+ astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
+ "line %d\n", ptr, func, file, lineno);
+ return NULL;
+ }
+
+ if (ptr && (size + sizeof(*reg) + sizeof(unsigned int) < reg->fullsize)) {
+ /* No need to allocate another block, but reset our FENCE_MAGIC now. */
+ unsigned int *fence = (unsigned int *) (reg->data + reg->len);
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->func, reg->file, reg->lineno);
- }
- free(reg);
- } else {
- astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
- ptr, func, file, lineno);
- }
-}
-
-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)
+ "line %d (ast_hold_realloc)\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
+ }
+ fence = (unsigned int *) reg->data + size;
+ reg->len = size;
+ put_unaligned_uint32(fence, FENCE_MAGIC);
+ return ptr;
+ }
+
+ if (!(tmp = __ast_hold_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
+ return NULL;
+
+ if (ptr) {
+ memcpy(tmp, ptr, reg->len);
+ __ast_free_region(ptr, file, lineno, func);
+ }
+
+ return tmp;
+}
+
+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))) {
+ struct ast_region *reg = NULL;
+
+ if (ptr && !(reg = ptr2region(ptr))) {
astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
"line %d\n", ptr, func, file, lineno);
return NULL;
@@ -240,32 +733,32 @@
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);
+ memcpy(tmp, ptr, reg->len);
__ast_free_region(ptr, file, lineno, func);
}
-
+
return tmp;
}
-char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
+char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
{
size_t len;
void *ptr;
- if (!s)
- return NULL;
+ if (!s) {
+ return NULL;
+ }
len = strlen(s) + 1;
- if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
+ 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)
+char *__ast_hold_strdup(const char *s, const char *file, int lineno, const char *func)
{
size_t len;
void *ptr;
@@ -274,12 +767,72 @@
return NULL;
len = strlen(s) + 1;
- if (len > n)
+ if ((ptr = __ast_hold_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)))
+ }
+ if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0))) {
strcpy(ptr, s);
+ }
return ptr;
+}
+
+char *__ast_hold_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_hold_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0))) {
+ strcpy(ptr, s);
+ }
+
+ return ptr;
+}
+
+int __ast_hold_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_hold_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_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
@@ -303,7 +856,26 @@
return size;
}
-int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
+int __ast_hold_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_hold_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
+ va_end(ap);
+ return -1;
+ }
+ vsnprintf(*strp, size + 1, fmt, 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;
@@ -322,6 +894,71 @@
return size;
}
+static char *handle_memory_show_free_available(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int sizes[100] = { 0, }, i, isfree = 0;
+ struct ast_region *reg, *prev = NULL, *master;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "memory show {free|available|polluted}";
+ e->usage =
+ "Usage: memory show {free|available|polluted}\n"
+ " Show memory held in various pools:\n"
+ " free - Memory released, but held back from reallocation\n"
+ " available - Expired memory that is available for reallocation\n"
+ " polluted - Expired memory that was accessed after it was freed\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (!strcasecmp(a->argv[2], "free")) {
+ master = regions[FREE_LIST];
+ isfree = 1;
+ } else if (!strcasecmp(a->argv[2], "available")) {
+ master = regions[AVAILABLE_LIST];
+ } else if (!strcasecmp(a->argv[2], "polluted")) {
+ master = regions[POLLUTED_LIST];
+ } else {
+ ast_cli(a->fd, "Unknown list %s\n", a->argv[2]);
+ return CLI_FAILURE;
+ }
+
+ ast_mutex_lock(®lock);
+
+ if (isfree && master) {
+ MPROTECT(master, sizeof(*master), PROT_READ);
+ ast_cli(a->fd, "Top item on the free list has a date of %d (now is %d) and it %s\n", master->freetime, (int) time(NULL), time(NULL) > master->freetime + HOLD_LENGTH ? "is already past due (BAD!)" : "hasn't expired yet");
+ }
+
+ for (reg = master; reg;
+ prev = reg,
+ reg = reg->next) {
+ int mysize;
+ if (isfree) {
+ MPROTECT(reg, sizeof(*reg), PROT_READ);
+ }
+ if ((mysize = reg->fullsize / pagesize) > 100) {
+ mysize = 100;
+ }
+ if (isfree && prev) {
+ MPROTECT(prev, sizeof(*prev), PROT_NONE);
+ }
+ sizes[mysize - 1]++;
+ }
+ if (isfree && prev) {
+ MPROTECT(prev, sizeof(*prev), PROT_NONE);
+ }
+ ast_mutex_unlock(®lock);
+
+ for (i = 0; i < ARRAY_LEN(sizes); i++) {
+ if (sizes[i] > 0) {
+ ast_cli(a->fd, "Size 4096 * %3d: %5d\n", i + 1, sizes[i]);
+ }
+ }
+ return CLI_SUCCESS;
+}
+
static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
char *fn = NULL;
@@ -351,22 +988,22 @@
ast_mutex_lock(®lock);
for (x = 0; x < SOME_PRIME; x++) {
for (reg = regions[x]; reg; reg = reg->next) {
- if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
+ 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->func, reg->file, reg->lineno);
+ 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->func, reg->file, reg->lineno);
+ "line %d (handle_show_memory)\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
}
}
- if (!fn || !strcasecmp(fn, reg->file)) {
+ if (!fn || !strcasecmp(fn, reg->alloc_file)) {
ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
(int) reg->len, reg->cache ? " (cache)" : "",
- reg->func, reg->lineno, reg->file);
+ reg->alloc_func, reg->alloc_lineno, reg->alloc_file);
len += reg->len;
if (reg->cache)
cache_len += reg->len;
@@ -375,12 +1012,12 @@
}
}
ast_mutex_unlock(®lock);
-
+
if (cache_len)
ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
else
ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
-
+
return CLI_SUCCESS;
}
@@ -399,7 +1036,7 @@
int count;
struct file_summary *next;
} *list = NULL, *cur;
-
+
switch (cmd) {
case CLI_INIT:
e->command = "memory show summary";
@@ -412,23 +1049,24 @@
return NULL;
}
- if (a->argc > 3)
+ if (a->argc > 3) {
fn = a->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->file))
+ if (fn && strcasecmp(fn, reg->alloc_file))
continue;
for (cur = list; cur; cur = cur->next) {
- if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
+ 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->func : reg->file, sizeof(cur->fn));
+ ast_copy_string(cur->fn, fn ? reg->alloc_func : reg->alloc_file, sizeof(cur->fn));
cur->next = list;
list = cur;
}
@@ -440,7 +1078,7 @@
}
}
ast_mutex_unlock(®lock);
-
+
/* Dump the whole list */
for (cur = list; cur; cur = cur->next) {
len += cur->len;
@@ -476,25 +1114,55 @@
static struct ast_cli_entry cli_memory[] = {
AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
+ AST_CLI_DEFINE(handle_memory_show_free_available, "Show memory in MALLOC_HOLD stages"),
};
+/*!
+ * 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];
size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
+ pthread_t notused;
+ struct sigaction sa = { .sa_flags = SA_SIGINFO | SA_NODEFER, .sa_sigaction = handle_sigsegv, };
+
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGSEGV, &sa, &default_sigsegv) == -1) {
+ ast_log(LOG_ERROR, "Cannot handle segmentation faults?");
+ abort();
+ }
+
+ if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) {
+ ast_log(LOG_ERROR, "Cannot retrieve system memory page size?");
+ abort();
+ }
+
+ /* Remove all memory protections before we exit */
+ ast_register_atexit(mm_cleanup);
+
+ ast_pthread_create_background(¬used, NULL, memory_destructor, NULL);
+ ast_pthread_create_background(¬used, NULL, memory_reporter, NULL);
if (pad) {
ast_log(LOG_ERROR, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad);
}
ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
-
+
snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
-
+
ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
-
+
if ((mmlog = fopen(filename, "a+"))) {
- fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
+ struct timeval now = ast_tvnow();
+ struct ast_tm tm;
+ char datetime[80];
+ ast_localtime(&now, &tm, NULL);
+ ast_strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", &tm);
+ fprintf(mmlog, "%s - New session\n", datetime);
fflush(mmlog);
}
}
Modified: team/tilghman/malloc_hold-1.6.2/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/malloc_hold-1.6.2/main/bridging.c?view=diff&rev=259359&r1=259358&r2=259359
[... 13 lines stripped ...]
More information about the asterisk-commits
mailing list