]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/smartall.c
Apply Preben 'Peppe' Guldberg <peppe@wielders.org>
[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-2004 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 /* We normally turn off debugging here.
49  *  If you want it, simply #ifdef all the
50  *  following off.
51  */
52 #undef Dmsg1
53 #undef Dmsg2
54 #undef Dmsg3
55 #undef Dmsg4
56 #define Dmsg1(l,f,a1)
57 #define Dmsg2(l,f,a1,a2)
58 #define Dmsg3(l,f,a1,a2,a3)
59 #define Dmsg4(l,f,a1,a2,a3,a4)
60
61
62 uint64_t sm_max_bytes = 0;
63 uint64_t sm_bytes = 0;
64 uint32_t sm_max_buffers = 0;
65 uint32_t sm_buffers = 0;
66
67 #ifdef SMARTALLOC
68
69 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
70
71 extern char my_name[];                /* daemon name */
72
73 typedef unsigned short sm_ushort;
74
75 #define EOS      '\0'              /* End of string sentinel */
76 #define sm_min(a, b) ((a) < (b) ? (a) : (b))
77
78 /*  Queue data structures  */
79
80 /*  Memory allocation control structures and storage.  */
81
82 struct abufhead {
83    struct b_queue abq;         /* Links on allocated queue */
84    unsigned ablen;             /* Buffer length in bytes */
85    const char *abfname;        /* File name pointer */
86    sm_ushort ablineno;         /* Line number of allocation */
87 };
88
89 static struct b_queue abqueue = {    /* Allocated buffer queue */
90    &abqueue, &abqueue
91 };
92
93
94 static bool bufimode = false;   /* Buffers not tracked when True */
95
96 #define HEAD_SIZE BALIGN(sizeof(struct abufhead))
97
98
99 /*  SMALLOC  --  Allocate buffer, enqueing on the orphaned buffer
100                  tracking list.  */
101
102 static void *smalloc(const char *fname, int lineno, unsigned int nbytes)
103 {
104    char *buf;
105
106    /* Note:  Unix  MALLOC  actually  permits  a zero length to be
107       passed and allocates a valid block with  zero  user  bytes.
108       Such  a  block  can  later  be expanded with realloc().  We
109       disallow this based on the belief that it's better to  make
110       a  special case and allocate one byte in the rare case this
111       is desired than to miss all the erroneous occurrences where
112       buffer length calculation code results in a zero.  */
113
114    ASSERT(nbytes > 0);
115
116    nbytes += HEAD_SIZE + 1;
117    if ((buf = (char *)malloc(nbytes)) != NULL) {
118       struct abufhead *head = (struct abufhead *)buf;
119       P(mutex);
120       /* Enqueue buffer on allocated list */
121       qinsert(&abqueue, (struct b_queue *) buf);
122       head->ablen = nbytes;
123       head->abfname = bufimode ? NULL : fname;
124       head->ablineno = (sm_ushort) lineno;
125       /* Emplace end-clobber detector at end of buffer */
126       buf[nbytes - 1] = (((long) buf) & 0xFF) ^ 0xC5;
127       buf += HEAD_SIZE;  /* Increment to user data start */
128       if (++sm_buffers > sm_max_buffers) {
129          sm_max_buffers = sm_buffers;
130       }
131       sm_bytes += nbytes;
132       if (sm_bytes > sm_max_bytes) {
133          sm_max_bytes = sm_bytes;
134       }
135       V(mutex);
136    } else {
137       Emsg0(M_ABORT, 0, _("Out of memory\n"));
138    }
139    Dmsg4(1150, "smalloc %d at %x from %s:%d\n", nbytes, buf, fname, lineno);
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    sm_buffers--;
197    sm_bytes -= head->ablen;
198
199    qdchain(qp);
200    V(mutex);
201
202    /* Now we wipe the contents of  the  just-released  buffer  with
203       "designer  garbage"  (Duff  Kurland's  phrase) of alternating
204       bits.  This is intended to ruin the day for any miscreant who
205       attempts to access data through a pointer into storage that's
206       been previously released. */
207
208    memset(cp, 0xAA, (int) head->ablen);
209
210    free(cp);
211 }
212
213 /*  SM_MALLOC  --  Allocate buffer.  NULL is returned if no memory
214                    was available.  */
215
216 void *sm_malloc(const char *fname, int lineno, unsigned int nbytes)
217 {
218    void *buf;
219
220    if ((buf = smalloc(fname, lineno, nbytes)) != NULL) {
221
222       /* To catch sloppy code that assumes  buffers  obtained  from
223          malloc()  are  zeroed,  we  preset  the buffer contents to
224          "designer garbage" consisting of alternating bits.  */
225
226       memset(buf, 0x55, (int) nbytes);
227    } else {
228       Emsg0(M_ABORT, 0, _("Out of memory\n"));
229    }
230    return buf;
231 }
232
233 /*  SM_CALLOC  --  Allocate an array and clear it to zero.  */
234
235 void *sm_calloc(const char *fname, int lineno,
236                 unsigned int nelem, unsigned int elsize)
237 {
238    void *buf;
239
240    if ((buf = smalloc(fname, lineno, nelem * elsize)) != NULL) {
241       memset(buf, 0, (int) (nelem * elsize));
242    } else {
243       Emsg0(M_ABORT, 0, _("Out of memory\n"));
244    }
245    return buf;
246 }
247
248 /*  SM_REALLOC  --  Adjust the size of a  previously  allocated  buffer.
249                     Note  that  the trick of "resurrecting" a previously
250                     freed buffer with realloc() is NOT supported by this
251                     function.   Further, because of the need to maintain
252                     our control storage, SM_REALLOC must always allocate
253                     a  new  block  and  copy  the data in the old block.
254                     This may result in programs which make heavy use  of
255                     realloc() running much slower than normally.  */
256
257 void *sm_realloc(const char *fname, int lineno, void *ptr, unsigned int size)
258 {
259    unsigned osize;
260    void *buf;
261    char *cp = (char *) ptr;
262
263    Dmsg4(400, "sm_realloc %s:%d 0x%x %d\n", fname, lineno, ptr, size);
264    if (size <= 0) {
265       e_msg(fname, lineno, M_ABORT, 0, "sm_realloc size: %d\n", size);
266    }
267
268    /*  If  the  old  block  pointer  is  NULL, treat realloc() as a
269       malloc().  SVID is silent  on  this,  but  many  C  libraries
270       permit this.  */
271
272    if (ptr == NULL) {
273       return sm_malloc(fname, lineno, size);
274    }
275
276    /* If the old and new sizes are the same, be a nice guy and just
277       return the buffer passed in.  */
278
279    cp -= HEAD_SIZE;
280    struct abufhead *head = (struct abufhead *)cp;
281    osize = head->ablen - (HEAD_SIZE + 1);
282    if (size == osize) {
283       return ptr;
284    }
285
286    /* Sizes differ.  Allocate a new buffer of the  requested  size.
287       If  we  can't  obtain  such a buffer, act as defined in SVID:
288       return NULL from  realloc()  and  leave  the  buffer  in  PTR
289       intact.  */
290
291    sm_buffers--;
292    sm_bytes -= osize;
293
294    if ((buf = smalloc(fname, lineno, size)) != NULL) {
295       memcpy(buf, ptr, (int) sm_min(size, osize));
296       /* If the new buffer is larger than the old, fill the balance
297          of it with "designer garbage". */
298       if (size > osize) {
299          memset(((char *) buf) + osize, 0x55, (int) (size - osize));
300       }
301
302       /* All done.  Free and dechain the original buffer. */
303
304       sm_free(__FILE__, __LINE__, ptr);
305    }
306    Dmsg4(150, "sm_realloc %d at %x from %s:%d\n", size, buf, fname, lineno);
307    return buf;
308 }
309
310 /*  ACTUALLYMALLOC  --  Call the system malloc() function to obtain
311                         storage which will eventually be released
312                         by system or library routines not compiled
313                         using SMARTALLOC.  */
314
315 void *actuallymalloc(unsigned int size)
316 {
317    return malloc(size);
318 }
319
320 /*  ACTUALLYCALLOC  --  Call the system calloc() function to obtain
321                         storage which will eventually be released
322                         by system or library routines not compiled
323                         using SMARTALLOC.  */
324
325 void *actuallycalloc(unsigned int nelem, unsigned int elsize)
326 {
327    return calloc(nelem, elsize);
328 }
329
330 /*  ACTUALLYREALLOC  --  Call the system realloc() function to obtain
331                          storage which will eventually be released
332                          by system or library routines not compiled
333                          using SMARTALLOC.  */
334
335 void *actuallyrealloc(void *ptr, unsigned int size)
336 {
337    Dmsg2(400, "Actuallyrealloc 0x%x %d\n", ptr, size);
338    return realloc(ptr, size);
339 }
340
341 /*  ACTUALLYFREE  --  Interface to system free() function to release
342                       buffers allocated by low-level routines. */
343
344 void actuallyfree(void *cp)
345 {
346    free(cp);
347 }
348
349 /*  SM_DUMP  --  Print orphaned buffers (and dump them if BUFDUMP is
350  *               True).
351  *  N.B. DO NOT USE any Bacula print routines (Dmsg, Jmsg, Emsg, ...)
352  *    as they have all been shut down at this point.
353  */
354 void sm_dump(bool bufdump)
355 {
356    struct abufhead *ap;
357
358    P(mutex);
359
360    ap = (struct abufhead *)abqueue.qnext;
361
362    while (ap != (struct abufhead *) &abqueue) {
363
364       if ((ap == NULL) ||
365           (ap->abq.qnext->qprev != (struct b_queue *) ap) ||
366           (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
367          fprintf(stderr,
368             "\nOrphaned buffers exist.  Dump terminated following\n");
369          fprintf(stderr,
370             "  discovery of bad links in chain of orphaned buffers.\n");
371          fprintf(stderr,
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