https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r78095 | russell | 2007-08-03 14:39:49 -0500 (Fri, 03 Aug 2007) | 28 lines Add some improvements to lock debugging. These changes take effect with DEBUG_THREADS enabled and provide the following: * This will keep track of which locks are held by which thread as well as which lock a thread is waiting for in a thread-local data structure. A reference to this structure is available on the stack in the dummy_start() function, which is the common entry point for all threads. This information can be easily retrieved using gdb if you switch to the dummy_start() stack frame of any thread and print the contents of the lock_info variable. * All of the thread-local structures for keeping track of this lock information are also stored in a list so that the information can be dumped to the CLI using the "core show locks" CLI command. This introduces a little bit of a performance hit as it requires additional underlying locking operations inside of every lock/unlock on an ast_mutex. However, the benefits of having this information available at the CLI is huge, especially considering this is only done in DEBUG_THREADS mode. It means that in most cases where we debug deadlocks, we no longer have to request access to the machine to analyze the contents of ast_mutex_t structures. We can now just ask them to get the output of "core show locks", which gives us all of the information we needed in most cases. I also had to make some additional changes to astmm.c to make this work when both MALLOC_DEBUG and DEBUG_THREADS are enabled. I disabled tracking of one of the locks in astmm.c because it gets used inside the replacement memory allocation routines, and the lock tracking code allocates memory. This caused infinite recursion. ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@78096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
474 lines
11 KiB
C
474 lines
11 KiB
C
/*
|
|
* Asterisk -- An open source telephony toolkit.
|
|
*
|
|
* Copyright (C) 1999 - 2006, Digium, Inc.
|
|
*
|
|
* Mark Spencer <markster@digium.com>
|
|
*
|
|
* See http://www.asterisk.org for more information about
|
|
* the Asterisk project. Please do not directly contact
|
|
* any of the maintainers of this project for assistance;
|
|
* the project provides a web site, mailing lists and IRC
|
|
* channels for your use.
|
|
*
|
|
* This program is free software, distributed under the terms of
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
* at the top of the source tree.
|
|
*/
|
|
|
|
/*! \file
|
|
*
|
|
* \brief Memory Management
|
|
*
|
|
* \author Mark Spencer <markster@digium.com>
|
|
*/
|
|
|
|
#ifdef __AST_DEBUG_MALLOC
|
|
|
|
#include "asterisk.h"
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "asterisk/cli.h"
|
|
#include "asterisk/logger.h"
|
|
#include "asterisk/options.h"
|
|
#include "asterisk/lock.h"
|
|
#include "asterisk/strings.h"
|
|
#include "asterisk/unaligned.h"
|
|
|
|
#define SOME_PRIME 563
|
|
|
|
enum func_type {
|
|
FUNC_CALLOC = 1,
|
|
FUNC_MALLOC,
|
|
FUNC_REALLOC,
|
|
FUNC_STRDUP,
|
|
FUNC_STRNDUP,
|
|
FUNC_VASPRINTF,
|
|
FUNC_ASPRINTF
|
|
};
|
|
|
|
/* Undefine all our macros */
|
|
#undef malloc
|
|
#undef calloc
|
|
#undef realloc
|
|
#undef strdup
|
|
#undef strndup
|
|
#undef free
|
|
#undef vasprintf
|
|
#undef asprintf
|
|
|
|
#define FENCE_MAGIC 0xdeadbeef
|
|
|
|
static FILE *mmlog;
|
|
|
|
static struct ast_region {
|
|
struct ast_region *next;
|
|
char file[40];
|
|
char func[40];
|
|
unsigned int lineno;
|
|
enum func_type which;
|
|
unsigned int cache; /* region was allocated as part of a cache pool */
|
|
size_t len;
|
|
unsigned int fence;
|
|
unsigned char data[0];
|
|
} *regions[SOME_PRIME];
|
|
|
|
#define HASH(a) \
|
|
(((unsigned long)(a)) % SOME_PRIME)
|
|
|
|
/*! Tracking this mutex will cause infinite recursion, as the mutex tracking
|
|
* code allocates memory */
|
|
AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
|
|
AST_MUTEX_DEFINE_STATIC(showmemorylock);
|
|
|
|
#define astmm_log(...) \
|
|
do { \
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
if (mmlog) { \
|
|
fprintf(mmlog, __VA_ARGS__); \
|
|
fflush(mmlog); \
|
|
} \
|
|
} 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;
|
|
|
|
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);
|
|
}
|
|
|
|
ast_copy_string(reg->file, file, sizeof(reg->file));
|
|
ast_copy_string(reg->func, func, sizeof(reg->func));
|
|
reg->lineno = lineno;
|
|
reg->len = size;
|
|
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;
|
|
}
|
|
|
|
static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
|
|
{
|
|
int hash = HASH(ptr);
|
|
struct ast_region *reg, *prev = NULL;
|
|
unsigned int *fence;
|
|
|
|
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->func, reg->file, reg->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);
|
|
}
|
|
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)
|
|
{
|
|
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(&showmemorylock);
|
|
for (x = 0; x < SOME_PRIME; x++) {
|
|
for (reg = regions[x]; reg; reg = reg->next) {
|
|
if (!fn || !strcasecmp(fn, reg->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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
if (!fn || !strcasecmp(fn, reg->file)) {
|
|
ast_cli(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);
|
|
len += reg->len;
|
|
if (reg->cache)
|
|
cache_len += reg->len;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
ast_mutex_unlock(&showmemorylock);
|
|
|
|
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->file))
|
|
continue;
|
|
|
|
for (cur = list; cur; cur = cur->next) {
|
|
if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->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));
|
|
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_memory[] = {
|
|
{ { "memory", "show", "allocations", NULL },
|
|
handle_show_memory, "Display outstanding memory allocations",
|
|
show_memory_help },
|
|
|
|
{ { "memory", "show", "summary", NULL },
|
|
handle_show_memory_summary, "Summarize outstanding memory allocations",
|
|
show_memory_summary_help },
|
|
};
|
|
|
|
void __ast_mm_init(void)
|
|
{
|
|
char filename[PATH_MAX];
|
|
|
|
ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
|
|
|
|
snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
|
|
|
|
if (option_verbose)
|
|
ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
|
|
|
|
if ((mmlog = fopen(filename, "a+"))) {
|
|
fprintf(mmlog, "%ld - New session\n", time(NULL));
|
|
fflush(mmlog);
|
|
}
|
|
}
|
|
|
|
#endif
|