Fixed some memory leaks around the tree.
+Mon Jun 17 21:30:55 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
+
+ * Added --enable-stack-trace option to configure. Added
+ memory allocation stack trace support. Added files
+ lib/silcutil/stacktrace.[ch]. Affected files are
+ lib/silcutil/silcmemory.[ch].
+
+ * Fixed memory leaks from libraries and server. Affected
+ files around the tree.
+
Sun Jun 16 11:49:45 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
* Added SILC_MESSAGE_FLAG_UTF8 to the protocol specs and the
/* Debugging */
#undef SILC_DEBUG
+#undef SILC_STACKTRACE
/* Multi-thread support */
#undef SILC_HAVE_PTHREAD
void silc_server_free(SilcServer server)
{
- if (server) {
+ int i;
+
+ if (!server)
+ return;
+
#ifdef SILC_SIM
+ {
SilcSim sim;
-
+
while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
silc_dlist_del(server->sim, sim);
silc_sim_free(sim);
}
silc_dlist_uninit(server->sim);
+ }
#endif
- silc_server_config_unref(&server->config_ref);
- if (server->rng)
- silc_rng_free(server->rng);
- if (server->pkcs)
- silc_pkcs_free(server->pkcs);
- if (server->public_key)
- silc_pkcs_public_key_free(server->public_key);
- if (server->private_key)
- silc_pkcs_private_key_free(server->private_key);
- if (server->pending_commands)
- silc_dlist_uninit(server->pending_commands);
- if (server->id_entry)
- silc_idlist_del_server(server->local_list, server->id_entry);
-
- silc_idcache_free(server->local_list->clients);
- silc_idcache_free(server->local_list->servers);
- silc_idcache_free(server->local_list->channels);
- silc_idcache_free(server->global_list->clients);
- silc_idcache_free(server->global_list->servers);
- silc_idcache_free(server->global_list->channels);
- silc_hash_table_free(server->watcher_list);
-
- silc_free(server->sockets);
- silc_free(server);
+ for (i = 0; i < server->config->param.connections_max; i++) {
+ if (!server->sockets[i])
+ continue;
+ silc_socket_free(server->sockets[i]);
}
+ silc_free(server->sockets);
+
+ silc_server_config_unref(&server->config_ref);
+ if (server->rng)
+ silc_rng_free(server->rng);
+ if (server->pkcs)
+ silc_pkcs_free(server->pkcs);
+ if (server->public_key)
+ silc_pkcs_public_key_free(server->public_key);
+ if (server->private_key)
+ silc_pkcs_private_key_free(server->private_key);
+ if (server->pending_commands)
+ silc_dlist_uninit(server->pending_commands);
+ if (server->id_entry)
+ silc_idlist_del_server(server->local_list, server->id_entry);
+
+ silc_idcache_free(server->local_list->clients);
+ silc_idcache_free(server->local_list->servers);
+ silc_idcache_free(server->local_list->channels);
+ silc_idcache_free(server->global_list->clients);
+ silc_idcache_free(server->global_list->servers);
+ silc_idcache_free(server->global_list->channels);
+ silc_hash_table_free(server->watcher_list);
+
+ silc_hash_free(server->md5hash);
+ silc_hash_free(server->sha1hash);
+ silc_hmac_unregister_all();
+ silc_hash_unregister_all();
+ silc_cipher_unregister_all();
+ silc_pkcs_unregister_all();
+
+ silc_free(server->local_list);
+ silc_free(server->global_list);
+ silc_free(server->server_name);
+ silc_free(server->id_string);
+ silc_free(server->purge_i);
+ silc_free(server->purge_g);
+ silc_free(server);
}
/* Creates a new server listener. */
}
/* Adds a secondary listener. */
+
bool silc_server_init_secondary(SilcServer server)
{
- int sock=0, sock_list[server->config->param.connections_max];
+ int sock = 0, sock_list[server->config->param.connections_max];
SilcSocketConnection newsocket = NULL;
SilcServerConfigServerInfoInterface *interface;
for (interface = server->config->server_info->secondary; interface;
interface = interface->next, sock++) {
-
+
if (!silc_server_listen(server,
interface->server_ip, interface->port, &sock_list[sock]))
goto err;
/* Add ourselves also to the socket table. The entry allocated above
is sent as argument for fast referencing in the future. */
- silc_socket_alloc(sock_list[sock],
- SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+ silc_socket_alloc(sock_list[sock],
+ SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
server->sockets[sock_list[sock]] = newsocket;
/* Perform name and address lookups to resolve the listenning address
newsocket->hostname = strdup(newsocket->ip);
}
newsocket->port = silc_net_get_local_port(sock);
-
+
newsocket->user_data = (void *)server->id_entry;
silc_schedule_task_add(server->schedule, sock_list[sock],
silc_server_accept_new_connection,
(void *)server, 0, 0,
SILC_TASK_FD,
SILC_TASK_PRI_NORMAL);
-
}
return TRUE;
-
-err:
+ err:
do silc_net_close_server(sock_list[sock--]); while (sock >= 0);
return FALSE;
-
}
/* Initializes the entire SILC server. This is called always before running
and removes the expired cache entries. */
/* Clients local list */
- purge = silc_calloc(1, sizeof(*purge));
+ server->purge_i = purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->local_list->clients;
purge->schedule = server->schedule;
purge->timeout = 600;
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
/* Clients global list */
- purge = silc_calloc(1, sizeof(*purge));
+ server->purge_g = purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->global_list->clients;
purge->schedule = server->schedule;
purge->timeout = 300;
/* Pending command queue */
SilcDList pending_commands;
+ /* Purge context for disconnected clients */
+ SilcIDListPurge purge_i;
+ SilcIDListPurge purge_g;
+
#ifdef SILC_SIM
/* SIM (SILC Module) list */
SilcDList sim;
silc_free(config->logging_errors->file);
if (config->logging_fatals)
silc_free(config->logging_fatals->file);
+ silc_free(config->logging_info);
+ silc_free(config->logging_warnings);
+ silc_free(config->logging_errors);
+ silc_free(config->logging_fatals);
+
+ silc_log_set_file(SILC_LOG_INFO, NULL, 0, NULL);
+ silc_log_set_file(SILC_LOG_WARNING, NULL, 0, NULL);
+ silc_log_set_file(SILC_LOG_ERROR, NULL, 0, NULL);
+ silc_log_set_file(SILC_LOG_FATAL, NULL, 0, NULL);
/* Destroy the ServerInfo struct */
if (config->server_info) {
silc_free(si->pid_file);
silc_pkcs_public_key_free(si->public_key);
silc_pkcs_private_key_free(si->private_key);
+ silc_free(si);
}
/* Now let's destroy the lists */
/* Flush the logging system */
silc_log_flush_all();
+ silc_free(silcd_config_file);
+ silc_free(opt_identifier);
+ silc_free(opt_keypath);
exit(0);
+
fail:
+ silc_free(silcd_config_file);
+ silc_free(opt_identifier);
+ silc_free(opt_keypath);
exit(1);
}
esac ], CFLAGS="-O2 -g $CFLAGS"
AC_MSG_RESULT(no))
+AC_MSG_CHECKING(for enabled stack tracing)
+AC_ARG_ENABLE(debug,
+[ --enable-stack-trace Enable memory stack trace],
+[ case "${enableval}" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(SILC_STACKTRACE)
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+esac ], AC_MSG_RESULT(no))
+
#
# Disable all assembler optimizations
#
return TRUE;
}
+bool silc_cipher_unregister_all(void)
+{
+#ifndef SILC_EPOC
+ SilcCipherObject *entry;
+
+ if (!silc_cipher_list)
+ return FALSE;
+
+ silc_dlist_start(silc_cipher_list);
+ while ((entry = silc_dlist_get(silc_cipher_list)) != SILC_LIST_END) {
+ silc_cipher_unregister(entry);
+ if (!silc_cipher_list)
+ break;
+ }
+#endif /* SILC_EPOC */
+ return TRUE;
+}
+
/* Allocates a new SILC cipher object. Function returns 1 on succes and 0
on error. The allocated cipher is returned in new_cipher argument. The
caller must set the key to the cipher after this function has returned
bool silc_cipher_register(const SilcCipherObject *cipher);
bool silc_cipher_unregister(SilcCipherObject *cipher);
bool silc_cipher_register_default(void);
+bool silc_cipher_unregister_all(void);
bool silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher);
void silc_cipher_free(SilcCipher cipher);
bool silc_cipher_is_supported(const unsigned char *name);
while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
if (hash == SILC_ALL_HASH_FUNCTIONS || entry == hash) {
silc_dlist_del(silc_hash_list, entry);
+ silc_free(entry->name);
+ silc_free(entry);
if (silc_dlist_count(silc_hash_list) == 0) {
silc_dlist_uninit(silc_hash_list);
return TRUE;
}
+bool silc_hash_unregister_all(void)
+{
+#ifndef SILC_EPOC
+ SilcHashObject *entry;
+
+ if (!silc_hash_list)
+ return FALSE;
+
+ silc_dlist_start(silc_hash_list);
+ while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
+ silc_hash_unregister(entry);
+ if (!silc_hash_list)
+ break;
+ }
+#endif /* SILC_EPOC */
+ return TRUE;
+}
+
/* Allocates a new SilcHash object. New object is returned into new_hash
argument. */
***/
bool silc_hash_register_default(void);
+/****f* silccrypt/SilcHashAPI/silc_hash_unregister_all
+ *
+ * SYNOPSIS
+ *
+ * bool silc_hash_unregister_all(void);
+ *
+ * DESCRIPTION
+ *
+ * Unregisters all registered hash functions.
+ *
+ ***/
+bool silc_hash_unregister_all(void);
+
/****f* silccrypt/SilcHashAPI/silc_hash_alloc
*
* SYNOPSIS
while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
if (hmac == SILC_ALL_HMACS || entry == hmac) {
silc_dlist_del(silc_hmac_list, entry);
+ silc_free(entry->name);
+ silc_free(entry);
if (silc_dlist_count(silc_hmac_list) == 0) {
silc_dlist_uninit(silc_hmac_list);
return TRUE;
}
+bool silc_hmac_unregister_all(void)
+{
+#ifndef SILC_EPOC
+ SilcHmacObject *entry;
+
+ if (!silc_hmac_list)
+ return FALSE;
+
+ silc_dlist_start(silc_hmac_list);
+ while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+ silc_hmac_unregister(entry);
+ if (!silc_hmac_list)
+ break;
+ }
+#endif /* SILC_EPOC */
+ return TRUE;
+}
+
/* Allocates a new SilcHmac object of name of `name'. The `hash' may
be provided as argument. If provided it is used as the hash function
of the HMAC. If it is NULL then the hash function is allocated and
***/
bool silc_hmac_register_default(void);
+/****f* silccrypt/SilcHMACAPI/silc_hmac_unregister_all
+ *
+ * SYNOPSIS
+ *
+ * bool silc_hmac_unregister_all(void);
+ *
+ * DESCRIPTION
+ *
+ * Unregisters all registered HMACs.
+ *
+ ***/
+bool silc_hmac_unregister_all(void);
+
/****f* silccrypt/SilcHMACAPI/silc_hmac_alloc
*
* SYNOPSIS
while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
if (pkcs == SILC_ALL_PKCS || entry == pkcs) {
silc_dlist_del(silc_pkcs_list, entry);
+ silc_free(entry->name);
+ silc_free(entry);
if (silc_dlist_count(silc_pkcs_list) == 0) {
silc_dlist_uninit(silc_pkcs_list);
return TRUE;
}
+bool silc_pkcs_unregister_all(void)
+{
+#ifndef SILC_EPOC
+ SilcPKCSObject *entry;
+
+ if (!silc_pkcs_list)
+ return FALSE;
+
+ silc_dlist_start(silc_pkcs_list);
+ while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
+ silc_pkcs_unregister(entry);
+ if (!silc_pkcs_list)
+ break;
+ }
+#endif /* SILC_EPOC */
+ return TRUE;
+}
+
/* Allocates a new SilcPKCS object. The new allocated object is returned
to the 'new_pkcs' argument. */
bool silc_pkcs_unregister(SilcPKCSObject *pkcs);
bool silc_pkcs_register_default(void);
+bool silc_pkcs_unregister_all(void);
bool silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs);
void silc_pkcs_free(SilcPKCS pkcs);
int silc_pkcs_is_supported(const unsigned char *name);
void silc_rng_free(SilcRng rng)
{
if (rng) {
+ SilcRngState t, n;
+
memset(rng->pool, 0, sizeof(rng->pool));
memset(rng->key, 0, sizeof(rng->key));
silc_hash_free(rng->sha1);
if (rng->fd_devurandom != -1)
close(rng->fd_devurandom);
+ for (t = rng->state->next; t != rng->state; ) {
+ n = t->next;
+ silc_free(t);
+ t = n;
+ }
+ silc_free(rng->state);
+
silc_free(rng);
}
}
silcutil.c \
silchashtable.c \
silcsockconn.c \
- silcprotocol.c
+ silcprotocol.c \
+ stacktrace.c
if SILC_DIST_TOOLKIT
include_HEADERS = \
if (file) {
/* XXX FIXME: this check could probably be removed later */
SilcUInt32 my_len = (SilcUInt32) (strchr(file->base, EOF) - file->base);
- SILC_CONFIG_DEBUG(("file=0x%x name=\"%s\" level=%d line=%lu", (SilcUInt32) file,
+ SILC_CONFIG_DEBUG(("file=0x%x name=\"%s\" level=%d line=%lu",
+ (SilcUInt32) file,
file->filename, file->level, file->line));
if (my_len != file->len) {
- fprintf(stderr, "FATAL ERROR: saved len and current len does not match!\n");
+ fprintf(stderr,
+ "FATAL ERROR: saved len and current len does not match!\n");
abort();
}
silc_free(file->filename);
long silc_log_flushdelay = 300;
/* Regular pattern matching expression for the debug output */
-char *silc_log_debug_string = NULL;
+char silc_log_debug_string[128];
/* Debug callbacks. If set, these are triggered for each specific output. */
static SilcLogDebugCb silc_log_debug_cb = NULL;
SilcLog log;
log = silc_log_find_by_type(type);
- if (!log || !scheduler)
+ if (!log)
return FALSE;
SILC_LOG_DEBUG(("Setting \"%s\" file to %s (max size=%d)",
log->maxsize = maxsize;
}
- if (silc_log_scheduled)
- return TRUE;
+ if (scheduler) {
+ if (silc_log_scheduled)
+ return TRUE;
- /* add schedule hook with a short delay to make sure we'll use right delay */
- silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback,
- (void *) scheduler, 10, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
- silc_log_scheduled = TRUE;
+ /* Add schedule hook with a short delay to make sure we'll use
+ right delay */
+ silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback,
+ (void *) scheduler, 10, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_log_scheduled = TRUE;
+ }
return TRUE;
}
void silc_log_set_debug_string(const char *debug_string)
{
- silc_free(silc_log_debug_string);
+ char *string;
if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
strchr(debug_string, '$'))
- silc_log_debug_string = strdup(debug_string);
+ string = strdup(debug_string);
else
- silc_log_debug_string = silc_string_regexify(debug_string);
+ string = silc_string_regexify(debug_string);
+ strncpy(silc_log_debug_string, string, strlen(string));
+ silc_free(string);
}
*
* SYNOPSIS
*
- * bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize,
+ * bool silc_log_set_file(SilcLogType type, char *filename,
+ * SilcUInt32 maxsize,
* SilcSchedule scheduler);
*
* DESCRIPTION
#include "silcincludes.h"
+#ifndef SILC_STACKTRACE
+
void *silc_malloc(size_t size)
{
void *addr;
addr[size] = '\0';
return (void *)addr;
}
+
+#endif /* !SILC_STACKTRACE */
/* Prototypes */
+#ifndef SILC_STACKTRACE
+
/****f* silcutil/SilcMemoryAPI/silc_malloc
*
* SYNOPSIS
***/
void *silc_memdup(const void *ptr, size_t size);
+#else
+#include "stacktrace.h"
+#endif /* SILC_STACKTRACE */
+
#endif /* SILCMEMORY_H */
silc_schedule_internal_uninit(schedule->internal);
silc_mutex_free(schedule->lock);
+ silc_free(schedule);
return TRUE;
}
--- /dev/null
+/*
+
+ stacktrace.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2002 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+
+#ifdef SILC_STACKTRACE
+
+static void *st_blocks = NULL;
+static unsigned long st_blocks_count = 0;
+static int dump = FALSE;
+
+#define SILC_ST_DEPTH 10
+
+/* Memory block with stack trace */
+typedef struct SilcStBlockStruct {
+ unsigned int dumpped : 1; /* Block is dumpped */
+ unsigned int depth : 8; /* Depth of stack trace */
+ unsigned int line : 23; /* Allocation line in program */
+ void *stack[SILC_ST_DEPTH]; /* Stack trace */
+ const char *file; /* Allocation file in program */
+ unsigned long size; /* Allocated memory size */
+ struct SilcStBlockStruct *next;
+ struct SilcStBlockStruct *prev;
+} *SilcStBlock;
+
+/* Get current frame pointer */
+#define SILC_ST_GET_FP(ret_fp) \
+do { \
+ register void *cfp; \
+ asm volatile ("movl %%ebp, %0" : "=r" (cfp)); \
+ (ret_fp) = cfp; \
+} while(0);
+
+#define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct)))
+#define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) - \
+ sizeof(struct SilcStBlockStruct)))
+#define SILC_ST_GET_PTR(p) (((unsigned char *)p) + \
+ sizeof(struct SilcStBlockStruct))
+
+void silc_st_stacktrace(SilcStBlock stack)
+{
+ void *fp;
+
+ if (!dump) {
+ atexit(silc_st_dump);
+ dump = TRUE;
+ }
+
+ /* Save the stack */
+ SILC_ST_GET_FP(fp);
+ for (stack->depth = 0; fp; stack->depth++) {
+ if (stack->depth == SILC_ST_DEPTH)
+ break;
+
+ /* Get program pointer and frame pointer from this frame */
+ stack->stack[stack->depth] = *((void **)(((unsigned char *)fp) + 4));
+ fp = *((void **)fp);
+ }
+}
+
+void *silc_st_malloc(size_t size, const char *file, int line)
+{
+ SilcStBlock stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
+ assert(stack != NULL);
+
+ stack->dumpped = 0;
+ stack->file = file;
+ stack->line = line;
+ stack->size = size;
+ silc_st_stacktrace(stack);
+
+ stack->next = st_blocks;
+ stack->prev = NULL;
+ if (st_blocks)
+ ((SilcStBlock)st_blocks)->prev = stack;
+ st_blocks = stack;
+ st_blocks_count++;
+
+ return SILC_ST_GET_PTR(stack);
+}
+
+void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
+{
+ void *addr = (void *)silc_st_malloc(items * size, file, line);
+ memset(addr, 0, items * size);
+ return addr;
+}
+
+void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
+{
+ SilcStBlock stack;
+
+ if (!ptr)
+ return silc_st_malloc(size, file, line);
+
+ stack = SILC_ST_GET_STACK(ptr);
+ if (stack->size >= size) {
+ stack->size = size;
+ return ptr;
+ } else {
+ void *addr = (void *)silc_st_malloc(size, file, line);
+ memcpy(addr, ptr, stack->size);
+ silc_st_free(ptr, file, line);
+ return addr;
+ }
+}
+
+void silc_st_free(void *ptr, const char *file, int line)
+{
+ SilcStBlock stack;
+
+ if (!ptr)
+ return;
+
+ stack = SILC_ST_GET_STACK(ptr);
+ if (stack->next)
+ stack->next->prev = stack->prev;
+ if (stack->prev)
+ stack->prev->next = stack->next;
+ else
+ st_blocks = stack->next;
+
+ st_blocks_count--;
+ free(stack);
+}
+
+void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
+{
+ unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
+ memcpy((void *)addr, ptr, size);
+ addr[size] = '\0';
+ return (void *)addr;
+}
+
+void *silc_st_strdup(const char *string, const char *file, int line)
+{
+ return silc_st_memdup(string, strlen(string), file, line);
+}
+
+/* Dumps the stack into file if there are leaks. The file can be read
+ with a special stacktrace tool. */
+
+void silc_st_dump(void)
+{
+ SilcStBlock stack, s;
+ unsigned long leaks = 0, blocks, bytes;
+ FILE *fp = NULL;
+ int i;
+
+ for (stack = st_blocks; stack; stack = stack->next) {
+ bytes = blocks = 0;
+
+ if (stack->dumpped)
+ continue;
+
+ leaks++;
+
+ if (!fp) {
+ fp = fopen("stacktrace.log", "wb");
+ if (!fp)
+ fp = stderr;
+ }
+
+ for (s = stack; s; s = s->next) {
+ if (s->file == stack->file && s->line == stack->line &&
+ s->depth == stack->depth &&
+ !memcmp(s->stack, stack->stack,
+ (s->depth * sizeof(stack->stack[0])))) {
+ blocks++;
+ bytes += s->size;
+ s->dumpped = 1;
+ }
+ }
+
+ if (blocks) {
+ fprintf(fp, "<stacktrace>%s:%d: #blocks=%lu, bytes=%lu\n",
+ stack->file, stack->line, blocks, bytes);
+ for (i = 0; i < stack->depth; i++)
+ fprintf(fp, "<pc>%p\n", stack->stack[i]);
+ }
+ }
+
+ if (!leaks) {
+ fprintf(stderr, "\nNo memory leaks\n");
+ } else {
+ fprintf(stderr,
+ "-----------------------------------------\n"
+ "-----------------------------------------\n"
+ " Memory leaks dumped to 'stacktrace.log'\n"
+ " Leaks: %lu leaks, %lu blocks\n"
+ "-----------------------------------------\n"
+ "-----------------------------------------\n",
+ leaks, st_blocks_count);
+ }
+
+ if (fp && fp != stderr)
+ fclose(fp);
+}
+
+#endif /* SILC_STACKTRACE */
--- /dev/null
+/*
+
+ stacktrace.h
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2002 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+
+#ifndef STACKTRACE_H
+#define STACKTRACE_H
+
+#ifndef SILCMEMORY_H
+#error "Do not include this file directly"
+#endif
+
+#if defined(__GNUC__) && defined(__i386__)
+
+#undef strdup
+#define silc_malloc(s) silc_st_malloc((s), __FILE__, __LINE__)
+#define silc_calloc(i, s) silc_st_calloc((i), (s), __FILE__, __LINE__)
+#define silc_realloc(p, s) silc_st_realloc((p), (s), __FILE__, __LINE__)
+#define silc_free(p) silc_st_free((p), __FILE__, __LINE__)
+#define silc_memdup(p, s) silc_st_memdup((p), (s), __FILE__, __LINE__)
+#define strdup(s) silc_st_strdup((s), __FILE__, __LINE__)
+
+void *silc_st_malloc(size_t size, const char *file, int line);
+void *silc_st_calloc(size_t items, size_t size, const char *file, int line);
+void *silc_st_realloc(void *ptr, size_t size, const char *file, int line);
+void silc_st_free(void *ptr, const char *file, int line);
+void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line);
+void *silc_st_strdup(const char *string, const char *file, int line);
+void silc_st_dump(void);
+
+#else
+#error "memory allocation stack trace not supported on this platform"
+#endif /* __GNUC__ && __i386__ */
+
+#endif /* STACKTRACE_H */