home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / dbmalloc.zip / stack.c < prev    next >
C/C++ Source or Header  |  1993-01-04  |  8KB  |  367 lines

  1.  
  2. /*
  3.  * (c) Copyright 1990, 1991, 1992 Conor P. Cahill (cpcahil@virtech.vti.com)
  4.  *
  5.  * This software may be distributed freely as long as the following conditions
  6.  * are met:
  7.  *         * the distribution, or any derivative thereof, may not be
  8.  *          included as part of a commercial product
  9.  *        * full source code is provided including this copyright
  10.  *        * there is no charge for the software itself (there may be
  11.  *          a minimal charge for the copying or distribution effort)
  12.  *        * this copyright notice is not modified or removed from any
  13.  *          source file
  14.  */
  15. #include <stdio.h>
  16. #include "mallocin.h"
  17. #include "tostring.h"
  18.  
  19. #define STACK_ALLOC    100 /* allocate 100 elements at a time */
  20. #ifndef lint
  21. static
  22. char rcs_hdr[] = "$Id: stack.c,v 1.7 1992/08/22 16:27:13 cpcahil Exp $";
  23. #endif
  24.  
  25. #define OUTBUFSIZE    128
  26. #define ERRSTR "I/O error durring stack dump"
  27.  
  28. #define WRITEOUT(fd,str,len)    if( write(fd,str,(WRTSIZE)(len)) != (len) ) \
  29.                 { \
  30.                     VOIDCAST write(2,ERRSTR,\
  31.                              (WRTSIZE)strlen(ERRSTR));\
  32.                     exit(120); \
  33.                 }
  34.  
  35. #define COPY(s,t,buf,len)    while( (*(s) = *((t)++) ) \
  36.                        && ( (s) < ((buf)+(len)-5) ) ) { (s)++; }
  37.  
  38.  
  39. /*
  40.  * stack.c - this file contains info used to maintain the malloc stack
  41.  *         trees that the user may use to identify where malloc is called
  42.  *         from.  Ideally, we would like to be able to read the stack
  43.  *         ourselves, but that is very system dependent.
  44.  *
  45.  *     The user interface to the stack routines will be as follows:
  46.  *
  47.  *    #define malloc_enter(a)    StackEnter(a,__FILE__,__LINE__)
  48.  *    #define malloc_leave(a) StackLeave(a,__FILE__,__LINE__)
  49.  *
  50.  *    NOTE: These functions depend upon the fact that they are called in a
  51.  *    symentric manner.  If you skip one of them, you will not have valid
  52.  *    information maintained in the stack.
  53.  *
  54.  *    Rather than keep a segment of the stack in each malloc region, we will
  55.  *     maintain a stack tree here and the malloc segment will have a pointer 
  56.  *    to the current leaf of the tree.  This should save considerably on the
  57.  *    amount of space taken up by maintaining this tree.
  58.  */
  59.  
  60. struct stack    * current;
  61. struct stack       root_node;
  62.  
  63. /*
  64.  * local function prototpyes
  65.  */
  66. struct stack    * StackNew __STDCARGS(( CONST char * func, CONST char * file,
  67.                     int line));
  68. int          StackMatch __STDCARGS((struct stack * this,CONST char * func,
  69.                      CONST char * file, int line ));
  70.  
  71. /*
  72.  * stk_enter() - called when one enters a new function.  This function adds the 
  73.  *          specified function as a new entry (unless it is already in the
  74.  *          list, in which case it just sets the current pointer).
  75.  */
  76.  
  77. void
  78. StackEnter( func, file, line )
  79.     CONST char    * func;
  80.     CONST char    * file;
  81.     int          line;
  82. {
  83.     int          match;
  84.     struct stack    * this;
  85.  
  86.     /*
  87.      * if there are no current entries yet
  88.      */
  89.     if( current == NULL )
  90.     {
  91.         this = &root_node;
  92.     }
  93.     else
  94.     {
  95.         this = current;
  96.     }
  97.  
  98.     /*
  99.      * if there are no entries below this func yet,
  100.      */
  101.     if( this->below == NULL )
  102.     {
  103.         this->below = StackNew(func,file,line);
  104.         this->below->above = this;
  105.         current = this->below;
  106.     }
  107.     else
  108.     {
  109.         /*
  110.          * drop down to the next level and look around for the
  111.          * specified function.
  112.          */
  113.         this = this->below;
  114.  
  115.         /*
  116.          * scan across this level looking for a match
  117.          */
  118.         while(      ( ! (match=StackMatch(this,func,file,line)) )
  119.              && (this->beside != NULL) )
  120.         {
  121.             this = this->beside;
  122.         }
  123.  
  124.         /*
  125.          * if we found the entry
  126.          */
  127.         if( match )
  128.         {
  129.             current = this;
  130.         }
  131.         else
  132.         {
  133.             current = this->beside = StackNew(func,file,line);
  134.             this->beside->above = this->above;
  135.         }
  136.     }
  137.  
  138. } /* StackEnter(... */
  139.  
  140. /*
  141.  * StackNew() - allocate a new stack structure and fill in the default values.
  142.  *        This is here as a function so that we can manage a list of
  143.  *        entries and therefore don't have to call malloc too often.
  144.  *
  145.  * NOTE: this function does not link the current function to the tree.  That
  146.  * must be done by the calling function.
  147.  */
  148. struct stack *
  149. StackNew(func,file,line)
  150.     CONST char        * func;
  151.     CONST char        * file;
  152.     int              line;
  153. {
  154.     static SIZETYPE          alloccnt;
  155.     static SIZETYPE          cnt;
  156.     static struct stack    * data;
  157.     struct mlist        * mptr;
  158.     static struct stack    * this;
  159.     static int          call_counter;
  160.  
  161.     /*
  162.      * if it is time to allocate more entries
  163.      */
  164.     if( cnt == alloccnt )
  165.     {
  166.         /*
  167.          * reset the counters
  168.          */
  169.         cnt = 0;
  170.         alloccnt = STACK_ALLOC;
  171.  
  172.         /*
  173.          * go allocate the data
  174.          */
  175.         data = (struct stack *)malloc(
  176.             (SIZETYPE)(alloccnt*sizeof(*data)) );
  177.  
  178.         /*
  179.          * if we failed to get the data, tell the user about it
  180.          */
  181.         if( data == NULL )
  182.         {
  183.             malloc_errno = M_CODE_NOMORE_MEM;
  184.             malloc_fatal("StackNew",file,line,(struct mlist *)NULL);
  185.         }
  186.  
  187.         /*
  188.          * change the id information put in by malloc so that the 
  189.          * record appears as a stack record (and doesn't get confused
  190.          * with other memory leaks)
  191.          */
  192.         mptr = (struct mlist *) (((char *)data) - M_SIZE);
  193.         mptr->id = call_counter++;
  194.         SETTYPE(mptr,M_T_STACK);
  195.         
  196.     }
  197.  
  198.     /*
  199.      * grab next element off of the list
  200.      */
  201.     this = data + cnt;
  202.  
  203.     /*
  204.      * setup the new structure and attach it to the tree
  205.      */
  206.     this->above = this->below = this->beside = NULL;
  207.     this->func  = func;
  208.     this->file  = file;
  209.     this->line  = line;
  210.     
  211.     /*
  212.      * increment the count since we used yet another entry
  213.      */
  214.     cnt++;
  215.  
  216.     /*
  217.      * return the pointer to the new entry
  218.      */
  219.     return(this);
  220.  
  221. } /* StackNew(... */
  222.  
  223. /*
  224.  * StackMatch() - determine if the specified stack entry matches the specified
  225.  *           set of func,file, and line.  We have to compare all three in
  226.  *          order to ensure that we get the correct function even if
  227.  *          there are two functions with the same name (or the caller
  228.  *          specified the wrong name on the arguement list)
  229.  */
  230. int
  231. StackMatch(this,func,file,line)
  232.     struct stack    * this;
  233.     CONST char    * func;
  234.     CONST char    * file;
  235.     int          line;
  236. {
  237.  
  238.     return(    (strcmp(this->func,func) == 0)
  239.         && (strcmp(this->file,file) == 0)
  240.         && (this->line == line )      ) ;
  241.  
  242. } /* StackMatch(... */
  243.     
  244.  
  245. /*
  246.  * StackLeave() - leave the current stack level (called at the end of a
  247.  *          function.
  248.  */
  249. void
  250. StackLeave( func, file, line )
  251.     CONST char    * func;
  252.     CONST char    * file;
  253.     int          line;
  254. {
  255.     if( current == NULL )
  256.     {
  257.         malloc_errno = M_CODE_STK_NOCUR;
  258.         malloc_fatal("stk_leave", file, line, (struct mlist *)NULL);
  259.     }
  260.     else if( strcmp(func,current->func) != 0 )
  261.     {
  262.         malloc_errno = M_CODE_STK_BADFUNC;
  263.         malloc_fatal("stk_leave", file, line, (struct mlist *)NULL);
  264.     }
  265.     else
  266.     {
  267.         current = current->above;
  268.     }
  269.     
  270. } /* StackLeave(... */
  271.  
  272. /*
  273.  * StackCurrent() - get the current stack pointer
  274.  */
  275. struct stack *
  276. StackCurrent()
  277. {
  278.  
  279.     return( current );
  280.  
  281. } /* StackCurrent(... */
  282.  
  283. /*
  284.  * StackDump() - dump the stack from the specified node 
  285.  */
  286. void
  287. StackDump(fd, msg, node )
  288.     int          fd;
  289.     CONST char    * msg;
  290.     struct stack    * node;
  291. {
  292.     char          outbuf[OUTBUFSIZE];
  293.     char        * s;
  294.     CONST char    * t;
  295.  
  296.     /*
  297.      * if there is nothing to show, just return
  298.      */
  299.     if( (node == NULL) || (node == &root_node) )
  300.     {
  301.         return;
  302.     }
  303.  
  304.     /*
  305.      * if caller specified a message to print out, print it out
  306.      */
  307.     if( msg )
  308.     {
  309.         WRITEOUT(fd,msg,strlen(msg));
  310.     }
  311.  
  312.     /*
  313.      * Ok, we have the info, so lets print it out
  314.      */
  315.     do
  316.     {
  317.         WRITEOUT(fd,"         -> ",12);
  318.  
  319.         s = outbuf;    
  320.  
  321.         /*
  322.          * perform some simple sanity checking on the node pointer
  323.          */
  324.         if(    (((DATATYPE *)node) < malloc_data_start)
  325.             || (((DATATYPE *)node) > malloc_data_end)
  326.             || ((((long)node) & 0x1) != 0) )
  327.         {
  328.             WRITEOUT(fd,"INVALID/BROKEN STACK CHAIN!!!\n",30);
  329.             break;
  330.         }
  331.         
  332.  
  333.         /*
  334.          * build the string for this level
  335.          */
  336.         s = outbuf;    
  337.         t = node->func;
  338.         COPY(s,t,outbuf,OUTBUFSIZE);
  339.         t = "() in ";
  340.         COPY(s,t,outbuf,OUTBUFSIZE);
  341.         t = node->file;
  342.         COPY(s,t,outbuf,OUTBUFSIZE);
  343.         *s++ = '(';
  344.         s += tostring(s,(ULONG) node->line,0,10,' ');
  345.         *s++ = ')';
  346.         *s++ = '\n';
  347.  
  348.         /*
  349.          * write out the string
  350.          */
  351.         WRITEOUT(fd,outbuf,s-outbuf);
  352.  
  353.         /*
  354.           * move up one level in the stack
  355.          */
  356.         node = node->above;
  357.  
  358.         /*
  359.          * until we get to the top of the tree
  360.          */
  361.     } while( (node != NULL) && (node != &root_node) );
  362.  
  363. } /* StackDump(... */
  364.  
  365.  
  366.  
  367.