Bash-4.3 distribution sources and documentation
[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 #ifdef SHELL
32 extern int interrupt_immediately, running_trap;
33 extern int signal_is_trapped __P((int));
34 #endif
35
36 extern int malloc_register;
37
38 #ifdef MALLOC_REGISTER
39
40 #define FIND_ALLOC      0x01    /* allocate new entry or find existing */
41 #define FIND_EXIST      0x02    /* find existing entry */
42
43 static int table_count = 0;
44 static int table_allocated = 0;
45 static mr_table_t mem_table[REG_TABLE_SIZE];
46 static mr_table_t mem_overflow;
47
48 /*
49  * NOTE: taken from dmalloc (http://dmalloc.com) and modified.
50  */
51 static unsigned int
52 mt_hash (key)
53      const PTR_T key;
54 {
55   unsigned int a, b, c;
56   unsigned long x;
57
58   /* set up the internal state */
59   a = 0x9e3779b9;       /* the golden ratio; an arbitrary value */
60   x = (unsigned long)key;               /* truncation is OK */
61   b = x >> 8;
62   c = x >> 3;                           /* XXX - was >> 4 */
63
64   HASH_MIX(a, b, c);
65   return c;
66 }
67
68 #if 0
69 static unsigned int
70 which_bucket (mem)
71      PTR_T mem;
72 {
73   return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1));
74 }
75 #else
76 #define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1));
77 #endif
78
79 static mr_table_t *
80 find_entry (mem, flags)
81      PTR_T mem;
82      int flags;
83 {
84   unsigned int bucket;
85   register mr_table_t *tp;
86   mr_table_t *endp, *lastp;
87
88   if (mem_overflow.mem == mem)
89     return (&mem_overflow);
90
91   bucket = which_bucket (mem);  /* get initial hash */
92   tp = endp = mem_table + bucket;
93   lastp = mem_table + REG_TABLE_SIZE;
94
95   while (1)
96     {
97       if (tp->mem == mem)
98         return (tp);
99       if (tp->mem == 0 && (flags & FIND_ALLOC))
100         {
101           table_count++;
102           return (tp);
103         }
104
105       tp++;
106
107       if (tp == lastp)          /* wrap around */
108         tp = mem_table;
109
110       if (tp == endp && (flags & FIND_EXIST))
111         return ((mr_table_t *)NULL);
112
113       if (tp == endp && (flags & FIND_ALLOC))
114         break;
115     }
116
117   /* oops.  table is full.  replace an existing free entry. */
118   do
119     {
120       /* If there are no free entries, punt right away without searching. */
121       if (table_allocated == REG_TABLE_SIZE)
122         break;
123
124       if (tp->flags & MT_FREE)
125         {
126           memset(tp, 0, sizeof (mr_table_t));
127           return (tp);
128         }
129       tp++;
130
131       if (tp == lastp)
132         tp = mem_table;
133     }
134   while (tp != endp);
135
136   /* wow. entirely full.  return mem_overflow dummy entry. */
137   tp = &mem_overflow;
138   memset (tp, 0, sizeof (mr_table_t));
139   return tp;
140 }
141
142 mr_table_t *
143 mr_table_entry (mem)
144      PTR_T mem;
145 {
146   return (find_entry (mem, FIND_EXIST));
147 }
148
149 void
150 mregister_describe_mem (mem, fp)
151      PTR_T mem;
152      FILE *fp;
153 {
154   mr_table_t *entry;
155
156   entry = find_entry (mem, FIND_EXIST);
157   if (entry == 0)
158     return;
159   fprintf (fp, "malloc: %p: %s: last %s from %s:%d\n",
160                 mem,
161                 (entry->flags & MT_ALLOC) ? "allocated" : "free",
162                 (entry->flags & MT_ALLOC) ? "allocated" : "freed",
163                 entry->file ? entry->file : "unknown",
164                 entry->line);
165 }
166
167 void
168 mregister_alloc (tag, mem, size, file, line)
169      const char *tag;
170      PTR_T mem;
171      size_t size;
172      const char *file;
173      int line;
174 {
175   mr_table_t *tentry;
176   sigset_t set, oset;
177   int blocked_sigs;
178
179   /* Block all signals in case we are executed from a signal handler. */
180   blocked_sigs = 0;
181 #ifdef SHELL
182   if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
183 #endif
184     {
185       _malloc_block_signals (&set, &oset);
186       blocked_sigs = 1;
187     }
188
189   tentry = find_entry (mem, FIND_ALLOC);
190
191   if (tentry == 0)
192     {
193       /* oops.  table is full.  punt. */
194       fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n"));
195       if (blocked_sigs)
196         _malloc_unblock_signals (&set, &oset);
197       return;
198     }
199   
200   if (tentry->flags & MT_ALLOC)
201     {
202       /* oops.  bad bookkeeping. ignore for now */
203       fprintf (stderr, _("register_alloc: %p already in table as allocated?\n"), mem);
204     }
205
206   tentry->mem = mem;
207   tentry->size = size;
208   tentry->func = tag;
209   tentry->flags = MT_ALLOC;
210   tentry->file = file;
211   tentry->line = line;
212   tentry->nalloc++;
213
214   if (tentry != &mem_overflow)
215     table_allocated++;
216
217   if (blocked_sigs)
218     _malloc_unblock_signals (&set, &oset);
219 }
220
221 void
222 mregister_free (mem, size, file, line)
223      PTR_T mem;
224      int size;
225      const char *file;
226      int line;
227 {
228   mr_table_t *tentry;
229   sigset_t set, oset;
230   int blocked_sigs;
231
232   /* Block all signals in case we are executed from a signal handler. */
233   blocked_sigs = 0;
234 #ifdef SHELL
235   if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
236 #endif
237     {
238       _malloc_block_signals (&set, &oset);
239       blocked_sigs = 1;
240     }
241
242   tentry = find_entry (mem, FIND_EXIST);
243   if (tentry == 0)
244     {
245       /* oops.  not found. */
246 #if 0
247       fprintf (stderr, "register_free: %p not in allocation table?\n", mem);
248 #endif
249       if (blocked_sigs)
250         _malloc_unblock_signals (&set, &oset);
251       return;
252     }
253   if (tentry->flags & MT_FREE)
254     {
255       /* oops.  bad bookkeeping. ignore for now */
256       fprintf (stderr, _("register_free: %p already in table as free?\n"), mem);
257     }
258         
259   tentry->flags = MT_FREE;
260   tentry->func = "free";
261   tentry->file = file;
262   tentry->line = line;
263   tentry->nfree++;
264
265   if (tentry != &mem_overflow)
266     table_allocated--;
267
268   if (blocked_sigs)
269     _malloc_unblock_signals (&set, &oset);
270 }
271
272 /* If we ever add more flags, this will require changes. */
273 static char *
274 _entry_flags(x)
275      int x;
276 {
277   if (x & MT_FREE)
278     return "free";
279   else if (x & MT_ALLOC)
280     return "allocated";
281   else
282     return "undetermined?";
283 }
284
285 static void
286 _register_dump_table(fp)
287      FILE *fp;
288 {
289   register int i;
290   mr_table_t entry;
291
292   for (i = 0; i < REG_TABLE_SIZE; i++)
293     {
294       entry = mem_table[i];
295       if (entry.mem)
296         fprintf (fp, "[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", i,
297                                                 entry.mem, entry.size,
298                                                 _entry_flags(entry.flags),
299                                                 entry.func ? entry.func : "unknown",
300                                                 entry.file ? entry.file : "unknown",
301                                                 entry.line,
302                                                 entry.nalloc, entry.nfree);
303     }
304 }
305  
306 void
307 mregister_dump_table()
308 {
309   _register_dump_table (stderr);
310 }
311
312 void
313 mregister_table_init ()
314 {
315   memset (mem_table, 0, sizeof(mr_table_t) * REG_TABLE_SIZE);
316   memset (&mem_overflow, 0, sizeof (mr_table_t));
317   table_count = 0;
318 }
319
320 #endif /* MALLOC_REGISTER */
321
322 int
323 malloc_set_register(n)
324      int n;
325 {
326   int old;
327
328   old = malloc_register;
329   malloc_register = n;
330   return old;
331 }