Re: INFO - memory leaks

From: Daniel Koepke (dkoepke@CALIFORNIA.COM)
Date: 08/16/97


On Fri, 15 Aug 1997, Tom Dailey wrote:

-+You're one of the most vocal coders on this list, and probably one of
-+my most respected admins but please don't insult my integrity
-+with that donation piece.  An admin from Zombie saw the web
-+site and thought it was interesting so I called joe at parasoft directly
-+via their 800 number and asked how we could obtain a license.
-+He was very kind, and since I told him the truth straight out (i.e.,
-+I was coding on a MUD and not a commercial user), he
-+let me have a copy.  I have 3 licenses.  One for our solaris os,
-+and 2 for linux (1 for me and the other for another admin).  But
-+to your 1 license comment, yes, we only have 1 license for
-+each machine.

Insult your integrity?  You can only be insulted if you allow yourself to
be insulted.  Obviously, your clearifiation vendicates you from any
breach of license, so why did your "integrity" take a hit?  No insult
to your integrity was actually intended, just two (perhaps intrusive)
questions, that, none-the-less should have been expected.  I mean, you
should realize at the meer mention of "donations" someone would inquire?
I could have been a lot more rude, about, but as it is, I didn't make any
implications (better than what I usually do).  My only concern lay within
the realm of the CircleMUD license, the second question was purely for
"curiousity" sake.

-+Funny thing about life, if you are honest and just be straight forward
-+and ask, you _might_ just get what you want....

Hm, a friend of mine had this theory that somewhat coincided.  He thought
that if a man wanted to get laid, he need only pick on 9 women with which
to ask.  Then he would proceed to ask each one if they would sleep with him.
the first eight would slap him, and the last one would say, "I thought you
wouldn't ask," and they'd be on their merry way.  <cough>  Whether it
actually works or not is up in the air (it takes a lot of balls to walk
up to a woman and say something like that; you might want to wear a cup and
be cautious of any 6'7", 300lb. boyfriends nearby; aside from the fact that
it's indecent and "Excuse me, would you fvck me?" isn't a great way to start
a relationship).

-+btw,  thanks for the info on free(), create(), and recreate().

Include -DMEM_TRACK in flags to include mem tracking.

  #ifdef MEM_TRACK

  typedef struct mem_track {
     struct mem_track *next;
     int line;
     char file[32];
     int size;
     void *var;
  } mem_track;

  mem_track *Memory = NULL;
  FILE *mtlog = NULL;

  /* this must be called during initialization if MEM_TRACK is defined */
  void init_memtrack()
  {
    if (!mtlog)
      if (!(mtlog = fopen("memtrack.log", "w")))
        mtlog = stderr;
  }

  void mt_CREATE(void *var, int size, int line, char *file)
  {
    mem_track *new_mt;

    /* we call malloc() here instead of CREATE() because if we called CREATE()
     * it'd call mt_CREATE(), which would call CREATE() again, which would
     * call mt_CREATE(), and so on and so forth forever...:) */
    new_mt = (mem_track *) malloc(sizeof(mem_track));
    new_mt->size = size;
    new_mt->line = line;
    new_mt->var = var;
    strcpy(new_mt->file, file);

    new_mt->next = Memory;
    Memory = new_mt;
  }

  void mt_FREE(void *var, int line, char *file)
  {
    mem_track *mt, *temp;

    for (mt = Memory; mt; mt = mt->next)
      if (mt->var == var)
        break;

    if (!mt) {
      fprintf(mtlog, "Attempt to FREE non-alloc'd var (%s,%d)\n", file, line);
      return;
    }

    REMOVE_FROM_LIST(Memory, mt, next);
    free(var);
  }

  /* must be called at end of program #ifdef MEM_TRACK */
  void uninitialize_memtrack()
  {
    mem_track *mt;

    /* you should probably call FREE() here for all the linked lists
     * allocated during CircleMUD that "expire" at the end of the program */
    FREE(descriptor_list);
    FREE(character_list);
    .
    .
    .

    for (mt = Memory; mt; mt = mt->next)
      if (mt->var) {
        fprintf(mtlog, "Memleak: %d bytes.  Line %d of %s.\n", mt->sz,
                mt->line, mt->file);
        free(mt->var);
      }
    free(Memory);
    fclose(mtlog);
  }

  #endif

--utils.h--

  #ifdef MEM_TRACK
  #  define CREATE(var, tp, sz) do { \
       if (((var) = (tp *) malloc(sizeof(tp)*(sz))) == NULL) {
         fprintf(stderr, "memory error\n");
         abort();
       } else
         mt_CREATE(var, sz, __LINE__, __FILE__);
     } while(0)
  #  define FREE(var) \
       do { mt_FREE(var, __LINE__, __FILE__); } while (0)
  #elseif
     // use standard CREATE
  #  define FREE(var) free(var)
  #endif

You'll have to do a bit of work to get this to work (probably, anyway), and
it isn't the best it can be [if you figure out how to "stringify" things
with GCC you might be able to get some extended information, but I'm
uncertain how to do that].  But this is the basic code.  Essentially it
works by creating a new CREATE() command that calls a function (mt_CREATE)
that enters information on it into a linked list.  When FREE is called, it
searches for the link in the linked list and removes it.  However, if the
link is not found, it reports that error and returns (hence avoiding a
crash, and helping you locate things like that).  Some additional checks
might be added to mt_FREE to check if "mt->var" is NULL, and log accordingly.
The idea is to prevent crashes, while providing as much information as you
possibly can on the memory problem.  At the end of the program, the
uninitialize_memtrack() function should FREE() all global variables that are
alloc'd before going on to log everything left in the Memory linked list.

This code (provided you get it to work properly) logs:
  - Attempts to free memory that has not been CREATE'd
  - CREATE'd memory that is not FREE'd

You must go through all of your existing code and change free() to FREE(),
except where absolutely necessary (eg., in the uninitialize_memtrack()
routine).  Please note that this code doesn't account for RECREATE.  In
other words, realloc(), as I believe I've mentioned before,

  void *realloc(void *ptr, size_t size); /* from memory, correct? */

when passed a variable==NULL as the 'ptr' acts like malloc().  Hence if
any of your code takes advantage of this (or uses it), mem_track is
ignorant to its presence.  Even further, a mt_RECREATE should be created
altogether to change the "size" variable in the Memory linked list.  This
will prevent an inaccurate report of lost bytes by uninitialize_memtrack()
on variables that have been RECREATE'd to a smaller size, or worse, a larger
size.  Have a field day... :>

-+ <snip>

Please practice better quoting.


--
Daniel Koepke -:- dkoepke@california.com -:-  [Shadowlord/Nether]
Think.


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     | http://democracy.queensu.ca/~fletcher/Circle/list-faq.html |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 12/08/00 PST