/* 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 . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include "imalloc.h" #include "table.h" extern int malloc_register; #ifdef MALLOC_REGISTER #define FIND_ALLOC 0x01 /* allocate new entry or find existing */ #define FIND_EXIST 0x02 /* find existing entry */ static int table_count = 0; static int table_allocated = 0; static mr_table_t mem_table[REG_TABLE_SIZE]; static mr_table_t mem_overflow; /* * 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)); #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, *lastp; if (mem_overflow.mem == mem) return (&mem_overflow); bucket = which_bucket (mem); /* get initial hash */ tp = endp = mem_table + bucket; lastp = mem_table + REG_TABLE_SIZE; while (1) { if (tp->mem == mem) return (tp); if (tp->mem == 0 && (flags & FIND_ALLOC)) { table_count++; return (tp); } tp++; if (tp == lastp) /* wrap around */ tp = mem_table; if (tp == endp && (flags & FIND_EXIST)) return ((mr_table_t *)NULL); if (tp == endp && (flags & FIND_ALLOC)) break; } /* oops. table is full. replace an existing free entry. */ do { /* If there are no free entries, punt right away without searching. */ if (table_allocated == REG_TABLE_SIZE) break; if (tp->flags & MT_FREE) { memset(tp, 0, sizeof (mr_table_t)); return (tp); } tp++; if (tp == lastp) tp = mem_table; } while (tp != endp); /* wow. entirely full. return mem_overflow dummy entry. */ tp = &mem_overflow; memset (tp, 0, sizeof (mr_table_t)); return tp; } 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; 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")); 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++; } void mregister_free (mem, size, file, line) PTR_T mem; int size; const char *file; int line; { mr_table_t *tentry; 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 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 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, "[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", 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; } #endif /* MALLOC_REGISTER */ int malloc_set_register(n) int n; { int old; old = malloc_register; malloc_register = n; return old; }