]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/smartall.c
18Nov06
[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-2006 Kern Sibbald
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
26    version 2 as amended with additional clauses defined in the
27    file LICENSE in the main source directory.
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 
32    the file LICENSE for additional details.
33
34  */
35
36 #include "bacula.h"
37 /* Use the real routines here */
38 #undef realloc
39 #undef calloc
40 #undef malloc
41 #undef free
42
43 /* We normally turn off debugging here.
44  *  If you want it, simply #ifdef all the
45  *  following off.
46  */
47 #undef Dmsg1
48 #undef Dmsg2
49 #undef Dmsg3
50 #undef Dmsg4
51 #define Dmsg1(l,f,a1)
52 #define Dmsg2(l,f,a1,a2)
53 #define Dmsg3(l,f,a1,a2,a3)
54 #define Dmsg4(l,f,a1,a2,a3,a4)
55
56
57 uint64_t sm_max_bytes = 0;
58 uint64_t sm_bytes = 0;
59 uint32_t sm_max_buffers = 0;
60 uint32_t sm_buffers = 0;
61
62 #ifdef SMARTALLOC
63
64 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
65
66 extern char my_name[];                /* daemon name */
67
68 typedef unsigned short sm_ushort;
69
70 #define EOS      '\0'              /* End of string sentinel */
71 #define sm_min(a, b) ((a) < (b) ? (a) : (b))
72
73 /*  Queue data structures  */
74
75 /*  Memory allocation control structures and storage.  */
76
77 struct abufhead {
78    struct b_queue abq;         /* Links on allocated queue */
79    unsigned ablen;             /* Buffer length in bytes */
80    const char *abfname;        /* File name pointer */
81    sm_ushort ablineno;         /* Line number of allocation */
82 };
83
84 static struct b_queue abqueue = {    /* Allocated buffer queue */
85    &abqueue, &abqueue
86 };
87
88
89 static bool bufimode = false;   /* Buffers not tracked when True */
90
91 #define HEAD_SIZE BALIGN(sizeof(struct abufhead))
92
93
94 /*  SMALLOC  --  Allocate buffer, enqueing on the orphaned buffer
95                  tracking list.  */
96
97 static void *smalloc(const char *fname, int lineno, unsigned int nbytes)
98 {
99    char *buf;
100
101    /* Note:  Unix  MALLOC  actually  permits  a zero length to be
102       passed and allocates a valid block with  zero  user  bytes.
103       Such  a  block  can  later  be expanded with realloc().  We
104       disallow this based on the belief that it's better to  make
105       a  special case and allocate one byte in the rare case this
106       is desired than to miss all the erroneous occurrences where
107       buffer length calculation code results in a zero.  */
108
109    ASSERT(nbytes > 0);
110
111    nbytes += HEAD_SIZE + 1;
112    if ((buf = (char *)malloc(nbytes)) != NULL) {
113       struct abufhead *head = (struct abufhead *)buf;
114       P(mutex);
115       /* Enqueue buffer on allocated list */
116       qinsert(&abqueue, (struct b_queue *) buf);
117       head->ablen = nbytes;
118       head->abfname = bufimode ? NULL : fname;
119       head->ablineno = (sm_ushort) lineno;
120       /* Emplace end-clobber detector at end of buffer */
121       buf[nbytes - 1] = (uint8_t)((((long) buf) & 0xFF) ^ 0xC5);
122       buf += HEAD_SIZE;  /* Increment to user data start */
123       if (++sm_buffers > sm_max_buffers) {
124          sm_max_buffers = sm_buffers;
125       }
126       sm_bytes += nbytes;
127       if (sm_bytes > sm_max_bytes) {
128          sm_max_bytes = sm_bytes;
129       }
130       V(mutex);
131    } else {
132       Emsg0(M_ABORT, 0, _("Out of memory\n"));
133    }
134    Dmsg4(1150, "smalloc %d at %x from %s:%d\n", nbytes, buf, fname, lineno);
135 #if    SMALLOC_SANITY_CHECK > 0
136    if (sm_bytes > SMALLOC_SANITY_CHECK) {
137       Emsg0(M_ABORT, 0, _("Too much memory used."));
138    }
139 #endif
140    return (void *)buf;
141 }
142
143 /*  SM_NEW_OWNER -- Update the File and line number for a buffer
144                     This is to accomodate mem_pool. */
145
146 void sm_new_owner(const char *fname, int lineno, char *buf)
147 {
148    buf -= HEAD_SIZE;  /* Decrement to header */
149    ((struct abufhead *)buf)->abfname = bufimode ? NULL : fname;
150    ((struct abufhead *)buf)->ablineno = (sm_ushort) lineno;
151    return;
152 }
153
154 /*  SM_FREE  --  Update free pool availability.  FREE is never called
155                  except  through  this interface or by actuallyfree().
156                  free(x)  is  defined  to  generate  a  call  to  this
157                  routine.  */
158
159 void sm_free(const char *file, int line, void *fp)
160 {
161    char *cp = (char *) fp;
162    struct b_queue *qp;
163
164    if (cp == NULL) {
165       Emsg2(M_ABORT, 0, _("Attempt to free NULL called from %s:%d\n"), file, line);
166    }
167
168    cp -= HEAD_SIZE;
169    qp = (struct b_queue *) cp;
170    struct abufhead *head = (struct abufhead *)cp;
171
172    P(mutex);
173    Dmsg4(1150, "sm_free %d at %x from %s:%d\n",
174          head->ablen, fp,
175          head->abfname, head->ablineno);
176
177    /* The following assertions will catch virtually every release
178       of an address which isn't an allocated buffer. */
179    if (qp->qnext->qprev != qp) {
180       V(mutex);
181       Emsg2(M_ABORT, 0, _("qp->qnext->qprev != qp called from %s:%d\n"), file, line);
182    }
183    if (qp->qprev->qnext != qp) {
184       V(mutex);
185       Emsg2(M_ABORT, 0, _("qp->qprev->qnext != qp called from %s:%d\n"), file, line);
186    }
187
188    /* The following assertion detects storing off the  end  of  the
189       allocated  space in the buffer by comparing the end of buffer
190       checksum with the address of the buffer.  */
191
192    if (((unsigned char *)cp)[head->ablen - 1] != ((((long) cp) & 0xFF) ^ 0xC5)) {
193       V(mutex);
194       Emsg2(M_ABORT, 0, _("Buffer overrun called from %s:%d\n"), file, line);
195    }
196    if (sm_buffers > 0) {
197       sm_buffers--;
198       sm_bytes -= head->ablen;
199    }
200
201    qdchain(qp);
202    V(mutex);
203
204    /* Now we wipe the contents of  the  just-released  buffer  with
205       "designer  garbage"  (Duff  Kurland's  phrase) of alternating
206       bits.  This is intended to ruin the day for any miscreant who
207       attempts to access data through a pointer into storage that's
208       been previously released. */
209
210    memset(cp, 0xAA, (int) head->ablen);
211
212    free(cp);
213 }
214
215 /*  SM_MALLOC  --  Allocate buffer.  NULL is returned if no memory
216                    was available.  */
217
218 void *sm_malloc(const char *fname, int lineno, unsigned int nbytes)
219 {
220    void *buf;
221
222    if ((buf = smalloc(fname, lineno, nbytes)) != NULL) {
223
224       /* To catch sloppy code that assumes  buffers  obtained  from
225          malloc()  are  zeroed,  we  preset  the buffer contents to
226          "designer garbage" consisting of alternating bits.  */
227
228       memset(buf, 0x55, (int) nbytes);
229    } else {
230       Emsg0(M_ABORT, 0, _("Out of memory\n"));
231    }
232    return buf;
233 }
234
235 /*  SM_CALLOC  --  Allocate an array and clear it to zero.  */
236
237 void *sm_calloc(const char *fname, int lineno,
238                 unsigned int nelem, unsigned int elsize)
239 {
240    void *buf;
241
242    if ((buf = smalloc(fname, lineno, nelem * elsize)) != NULL) {
243       memset(buf, 0, (int) (nelem * elsize));
244    } else {
245       Emsg0(M_ABORT, 0, _("Out of memory\n"));
246    }
247    return buf;
248 }
249
250 /*  SM_REALLOC  --  Adjust the size of a  previously  allocated  buffer.
251                     Note  that  the trick of "resurrecting" a previously
252                     freed buffer with realloc() is NOT supported by this
253                     function.   Further, because of the need to maintain
254                     our control storage, SM_REALLOC must always allocate
255                     a  new  block  and  copy  the data in the old block.
256                     This may result in programs which make heavy use  of
257                     realloc() running much slower than normally.  */
258
259 void *sm_realloc(const char *fname, int lineno, void *ptr, unsigned int size)
260 {
261    unsigned osize;
262    void *buf;
263    char *cp = (char *) ptr;
264
265    Dmsg4(400, "sm_realloc %s:%d 0x%x %d\n", fname, lineno, ptr, size);
266    if (size <= 0) {
267       e_msg(fname, lineno, M_ABORT, 0, _("sm_realloc size: %d\n"), size);
268    }
269
270    /*  If  the  old  block  pointer  is  NULL, treat realloc() as a
271       malloc().  SVID is silent  on  this,  but  many  C  libraries
272       permit this.  */
273
274    if (ptr == NULL) {
275       return sm_malloc(fname, lineno, size);
276    }
277
278    /* If the old and new sizes are the same, be a nice guy and just
279       return the buffer passed in.  */
280
281    cp -= HEAD_SIZE;
282    struct abufhead *head = (struct abufhead *)cp;
283    osize = head->ablen - (HEAD_SIZE + 1);
284    if (size == osize) {
285       return ptr;
286    }
287
288    /* Sizes differ.  Allocate a new buffer of the  requested  size.
289       If  we  can't  obtain  such a buffer, act as defined in SVID:
290       return NULL from  realloc()  and  leave  the  buffer  in  PTR
291       intact.  */
292
293 // sm_buffers--;
294 // sm_bytes -= head->ablen;
295
296    if ((buf = smalloc(fname, lineno, size)) != NULL) {
297       memcpy(buf, ptr, (int) sm_min(size, osize));
298       /* If the new buffer is larger than the old, fill the balance
299          of it with "designer garbage". */
300       if (size > osize) {
301          memset(((char *) buf) + osize, 0x55, (int) (size - osize));
302       }
303
304       /* All done.  Free and dechain the original buffer. */
305
306       sm_free(__FILE__, __LINE__, ptr);
307    }
308    Dmsg4(150, _("sm_realloc %d at %x from %s:%d\n"), size, buf, fname, lineno);
309    return buf;
310 }
311
312 /*  ACTUALLYMALLOC  --  Call the system malloc() function to obtain
313                         storage which will eventually be released
314                         by system or library routines not compiled
315                         using SMARTALLOC.  */
316
317 void *actuallymalloc(unsigned int size)
318 {
319    return malloc(size);
320 }
321
322 /*  ACTUALLYCALLOC  --  Call the system calloc() function to obtain
323                         storage which will eventually be released
324                         by system or library routines not compiled
325                         using SMARTALLOC.  */
326
327 void *actuallycalloc(unsigned int nelem, unsigned int elsize)
328 {
329    return calloc(nelem, elsize);
330 }
331
332 /*  ACTUALLYREALLOC  --  Call the system realloc() function to obtain
333                          storage which will eventually be released
334                          by system or library routines not compiled
335                          using SMARTALLOC.  */
336
337 void *actuallyrealloc(void *ptr, unsigned int size)
338 {
339    Dmsg2(400, "Actuallyrealloc 0x%x %d\n", ptr, size);
340    return realloc(ptr, size);
341 }
342
343 /*  ACTUALLYFREE  --  Interface to system free() function to release
344                       buffers allocated by low-level routines. */
345
346 void actuallyfree(void *cp)
347 {
348    free(cp);
349 }
350
351 /*  SM_DUMP  --  Print orphaned buffers (and dump them if BUFDUMP is
352  *               True).
353  *  N.B. DO NOT USE any Bacula print routines (Dmsg, Jmsg, Emsg, ...)
354  *    as they have all been shut down at this point.
355  */
356 void sm_dump(bool bufdump)
357 {
358    struct abufhead *ap;
359
360    P(mutex);
361
362    ap = (struct abufhead *)abqueue.qnext;
363
364    while (ap != (struct abufhead *) &abqueue) {
365
366       if ((ap == NULL) ||
367           (ap->abq.qnext->qprev != (struct b_queue *) ap) ||
368           (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
369          fprintf(stderr, _(
370             "\nOrphaned buffers exist.  Dump terminated following\n"
371             "  discovery of bad links in chain of orphaned buffers.\n"
372             "  Buffer address with bad links: %lx\n"), (long) ap);
373          break;
374       }
375
376       if (ap->abfname != NULL) {
377          unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
378          char errmsg[500];
379
380          bsnprintf(errmsg, sizeof(errmsg),
381            _("Orphaned buffer:  %6u bytes allocated at line %d of %s %s\n"),
382             memsize, ap->ablineno, my_name, ap->abfname
383          );
384          fprintf(stderr, "%s", errmsg);
385          if (bufdump) {
386             char buf[20];
387             unsigned llen = 0;
388             char *cp = ((char *) ap) + HEAD_SIZE;
389
390             errmsg[0] = EOS;
391             while (memsize) {
392                if (llen >= 16) {
393                   bstrncat(errmsg, "\n", sizeof(errmsg));
394                   llen = 0;
395                   fprintf(stderr, "%s", errmsg);
396                   errmsg[0] = EOS;
397                }
398                bsnprintf(buf, sizeof(buf), " %02X",
399                   (*cp++) & 0xFF);
400                bstrncat(errmsg, buf, sizeof(errmsg));
401                llen++;
402                memsize--;
403             }
404             fprintf(stderr, "%s\n", errmsg);
405          }
406       }
407       ap = (struct abufhead *) ap->abq.qnext;
408    }
409    V(mutex);
410 }
411
412 #undef sm_check
413 /*  SM_CHECK --  Check the buffers and dump if any damage exists. */
414 void sm_check(const char *fname, int lineno, bool bufdump)
415 {
416         if (!sm_check_rtn(fname, lineno, bufdump)) {
417            Emsg2(M_ABORT, 0, _("Damaged buffer found. Called from %s:%d\n"),
418               fname, lineno);
419         }
420 }
421
422 #undef sm_check_rtn
423 /*  SM_CHECK_RTN -- Check the buffers and return 1 if OK otherwise 0 */
424 int sm_check_rtn(const char *fname, int lineno, bool bufdump)
425 {
426    struct abufhead *ap;
427    int bad, badbuf = 0;
428
429    P(mutex);
430    ap = (struct abufhead *) abqueue.qnext;
431    while (ap != (struct abufhead *) &abqueue) {
432       bad = 0;
433       if ((ap == NULL) ||
434           (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
435          bad = 0x1;
436       }
437       if (ap->abq.qprev->qnext != (struct b_queue *) ap) {
438          bad |= 0x2;
439       }
440       if (((unsigned char *) ap)[((struct abufhead *) ap)->ablen - 1] !=
441            ((((long) ap) & 0xFF) ^ 0xC5)) {
442          bad |= 0x4;
443       }
444       badbuf |= bad;
445       if (bad) {
446          fprintf(stderr,
447             _("\nDamaged buffers found at %s:%d\n"), fname, lineno);
448
449          if (bad & 0x1) {
450             fprintf(stderr, _("  discovery of bad prev link.\n"));
451          }
452          if (bad & 0x2) {
453             fprintf(stderr, _("  discovery of bad next link.\n"));
454          }
455          if (bad & 0x4) {
456             fprintf(stderr, _("  discovery of data overrun.\n"));
457          }
458
459          fprintf(stderr, _("  Buffer address: %lx\n"), (long) ap);
460
461          if (ap->abfname != NULL) {
462             unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
463             char errmsg[80];
464
465             fprintf(stderr,
466               _("Damaged buffer:  %6u bytes allocated at line %d of %s %s\n"),
467                memsize, ap->ablineno, my_name, ap->abfname
468             );
469             if (bufdump) {
470                unsigned llen = 0;
471                char *cp = ((char *) ap) + HEAD_SIZE;
472
473                errmsg[0] = EOS;
474                while (memsize) {
475                   if (llen >= 16) {
476                      strcat(errmsg, "\n");
477                      llen = 0;
478                      fprintf(stderr, "%s", errmsg);
479                      errmsg[0] = EOS;
480                   }
481                   if (*cp < 0x20) {
482                      sprintf(errmsg + strlen(errmsg), " %02X",
483                         (*cp++) & 0xFF);
484                   } else {
485                      sprintf(errmsg + strlen(errmsg), " %c ",
486                         (*cp++) & 0xFF);
487                   }
488                   llen++;
489                   memsize--;
490                }
491                fprintf(stderr, "%s\n", errmsg);
492             }
493          }
494       }
495       ap = (struct abufhead *) ap->abq.qnext;
496    }
497    V(mutex);
498    return badbuf ? 0 : 1;
499 }
500
501
502 /*  SM_STATIC  --  Orphaned buffer detection can be disabled  (for  such
503                    items  as buffers allocated during initialisation) by
504                    calling   sm_static(1).    Normal   orphaned   buffer
505                    detection  can be re-enabled with sm_static(0).  Note
506                    that all the other safeguards still apply to  buffers
507                    allocated  when  sm_static(1)  mode is in effect.  */
508
509 void sm_static(int mode)
510 {
511    bufimode = (bool) (mode != 0);
512 }
513
514 /*
515  * Here we overload C++'s global new and delete operators
516  *  so that the memory is allocated through smartalloc.
517  */
518
519 #ifdef xxx
520 void * operator new(size_t size)
521 {
522 // Dmsg1(000, "new called %d\n", size);
523    return sm_malloc(__FILE__, __LINE__, size);
524 }
525
526 void operator delete(void *buf)
527 {
528 // Dmsg1(000, "free called 0x%x\n", buf);
529    sm_free(__FILE__, __LINE__, buf);
530 }
531 #endif
532
533 #endif