]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/smartall.c
mtx-changer update, message fixes
[bacula/bacula] / bacula / src / lib / smartall.c
1 /*
2
3                          S M A R T A L L O C
4                         Smart Memory Allocator
5
6         Evolved   over   several  years,  starting  with  the  initial
7         SMARTALLOC code for AutoSketch in 1986, guided  by  the  Blind
8         Watchbreaker,  John  Walker.  Isolated in this general-purpose
9         form in  September  of  1989.   Updated  with  be  more  POSIX
10         compliant  and  to  include Web-friendly HTML documentation in
11         October  of  1998  by  the  same  culprit.    For   additional
12         information and the current version visit the Web page:
13
14                   http://www.fourmilab.ch/smartall/
15
16   
17          Version $Id$
18
19 */
20
21 /*
22    Copyright (C) 2000-2003 Kern Sibbald and John Walker
23
24    This program is free software; you can redistribute it and/or
25    modify it under the terms of the GNU General Public License as
26    published by the Free Software Foundation; either version 2 of
27    the License, or (at your option) any later version.
28
29    This program is distributed in the hope that it will be useful,
30    but WITHOUT ANY WARRANTY; without even the implied warranty of
31    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32    General Public License for more details.
33
34    You should have received a copy of the GNU General Public
35    License along with this program; if not, write to the Free
36    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
37    MA 02111-1307, USA.
38
39  */
40
41 #include "bacula.h"
42 /* Use the real routines here */
43 #undef realloc
44 #undef calloc
45 #undef malloc
46 #undef free
47       
48
49
50 #ifdef SMARTALLOC
51
52 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
53
54 extern char my_name[];                /* daemon name */
55
56 typedef unsigned short sm_ushort;
57
58 #define EOS      '\0'              /* End of string sentinel */
59 #define sm_min(a, b) ((a) < (b) ? (a) : (b))
60
61 /*  Queue data structures  */
62
63 /*  Memory allocation control structures and storage.  */
64
65 struct abufhead {
66         struct b_queue abq;          /* Links on allocated queue */
67         unsigned ablen;            /* Buffer length in bytes */
68         char *abfname;             /* File name pointer */
69         sm_ushort ablineno;        /* Line number of allocation */ 
70 };
71
72 static struct b_queue abqueue = {    /* Allocated buffer queue */
73         &abqueue, &abqueue
74 };
75
76 static Boolean bufimode = False;   /* Buffers not tracked when True */
77
78 #define HEAD_SIZE BALIGN(sizeof(struct abufhead))
79
80
81 /*  SMALLOC  --  Allocate buffer, enqueing on the orphaned buffer
82                  tracking list.  */
83
84 static void *smalloc(char *fname, int lineno, unsigned int nbytes)
85 {
86         char *buf;
87
88         /* Note:  Unix  MALLOC  actually  permits  a zero length to be
89            passed and allocates a valid block with  zero  user  bytes.
90            Such  a  block  can  later  be expanded with realloc().  We
91            disallow this based on the belief that it's better to  make
92            a  special case and allocate one byte in the rare case this
93            is desired than to miss all the erroneous occurrences where
94            buffer length calculation code results in a zero.  */
95
96         ASSERT(nbytes > 0);
97
98         nbytes += HEAD_SIZE + 1;
99         if ((buf = (char *)malloc(nbytes)) != NULL) {
100            P(mutex);
101            /* Enqueue buffer on allocated list */
102            qinsert(&abqueue, (struct b_queue *) buf);
103            ((struct abufhead *) buf)->ablen = nbytes;
104            ((struct abufhead *) buf)->abfname = bufimode ? NULL : fname;
105            ((struct abufhead *) buf)->ablineno = (sm_ushort) lineno;
106            /* Emplace end-clobber detector at end of buffer */
107            buf[nbytes - 1] = (((long) buf) & 0xFF) ^ 0xC5;
108            buf += HEAD_SIZE;  /* Increment to user data start */
109            V(mutex);
110         }
111         sm_check(fname, lineno, True);
112         Dmsg4(1150, "smalloc %d at %x from %s:%d\n", nbytes, buf, fname, lineno);
113         return (void *)buf;
114 }
115
116 /*  SM_NEW_OWNER -- Update the File and line number for a buffer
117                     This is to accomodate mem_pool. */
118
119 void sm_new_owner(char *fname, int lineno, char *buf)
120 {
121         buf -= HEAD_SIZE;  /* Decrement to header */
122         ((struct abufhead *)buf)->abfname = bufimode ? NULL : fname;
123         ((struct abufhead *)buf)->ablineno = (sm_ushort) lineno;
124         return;
125 }
126
127 /*  SM_FREE  --  Update free pool availability.  FREE is never called
128                  except  through  this interface or by actuallyfree().
129                  free(x)  is  defined  to  generate  a  call  to  this
130                  routine.  */
131
132 void sm_free(char *file, int line, void *fp)
133 {
134         char *cp = (char *) fp;
135         struct b_queue *qp;
136
137         sm_check(__FILE__, __LINE__, True);
138         if (cp == NULL) {
139            Emsg2(M_ABORT, 0, "Attempt to free NULL called from %s:%d\n", file, line);
140         }
141
142         cp -= HEAD_SIZE;
143         qp = (struct b_queue *) cp;
144
145         P(mutex);
146         Dmsg4(1150, "sm_free %d at %x from %s:%d\n", 
147               ((struct abufhead *)cp)->ablen, fp, 
148               ((struct abufhead *)cp)->abfname, ((struct abufhead *)cp)->ablineno);
149
150         /* The following assertions will catch virtually every release
151            of an address which isn't an allocated buffer. */
152         if (qp->qnext->qprev != qp) {
153            V(mutex);
154            Emsg2(M_ABORT, 0, "qp->qnext->qprev != qp called from %s:%d\n", file, line);
155         }
156         if (qp->qprev->qnext != qp) {
157            V(mutex);
158            Emsg2(M_ABORT, 0, "qp->qprev->qnext != qp called from %s:%d\n", file, line);
159         }
160
161         /* The following assertion detects storing off the  end  of  the
162            allocated  space in the buffer by comparing the end of buffer
163            checksum with the address of the buffer.  */
164
165         if (((unsigned char *) cp)[((struct abufhead *) cp)->ablen - 1] !=
166                  ((((long) cp) & 0xFF) ^ 0xC5)) {
167            V(mutex);
168            Emsg2(M_ABORT, 0, "Buffer overrun called from %s:%d\n", file, line);
169         }
170
171
172         qdchain(qp);
173         V(mutex);
174
175         /* Now we wipe the contents of  the  just-released  buffer  with
176            "designer  garbage"  (Duff  Kurland's  phrase) of alternating
177            bits.  This is intended to ruin the day for any miscreant who
178            attempts to access data through a pointer into storage that's
179            been previously released. */
180
181         memset(cp, 0xAA, (int) ((struct abufhead *) cp)->ablen);
182
183         free(cp);
184 }
185
186 /*  SM_MALLOC  --  Allocate buffer.  NULL is returned if no memory
187                    was available.  */
188
189 void *sm_malloc(char *fname, int lineno, unsigned int nbytes)
190 {
191         void *buf;
192
193         if ((buf = smalloc(fname, lineno, nbytes)) != NULL) {
194
195            /* To catch sloppy code that assumes  buffers  obtained  from
196               malloc()  are  zeroed,  we  preset  the buffer contents to
197               "designer garbage" consisting of alternating bits.  */
198
199            memset(buf, 0x55, (int) nbytes);
200         }
201         return buf;
202 }
203
204 /*  SM_CALLOC  --  Allocate an array and clear it to zero.  */
205
206 void *sm_calloc(char *fname, int lineno,
207                 unsigned int nelem, unsigned int elsize)
208 {
209         void *buf;
210
211         if ((buf = smalloc(fname, lineno, nelem * elsize)) != NULL) {
212            memset(buf, 0, (int) (nelem * elsize));
213         }
214         return buf;
215 }
216
217 /*  SM_REALLOC  --  Adjust the size of a  previously  allocated  buffer.
218                     Note  that  the trick of "resurrecting" a previously
219                     freed buffer with realloc() is NOT supported by this
220                     function.   Further, because of the need to maintain
221                     our control storage, SM_REALLOC must always allocate
222                     a  new  block  and  copy  the data in the old block.
223                     This may result in programs which make heavy use  of
224                     realloc() running much slower than normally.  */
225
226 void *sm_realloc(char *fname, int lineno, void *ptr, unsigned int size)
227 {
228         unsigned osize;
229         void *buf;
230         char *cp = (char *) ptr;
231
232         Dmsg4(400, "sm_realloc %s:%d 0x%x %d\n", fname, lineno, ptr, size);
233         sm_check(fname, lineno, True);
234         if (size <= 0) {
235            e_msg(fname, lineno, M_ABORT, 0, "sm_realloc size: %d\n", size);
236         }
237
238         /*  If  the  old  block  pointer  is  NULL, treat realloc() as a
239            malloc().  SVID is silent  on  this,  but  many  C  libraries
240            permit this.  */
241
242         if (ptr == NULL)
243            return sm_malloc(fname, lineno, size);
244
245         /* If the old and new sizes are the same, be a nice guy and just
246            return the buffer passed in.  */
247
248         cp -= HEAD_SIZE;
249         osize = ((struct abufhead *) cp)->ablen - (HEAD_SIZE + 1);
250         if (size == osize) {
251            return ptr;
252         }
253
254         /* Sizes differ.  Allocate a new buffer of the  requested  size.
255            If  we  can't  obtain  such a buffer, act as defined in SVID:
256            return NULL from  realloc()  and  leave  the  buffer  in  PTR
257            intact.  */
258
259         if ((buf = smalloc(fname, lineno, size)) != NULL) {
260            memcpy(buf, ptr, (int) sm_min(size, osize));
261            /* If the new buffer is larger than the old, fill the balance
262               of it with "designer garbage". */
263            if (size > osize) {
264               memset(((char *) buf) + osize, 0x55, (int) (size - osize));
265            }
266
267            /* All done.  Free and dechain the original buffer. */
268
269            sm_free(__FILE__, __LINE__, ptr);
270         }
271         Dmsg4(150, "sm_realloc %d at %x from %s:%d\n", size, buf, fname, lineno);
272         sm_check(fname, lineno, True);
273         return buf;
274 }
275
276 /*  ACTUALLYMALLOC  --  Call the system malloc() function to obtain
277                         storage which will eventually be released
278                         by system or library routines not compiled
279                         using SMARTALLOC.  */
280
281 void *actuallymalloc(unsigned int size)
282 {
283         return malloc(size);
284 }
285
286 /*  ACTUALLYCALLOC  --  Call the system calloc() function to obtain
287                         storage which will eventually be released
288                         by system or library routines not compiled
289                         using SMARTALLOC.  */
290
291 void *actuallycalloc(unsigned int nelem, unsigned int elsize)
292 {
293         return calloc(nelem, elsize);
294 }
295
296 /*  ACTUALLYREALLOC  --  Call the system realloc() function to obtain
297                          storage which will eventually be released
298                          by system or library routines not compiled
299                          using SMARTALLOC.  */
300
301 void *actuallyrealloc(void *ptr, unsigned int size)
302 {
303         Dmsg2(400, "Actuallyrealloc 0x%x %d\n", ptr, size);
304         return realloc(ptr, size);
305 }
306
307 /*  ACTUALLYFREE  --  Interface to system free() function to release
308                       buffers allocated by low-level routines. */
309
310 void actuallyfree(void *cp)
311 {
312         free(cp);
313 }
314
315 /*  SM_DUMP  --  Print orphaned buffers (and dump them if BUFDUMP is
316  *               True).
317  *  N.B. DO NOT USE any Bacula print routines (Dmsg, Jmsg, Emsg, ...)
318  *    as they have all been shut down at this point.
319  */
320 void sm_dump(Boolean bufdump)
321 {
322         struct abufhead *ap;
323
324         P(mutex);
325
326         ap = (struct abufhead *)abqueue.qnext;
327
328         while (ap != (struct abufhead *) &abqueue) {
329
330            if ((ap == NULL) ||
331                (ap->abq.qnext->qprev != (struct b_queue *) ap) || 
332                (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
333               fprintf(stderr,
334                  "\nOrphaned buffers exist.  Dump terminated following\n");
335               fprintf(stderr,
336                  "  discovery of bad links in chain of orphaned buffers.\n");
337               fprintf(stderr,
338                  "  Buffer address with bad links: %lx\n", (long) ap);
339               break;
340            }
341
342            if (ap->abfname != NULL) {
343               unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
344               char errmsg[80];
345
346               sprintf(errmsg,
347                 "Orphaned buffer:  %6u bytes allocated at line %d of %s %s\n",
348                  memsize, ap->ablineno, my_name, ap->abfname
349               );
350               fprintf(stderr, "%s", errmsg);
351               if (bufdump) {
352                  unsigned llen = 0;
353                  char *cp = ((char *) ap) + HEAD_SIZE;
354
355                  errmsg[0] = EOS;
356                  while (memsize) {
357                     if (llen >= 16) {
358                        strcat(errmsg, "\n");
359                        llen = 0;
360                        fprintf(stderr, "%s", errmsg);
361                        errmsg[0] = EOS;
362                     }
363                     sprintf(errmsg + strlen(errmsg), " %02X",
364                        (*cp++) & 0xFF);
365                     llen++;
366                     memsize--;
367                  }
368                  fprintf(stderr, "%s\n", errmsg);
369               }
370            }
371            ap = (struct abufhead *) ap->abq.qnext;
372         }
373         V(mutex);
374 }
375
376 #undef sm_check
377 /*  SM_CHECK --  Check the buffers and dump if any damage exists. */
378 void sm_check(char *fname, int lineno, Boolean bufdump)
379 {
380         if (!sm_check_rtn(fname, lineno, bufdump)) {
381            Emsg2(M_ABORT, 0, "Damaged buffer found. Called from %s:%d\n",
382               fname, lineno);
383         }
384 }
385
386 #undef sm_check_rtn
387 /*  SM_CHECK_RTN -- Check the buffers and return 1 if OK otherwise 0 */
388 int sm_check_rtn(char *fname, int lineno, Boolean bufdump)
389 {
390         struct abufhead *ap;
391         int bad, badbuf = 0;
392
393         P(mutex);
394         ap = (struct abufhead *) abqueue.qnext;
395         while (ap != (struct abufhead *) &abqueue) {
396            bad = 0;
397            if ((ap == NULL) ||
398                (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
399               bad = 0x1;
400            }
401            if (ap->abq.qprev->qnext != (struct b_queue *) ap) { 
402               bad |= 0x2;
403            }
404            if (((unsigned char *) ap)[((struct abufhead *) ap)->ablen - 1] !=
405                 ((((long) ap) & 0xFF) ^ 0xC5)) {
406               bad |= 0x4;
407            }
408            badbuf |= bad;
409            if (bad) {
410               fprintf(stderr,
411                  "\nDamaged buffers found at %s:%d\n", fname, lineno);
412
413               if (bad & 0x1) {
414                  fprintf(stderr, "  discovery of bad prev link.\n");
415               }
416               if (bad & 0x2) {
417                  fprintf(stderr, "  discovery of bad next link.\n");
418               }
419               if (bad & 0x4) {
420                  fprintf(stderr, "  discovery of data overrun.\n");
421               }
422
423               fprintf(stderr, "  Buffer address: %lx\n", (long) ap);
424
425               if (ap->abfname != NULL) {
426                  unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
427                  char errmsg[80];
428
429                  fprintf(stderr,
430                    "Damaged buffer:  %6u bytes allocated at line %d of %s %s\n",
431                     memsize, ap->ablineno, my_name, ap->abfname
432                  );
433                  if (bufdump) {
434                     unsigned llen = 0;
435                     char *cp = ((char *) ap) + HEAD_SIZE;
436
437                     errmsg[0] = EOS;
438                     while (memsize) {
439                        if (llen >= 16) {
440                           strcat(errmsg, "\n");
441                           llen = 0;
442                           fprintf(stderr, "%s", errmsg);
443                           errmsg[0] = EOS;
444                        }
445                        if (*cp < 0x20) {
446                           sprintf(errmsg + strlen(errmsg), " %02X",
447                              (*cp++) & 0xFF);
448                        } else {
449                           sprintf(errmsg + strlen(errmsg), " %c ",
450                              (*cp++) & 0xFF);
451                        }
452                        llen++;
453                        memsize--;
454                     }
455                     fprintf(stderr, "%s\n", errmsg);
456                  }
457               }
458            }
459            ap = (struct abufhead *) ap->abq.qnext;
460         }
461         V(mutex);
462         return badbuf ? 0 : 1;
463 }
464
465
466 /*  SM_STATIC  --  Orphaned buffer detection can be disabled  (for  such
467                    items  as buffers allocated during initialisation) by
468                    calling   sm_static(1).    Normal   orphaned   buffer
469                    detection  can be re-enabled with sm_static(0).  Note
470                    that all the other safeguards still apply to  buffers
471                    allocated  when  sm_static(1)  mode is in effect.  */
472
473 void sm_static(int mode)
474 {
475         bufimode = (Boolean) (mode != 0);
476 }
477
478 #endif