Newer
Older
Mark Spencer
committed
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Variables
*
* Copyright (C) 2002, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#ifdef __AST_DEBUG_MALLOC
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
Mark Spencer
committed
#include <asterisk/cli.h>
#include <asterisk/logger.h>
#include <asterisk/options.h>
Mark Spencer
committed
#define SOME_PRIME 563
#define FUNC_CALLOC 1
#define FUNC_MALLOC 2
#define FUNC_REALLOC 3
#define FUNC_STRDUP 4
#define FUNC_STRNDUP 5
/* Undefine all our macros */
#undef malloc
#undef calloc
#undef realloc
#undef strdup
#undef strndup
#undef free
Mark Spencer
committed
static struct ast_region {
struct ast_region *next;
char file[40];
char func[40];
int lineno;
int which;
size_t len;
unsigned char data[0];
} *regions[SOME_PRIME];
#define HASH(a) \
(((unsigned long)(a)) % SOME_PRIME)
static ast_mutex_t reglock = AST_MUTEX_INITIALIZER;
Mark Spencer
committed
static inline void *__ast_alloc_region(size_t size, int which, const char *file, int lineno, const char *func)
{
struct ast_region *reg;
void *ptr=NULL;
int hash;
reg = malloc(size + sizeof(struct ast_region));
ast_mutex_lock(®lock);
Mark Spencer
committed
if (reg) {
strncpy(reg->file, file, sizeof(reg->file) - 1);
reg->file[sizeof(reg->file) - 1] = '\0';
strncpy(reg->func, func, sizeof(reg->func) - 1);
reg->func[sizeof(reg->func) - 1] = '\0';
reg->lineno = lineno;
reg->len = size;
reg->which = which;
ptr = reg->data;
hash = HASH(ptr);
reg->next = regions[hash];
regions[hash] = reg;
}
ast_mutex_unlock(®lock);
Mark Spencer
committed
if (!reg) {
fprintf(stderr, "Out of memory :(\n");
if (mmlog) {
fprintf(mmlog, "%ld - Out of memory\n", time(NULL));
fflush(mmlog);
}
Mark Spencer
committed
}
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);
Mark Spencer
committed
reg = regions[hash];
while(reg) {
if (reg->data == ptr) {
len = reg->len;
break;
}
reg = reg->next;
}
ast_mutex_unlock(®lock);
Mark Spencer
committed
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;
ast_mutex_lock(®lock);
Mark Spencer
committed
reg = regions[hash];
while(reg) {
if (reg->data == ptr) {
if (prev)
prev->next = reg->next;
else
regions[hash] = reg->next;
break;
}
prev = reg;
reg = reg->next;
}
ast_mutex_unlock(®lock);
Mark Spencer
committed
if (reg) {
free(reg);
Mark Spencer
committed
fprintf(stderr, "WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
ptr, func, file, lineno);
fprintf(mmlog, "%ld - WARNING: Freeing unused memory at %p, in %s of %s, line %d\n", time(NULL),
ptr, func, file, lineno);
Mark Spencer
committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
}
void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
{
void *ptr;
ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func);
if (ptr)
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);
}
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);
if (!len) {
fprintf(stderr, "WARNING: Realloc of unalloced memory at %p, in %s of %s, line %d\n",
ptr, func, file, lineno);
fprintf(mmlog, "%ld - WARNING: Realloc of unalloced memory at %p, in %s of %s, line %d\n",
time(NULL), ptr, func, file, lineno);
Mark Spencer
committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
return NULL;
}
}
tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func);
if (tmp) {
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;
ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func);
if (ptr)
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;
ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func);
if (ptr)
strcpy(ptr, s);
return ptr;
}
static int handle_show_memory(int fd, int argc, char *argv[])
{
char *fn = NULL;
int x;
struct ast_region *reg;
unsigned int len=0;
int count = 0;
if (argc >3)
fn = argv[3];
/* try to lock applications list ... */
ast_mutex_lock(®lock);
Mark Spencer
committed
for (x=0;x<SOME_PRIME;x++) {
reg = regions[x];
while(reg) {
if (!fn || !strcasecmp(fn, reg->file)) {
ast_cli(fd, "%10d bytes allocated in %20s at line %5d of %s\n", reg->len, reg->func, reg->lineno, reg->file);
len += reg->len;
count++;
}
reg = reg->next;
}
}
ast_cli(fd, "%d bytes allocated %d units total\n", len, count);
ast_mutex_unlock(®lock);
Mark Spencer
committed
return RESULT_SUCCESS;
}
struct file_summary {
char fn[80];
int len;
int count;
struct file_summary *next;
};
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;
int count = 0;
struct file_summary *list = NULL, *cur;
if (argc >3)
fn = argv[3];
/* try to lock applications list ... */
ast_mutex_lock(®lock);
Mark Spencer
committed
for (x=0;x<SOME_PRIME;x++) {
reg = regions[x];
while(reg) {
if (!fn || !strcasecmp(fn, reg->file)) {
cur = list;
while(cur) {
if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
break;
cur = cur->next;
}
if (!cur) {
cur = alloca(sizeof(struct file_summary));
memset(cur, 0, sizeof(struct file_summary));
strncpy(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn) - 1);
cur->next = list;
list = cur;
}
cur->len += reg->len;
cur->count++;
}
reg = reg->next;
}
}
ast_mutex_unlock(®lock);
Mark Spencer
committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/* Dump the whole list */
while(list) {
cur = list;
len += list->len;
count += list->count;
if (fn)
ast_cli(fd, "%10d bytes in %5d allocations in function '%s' of '%s'\n", list->len, list->count, list->fn, fn);
else
ast_cli(fd, "%10d bytes in %5d allocations in file '%s'\n", list->len, list->count, list->fn);
list = list->next;
#if 0
free(cur);
#endif
}
ast_cli(fd, "%d bytes allocated %d units total\n", len, count);
return RESULT_SUCCESS;
}
static char show_memory_help[] =
"Usage: show memory 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: show memory 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 show_memory_allocations_cli =
{ { "show", "memory", "allocations", NULL },
handle_show_memory, "Display outstanding memory allocations",
show_memory_help };
static struct ast_cli_entry show_memory_summary_cli =
{ { "show", "memory", "summary", NULL },
handle_show_memory_summary, "Summarize outstanding memory allocations",
show_memory_summary_help };
void __ast_mm_init(void)
{
ast_cli_register(&show_memory_allocations_cli);
ast_cli_register(&show_memory_summary_cli);
mmlog = fopen("/var/log/asterisk/mmlog", "a+");
if (option_verbose)
ast_verbose("Asterisk Malloc Debugger Started (see /var/log/asterisk/mmlog)\n");
fprintf(mmlog, "%ld - New session\n", time(NULL));