| /* 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 |
| |
| #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; |
| 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_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, "[%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; |
| } |