malloc.c

Go to the documentation of this file.
00001 /*                        M A L L O C . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2004-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** \addtogroup malloc */
00023 /*@{*/
00024 /** 
00025  * @file malloc.c
00026  *
00027  *@brief
00028  *  Parallel-protected debugging-enhanced wrapper around system malloc().
00029  *
00030  *  The bu_malloc() routines can't use bu_log() because that uses
00031  *  the bu_vls() routines which depend on bu_malloc().  So it goes direct
00032  *  to stderr, semaphore protected.
00033  *
00034  * @par  Functions
00035  *      bu_malloc       Allocate storage, with visibility & checking
00036  * @n   bu_free         Similarly, free storage
00037  * @n   bu_realloc      Reallocate storage, with visibility & checking
00038  * @n   bu_calloc       Allocate zero'ed storage
00039  * @n   bu_prmem        When debugging, print memory map
00040  * @n   bu_strdup_body  Duplicate a string in dynamic memory
00041  * @n   bu_malloc_len_roundup   Optimize sizing of malloc() requests
00042  * @n   bu_free_array   free elements of an array
00043  *
00044  *
00045  *  @author     Michael John Muuss
00046  *  @author      Christopher Sean Morrison
00047  *
00048  * @par  Source -
00049  *      The U. S. Army Research Laboratory
00050  * @n   Aberdeen Proving Ground, Maryland  21005-5068  USA
00051  */
00052 
00053 #ifndef lint
00054 static const char RCSmalloc[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/libbu/malloc.c,v 14.26 2006/08/31 23:16:38 lbutler Exp $ (ARL)";
00055 #endif
00056 
00057 #include "common.h"
00058 
00059 #include <stdlib.h>
00060 #include <stdio.h>
00061 #ifdef HAVE_STRING_H
00062 #  include <string.h>
00063 #else
00064 #  include <strings.h>
00065 #endif
00066 #ifdef HAVE_UNISTD_H
00067 #  include <unistd.h>
00068 #endif
00069 
00070 #include "machine.h"
00071 #include "bu.h"
00072 
00073 /** this variable controls the libbu debug level */
00074 int     bu_debug = 0;
00075 
00076 
00077 /** used by the memory allocation routines passed to bu_alloc by
00078  * default to indicate whether allocated memory should be zero'd
00079  * first.
00080  */
00081 typedef enum {
00082   MALLOC,
00083   CALLOC
00084 } alloc_t;
00085 
00086 
00087 /* These counters are not semaphore-protected, and thus are only estimates */
00088 long    bu_n_malloc = 0;
00089 long    bu_n_free = 0;
00090 long    bu_n_realloc = 0;
00091 
00092 #define MDB_MAGIC       0x12348969
00093 struct memdebug {
00094         long            magic;          /* corruption can be everywhere */
00095         genptr_t        mdb_addr;
00096         const char      *mdb_str;
00097         int             mdb_len;
00098 };
00099 static struct memdebug  *bu_memdebug = (struct memdebug *)NULL;
00100 static struct memdebug  *bu_memdebug_lowat = (struct memdebug *)NULL;
00101 static size_t           bu_memdebug_len = 0;
00102 #define MEMDEBUG_NULL   ((struct memdebug *)0)
00103 
00104 struct memqdebug {
00105         struct bu_list  q;
00106         struct memdebug m;
00107 };
00108 
00109 static struct bu_list *bu_memq = BU_LIST_NULL;
00110 static struct bu_list bu_memqhd;
00111 #define MEMQDEBUG_NULL  ((struct memqdebug *)0)
00112 
00113 const char bu_strdup_message[] = "bu_strdup string";
00114 extern const char bu_vls_message[];     /* from vls.c */
00115 
00116 
00117 #ifdef _WIN32
00118 char *sbrk(i)
00119 {
00120         return( (char *)0 );
00121 }
00122 #endif
00123 
00124 
00125 /**
00126  *                      B U _ M E M D E B U G _ A D D
00127  *
00128  *  Add another entry to the memory debug table
00129  */
00130 HIDDEN void
00131 bu_memdebug_add(char *ptr, unsigned int cnt, const char *str)
00132 {
00133         register struct memdebug *mp;
00134 top:
00135         bu_semaphore_acquire( BU_SEM_SYSCALL );
00136         if( bu_memdebug )  {
00137                 mp = &bu_memdebug[bu_memdebug_len-1];
00138                 if( bu_memdebug_lowat > bu_memdebug &&
00139                     bu_memdebug_lowat < mp )  {
00140                         mp = bu_memdebug_lowat;
00141                 } else {
00142                         bu_memdebug_lowat = mp;
00143                 }
00144 again:
00145                 for( ; mp >= bu_memdebug; mp-- )  {
00146                         /* Search for an empty slot */
00147                         if( mp->mdb_len > 0 )  continue;
00148                         mp->magic = MDB_MAGIC;
00149                         mp->mdb_addr = ptr;
00150                         mp->mdb_len = cnt;
00151                         mp->mdb_str = str;
00152                         bu_memdebug_lowat = mp-1;
00153                         bu_semaphore_release( BU_SEM_SYSCALL );
00154                         return;
00155                 }
00156                 /* Didn't find a slot.  If started in middle, go again */
00157                 mp = &bu_memdebug[bu_memdebug_len-1];
00158                 if( bu_memdebug_lowat != mp )  {
00159                         bu_memdebug_lowat = mp;
00160                         goto again;
00161                 }
00162         }
00163 
00164         /* Need to make more slots */
00165         if( bu_memdebug_len <= 0 )  {
00166                 bu_memdebug_len = 5120-2;
00167                 bu_memdebug = (struct memdebug *)calloc(
00168                         bu_memdebug_len, sizeof(struct memdebug) );
00169                 if( bu_memdebug == (struct memdebug *)0 )
00170                         bu_bomb("bu_memdebug_add() malloc failure\n");
00171         } else {
00172                 size_t  old_len = bu_memdebug_len;
00173                 bu_memdebug_len *= 16;
00174                 bu_memdebug = (struct memdebug *)realloc(
00175                         (char *)bu_memdebug,
00176                         sizeof(struct memdebug) * bu_memdebug_len );
00177                 if( bu_memdebug == (struct memdebug *)0 )
00178                         bu_bomb("bu_memdebug_add() malloc failure\n");
00179                 bzero( (char *)&bu_memdebug[old_len],
00180                         (bu_memdebug_len-old_len) * sizeof(struct memdebug) );
00181         }
00182         bu_semaphore_release( BU_SEM_SYSCALL );
00183 
00184         goto top;
00185 }
00186 
00187 /**
00188  *                      B U _ M E M D E B U G _ C H E C K
00189  *
00190  *  Check an entry against the memory debug table, based upon it's address.
00191  */
00192 HIDDEN struct memdebug *
00193 bu_memdebug_check(register char *ptr, const char *str)
00194 {
00195         register struct memdebug *mp = &bu_memdebug[bu_memdebug_len-1];
00196         register long   *ip;
00197 
00198         if( bu_memdebug == (struct memdebug *)0 )  {
00199                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00200                 fprintf(stderr,"bu_memdebug_check(x%lx, %s)  no memdebug table yet\n",
00201                         (long)ptr, str);
00202                 bu_semaphore_release(BU_SEM_SYSCALL);
00203                 return MEMDEBUG_NULL;
00204         }
00205         for( ; mp >= bu_memdebug; mp-- )  {
00206                 if( !mp->magic )  continue;
00207                 if( mp->magic != MDB_MAGIC )  bu_bomb("bu_memdebug_check() malloc tracing table corrupted!\n");
00208                 if( mp->mdb_len <= 0 )  continue;
00209                 if( mp->mdb_addr != ptr )  continue;
00210                 ip = (long *)(ptr+mp->mdb_len-sizeof(long));
00211                 if( *ip != MDB_MAGIC )  {
00212                         bu_semaphore_acquire(BU_SEM_SYSCALL);
00213                         fprintf(stderr,"ERROR bu_memdebug_check(x%lx, %s) %s, barrier word corrupted!\nbarrier at x%lx was=x%lx s/b=x%x, len=%d\n",
00214                                 (long)ptr, str, mp->mdb_str,
00215                                 (long)ip, *ip, MDB_MAGIC, mp->mdb_len);
00216                         bu_semaphore_release(BU_SEM_SYSCALL);
00217                         bu_bomb("bu_memdebug_check() memory corruption\n");
00218                 }
00219                 return(mp);             /* OK */
00220         }
00221         return MEMDEBUG_NULL;
00222 }
00223 
00224 
00225 /**
00226  *                      B U _ A L L O C
00227  *
00228  *  This routine only returns on successful allocation.
00229  *  We promise never to return a NULL pointer; caller doesn't have to check.
00230  *
00231  *  Requesting allocation of zero bytes is considered a irrecoverable
00232  *  mistake in order to fulfill the non-NULL promise.
00233  *
00234  *  Failure results in bu_bomb() being called.
00235  *
00236  *  type is 0 for malloc, 1 for calloc
00237  */
00238 static genptr_t
00239 bu_alloc(alloc_t type, unsigned int cnt, unsigned int sz, const char *str)
00240 {
00241         register genptr_t ptr;
00242         register unsigned long int size = cnt * sz;
00243 
00244         if( size == 0 )  {
00245                 fprintf(stderr,"ERROR: bu_alloc size=0 (cnt=%d, sz=%d) %s\n", cnt, sz, str );
00246                 bu_bomb("ERROR: bu_malloc(0)\n");
00247         }
00248 
00249         if( size < sizeof( int ) ) {
00250                 size = sizeof( int );
00251         }
00252 
00253         if( bu_debug&BU_DEBUG_MEM_CHECK )  {
00254                 /* Pad, plus full int for magic number */
00255                 size = (size+2*sizeof(long)-1)&(~(sizeof(long)-1));
00256         } else if (bu_debug&BU_DEBUG_MEM_QCHECK ) {
00257                 size = (size+2*sizeof(struct memqdebug)-1)
00258                         &(~(sizeof(struct memqdebug)-1));
00259         }
00260 
00261 #if defined(MALLOC_NOT_MP_SAFE)
00262         bu_semaphore_acquire( BU_SEM_SYSCALL );
00263 #endif
00264 
00265         switch (type) {
00266           case MALLOC:
00267             ptr = malloc(size);
00268             break;
00269           case CALLOC:
00270 #if defined(HAVE_CALLOC)
00271               /* if we're debugging, we need a slightly larger
00272                * allocation size for debug tracking.
00273                */
00274               if( bu_debug&(BU_DEBUG_MEM_CHECK|BU_DEBUG_MEM_QCHECK) )  {
00275                   ptr = malloc(size);
00276                   bzero(ptr, size);
00277               } else {
00278                   ptr = calloc(cnt, sz);
00279               }
00280 #else
00281             ptr = malloc(size);
00282             bzero(ptr, size);
00283 #endif
00284             break;
00285           default:
00286             bu_bomb("ERROR: bu_alloc with unknown type\n");
00287         }
00288 
00289         if( ptr==(char *)0 || bu_debug&BU_DEBUG_MEM_LOG )  {
00290                 fprintf(stderr, "%8lx malloc%7ld %s\n", (long)ptr, size, str);
00291         }
00292 #if defined(MALLOC_NOT_MP_SAFE)
00293         bu_semaphore_release( BU_SEM_SYSCALL );
00294 #endif
00295 
00296         if( ptr==(char *)0 )  {
00297                 fprintf(stderr,"bu_malloc: Insufficient memory available, sbrk(0)=x%lx\n", (long)sbrk(0));
00298                 bu_bomb("bu_malloc: malloc failure");
00299         }
00300         if( bu_debug&BU_DEBUG_MEM_CHECK )  {
00301                 bu_memdebug_add( ptr, size, str );
00302 
00303                 /* Install a barrier word at the end of the dynamic arena */
00304                 /* Correct location depends on 'size' being rounded up, above */
00305 
00306                 *((long *)(((char *)ptr)+size-sizeof(long))) = MDB_MAGIC;
00307         } else if (bu_debug&BU_DEBUG_MEM_QCHECK ) {
00308                 struct memqdebug *mp = (struct memqdebug *)ptr;
00309                 ptr = (genptr_t)(((struct memqdebug *)ptr)+1);
00310                 mp->m.magic = MDB_MAGIC;
00311                 mp->m.mdb_addr = ptr;
00312                 mp->m.mdb_len = size;
00313                 mp->m.mdb_str = str;
00314                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00315                 if (bu_memq == BU_LIST_NULL) {
00316                         bu_memq = &bu_memqhd;
00317                         BU_LIST_INIT(bu_memq);
00318                 }
00319                 BU_LIST_APPEND(bu_memq,&(mp->q));
00320                 BU_LIST_MAGIC_SET(&(mp->q),MDB_MAGIC);
00321                 bu_semaphore_release(BU_SEM_SYSCALL);
00322         }
00323         bu_n_malloc++;
00324         return(ptr);
00325 }
00326 
00327 /**
00328  *                      B U _ M A L L O C
00329  *
00330  *  This routine only returns on successful allocation.
00331  *  We promise never to return a NULL pointer; caller doesn't have to check.
00332  *  Failure results in bu_bomb() being called.
00333 w */
00334 genptr_t
00335 bu_malloc(unsigned int size, const char *str)
00336 {
00337   return bu_alloc(MALLOC, 1, size, str);
00338 }
00339 
00340 
00341 /**
00342  *                      B U _ C A L L O C
00343  *
00344  *  This routine only returns on successful allocation.
00345  *  We promise never to return a NULL pointer; caller doesn't have to check.
00346  *  Failure results in bu_bomb() being called.
00347  */
00348 genptr_t
00349 bu_calloc(unsigned int nelem, unsigned int elsize, const char *str)
00350 {
00351   return bu_alloc(CALLOC, nelem, elsize, str);
00352 }
00353 
00354 
00355 /*
00356  *                      B U _ F R E E
00357  */
00358 void
00359 bu_free(genptr_t ptr, const char *str)
00360 {
00361         if(bu_debug&BU_DEBUG_MEM_LOG) {
00362                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00363                 fprintf(stderr, "%8lx free          %s\n", (long)ptr, str);
00364                 bu_semaphore_release(BU_SEM_SYSCALL);
00365         }
00366         if(ptr == (char *)0 || ptr == (char *)(-1L) )  {
00367                 fprintf(stderr,"%8lx free ERROR %s\n", (long)ptr, str);
00368                 return;
00369         }
00370         if( bu_debug&BU_DEBUG_MEM_CHECK )  {
00371                 struct memdebug *mp;
00372                 if( (mp = bu_memdebug_check( ptr, str )) == MEMDEBUG_NULL )  {
00373                     fprintf(stderr,"ERROR bu_free(x%lx, %s) pointer bad, or not allocated with bu_malloc!  Ignored.\n", (long)ptr, str);
00374                 } else {
00375                     mp->mdb_len = 0;    /* successful delete */
00376                 }
00377         } else if (bu_debug&BU_DEBUG_MEM_QCHECK ) {
00378                 struct memqdebug *mp = ((struct memqdebug *)ptr)-1;
00379                 if (BU_LIST_MAGIC_WRONG(&(mp->q),MDB_MAGIC)) {
00380                     fprintf(stderr,"ERROR bu_free(x%lx, %s) pointer bad, or not allocated with bu_malloc!  Ignored.\n", (long)ptr, str);
00381                 } else {
00382                     ptr = (genptr_t)mp;
00383                     bu_semaphore_acquire(BU_SEM_SYSCALL);
00384                     BU_LIST_DEQUEUE(&(mp->q));
00385                     bu_semaphore_release(BU_SEM_SYSCALL);
00386                 }
00387         }
00388 
00389 #if defined(MALLOC_NOT_MP_SAFE)
00390         bu_semaphore_acquire(BU_SEM_SYSCALL);
00391 #endif
00392 /* Windows does not like */
00393 #ifndef _WIN32
00394         *((int *)ptr) = -1;     /* zappo! */
00395 #endif
00396         free(ptr);
00397 #if defined(MALLOC_NOT_MP_SAFE)
00398         bu_semaphore_release(BU_SEM_SYSCALL);
00399 #endif
00400         bu_n_free++;
00401 }
00402 
00403 /**
00404  *                      B U _ R E A L L O C
00405  *
00406  *  bu_malloc()/bu_free() compatible wrapper for realloc().
00407  *
00408  *  While the string 'str' is provided for the log messages, don't
00409  *  disturb the mdb_str value, so that this storage allocation can be
00410  *  tracked back to it's original creator.
00411  */
00412 genptr_t
00413 bu_realloc(register genptr_t ptr, unsigned int cnt, const char *str)
00414 {
00415         struct memdebug         *mp=NULL;
00416         char    *original_ptr;
00417 
00418         if ( ! ptr ) {
00419             /* This is so we are compatible with system realloc.
00420              * It seems like an odd behaviour, but some non-BRLCAD
00421              * code relies on this.
00422              */
00423             return bu_malloc(cnt, str);
00424         }
00425 
00426         if( bu_debug&BU_DEBUG_MEM_CHECK )  {
00427                 if( ptr && (mp = bu_memdebug_check( ptr, str )) == MEMDEBUG_NULL )  {
00428                         fprintf(stderr,"%8lx realloc%6d %s ** barrier check failure\n",
00429                                 (long)ptr, cnt, str );
00430                 }
00431                 /* Pad, plus full long for magic number */
00432                 cnt = (cnt+2*sizeof(long)-1)&(~(sizeof(long)-1));
00433         } else if ( bu_debug&BU_DEBUG_MEM_QCHECK ) {
00434                 struct memqdebug *mp = ((struct memqdebug *)ptr)-1;
00435 
00436                 cnt = (cnt + 2*sizeof(struct memqdebug) - 1)
00437                     &(~(sizeof(struct memqdebug)-1));
00438 
00439                 if (BU_LIST_MAGIC_WRONG(&(mp->q),MDB_MAGIC)) {
00440                         fprintf(stderr,"ERROR bu_realloc(x%lx, %s) pointer bad, "
00441                                 "or not allocated with bu_malloc!  Ignored.\n",
00442                                 (long)ptr, str);
00443                         /*
00444                          * Since we're ignoring this, atleast return
00445                          * the pointer that was passed in. We should
00446                          * probably return NULL.
00447                          */
00448                         return ptr;
00449                 }
00450                 ptr = (genptr_t)mp;
00451                 BU_LIST_DEQUEUE(&(mp->q));
00452         }
00453 
00454         original_ptr = ptr;
00455 
00456 #if defined(MALLOC_NOT_MP_SAFE)
00457         bu_semaphore_acquire(BU_SEM_SYSCALL);
00458 #endif
00459         ptr = realloc(ptr,cnt);
00460 #if defined(MALLOC_NOT_MP_SAFE)
00461         bu_semaphore_release(BU_SEM_SYSCALL);
00462 #endif
00463 
00464         if( ptr==(char *)0 || bu_debug&BU_DEBUG_MEM_LOG )  {
00465                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00466                 if (ptr == original_ptr) {
00467                         fprintf(stderr,"%8lx realloc%6d %s [grew in place]\n",
00468                                    (long)ptr,       cnt, str );
00469                 } else {
00470                         fprintf(stderr,"%8lx realloc%6d %s [moved from %8lx]\n",
00471                                    (long)ptr,       cnt, str, original_ptr);
00472                 }
00473 
00474                 bu_semaphore_release(BU_SEM_SYSCALL);
00475         }
00476         if( ptr==(char *)0 && cnt > 0 )  {
00477                 fprintf(stderr,"bu_realloc: Insufficient memory available, sbrk(0)=x%lx\n", (long)sbrk(0));
00478                 bu_bomb("bu_realloc: malloc failure");
00479         }
00480         if( bu_debug&BU_DEBUG_MEM_CHECK && ptr )  {
00481                 /* Even if ptr didn't change, need to update cnt & barrier */
00482                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00483                 mp->mdb_addr = ptr;
00484                 mp->mdb_len = cnt;
00485 
00486                 /* Install a barrier word at the new end of the dynamic arena */
00487                 /* Correct location depends on 'cnt' being rounded up, above */
00488                 *((long *)(((char *)ptr)+cnt-sizeof(long))) = MDB_MAGIC;
00489                 bu_semaphore_release(BU_SEM_SYSCALL);
00490         } else if ( bu_debug&BU_DEBUG_MEM_QCHECK && ptr ) {
00491                 struct memqdebug *mp;
00492                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00493                 mp = (struct memqdebug *)ptr;
00494                 ptr = (genptr_t)(((struct memqdebug *)ptr)+1);
00495                 mp->m.magic = MDB_MAGIC;
00496                 mp->m.mdb_addr = ptr;
00497                 mp->m.mdb_len = cnt;
00498                 mp->m.mdb_str = str;
00499                 BU_ASSERT(bu_memq != BU_LIST_NULL);
00500                 BU_LIST_APPEND(bu_memq,&(mp->q));
00501                 BU_LIST_MAGIC_SET(&(mp->q),MDB_MAGIC);
00502                 bu_semaphore_release(BU_SEM_SYSCALL);
00503         }
00504         bu_n_realloc++;
00505         return(ptr);
00506 }
00507 
00508 
00509 /**
00510  *                      B U _ P R M E M
00511  *
00512  *  Print map of memory currently in use.
00513  */
00514 void
00515 bu_prmem(const char *str)
00516 {
00517     register struct memdebug *mp;
00518     register struct memqdebug *mqp;
00519     register long *ip;
00520     register size_t count = 0;
00521 
00522     fprintf(stderr,"\nbu_prmem(): dynamic memory use (%s)\n", str);
00523     if( (bu_debug&(BU_DEBUG_MEM_CHECK|BU_DEBUG_MEM_QCHECK)) == 0 )  {
00524         fprintf(stderr,"\tMemory debugging is now OFF\n");
00525     }
00526 #if 0
00527     fprintf(stderr,"\t%ld slots in memdebug table (not # of allocs)\n Address Length Purpose\n",
00528             (long)bu_memdebug_len);
00529 #else
00530     fprintf(stderr," Address Length Purpose\n");
00531 #endif
00532     if( bu_memdebug_len > 0 )  {
00533         mp = &bu_memdebug[bu_memdebug_len-1];
00534         for( ; mp >= bu_memdebug; mp-- )  {
00535             if( !mp->magic )  continue;
00536             if( mp->magic != MDB_MAGIC )  bu_bomb("bu_memdebug_check() malloc tracing table corrupted!\n");
00537             if( mp->mdb_len <= 0 )  continue;
00538 
00539             count++;
00540             ip = (long *)(((char *)mp->mdb_addr)+mp->mdb_len-sizeof(long));
00541             if( mp->mdb_str == bu_strdup_message )  {
00542                 fprintf(stderr,"%8lx %6d bu_strdup: \"%s\"\n",
00543                         (long)(mp->mdb_addr), mp->mdb_len,
00544                         ((char *)mp->mdb_addr) );
00545             } else if( mp->mdb_str == bu_vls_message )  {
00546                 fprintf(stderr,"%8lx %6d bu_vls: \"%s\"\n",
00547                         (long)(mp->mdb_addr), mp->mdb_len,
00548                         ((char *)mp->mdb_addr) );
00549             } else {
00550                 fprintf(stderr,"%8lx %6d %s\n",
00551                         (long)(mp->mdb_addr), mp->mdb_len,
00552                         mp->mdb_str);
00553             }
00554             if( *ip != MDB_MAGIC )  {
00555                 fprintf(stderr,"\tCorrupted end marker was=x%lx\ts/b=x%x\n",
00556                         *ip, MDB_MAGIC);
00557             }
00558         }
00559     }
00560 
00561 
00562     if (bu_memq != BU_LIST_NULL)  {
00563         fprintf(stderr,"memdebug queue\n Address Length Purpose\n");
00564         BU_LIST_EACH(bu_memq, mqp, struct memqdebug) {
00565             if (BU_LIST_MAGIC_WRONG(&(mqp->q),MDB_MAGIC)
00566                 || BU_LIST_MAGIC_WRONG(&(mqp->m),MDB_MAGIC))
00567                 bu_bomb("bu_prmem() malloc tracing queue corrupted!\n");
00568             if( mqp->m.mdb_str == bu_strdup_message )  {
00569                 fprintf(stderr,"%8lx %6d bu_strdup: \"%s\"\n",
00570                         (long)(mqp->m.mdb_addr), mqp->m.mdb_len,
00571                         ((char *)mqp->m.mdb_addr) );
00572             } else if( mqp->m.mdb_str == bu_vls_message )  {
00573                 fprintf(stderr,"%8lx %6d bu_vls: \"%s\"\n",
00574                         (long)(mqp->m.mdb_addr), mqp->m.mdb_len,
00575                         ((char *)mqp->m.mdb_addr) );
00576             } else {
00577                 fprintf(stderr,"%8lx %6d %s\n",
00578                         (long)(mqp->m.mdb_addr), mqp->m.mdb_len,
00579                         mqp->m.mdb_str);
00580             }
00581         }
00582     }
00583 
00584     fprintf(stderr, "%lu allocation entries\n", count);
00585 
00586 
00587 }
00588 
00589 /**
00590  *                      B U _ S T R D U P
00591  *
00592  * Given a string, allocate enough memory to hold it using bu_malloc(),
00593  * duplicate the strings, returns a pointer to the new string.
00594  */
00595 #if 0
00596 char *
00597 bu_strdup(register const char *cp)
00598 {
00599         return bu_strdupm(cp, bu_strdup_message);
00600 }
00601 #endif
00602 char *
00603 bu_strdupm(register const char *cp, const char *label)
00604 {
00605         register char   *base;
00606         register size_t len;
00607 
00608         len = strlen( cp )+2;
00609         base = bu_malloc( len, label);
00610 
00611         if(bu_debug&BU_DEBUG_MEM_LOG) {
00612                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00613                 fprintf(stderr, "%8lx strdup%7ld \"%s\"\n", (long)base, (long)len, cp );
00614                 bu_semaphore_release(BU_SEM_SYSCALL);
00615         }
00616 
00617         memcpy( base, cp, len );
00618         return(base);
00619 }
00620 
00621 /**
00622  *                      B U _ D I R N A M E
00623  *
00624  *  Given a filesystem pathname, return a pointer to a dynamic string
00625  *  which is the parent directory of that file/directory.
00626  *
00627  *      /usr/dir/file   /usr/dir
00628  * @n   /usr/dir/       /usr
00629  * @n   /usr/file       /usr
00630  * @n   /usr/           /
00631  * @n   /usr            /
00632  * @n   /               /
00633  * @n   .               .
00634  * @n   ..              .
00635  * @n   usr             .
00636  * @n   a/b             a
00637  * @n   a/              .
00638  * @n   ../a/b          ../a
00639  */
00640 char *
00641 bu_dirname(const char *cp)
00642 {
00643         char    *ret;
00644         char    *slash;
00645         int     len;
00646 
00647         /* Special cases */
00648         if( cp == NULL )  return bu_strdup(".");
00649         if( strcmp( cp, "/" ) == 0 )
00650                 return bu_strdup("/");
00651         if( strcmp( cp, "." ) == 0 ||
00652             strcmp( cp, ".." ) == 0 ||
00653             strrchr(cp, '/') == NULL )
00654                 return bu_strdup(".");
00655 
00656         /* Make a duplicate copy of the string, and shorten it in place */
00657         ret = bu_strdup(cp);
00658 
00659         /* A trailing slash doesn't count */
00660         len = strlen(ret);
00661         if( ret[len-1] == '/' )  ret[len-1] = '\0';
00662 
00663         /* If no slashes remain, return "." */
00664         if( (slash = strrchr(ret, '/')) == NULL )  {
00665                 bu_free( ret, "bu_dirname" );
00666                 return bu_strdup(".");
00667         }
00668 
00669         /* Remove trailing slash, unless it's at front */
00670         if( slash == ret )
00671                 ret[1] = '\0';          /* ret == "/" */
00672         else
00673                 *slash = '\0';
00674 
00675         return ret;
00676 }
00677 
00678 /**
00679  *                      B U _ M A L L O C _ L E N _ R O U N D U P
00680  *
00681  *  On systems with the CalTech malloc(), the amount of storage
00682  *  ACTUALLY ALLOCATED is the amount requested rounded UP to the
00683  *  nearest power of two.  For structures which are acquired and
00684  *  released often, this works well, but for structures which will
00685  *  remain unchanged for the duration of the program, this wastes
00686  *  as much as 50% of the address space (and usually memory as well).
00687  *  Here, we round up a byte size to the nearest power of two,
00688  *  leaving off the malloc header, so as to ask for storage without
00689  *  wasting any.
00690  *
00691  *  On systems with the traditional malloc(), this strategy will just
00692  *  consume the memory in somewhat larger chunks, but overall little
00693  *  unused memory will be consumed.
00694  */
00695 int
00696 bu_malloc_len_roundup(register int nbytes)
00697 {
00698 #if !defined(HAVE_CALTECH_MALLOC)
00699         return(nbytes);
00700 #else
00701         static int pagesz;
00702         register int n;
00703         register int amt;
00704 
00705         if (pagesz == 0)
00706                 pagesz = getpagesize();
00707 
00708 #define OVERHEAD        (4*sizeof(unsigned char) + \
00709                         2*sizeof(unsigned short) + \
00710                         sizeof(unsigned int) )
00711         n = pagesz - OVERHEAD;
00712         if (nbytes <= n)
00713                 return(n);
00714         amt = pagesz;
00715 
00716         while (nbytes > amt + n) {
00717                 amt <<= 1;
00718         }
00719         return(amt-OVERHEAD-sizeof(int));
00720 #endif
00721 }
00722 
00723 /**
00724  *                      B U _ C K _ M A L L O C _ P T R
00725  *
00726  *      For a given pointer allocated by bu_malloc(),
00727  *      Check the magic number stored after the allocation area
00728  *      when BU_DEBUG_MEM_CHECK is set.
00729  *
00730  *      This is the individual version of bu_mem_barriercheck().
00731  *
00732  *      returns if pointer good or BU_DEBUG_MEM_CHECK not set,
00733  *      bombs if memory is corrupted.
00734  */
00735 void
00736 bu_ck_malloc_ptr(genptr_t ptr, const char *str)
00737 {
00738         register struct memdebug *mp = &bu_memdebug[bu_memdebug_len-1];
00739         register long   *ip;
00740 
00741 
00742         if (ptr == (char *)NULL) {
00743                 fprintf(stderr,"bu_ck_malloc_ptr(x%lx, %s) null pointer\n\n", (long)ptr, str);
00744                 bu_bomb("Goodbye");
00745         }
00746 
00747         if (bu_debug&BU_DEBUG_MEM_CHECK) {
00748         if( bu_memdebug == (struct memdebug *)0 )  {
00749                 fprintf(stderr,"bu_ck_malloc_ptr(x%lx, %s)  no memdebug table yet\n",
00750                         (long)ptr, str);
00751                 /* warning only -- the program is just getting started */
00752                 return;
00753         }
00754 
00755         for( ; mp >= bu_memdebug; mp-- )  {
00756                 if( !mp->magic )  continue;
00757                 if( mp->magic != MDB_MAGIC )  bu_bomb("bu_ck_malloc_ptr() malloc tracing table corrupted!\n");
00758                 if( mp->mdb_len <= 0 || mp->mdb_addr != ptr )  continue;
00759 
00760                 /* Found the relevant entry */
00761                 ip = (long *)(((char *)ptr)+mp->mdb_len-sizeof(long));
00762                 if( *ip != MDB_MAGIC )  {
00763                         fprintf(stderr,"ERROR bu_ck_malloc_ptr(x%lx, %s) barrier word corrupted! was=x%lx s/b=x%x\n",
00764                                 (long)ptr, str, (long)*ip, MDB_MAGIC);
00765                         bu_bomb("bu_ck_malloc_ptr\n");
00766                 }
00767                 return;         /* OK */
00768         }
00769         fprintf(stderr,"WARNING: bu_ck_malloc_ptr(x%lx, %s)\
00770         pointer not in table of allocated memory.\n", (long)ptr, str);
00771         } else if (bu_debug&BU_DEBUG_MEM_QCHECK) {
00772                 struct memqdebug *mp = (struct memqdebug *)ptr;
00773                 if (BU_LIST_MAGIC_WRONG(&(mp->q),MDB_MAGIC)
00774                     || mp->m.magic != MDB_MAGIC) {
00775                         fprintf(stderr,"WARNING: bu_ck_malloc_ptr(x%lx, %s)"
00776                                 " memory corrupted.\n", (long)ptr, str);
00777                 }
00778         }
00779 }
00780 
00781 /**
00782  *                      B U _ M E M _ B A R R I E R C H E C K
00783  *
00784  *  Check *all* entries in the memory debug table for barrier word
00785  *  corruption.
00786  *  Intended to be called periodicly through an application during debugging.
00787  *  Has to run single-threaded, to prevent table mutation.
00788  *
00789  *  This is the bulk version of bu_ck_malloc_ptr()
00790  *
00791  *  Returns -
00792  *      -1      something is wrong
00793  *       0      all is OK;
00794  */
00795 int
00796 bu_mem_barriercheck(void)
00797 {
00798         register struct memdebug *mp = &bu_memdebug[bu_memdebug_len-1];
00799         register long   *ip;
00800 
00801         if( bu_memdebug == (struct memdebug *)0 )  {
00802                 fprintf(stderr,"bu_mem_barriercheck()  no memdebug table yet\n");
00803                 return 0;
00804         }
00805         bu_semaphore_acquire( BU_SEM_SYSCALL );
00806         for( ; mp >= bu_memdebug; mp-- )  {
00807                 if( !mp->magic )  continue;
00808                 if( mp->magic != MDB_MAGIC )  {
00809                         bu_semaphore_release( BU_SEM_SYSCALL );
00810                         fprintf(stderr,"  mp->magic = x%lx, s/b=x%x\n", (long)(mp->magic), MDB_MAGIC );
00811                         bu_bomb("bu_mem_barriercheck() malloc tracing table corrupted!\n");
00812                 }
00813                 if( mp->mdb_len <= 0 )  continue;
00814                 ip = (long *)(((char *)mp->mdb_addr)+mp->mdb_len-sizeof(long));
00815                 if( *ip != MDB_MAGIC )  {
00816                         bu_semaphore_release( BU_SEM_SYSCALL );
00817                         fprintf(stderr,"ERROR bu_mem_barriercheck(x%lx, len=%d) barrier word corrupted!\n\tbarrier at x%lx was=x%lx s/b=x%x %s\n",
00818                                 (long)mp->mdb_addr, mp->mdb_len,
00819                                 (long)ip, *ip, MDB_MAGIC, mp->mdb_str);
00820                         return -1;      /* FAIL */
00821                 }
00822         }
00823         bu_semaphore_release( BU_SEM_SYSCALL );
00824         return 0;                       /* OK */
00825 }
00826 
00827 
00828 /** b u _ f r e e _ a r r a y
00829  *
00830  * free up to argc elements of memory allocated to an array without
00831  * free'ing the array itself.
00832  */
00833 void bu_free_array(int argc, char *argv[], const char *str)
00834 {
00835   int count = 0;
00836 
00837   if (!argv || argc <= 0) {
00838     return;
00839   }
00840 
00841   while (count < argc) {
00842     if (argv[count]) {
00843       bu_free(argv[count], str);
00844       argv[count] = NULL;
00845     }
00846     count++;
00847   }
00848 
00849   return;
00850 }
00851 
00852 /*@}*/
00853 
00854 /*
00855  * Local Variables:
00856  * mode: C
00857  * tab-width: 8
00858  * c-basic-offset: 4
00859  * indent-tabs-mode: t
00860  * End:
00861  * ex: shiftwidth=4 tabstop=8
00862  */

Generated on Mon Sep 18 01:24:48 2006 for BRL-CAD by  doxygen 1.4.6