]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/smartall.c
This commit was manufactured by cvs2svn to create tag
[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    return (void *)buf;
136 }
137
138 /*  SM_NEW_OWNER -- Update the File and line number for a buffer
139                     This is to accomodate mem_pool. */
140
141 void sm_new_owner(const char *fname, int lineno, char *buf)
142 {
143    buf -= HEAD_SIZE;  /* Decrement to header */
144    ((struct abufhead *)buf)->abfname = bufimode ? NULL : fname;
145    ((struct abufhead *)buf)->ablineno = (sm_ushort) lineno;
146    return;
147 }
148
149 /*  SM_FREE  --  Update free pool availability.  FREE is never called
150                  except  through  this interface or by actuallyfree().
151                  free(x)  is  defined  to  generate  a  call  to  this
152                  routine.  */
153
154 void sm_free(const char *file, int line, void *fp)
155 {
156    char *cp = (char *) fp;
157    struct b_queue *qp;
158
159    if (cp == NULL) {
160       Emsg2(M_ABORT, 0, _("Attempt to free NULL called from %s:%d\n"), file, line);
161    }
162
163    cp -= HEAD_SIZE;
164    qp = (struct b_queue *) cp;
165    struct abufhead *head = (struct abufhead *)cp;
166
167    P(mutex);
168    Dmsg4(1150, "sm_free %d at %x from %s:%d\n",
169          head->ablen, fp,
170          head->abfname, head->ablineno);
171
172    /* The following assertions will catch virtually every release
173       of an address which isn't an allocated buffer. */
174    if (qp->qnext->qprev != qp) {
175       V(mutex);
176       Emsg2(M_ABORT, 0, _("qp->qnext->qprev != qp called from %s:%d\n"), file, line);
177    }
178    if (qp->qprev->qnext != qp) {
179       V(mutex);
180       Emsg2(M_ABORT, 0, _("qp->qprev->qnext != qp called from %s:%d\n"), file, line);
181    }
182
183    /* The following assertion detects storing off the  end  of  the
184       allocated  space in the buffer by comparing the end of buffer
185       checksum with the address of the buffer.  */
186
187    if (((unsigned char *)cp)[head->ablen - 1] != ((((long) cp) & 0xFF) ^ 0xC5)) {
188       V(mutex);
189       Emsg2(M_ABORT, 0, _("Buffer overrun called from %s:%d\n"), file, line);
190    }
191    if (sm_buffers > 0) {
192       sm_buffers--;
193       sm_bytes -= head->ablen;
194    }
195
196    qdchain(qp);
197    V(mutex);
198
199    /* Now we wipe the contents of  the  just-released  buffer  with
200       "designer  garbage"  (Duff  Kurland's  phrase) of alternating
201       bits.  This is intended to ruin the day for any miscreant who
202       attempts to access data through a pointer into storage that's
203       been previously released. */
204
205    memset(cp, 0xAA, (int) head->ablen);
206
207    free(cp);
208 }
209
210 /*  SM_MALLOC  --  Allocate buffer.  NULL is returned if no memory
211                    was available.  */
212
213 void *sm_malloc(const char *fname, int lineno, unsigned int nbytes)
214 {
215    void *buf;
216
217    if ((buf = smalloc(fname, lineno, nbytes)) != NULL) {
218
219       /* To catch sloppy code that assumes  buffers  obtained  from
220          malloc()  are  zeroed,  we  preset  the buffer contents to
221          "designer garbage" consisting of alternating bits.  */
222
223       memset(buf, 0x55, (int) nbytes);
224    } else {
225       Emsg0(M_ABORT, 0, _("Out of memory\n"));
226    }
227    return buf;
228 }
229
230 /*  SM_CALLOC  --  Allocate an array and clear it to zero.  */
231
232 void *sm_calloc(const char *fname, int lineno,
233                 unsigned int nelem, unsigned int elsize)
234 {
235    void *buf;
236
237    if ((buf = smalloc(fname, lineno, nelem * elsize)) != NULL) {
238       memset(buf, 0, (int) (nelem * elsize));
239    } else {
240       Emsg0(M_ABORT, 0, _("Out of memory\n"));
241    }
242    return buf;
243 }
244
245 /*  SM_REALLOC  --  Adjust the size of a  previously  allocated  buffer.
246                     Note  that  the trick of "resurrecting" a previously
247                     freed buffer with realloc() is NOT supported by this
248                     function.   Further, because of the need to maintain
249                     our control storage, SM_REALLOC must always allocate
250                     a  new  block  and  copy  the data in the old block.
251                     This may result in programs which make heavy use  of
252                     realloc() running much slower than normally.  */
253
254 void *sm_realloc(const char *fname, int lineno, void *ptr, unsigned int size)
255 {
256    unsigned osize;
257    void *buf;
258    char *cp = (char *) ptr;
259
260    Dmsg4(400, "sm_realloc %s:%d 0x%x %d\n", fname, lineno, ptr, size);
261    if (size <= 0) {
262       e_msg(fname, lineno, M_ABORT, 0, _("sm_realloc size: %d\n"), size);
263    }
264
265    /*  If  the  old  block  pointer  is  NULL, treat realloc() as a
266       malloc().  SVID is silent  on  this,  but  many  C  libraries
267       permit this.  */
268
269    if (ptr == NULL) {
270       return sm_malloc(fname, lineno, size);
271    }
272
273    /* If the old and new sizes are the same, be a nice guy and just
274       return the buffer passed in.  */
275
276    cp -= HEAD_SIZE;
277    struct abufhead *head = (struct abufhead *)cp;
278    osize = head->ablen - (HEAD_SIZE + 1);
279    if (size == osize) {
280       return ptr;
281    }
282
283    /* Sizes differ.  Allocate a new buffer of the  requested  size.
284       If  we  can't  obtain  such a buffer, act as defined in SVID:
285       return NULL from  realloc()  and  leave  the  buffer  in  PTR
286       intact.  */
287
288 // sm_buffers--;
289 // sm_bytes -= head->ablen;
290
291    if ((buf = smalloc(fname, lineno, size)) != NULL) {
292       memcpy(buf, ptr, (int) sm_min(size, osize));
293       /* If the new buffer is larger than the old, fill the balance
294          of it with "designer garbage". */
295       if (size > osize) {
296          memset(((char *) buf) + osize, 0x55, (int) (size - osize));
297       }
298
299       /* All done.  Free and dechain the original buffer. */
300
301       sm_free(__FILE__, __LINE__, ptr);
302    }
303    Dmsg4(150, _("sm_realloc %d at %x from %s:%d\n"), size, buf, fname, lineno);
304    return buf;
305 }
306
307 /*  ACTUALLYMALLOC  --  Call the system malloc() function to obtain
308                         storage which will eventually be released
309                         by system or library routines not compiled
310                         using SMARTALLOC.  */
311
312 void *actuallymalloc(unsigned int size)
313 {
314    return malloc(size);
315 }
316
317 /*  ACTUALLYCALLOC  --  Call the system calloc() function to obtain
318                         storage which will eventually be released
319                         by system or library routines not compiled
320                         using SMARTALLOC.  */
321
322 void *actuallycalloc(unsigned int nelem, unsigned int elsize)
323 {
324    return calloc(nelem, elsize);
325 }
326
327 /*  ACTUALLYREALLOC  --  Call the system realloc() function to obtain
328                          storage which will eventually be released
329                          by system or library routines not compiled
330                          using SMARTALLOC.  */
331
332 void *actuallyrealloc(void *ptr, unsigned int size)
333 {
334    Dmsg2(400, "Actuallyrealloc 0x%x %d\n", ptr, size);
335    return realloc(ptr, size);
336 }
337
338 /*  ACTUALLYFREE  --  Interface to system free() function to release
339                       buffers allocated by low-level routines. */
340
341 void actuallyfree(void *cp)
342 {
343    free(cp);
344 }
345
346 /*  SM_DUMP  --  Print orphaned buffers (and dump them if BUFDUMP is
347  *               True).
348  *  N.B. DO NOT USE any Bacula print routines (Dmsg, Jmsg, Emsg, ...)
349  *    as they have all been shut down at this point.
350  */
351 void sm_dump(bool bufdump)
352 {
353    struct abufhead *ap;
354
355    P(mutex);
356
357    ap = (struct abufhead *)abqueue.qnext;
358
359    while (ap != (struct abufhead *) &abqueue) {
360
361       if ((ap == NULL) ||
362           (ap->abq.qnext->qprev != (struct b_queue *) ap) ||
363           (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
364          fprintf(stderr, _(
365             "\nOrphaned buffers exist.  Dump terminated following\n"
366             "  discovery of bad links in chain of orphaned buffers.\n"
367             "  Buffer address with bad links: %lx\n"), (long) ap);
368          break;
369       }
370
371       if (ap->abfname != NULL) {
372          unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
373          char errmsg[500];
374
375          bsnprintf(errmsg, sizeof(errmsg),
376            _("Orphaned buffer:  %6u bytes allocated at line %d of %s %s\n"),
377             memsize, ap->ablineno, my_name, ap->abfname
378          );
379          fprintf(stderr, "%s", errmsg);
380          if (bufdump) {
381             char buf[20];
382             unsigned llen = 0;
383             char *cp = ((char *) ap) + HEAD_SIZE;
384
385             errmsg[0] = EOS;
386             while (memsize) {
387                if (llen >= 16) {
388                   bstrncat(errmsg, "\n", sizeof(errmsg));
389                   llen = 0;
390                   fprintf(stderr, "%s", errmsg);
391                   errmsg[0] = EOS;
392                }
393                bsnprintf(buf, sizeof(buf), " %02X",
394                   (*cp++) & 0xFF);
395                bstrncat(errmsg, buf, sizeof(errmsg));
396                llen++;
397                memsize--;
398             }
399             fprintf(stderr, "%s\n", errmsg);
400          }
401       }
402       ap = (struct abufhead *) ap->abq.qnext;
403    }
404    V(mutex);
405 }
406
407 #undef sm_check
408 /*  SM_CHECK --  Check the buffers and dump if any damage exists. */
409 void sm_check(const char *fname, int lineno, bool bufdump)
410 {
411         if (!sm_check_rtn(fname, lineno, bufdump)) {
412            Emsg2(M_ABORT, 0, _("Damaged buffer found. Called from %s:%d\n"),
413               fname, lineno);
414         }
415 }
416
417 #undef sm_check_rtn
418 /*  SM_CHECK_RTN -- Check the buffers and return 1 if OK otherwise 0 */
419 int sm_check_rtn(const char *fname, int lineno, bool bufdump)
420 {
421    struct abufhead *ap;
422    int bad, badbuf = 0;
423
424    P(mutex);
425    ap = (struct abufhead *) abqueue.qnext;
426    while (ap != (struct abufhead *) &abqueue) {
427       bad = 0;
428       if ((ap == NULL) ||
429           (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
430          bad = 0x1;
431       }
432       if (ap->abq.qprev->qnext != (struct b_queue *) ap) {
433          bad |= 0x2;
434       }
435       if (((unsigned char *) ap)[((struct abufhead *) ap)->ablen - 1] !=
436            ((((long) ap) & 0xFF) ^ 0xC5)) {
437          bad |= 0x4;
438       }
439       badbuf |= bad;
440       if (bad) {
441          fprintf(stderr,
442             _("\nDamaged buffers found at %s:%d\n"), fname, lineno);
443
444          if (bad & 0x1) {
445             fprintf(stderr, _("  discovery of bad prev link.\n"));
446          }
447          if (bad & 0x2) {
448             fprintf(stderr, _("  discovery of bad next link.\n"));
449          }
450          if (bad & 0x4) {
451             fprintf(stderr, _("  discovery of data overrun.\n"));
452          }
453
454          fprintf(stderr, _("  Buffer address: %lx\n"), (long) ap);
455
456          if (ap->abfname != NULL) {
457             unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
458             char errmsg[80];
459
460             fprintf(stderr,
461               _("Damaged buffer:  %6u bytes allocated at line %d of %s %s\n"),
462                memsize, ap->ablineno, my_name, ap->abfname
463             );
464             if (bufdump) {
465                unsigned llen = 0;
466                char *cp = ((char *) ap) + HEAD_SIZE;
467
468                errmsg[0] = EOS;
469                while (memsize) {
470                   if (llen >= 16) {
471                      strcat(errmsg, "\n");
472                      llen = 0;
473                      fprintf(stderr, "%s", errmsg);
474                      errmsg[0] = EOS;
475                   }
476                   if (*cp < 0x20) {
477                      sprintf(errmsg + strlen(errmsg), " %02X",
478                         (*cp++) & 0xFF);
479                   } else {
480                      sprintf(errmsg + strlen(errmsg), " %c ",
481                         (*cp++) & 0xFF);
482                   }
483                   llen++;
484                   memsize--;
485                }
486                fprintf(stderr, "%s\n", errmsg);
487             }
488          }
489       }
490       ap = (struct abufhead *) ap->abq.qnext;
491    }
492    V(mutex);
493    return badbuf ? 0 : 1;
494 }
495
496
497 /*  SM_STATIC  --  Orphaned buffer detection can be disabled  (for  such
498                    items  as buffers allocated during initialisation) by
499                    calling   sm_static(1).    Normal   orphaned   buffer
500                    detection  can be re-enabled with sm_static(0).  Note
501                    that all the other safeguards still apply to  buffers
502                    allocated  when  sm_static(1)  mode is in effect.  */
503
504 void sm_static(int mode)
505 {
506    bufimode = (bool) (mode != 0);
507 }
508
509 /*
510  * Here we overload C++'s global new and delete operators
511  *  so that the memory is allocated through smartalloc.
512  */
513
514 #ifdef xxx
515 void * operator new(size_t size)
516 {
517 // Dmsg1(000, "new called %d\n", size);
518    return sm_malloc(__FILE__, __LINE__, size);
519 }
520
521 void operator delete(void *buf)
522 {
523 // Dmsg1(000, "free called 0x%x\n", buf);
524    sm_free(__FILE__, __LINE__, buf);
525 }
526 #endif
527
528 #endif