diff options
Diffstat (limited to 'lib/malloc/table.c')
-rw-r--r-- | lib/malloc/table.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/lib/malloc/table.c b/lib/malloc/table.c new file mode 100644 index 0000000..97946dc --- /dev/null +++ b/lib/malloc/table.c @@ -0,0 +1,429 @@ +/* table.c - bookkeeping functions for allocated memory */ + +/* Copyright (C) 2001-2003 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash 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 3 of the License, or + (at your option) any later version. + + Bash 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 Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include "imalloc.h" +#include "table.h" + +#ifdef SHELL +extern int interrupt_immediately, running_trap; +extern int signal_is_trapped __P((int)); +#endif + +extern int malloc_register; + +#ifdef MALLOC_REGISTER + +extern FILE *_imalloc_fopen __P((char *, char *, char *, char *, size_t)); + +#define FIND_ALLOC 0x01 /* find slot for new allocation */ +#define FIND_EXIST 0x02 /* find slot for existing entry for free() or search */ + +static int table_count = 0; +static int table_allocated = 0; +static int table_bucket_index = REG_TABLE_SIZE-1; +static mr_table_t mem_table[REG_TABLE_SIZE]; +static mr_table_t mem_overflow; + +#ifndef STREQ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#endif + +static int location_table_index = 0; +static int location_table_count = 0; +static ma_table_t mlocation_table[REG_TABLE_SIZE]; + +/* + * NOTE: taken from dmalloc (http://dmalloc.com) and modified. + */ +static unsigned int +mt_hash (key) + const PTR_T key; +{ + unsigned int a, b, c; + unsigned long x; + + /* set up the internal state */ + a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + x = (unsigned long)key; /* truncation is OK */ + b = x >> 8; + c = x >> 3; /* XXX - was >> 4 */ + + HASH_MIX(a, b, c); + return c; +} + +#if 0 +static unsigned int +which_bucket (mem) + PTR_T mem; +{ + return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1)); +} + +#else +#define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1)); + +#define next_bucket() ((table_bucket_index + 1) & (REG_TABLE_SIZE-1)) +#define next_entry(mem) ((mem == mem_table + REG_TABLE_SIZE - 1) ? mem_table : ++mem) + +#define prev_bucket() (table_bucket_index == 0 ? REG_TABLE_SIZE-1 : table_bucket_index-1) +#define prev_entry(mem) ((mem == mem_table) ? mem_table + REG_TABLE_SIZE - 1 : mem - 1) +#endif + +static mr_table_t * +find_entry (mem, flags) + PTR_T mem; + int flags; +{ + unsigned int bucket; + register mr_table_t *tp; + mr_table_t *endp; + + if (mem_overflow.mem == mem) + return (&mem_overflow); + + /* If we want to insert an allocation entry just use the next slot */ + if (flags & FIND_ALLOC) + { + table_bucket_index = next_bucket(); + table_count++; + tp = mem_table + table_bucket_index; + memset(tp, 0, sizeof (mr_table_t)); /* overwrite next existing entry */ + return tp; + } + + tp = endp = mem_table + table_bucket_index; + + /* search for last allocation corresponding to MEM, return entry pointer */ + while (1) + { + if (tp->mem == mem) + return (tp); + + tp = prev_entry (tp); + + /* if we went all the way around and didn't find it, return NULL */ + if (tp == endp) + return ((mr_table_t *)NULL); + } + + return (mr_table_t *)NULL; +} + +mr_table_t * +mr_table_entry (mem) + PTR_T mem; +{ + return (find_entry (mem, FIND_EXIST)); +} + +void +mregister_describe_mem (mem, fp) + PTR_T mem; + FILE *fp; +{ + mr_table_t *entry; + + entry = find_entry (mem, FIND_EXIST); + if (entry == 0) + return; + fprintf (fp, "malloc: %p: %s: last %s from %s:%d\n", + mem, + (entry->flags & MT_ALLOC) ? "allocated" : "free", + (entry->flags & MT_ALLOC) ? "allocated" : "freed", + entry->file ? entry->file : "unknown", + entry->line); +} + +void +mregister_alloc (tag, mem, size, file, line) + const char *tag; + PTR_T mem; + size_t size; + const char *file; + int line; +{ + mr_table_t *tentry; + sigset_t set, oset; + int blocked_sigs; + + /* Block all signals in case we are executed from a signal handler. */ + blocked_sigs = 0; +#ifdef SHELL + if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD)) +#endif + { + _malloc_block_signals (&set, &oset); + blocked_sigs = 1; + } + + mlocation_register_alloc (file, line); + + tentry = find_entry (mem, FIND_ALLOC); + + if (tentry == 0) + { + /* oops. table is full. punt. */ + fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n")); + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); + return; + } + + if (tentry->flags & MT_ALLOC) + { + /* oops. bad bookkeeping. ignore for now */ + fprintf (stderr, _("register_alloc: %p already in table as allocated?\n"), mem); + } + + tentry->mem = mem; + tentry->size = size; + tentry->func = tag; + tentry->flags = MT_ALLOC; + tentry->file = file; + tentry->line = line; + tentry->nalloc++; + + if (tentry != &mem_overflow) + table_allocated++; + + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); +} + +void +mregister_free (mem, size, file, line) + PTR_T mem; + int size; + const char *file; + int line; +{ + mr_table_t *tentry; + sigset_t set, oset; + int blocked_sigs; + + /* Block all signals in case we are executed from a signal handler. */ + blocked_sigs = 0; +#ifdef SHELL + if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD)) +#endif + { + _malloc_block_signals (&set, &oset); + blocked_sigs = 1; + } + + tentry = find_entry (mem, FIND_EXIST); + if (tentry == 0) + { + /* oops. not found. */ +#if 0 + fprintf (stderr, "register_free: %p not in allocation table?\n", mem); +#endif + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); + return; + } + if (tentry->flags & MT_FREE) + { + /* oops. bad bookkeeping. ignore for now */ + fprintf (stderr, _("register_free: %p already in table as free?\n"), mem); + } + + tentry->flags = MT_FREE; + tentry->func = "free"; + tentry->file = file; + tentry->line = line; + tentry->nfree++; + + if (tentry != &mem_overflow) + table_allocated--; + + if (blocked_sigs) + _malloc_unblock_signals (&set, &oset); +} + +/* If we ever add more flags, this will require changes. */ +static char * +_entry_flags(x) + int x; +{ + if (x & MT_FREE) + return "free"; + else if (x & MT_ALLOC) + return "allocated"; + else + return "undetermined?"; +} + +static void +_register_dump_table(fp) + FILE *fp; +{ + register int i; + mr_table_t entry; + + for (i = 0; i < REG_TABLE_SIZE; i++) + { + entry = mem_table[i]; + if (entry.mem) + fprintf (fp, "%s[%d] %p:%zu:%s:%s:%s:%d:%d:%d\n", + (i == table_bucket_index) ? "*" : "", + i, + entry.mem, entry.size, + _entry_flags(entry.flags), + entry.func ? entry.func : "unknown", + entry.file ? entry.file : "unknown", + entry.line, + entry.nalloc, entry.nfree); + } +} + +void +mregister_dump_table() +{ + _register_dump_table (stderr); +} + +void +mregister_table_init () +{ + memset (mem_table, 0, sizeof(mr_table_t) * REG_TABLE_SIZE); + memset (&mem_overflow, 0, sizeof (mr_table_t)); + table_count = 0; +} + +/* Simple for now */ + +static ma_table_t * +find_location_entry (file, line) + const char *file; + int line; +{ + register ma_table_t *tp, *endp; + + endp = mlocation_table + location_table_count; + for (tp = mlocation_table; tp <= endp; tp++) + { + if (tp->line == line && STREQ (file, tp->file)) + return tp; + } + return (ma_table_t *)NULL; +} + +void +mlocation_register_alloc (file, line) + const char *file; + int line; +{ + ma_table_t *lentry; + const char *nfile; + + if (file == 0) + { + mlocation_table[0].nalloc++; + return; + } + + nfile = strrchr (file, '/'); + if (nfile) + nfile++; + else + nfile = file; + + lentry = find_location_entry (nfile, line); + if (lentry == 0) + { + location_table_index++; + if (location_table_index == REG_TABLE_SIZE) + location_table_index = 1; /* slot 0 reserved */ + lentry = mlocation_table + location_table_index; + lentry->file = nfile; + lentry->line = line; + lentry->nalloc = 1; + if (location_table_count < REG_TABLE_SIZE) + location_table_count++; /* clamp at REG_TABLE_SIZE for now */ + } + else + lentry->nalloc++; +} + +static void +_location_dump_table (fp) + FILE *fp; +{ + register ma_table_t *tp, *endp; + + endp = mlocation_table + location_table_count; + for (tp = mlocation_table; tp < endp; tp++) + fprintf (fp, "%s:%d\t%d\n", tp->file ? tp->file : "unknown", + tp->line ? tp->line : 0, + tp->nalloc); +} + +void +mlocation_dump_table () +{ + _location_dump_table (stderr); +} + +#define LOCROOT "/var/tmp/maltrace/locations." + +void +mlocation_write_table () +{ + FILE *fp; + char defname[sizeof (LOCROOT) + 64]; + + fp = _imalloc_fopen ((char *)NULL, (char *)NULL, LOCROOT, defname, sizeof (defname)); + if (fp == 0) + return; /* XXX - no error message yet */ + _location_dump_table (fp); + fclose (fp); +} + +void +mlocation_table_init () +{ + memset (mlocation_table, 0, sizeof (ma_table_t) * REG_TABLE_SIZE); + mlocation_table[0].file = ""; /* reserve slot 0 for unknown locations */ + mlocation_table[0].line = 0; + mlocation_table[0].nalloc = 0; + location_table_count = 1; +} + +#endif /* MALLOC_REGISTER */ + +int +malloc_set_register(n) + int n; +{ + int old; + + old = malloc_register; + malloc_register = n; + return old; +} |