Imported from ../bash-3.0.tar.gz.
[platform/upstream/bash.git] / lib / malloc / table.c
1 /* table.c - bookkeeping functions for allocated memory */
2
3 /* Copyright (C) 2001-2003 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2, or (at your option) any later
10    version.
11
12    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15    for more details.
16
17    You should have received a copy of the GNU General Public License along
18    with Bash; see the file COPYING.  If not, write to the Free Software
19    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include <stdio.h>
25
26 #include "imalloc.h"
27 #include "table.h"
28
29 extern int malloc_register;
30
31 #ifdef MALLOC_REGISTER
32
33 #define FIND_ALLOC      0x01    /* allocate new entry or find existing */
34 #define FIND_EXIST      0x02    /* find existing entry */
35
36 static int table_count = 0;
37 static int table_allocated = 0;
38 static mr_table_t mem_table[REG_TABLE_SIZE];
39 static mr_table_t mem_overflow;
40
41 /*
42  * NOTE: taken from dmalloc (http://dmalloc.com) and modified.
43  */
44 static unsigned int
45 mt_hash (key)
46      const PTR_T key;
47 {
48   unsigned int a, b, c;
49   unsigned long x;
50
51   /* set up the internal state */
52   a = 0x9e3779b9;       /* the golden ratio; an arbitrary value */
53   x = (unsigned long)key;               /* truncation is OK */
54   b = x >> 8;
55   c = x >> 3;                           /* XXX - was >> 4 */
56
57   HASH_MIX(a, b, c);
58   return c;
59 }
60
61 #if 0
62 static unsigned int
63 which_bucket (mem)
64      PTR_T mem;
65 {
66   return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1));
67 }
68 #else
69 #define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1));
70 #endif
71
72 static mr_table_t *
73 find_entry (mem, flags)
74      PTR_T mem;
75      int flags;
76 {
77   unsigned int bucket;
78   register mr_table_t *tp;
79   mr_table_t *endp, *lastp;
80
81   if (mem_overflow.mem == mem)
82     return (&mem_overflow);
83
84   bucket = which_bucket (mem);  /* get initial hash */
85   tp = endp = mem_table + bucket;
86   lastp = mem_table + REG_TABLE_SIZE;
87
88   while (1)
89     {
90       if (tp->mem == mem)
91         return (tp);
92       if (tp->mem == 0 && (flags & FIND_ALLOC))
93         {
94           table_count++;
95           return (tp);
96         }
97
98       tp++;
99
100       if (tp == lastp)          /* wrap around */
101         tp = mem_table;
102
103       if (tp == endp && (flags & FIND_EXIST))
104         return ((mr_table_t *)NULL);
105
106       if (tp == endp && (flags & FIND_ALLOC))
107         break;
108     }
109
110   /* oops.  table is full.  replace an existing free entry. */
111   do
112     {
113       /* If there are no free entries, punt right away without searching. */
114       if (table_allocated == REG_TABLE_SIZE)
115         break;
116
117       if (tp->flags & MT_FREE)
118         {
119           memset(tp, 0, sizeof (mr_table_t));
120           return (tp);
121         }
122       tp++;
123
124       if (tp == lastp)
125         tp = mem_table;
126     }
127   while (tp != endp);
128
129   /* wow. entirely full.  return mem_overflow dummy entry. */
130   tp = &mem_overflow;
131   memset (tp, 0, sizeof (mr_table_t));
132   return tp;
133 }
134
135 mr_table_t *
136 mr_table_entry (mem)
137      PTR_T mem;
138 {
139   return (find_entry (mem, FIND_EXIST));
140 }
141
142 void
143 mregister_describe_mem (mem, fp)
144      PTR_T mem;
145      FILE *fp;
146 {
147   mr_table_t *entry;
148
149   entry = find_entry (mem, FIND_EXIST);
150   if (entry == 0)
151     return;
152   fprintf (fp, "malloc: %p: %s: last %s from %s:%d\n",
153                 mem,
154                 (entry->flags & MT_ALLOC) ? "allocated" : "free",
155                 (entry->flags & MT_ALLOC) ? "allocated" : "freed",
156                 entry->file ? entry->file : "unknown",
157                 entry->line);
158 }
159
160 void
161 mregister_alloc (tag, mem, size, file, line)
162      const char *tag;
163      PTR_T mem;
164      size_t size;
165      const char *file;
166      int line;
167 {
168   mr_table_t *tentry;
169
170   tentry = find_entry (mem, FIND_ALLOC);
171
172   if (tentry == 0)
173     {
174       /* oops.  table is full.  punt. */
175       fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n"));
176       return;
177     }
178   
179   if (tentry->flags & MT_ALLOC)
180     {
181       /* oops.  bad bookkeeping. ignore for now */
182       fprintf (stderr, _("register_alloc: %p already in table as allocated?\n"), mem);
183     }
184
185   tentry->mem = mem;
186   tentry->size = size;
187   tentry->func = tag;
188   tentry->flags = MT_ALLOC;
189   tentry->file = file;
190   tentry->line = line;
191   tentry->nalloc++;
192
193   if (tentry != &mem_overflow)
194     table_allocated++;
195 }
196
197 void
198 mregister_free (mem, size, file, line)
199      PTR_T mem;
200      int size;
201      const char *file;
202      int line;
203 {
204   mr_table_t *tentry;
205
206   tentry = find_entry (mem, FIND_EXIST);
207   if (tentry == 0)
208     {
209       /* oops.  not found. */
210 #if 0
211       fprintf (stderr, "register_free: %p not in allocation table?\n", mem);
212 #endif
213       return;
214     }
215   if (tentry->flags & MT_FREE)
216     {
217       /* oops.  bad bookkeeping. ignore for now */
218       fprintf (stderr, _("register_free: %p already in table as free?\n"), mem);
219     }
220         
221   tentry->flags = MT_FREE;
222   tentry->func = "free";
223   tentry->file = file;
224   tentry->line = line;
225   tentry->nfree++;
226
227   if (tentry != &mem_overflow)
228     table_allocated--;
229 }
230
231 /* If we ever add more flags, this will require changes. */
232 static char *
233 _entry_flags(x)
234      int x;
235 {
236   if (x & MT_FREE)
237     return "free";
238   else if (x & MT_ALLOC)
239     return "allocated";
240   else
241     return "undetermined?";
242 }
243
244 static void
245 _register_dump_table(fp)
246      FILE *fp;
247 {
248   register int i;
249   mr_table_t entry;
250
251   for (i = 0; i < REG_TABLE_SIZE; i++)
252     {
253       entry = mem_table[i];
254       if (entry.mem)
255         fprintf (fp, "[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", i,
256                                                 entry.mem, entry.size,
257                                                 _entry_flags(entry.flags),
258                                                 entry.func ? entry.func : "unknown",
259                                                 entry.file ? entry.file : "unknown",
260                                                 entry.line,
261                                                 entry.nalloc, entry.nfree);
262     }
263 }
264  
265 void
266 mregister_dump_table()
267 {
268   _register_dump_table (stderr);
269 }
270
271 void
272 mregister_table_init ()
273 {
274   memset (mem_table, 0, sizeof(mr_table_t) * REG_TABLE_SIZE);
275   memset (&mem_overflow, 0, sizeof (mr_table_t));
276   table_count = 0;
277 }
278
279 #endif /* MALLOC_REGISTER */
280
281 int
282 malloc_set_register(n)
283      int n;
284 {
285   int old;
286
287   old = malloc_register;
288   malloc_register = n;
289   return old;
290 }