]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/smartall.c
5950cf794df2de8322d583bce8572e2bd22cb0c6
[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    } else {
111       Emsg0(M_ABORT, 0, _("Out of memory\n"));
112    }
113    Dmsg4(1150, "smalloc %d at %x from %s:%d\n", nbytes, buf, fname, lineno);
114    return (void *)buf;
115 }
116
117 /*  SM_NEW_OWNER -- Update the File and line number for a buffer
118                     This is to accomodate mem_pool. */
119
120 void sm_new_owner(char *fname, int lineno, char *buf)
121 {
122    buf -= HEAD_SIZE;  /* Decrement to header */
123    ((struct abufhead *)buf)->abfname = bufimode ? NULL : fname;
124    ((struct abufhead *)buf)->ablineno = (sm_ushort) lineno;
125    return;
126 }
127
128 /*  SM_FREE  --  Update free pool availability.  FREE is never called
129                  except  through  this interface or by actuallyfree().
130                  free(x)  is  defined  to  generate  a  call  to  this
131                  routine.  */
132
133 void sm_free(char *file, int line, void *fp)
134 {
135    char *cp = (char *) fp;
136    struct b_queue *qp;
137
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    } else {
201       Emsg0(M_ABORT, 0, _("Out of memory\n"));
202    }
203    return buf;
204 }
205
206 /*  SM_CALLOC  --  Allocate an array and clear it to zero.  */
207
208 void *sm_calloc(char *fname, int lineno,
209                 unsigned int nelem, unsigned int elsize)
210 {
211    void *buf;
212
213    if ((buf = smalloc(fname, lineno, nelem * elsize)) != NULL) {
214       memset(buf, 0, (int) (nelem * elsize));
215    } else {
216       Emsg0(M_ABORT, 0, _("Out of memory\n"));
217    }
218    return buf;
219 }
220
221 /*  SM_REALLOC  --  Adjust the size of a  previously  allocated  buffer.
222                     Note  that  the trick of "resurrecting" a previously
223                     freed buffer with realloc() is NOT supported by this
224                     function.   Further, because of the need to maintain
225                     our control storage, SM_REALLOC must always allocate
226                     a  new  block  and  copy  the data in the old block.
227                     This may result in programs which make heavy use  of
228                     realloc() running much slower than normally.  */
229
230 void *sm_realloc(char *fname, int lineno, void *ptr, unsigned int size)
231 {
232    unsigned osize;
233    void *buf;
234    char *cp = (char *) ptr;
235
236    Dmsg4(400, "sm_realloc %s:%d 0x%x %d\n", fname, lineno, ptr, size);
237    if (size <= 0) {
238       e_msg(fname, lineno, M_ABORT, 0, "sm_realloc size: %d\n", size);
239    }
240
241    /*  If  the  old  block  pointer  is  NULL, treat realloc() as a
242       malloc().  SVID is silent  on  this,  but  many  C  libraries
243       permit this.  */
244
245    if (ptr == NULL) {
246       return sm_malloc(fname, lineno, size);
247    }
248
249    /* If the old and new sizes are the same, be a nice guy and just
250       return the buffer passed in.  */
251
252    cp -= HEAD_SIZE;
253    osize = ((struct abufhead *) cp)->ablen - (HEAD_SIZE + 1);
254    if (size == osize) {
255       return ptr;
256    }
257
258    /* Sizes differ.  Allocate a new buffer of the  requested  size.
259       If  we  can't  obtain  such a buffer, act as defined in SVID:
260       return NULL from  realloc()  and  leave  the  buffer  in  PTR
261       intact.  */
262
263    if ((buf = smalloc(fname, lineno, size)) != NULL) {
264       memcpy(buf, ptr, (int) sm_min(size, osize));
265       /* If the new buffer is larger than the old, fill the balance
266          of it with "designer garbage". */
267       if (size > osize) {
268          memset(((char *) buf) + osize, 0x55, (int) (size - osize));
269       }
270
271       /* All done.  Free and dechain the original buffer. */
272
273       sm_free(__FILE__, __LINE__, ptr);
274    }
275    Dmsg4(150, "sm_realloc %d at %x from %s:%d\n", size, buf, fname, lineno);
276    return buf;
277 }
278
279 /*  ACTUALLYMALLOC  --  Call the system malloc() function to obtain
280                         storage which will eventually be released
281                         by system or library routines not compiled
282                         using SMARTALLOC.  */
283
284 void *actuallymalloc(unsigned int size)
285 {
286    return malloc(size);
287 }
288
289 /*  ACTUALLYCALLOC  --  Call the system calloc() function to obtain
290                         storage which will eventually be released
291                         by system or library routines not compiled
292                         using SMARTALLOC.  */
293
294 void *actuallycalloc(unsigned int nelem, unsigned int elsize)
295 {
296    return calloc(nelem, elsize);
297 }
298
299 /*  ACTUALLYREALLOC  --  Call the system realloc() function to obtain
300                          storage which will eventually be released
301                          by system or library routines not compiled
302                          using SMARTALLOC.  */
303
304 void *actuallyrealloc(void *ptr, unsigned int size)
305 {
306    Dmsg2(400, "Actuallyrealloc 0x%x %d\n", ptr, size);
307    return realloc(ptr, size);
308 }
309
310 /*  ACTUALLYFREE  --  Interface to system free() function to release
311                       buffers allocated by low-level routines. */
312
313 void actuallyfree(void *cp)
314 {
315    free(cp);
316 }
317
318 /*  SM_DUMP  --  Print orphaned buffers (and dump them if BUFDUMP is
319  *               True).
320  *  N.B. DO NOT USE any Bacula print routines (Dmsg, Jmsg, Emsg, ...)
321  *    as they have all been shut down at this point.
322  */
323 void sm_dump(Boolean bufdump)
324 {
325    struct abufhead *ap;
326
327    P(mutex);
328
329    ap = (struct abufhead *)abqueue.qnext;
330
331    while (ap != (struct abufhead *) &abqueue) {
332
333       if ((ap == NULL) ||
334           (ap->abq.qnext->qprev != (struct b_queue *) ap) || 
335           (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
336          fprintf(stderr,
337             "\nOrphaned buffers exist.  Dump terminated following\n");
338          fprintf(stderr,
339             "  discovery of bad links in chain of orphaned buffers.\n");
340          fprintf(stderr,
341             "  Buffer address with bad links: %lx\n", (long) ap);
342          break;
343       }
344
345       if (ap->abfname != NULL) {
346          unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
347          char errmsg[80];
348
349          sprintf(errmsg,
350            "Orphaned buffer:  %6u bytes allocated at line %d of %s %s\n",
351             memsize, ap->ablineno, my_name, ap->abfname
352          );
353          fprintf(stderr, "%s", errmsg);
354          if (bufdump) {
355             unsigned llen = 0;
356             char *cp = ((char *) ap) + HEAD_SIZE;
357
358             errmsg[0] = EOS;
359             while (memsize) {
360                if (llen >= 16) {
361                   strcat(errmsg, "\n");
362                   llen = 0;
363                   fprintf(stderr, "%s", errmsg);
364                   errmsg[0] = EOS;
365                }
366                sprintf(errmsg + strlen(errmsg), " %02X",
367                   (*cp++) & 0xFF);
368                llen++;
369                memsize--;
370             }
371             fprintf(stderr, "%s\n", errmsg);
372          }
373       }
374       ap = (struct abufhead *) ap->abq.qnext;
375    }
376    V(mutex);
377 }
378
379 #undef sm_check
380 /*  SM_CHECK --  Check the buffers and dump if any damage exists. */
381 void sm_check(char *fname, int lineno, Boolean bufdump)
382 {
383         if (!sm_check_rtn(fname, lineno, bufdump)) {
384            Emsg2(M_ABORT, 0, "Damaged buffer found. Called from %s:%d\n",
385               fname, lineno);
386         }
387 }
388
389 #undef sm_check_rtn
390 /*  SM_CHECK_RTN -- Check the buffers and return 1 if OK otherwise 0 */
391 int sm_check_rtn(char *fname, int lineno, Boolean bufdump)
392 {
393    struct abufhead *ap;
394    int bad, badbuf = 0;
395
396    P(mutex);
397    ap = (struct abufhead *) abqueue.qnext;
398    while (ap != (struct abufhead *) &abqueue) {
399       bad = 0;
400       if ((ap == NULL) ||
401           (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
402          bad = 0x1;
403       }
404       if (ap->abq.qprev->qnext != (struct b_queue *) ap) { 
405          bad |= 0x2;
406       }
407       if (((unsigned char *) ap)[((struct abufhead *) ap)->ablen - 1] !=
408            ((((long) ap) & 0xFF) ^ 0xC5)) {
409          bad |= 0x4;
410       }
411       badbuf |= bad;
412       if (bad) {
413          fprintf(stderr,
414             "\nDamaged buffers found at %s:%d\n", fname, lineno);
415
416          if (bad & 0x1) {
417             fprintf(stderr, "  discovery of bad prev link.\n");
418          }
419          if (bad & 0x2) {
420             fprintf(stderr, "  discovery of bad next link.\n");
421          }
422          if (bad & 0x4) {
423             fprintf(stderr, "  discovery of data overrun.\n");
424          }
425
426          fprintf(stderr, "  Buffer address: %lx\n", (long) ap);
427
428          if (ap->abfname != NULL) {
429             unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
430             char errmsg[80];
431
432             fprintf(stderr,
433               "Damaged buffer:  %6u bytes allocated at line %d of %s %s\n",
434                memsize, ap->ablineno, my_name, ap->abfname
435             );
436             if (bufdump) {
437                unsigned llen = 0;
438                char *cp = ((char *) ap) + HEAD_SIZE;
439
440                errmsg[0] = EOS;
441                while (memsize) {
442                   if (llen >= 16) {
443                      strcat(errmsg, "\n");
444                      llen = 0;
445                      fprintf(stderr, "%s", errmsg);
446                      errmsg[0] = EOS;
447                   }
448                   if (*cp < 0x20) {
449                      sprintf(errmsg + strlen(errmsg), " %02X",
450                         (*cp++) & 0xFF);
451                   } else {
452                      sprintf(errmsg + strlen(errmsg), " %c ",
453                         (*cp++) & 0xFF);
454                   }
455                   llen++;
456                   memsize--;
457                }
458                fprintf(stderr, "%s\n", errmsg);
459             }
460          }
461       }
462       ap = (struct abufhead *) ap->abq.qnext;
463    }
464    V(mutex);
465    return badbuf ? 0 : 1;
466 }
467
468
469 /*  SM_STATIC  --  Orphaned buffer detection can be disabled  (for  such
470                    items  as buffers allocated during initialisation) by
471                    calling   sm_static(1).    Normal   orphaned   buffer
472                    detection  can be re-enabled with sm_static(0).  Note
473                    that all the other safeguards still apply to  buffers
474                    allocated  when  sm_static(1)  mode is in effect.  */
475
476 void sm_static(int mode)
477 {
478    bufimode = (Boolean) (mode != 0);
479 }
480
481 /* 
482  * Here we overload C++'s global new and delete operators
483  *  so that the memory is allocated through smartalloc.
484  */
485
486 void * operator new(size_t size)
487 {
488 // Dmsg1(000, "new called %d\n", size);
489    return sm_malloc(__FILE__, __LINE__, size);
490 }
491
492 void operator delete(void *buf)
493 {
494 // Dmsg1(000, "free called 0x%x\n", buf);
495    sm_free(__FILE__, __LINE__, buf);
496 }
497
498 #endif