From e2c551b9693b6d42e5997b9df416a17fb94c1ccb Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Mon, 17 Jun 2002 19:54:17 +0000 Subject: [PATCH] Added stacktrace support with --enable-stack-trace option. Fixed some memory leaks around the tree. --- CHANGES | 10 ++ acconfig.h.pre | 1 + apps/silcd/server.c | 96 ++++++++++------ apps/silcd/server_internal.h | 4 + apps/silcd/serverconfig.c | 10 ++ apps/silcd/silcd.c | 7 ++ configure.in.pre | 13 +++ lib/silccrypt/silccipher.c | 18 +++ lib/silccrypt/silccipher.h | 1 + lib/silccrypt/silchash.c | 20 ++++ lib/silccrypt/silchash.h | 13 +++ lib/silccrypt/silchmac.c | 20 ++++ lib/silccrypt/silchmac.h | 13 +++ lib/silccrypt/silcpkcs.c | 20 ++++ lib/silccrypt/silcpkcs.h | 1 + lib/silccrypt/silcrng.c | 9 ++ lib/silcutil/Makefile.am | 3 +- lib/silcutil/silcconfig.c | 6 +- lib/silcutil/silclog.c | 30 ++--- lib/silcutil/silclog.h | 3 +- lib/silcutil/silcmemory.c | 4 + lib/silcutil/silcmemory.h | 6 + lib/silcutil/silcschedule.c | 1 + lib/silcutil/stacktrace.c | 216 +++++++++++++++++++++++++++++++++++ lib/silcutil/stacktrace.h | 49 ++++++++ 25 files changed, 520 insertions(+), 54 deletions(-) create mode 100644 lib/silcutil/stacktrace.c create mode 100644 lib/silcutil/stacktrace.h diff --git a/CHANGES b/CHANGES index 21346a55..f33435d5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,13 @@ +Mon Jun 17 21:30:55 EEST 2002 Pekka Riikonen + + * 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 * Added SILC_MESSAGE_FLAG_UTF8 to the protocol specs and the diff --git a/acconfig.h.pre b/acconfig.h.pre index 5b61eb24..bd9e10da 100644 --- a/acconfig.h.pre +++ b/acconfig.h.pre @@ -15,6 +15,7 @@ /* Debugging */ #undef SILC_DEBUG +#undef SILC_STACKTRACE /* Multi-thread support */ #undef SILC_HAVE_PTHREAD diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 5cbe0bbc..10530e00 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -77,42 +77,66 @@ int silc_server_alloc(SilcServer *new_server) 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. */ @@ -130,15 +154,16 @@ static bool silc_server_listen(SilcServer server, const char *server_ip, } /* 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; @@ -148,8 +173,8 @@ bool silc_server_init_secondary(SilcServer server) /* 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 @@ -168,23 +193,20 @@ bool silc_server_init_secondary(SilcServer server) 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 @@ -394,7 +416,7 @@ bool silc_server_init(SilcServer server) 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; @@ -404,7 +426,7 @@ bool silc_server_init(SilcServer server) 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; diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h index c435c776..7b27b6c3 100644 --- a/apps/silcd/server_internal.h +++ b/apps/silcd/server_internal.h @@ -129,6 +129,10 @@ struct SilcServerStruct { /* 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; diff --git a/apps/silcd/serverconfig.c b/apps/silcd/serverconfig.c index d9fe747a..505e8762 100644 --- a/apps/silcd/serverconfig.c +++ b/apps/silcd/serverconfig.c @@ -1314,6 +1314,15 @@ void silc_server_config_destroy(SilcServerConfig config) 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) { @@ -1338,6 +1347,7 @@ void silc_server_config_destroy(SilcServerConfig config) 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 */ diff --git a/apps/silcd/silcd.c b/apps/silcd/silcd.c index 6654ef95..c1a5277e 100644 --- a/apps/silcd/silcd.c +++ b/apps/silcd/silcd.c @@ -485,8 +485,15 @@ int main(int argc, char **argv) /* 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); } diff --git a/configure.in.pre b/configure.in.pre index a118a41a..10d3d993 100644 --- a/configure.in.pre +++ b/configure.in.pre @@ -681,6 +681,19 @@ AC_ARG_ENABLE(debug, 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 # diff --git a/lib/silccrypt/silccipher.c b/lib/silccrypt/silccipher.c index ba13a022..45a596e5 100644 --- a/lib/silccrypt/silccipher.c +++ b/lib/silccrypt/silccipher.c @@ -174,6 +174,24 @@ bool silc_cipher_register_default(void) 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 diff --git a/lib/silccrypt/silccipher.h b/lib/silccrypt/silccipher.h index f33ef64f..01a60393 100644 --- a/lib/silccrypt/silccipher.h +++ b/lib/silccrypt/silccipher.h @@ -120,6 +120,7 @@ SilcUInt32 silc_##cipher##_context_len() 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); diff --git a/lib/silccrypt/silchash.c b/lib/silccrypt/silchash.c index ed9999a2..fcf40fed 100644 --- a/lib/silccrypt/silchash.c +++ b/lib/silccrypt/silchash.c @@ -100,6 +100,8 @@ bool silc_hash_unregister(SilcHashObject *hash) 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); @@ -130,6 +132,24 @@ bool silc_hash_register_default(void) 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. */ diff --git a/lib/silccrypt/silchash.h b/lib/silccrypt/silchash.h index 2de31aaf..206427eb 100644 --- a/lib/silccrypt/silchash.h +++ b/lib/silccrypt/silchash.h @@ -168,6 +168,19 @@ bool silc_hash_unregister(SilcHashObject *hash); ***/ 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 diff --git a/lib/silccrypt/silchmac.c b/lib/silccrypt/silchmac.c index 970cc600..1cce72cd 100644 --- a/lib/silccrypt/silchmac.c +++ b/lib/silccrypt/silchmac.c @@ -130,6 +130,8 @@ bool silc_hmac_unregister(SilcHmacObject *hmac) 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); @@ -160,6 +162,24 @@ bool silc_hmac_register_default(void) 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 diff --git a/lib/silccrypt/silchmac.h b/lib/silccrypt/silchmac.h index c9b3bed0..290fbed1 100644 --- a/lib/silccrypt/silchmac.h +++ b/lib/silccrypt/silchmac.h @@ -133,6 +133,19 @@ bool silc_hmac_unregister(SilcHmacObject *hmac); ***/ 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 diff --git a/lib/silccrypt/silcpkcs.c b/lib/silccrypt/silcpkcs.c index 0fa9d29a..d9662663 100644 --- a/lib/silccrypt/silcpkcs.c +++ b/lib/silccrypt/silcpkcs.c @@ -110,6 +110,8 @@ bool silc_pkcs_unregister(SilcPKCSObject *pkcs) 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); @@ -140,6 +142,24 @@ bool silc_pkcs_register_default(void) 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. */ diff --git a/lib/silccrypt/silcpkcs.h b/lib/silccrypt/silcpkcs.h index 1beae915..787424d7 100644 --- a/lib/silccrypt/silcpkcs.h +++ b/lib/silccrypt/silcpkcs.h @@ -258,6 +258,7 @@ bool silc_pkcs_register(const SilcPKCSObject *pkcs); 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); diff --git a/lib/silccrypt/silcrng.c b/lib/silccrypt/silcrng.c index 4fa86fa4..02fbbfa9 100644 --- a/lib/silccrypt/silcrng.c +++ b/lib/silccrypt/silcrng.c @@ -158,6 +158,8 @@ SilcRng silc_rng_alloc(void) 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); @@ -166,6 +168,13 @@ void silc_rng_free(SilcRng rng) 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); } } diff --git a/lib/silcutil/Makefile.am b/lib/silcutil/Makefile.am index 2b65ce0f..b91e5c5d 100644 --- a/lib/silcutil/Makefile.am +++ b/lib/silcutil/Makefile.am @@ -49,7 +49,8 @@ libsilcutil_a_SOURCES = \ silcutil.c \ silchashtable.c \ silcsockconn.c \ - silcprotocol.c + silcprotocol.c \ + stacktrace.c if SILC_DIST_TOOLKIT include_HEADERS = \ diff --git a/lib/silcutil/silcconfig.c b/lib/silcutil/silcconfig.c index c2f6c1b8..8815cf7e 100644 --- a/lib/silcutil/silcconfig.c +++ b/lib/silcutil/silcconfig.c @@ -275,10 +275,12 @@ void silc_config_close(SilcConfigFile *file) 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); diff --git a/lib/silcutil/silclog.c b/lib/silcutil/silclog.c index 040f51e0..a28e9f4f 100644 --- a/lib/silcutil/silclog.c +++ b/lib/silcutil/silclog.c @@ -60,7 +60,7 @@ bool silc_debug_hexdump = FALSE; 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; @@ -256,7 +256,7 @@ bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize, 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)", @@ -286,15 +286,17 @@ bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize, 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; } @@ -483,10 +485,12 @@ void silc_log_reset_debug_callbacks() 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); } diff --git a/lib/silcutil/silclog.h b/lib/silcutil/silclog.h index adad8ddd..b91ab5cb 100644 --- a/lib/silcutil/silclog.h +++ b/lib/silcutil/silclog.h @@ -421,7 +421,8 @@ char *silc_log_get_file(SilcLogType type); * * 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 diff --git a/lib/silcutil/silcmemory.c b/lib/silcutil/silcmemory.c index eb246557..e2e455a9 100644 --- a/lib/silcutil/silcmemory.c +++ b/lib/silcutil/silcmemory.c @@ -20,6 +20,8 @@ #include "silcincludes.h" +#ifndef SILC_STACKTRACE + void *silc_malloc(size_t size) { void *addr; @@ -57,3 +59,5 @@ void *silc_memdup(const void *ptr, size_t size) addr[size] = '\0'; return (void *)addr; } + +#endif /* !SILC_STACKTRACE */ diff --git a/lib/silcutil/silcmemory.h b/lib/silcutil/silcmemory.h index 691cc698..233f3894 100644 --- a/lib/silcutil/silcmemory.h +++ b/lib/silcutil/silcmemory.h @@ -35,6 +35,8 @@ /* Prototypes */ +#ifndef SILC_STACKTRACE + /****f* silcutil/SilcMemoryAPI/silc_malloc * * SYNOPSIS @@ -117,4 +119,8 @@ void silc_free(void *ptr); ***/ void *silc_memdup(const void *ptr, size_t size); +#else +#include "stacktrace.h" +#endif /* SILC_STACKTRACE */ + #endif /* SILCMEMORY_H */ diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index db939c3a..6b3f34d6 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -287,6 +287,7 @@ bool silc_schedule_uninit(SilcSchedule schedule) silc_schedule_internal_uninit(schedule->internal); silc_mutex_free(schedule->lock); + silc_free(schedule); return TRUE; } diff --git a/lib/silcutil/stacktrace.c b/lib/silcutil/stacktrace.c new file mode 100644 index 00000000..eb8f2458 --- /dev/null +++ b/lib/silcutil/stacktrace.c @@ -0,0 +1,216 @@ +/* + + stacktrace.c + + Author: Pekka Riikonen + + 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, "%s:%d: #blocks=%lu, bytes=%lu\n", + stack->file, stack->line, blocks, bytes); + for (i = 0; i < stack->depth; i++) + fprintf(fp, "%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 */ diff --git a/lib/silcutil/stacktrace.h b/lib/silcutil/stacktrace.h new file mode 100644 index 00000000..5c468868 --- /dev/null +++ b/lib/silcutil/stacktrace.h @@ -0,0 +1,49 @@ +/* + + stacktrace.h + + Author: Pekka Riikonen + + 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 */ -- 2.24.0