diff options
Diffstat (limited to 'src/core/memdebug.c')
-rw-r--r-- | src/core/memdebug.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/src/core/memdebug.c b/src/core/memdebug.c new file mode 100644 index 00000000..1be7bbd8 --- /dev/null +++ b/src/core/memdebug.c @@ -0,0 +1,356 @@ +/* + memdebug.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + 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; either version 2 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#define ENABLE_BUFFER_CHECKS +#define BUFFER_CHECK_SIZE 5 +#define MIN_BUFFER_CHECK_SIZE 2 + +typedef struct { + void *p; + int size; + char *file; + int line; + char *comment; +} MEM_REC; + +static GHashTable *data = NULL, *preallocs = NULL; +static const char *comment = ""; + +static void add_flow_checks(guchar *p, unsigned long size) +{ +#ifdef ENABLE_BUFFER_CHECKS + int n; + + for (n = 0; n < BUFFER_CHECK_SIZE; n++) + p[n] = n ^ 0x7f; + for (n = 0; n < BUFFER_CHECK_SIZE; n++) + p[size-BUFFER_CHECK_SIZE+n] = n ^ 0x7f; +#endif +} + +void ig_memcheck_rec(void *key, MEM_REC *rec) +{ + guchar *p; + int n; + + if (rec->size != INT_MIN){ + p = rec->p; + + for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++) + if (p[n] != (n ^ 0x7f)) + g_error("buffer underflow, file %s line %d!\n", rec->file, rec->line); + + for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++) + if (p[rec->size-BUFFER_CHECK_SIZE+n] != (n ^ 0x7f)) + g_error("buffer overflow, file %s line %d!\n", rec->file, rec->line); + } +} + +static void mem_check(void) +{ +#ifdef ENABLE_BUFFER_CHECKS + g_hash_table_foreach(data, (GHFunc) ig_memcheck_rec, NULL); +#endif +} + +static void data_add(void *p, int size, const char *file, int line) +{ + MEM_REC *rec; + + if (size <= 0 && size != INT_MIN) + g_error("size = %d, file %s line %d", size, file, line); + + if (data == NULL) { + data = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); + preallocs = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); + } + + if (g_hash_table_lookup(data, p) != NULL) + g_error("data_add() already malloc()'ed %p (in %s:%d)", p, file, line); + + rec = g_new(MEM_REC, 1); + g_hash_table_insert(data, p, rec); + + rec->p = p; + rec->size = size; + rec->file = (char *) file; + rec->line = line; + rec->comment = g_strdup(comment); + + if (size == INT_MIN) + g_hash_table_insert(preallocs, p-BUFFER_CHECK_SIZE, p); + else + add_flow_checks(p, size); + mem_check(); +} + +static void *data_remove(void *p, const char *file, int line) +{ + MEM_REC *rec; + + mem_check(); + + if (g_hash_table_lookup(preallocs, p) != NULL) { + g_hash_table_remove(preallocs, p); + p += BUFFER_CHECK_SIZE; + } + + rec = g_hash_table_lookup(data, p); + if (rec == NULL) { + g_warning("data_remove() data %p not found (in %s:%d)", p, file, line); + return p+BUFFER_CHECK_SIZE; + } + + g_hash_table_remove(data, p); + g_free(rec->comment); + g_free(rec); + + return p; +} + +void *ig_malloc(int size, const char *file, int line) +{ +#if 1 + void *p; + + size += BUFFER_CHECK_SIZE*2; + p = g_malloc(size); + data_add(p, size, file, line); + return p+BUFFER_CHECK_SIZE; +#else + return g_malloc(size); +#endif +} + +void *ig_malloc0(int size, const char *file, int line) +{ +#if 1 + void *p; + + size += BUFFER_CHECK_SIZE*2; + p = g_malloc0(size); + data_add(p, size, file, line); + return p+BUFFER_CHECK_SIZE; +#else + return g_malloc0(size); +#endif +} + +void *ig_realloc(void *mem, unsigned long size, const char *file, int line) +{ +#if 1 + void *p; + + size += BUFFER_CHECK_SIZE*2; + mem -= BUFFER_CHECK_SIZE; + data_remove(mem, file, line); + p = g_realloc(mem, size); + data_add(p, size, file, line); + return p+BUFFER_CHECK_SIZE; +#else + return g_realloc(mem, size); +#endif +} + +char *ig_strdup(const char *str, const char *file, int line) +{ + void *p; + + if (str == NULL) return NULL; + + p = ig_malloc(strlen(str)+1, file, line); + strcpy(p, str); + + return p; +} + +char *ig_strndup(const char *str, int count, const char *file, int line) +{ + char *p; + + if (str == NULL) return NULL; + + p = ig_malloc(count+1, file, line); + strncpy(p, str, count); p[count] = '\0'; + + return p; +} + +char *ig_strconcat(const char *file, int line, const char *str, ...) +{ + guint l; + va_list args; + char *s; + char *concat; + + g_return_val_if_fail (str != NULL, NULL); + + l = 1 + strlen (str); + va_start (args, str); + s = va_arg (args, char*); + while (s) + { + l += strlen (s); + s = va_arg (args, char*); + } + va_end (args); + + concat = ig_malloc(l, file, line); + concat[0] = 0; + + strcat (concat, str); + va_start (args, str); + s = va_arg (args, char*); + while (s) + { + strcat (concat, s); + s = va_arg (args, char*); + } + va_end (args); + + return concat; +} + +char *ig_strdup_printf(const char *file, int line, const char *format, ...) +{ + char *buffer, *p; + va_list args; + + va_start (args, format); + buffer = g_strdup_vprintf (format, args); + va_end (args); + + p = ig_malloc(strlen(buffer)+1, file, line); + strcpy(p, buffer); + g_free(buffer); + + return p; +} + +char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args) +{ + char *buffer, *p; + + buffer = g_strdup_vprintf (format, args); + + p = ig_malloc(strlen(buffer)+1, file, line); + strcpy(p, buffer); + g_free(buffer); + + return p; +} + +void ig_free(void *p) +{ +#if 1 + p -= BUFFER_CHECK_SIZE; + p = data_remove(p, "??", 0); + if (p != NULL) +#endif + g_free(p); +} + +GString *ig_string_new(const char *file, int line, const char *str) +{ + GString *ret; + + ret = g_string_new(str); + data_add(ret, INT_MIN, file, line); + return ret; +} + +void ig_string_free(const char *file, int line, GString *str, gboolean freeit) +{ + data_remove(str, file, line); + if (!freeit) + data_add(str->str, INT_MIN, file, line); + + g_string_free(str, freeit); +} + +char *ig_strjoinv(const char *file, int line, const char *sepa, char **array) +{ + char *ret; + + ret = g_strjoinv(sepa, array); + data_add(ret, INT_MIN, file, line); + return ret; +} + +void ig_profile_line(void *key, MEM_REC *rec) +{ + char *data; + + if (*rec->comment == '\0' && + (strcmp(rec->file, "ig_strdup_printf") == 0 || + strcmp(rec->file, "ig_strdup_vprintf") == 0 || + strcmp(rec->file, "ig_strconcat") == 0 || + strcmp(rec->file, "ig_string_free (free = FALSE)") == 0)) + data = rec->p + BUFFER_CHECK_SIZE; + else + data = rec->comment; + fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data); +} + +void ig_mem_profile(void) +{ + g_hash_table_foreach(data, (GHFunc) ig_profile_line, NULL); + g_hash_table_destroy(data); + g_hash_table_destroy(preallocs); +} + +static MEM_REC *largest[10]; + +void ig_profile_largest(void *key, MEM_REC *rec) +{ + int n; + + for (n = 0; n < 10; n++) + { + if (largest[n] == NULL || rec->size > largest[n]->size) + { + g_memmove(largest+n+1, largest+n, sizeof(void *)*(9-n)); + largest[n] = rec; + } + } +} + +void ig_mem_profile_largest(void) +{ + /*int n;*/ + + memset(&largest, 0, sizeof(MEM_REC*)*10); + /*g_hash_table_foreach(data, (GHFunc) ig_profile_largest, NULL); + + for (n = 0; n < 10 && largest[n] != NULL; n++) + { + ig_profile_line(NULL, largest[n]); + }*/ +} + +void ig_set_data(const char *data) +{ + comment = data; +} |