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