Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 8 Apr 1998 18:22:17 +0200 (SAT)
From:      Graham Wheeler <gram@cdsec.com>
To:        hackers@FreeBSD.ORG
Subject:   Re: Debugging new/delete for C++
Message-ID:  <199804081622.SAA06201@cdsec.com>

next in thread | raw e-mail | index | archive | help
> Anyone know of a debugging allocator package for C++ programs.
> Essentially all I want is to dump out a list of all unfreed objects
> at program termination listing the file and line number where they
> were allocated, the size, and perhaps an allocation number.

I wrote a simple macro package some time back for this. It isn't perfect, 
does require code modifiication, and also only works for classes, but may
suit your requirements.

Here it is:

------- debug.h --------------------------------------------------------
#ifndef __DEBUG_H
#define __DEBUG_H

#ifdef MEMTRACE

// A class for tracing construction/destruction of objects

#include <stdio.h>

class _OT
{
  protected:
    char *cname;
    char *fname;
    int linenum;
    long id;
  public:
    _OT(char *cname_in, char *fname_in, int linenum_in);
    _OT(const _OT &ot);
    _OT& operator=(const _OT&)
    {
	return *this;
    }
    ~_OT();
};

struct _MT
{
  public:
    _MT(char *nm);
    ~_MT();
};

#define TRACE(cn)	struct OT : public _OT { OT() : _OT(#cn,__FILE__, __LINE__) {} } xxxxx;
#define MAINTRACE(nm)	struct _MT yxyxyx(#nm);

#else

#define	TRACE(cn)
#define MAINTRACE(nm)

#endif // MEMTRACE

#endif
------- debug.cc -------------------------------------------------------
#ifdef MEMTRACE

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "debug.h"

static FILE *tracefp = 0;
static long nextid = 1;
static int flush = 0;
static char fname[80] = { 0 };

static void TraceClose()
{
    if (tracefp)
    {
	fprintf(tracefp, "Closing\n");
	fclose(tracefp);
	tracefp = 0;
    }
    flush = 2;
}

static void TraceOpen()
{
    if (fname[0]==0)
    {
        if (stdout==0) return; // sanity
        sprintf(fname, "/tmp/mtrace.%ld", getpid());
	unlink(fname);
	atexit(TraceClose);
    }
    if (fname[0] && stdout != 0 && tracefp==0)
        tracefp = fopen(fname, "a");
}

static void TraceFlush()
{
    if (flush == 1) fflush(tracefp);
    else if (flush == 2) { fclose(tracefp); tracefp = 0; }
}

static void Trace(char *op, long id, char *name, char *where, int lnum)
{
    TraceOpen();
    if (tracefp)
    {
        fprintf(tracefp, "%-8ld %s [%d:%s:%d] %s\n",
			id, name, getpid(), where, lnum, op);
        TraceFlush();
    }
}

// we make separate ones for construct/destruct to ease debugging
// using leak IDs and breakpoints

static void TraceC(char *op, long id, char *name, char *where, int lnum)
{
    Trace(op, id, name, where, lnum);
}

static void TraceD(char *op, long id, char *name, char *where, int lnum)
{
    Trace(op, id, name, where, lnum);
}


//----------------------------------------------------------------------

_OT::_OT(char *cname_in, char *fname_in, int linenum_in)
    : cname(cname_in), fname(fname_in), linenum(linenum_in), id(nextid++)
{
    TraceC("created", id, cname, fname, linenum);
}

_OT::_OT(const _OT &ot)
    : cname(ot.cname), fname(ot.fname), linenum(ot.linenum), id(nextid++)
{
    TraceC("copy created", id, cname, fname, linenum);
}

_OT::~_OT()
{
    TraceD("destroyed", id, cname, fname, linenum);
}

//-----------------------------------------------------------------------

_MT::_MT(char *nm)
{
    TraceOpen();
    if (tracefp)
    {
        fprintf(tracefp, "Starting %s\n", nm);
        TraceFlush();
    }
}

_MT::~_MT()
{
    TraceOpen();
    if (tracefp)
    {
        fprintf(tracefp, "Main exiting\n");
        TraceFlush();
    }
}

#endif
------- end ------------------------------------------------------------

To use it, you put a line:

    TRACE(Classname)

inside each class you want traced, and add a line:

    MAINTRACE(Programname)

to your main() routine.

After running the program, the file /tmp/mtrace.PID will contain an audit
trail of all traced class allocations and deallocations. You can then use
the following program to show any leaks:

-------- findleak.cc -------------------------------------------------------
// Scan the debug file for constructtrace and destructtrace messages,
// and report any memory leaks. Quick 'n dirty...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAXOBJ	10000 // maximum allowed concurrent allocations

struct objrecord
{
    int id;
    char *loc;
    char *name;
};

objrecord records[MAXOBJ];
int count = 0;

void Init()
{
    for (int i = 0; i < MAXOBJ; i++)
    {
	records[i].name = 0;
	records[i].loc = 0;
	records[i].id = 0;
    }
}

int FindEntry(int id)
{
    assert(id != 0);
    for (int i = 0; i < MAXOBJ; i++)
	if (records[i].id == id)
	    return i;
    return -1;
}

int FindSpace() // linear search :-(
{
    for (int i = 0; i < MAXOBJ; i++)
	if (records[i].id == 0)
	    return i;
    return -1;
}

void DoLine(FILE *fp, int pid)
{
    int id;
    char buf[256], oname[256], loc[256], op[256];
    if (fgets(buf, sizeof(buf), fp) == 0) return;
    buf[strlen(buf)-1] = 0; // strip \n
    if (sscanf(buf, "%d%s%s%s", &id, oname, loc, op)==4)
    {
	if (pid && atoi(loc+1) != pid) return;
	int idx = FindEntry(id);
	if (strcmp(op, "created") == 0)
	{
	    if (idx >= 0) 
		fprintf(stderr, "Bad construct of %s at %s, id %d!\n",
					oname, loc, id);
	    else
	    {
		idx = FindSpace();
		if (idx < 0)
		    fprintf(stderr, "Out of space!\n");
		else
		{
		    records[idx].id = id;
		    delete [] records[idx].loc;
		    delete [] records[idx].name;
		    records[idx].loc = new char[strlen(loc)+1];
		    records[idx].name = new char[strlen(oname)+1];
		    strcpy(records[idx].loc, loc);
		    strcpy(records[idx].name, oname);
		}
	    }
	}
	else if (strcmp(op, "destroyed") == 0)
	{
	    if (idx < 0) 
		fprintf(stderr, "Bad destroy of %s at %s, id %d!\n",
				oname, loc, id);
	    else
	    {
		records[idx].id = 0;
		count++;
	    }
	}
	else printf("Bad op %s\n", op);
    }
}

void Report()
{
    for (int i = 0; i < MAXOBJ; i++)
    {
	if (records[i].id!=0)
	    fprintf(stdout, "Leak of %s at %s, id %d\n",
		records[i].name, records[i].loc, records[i].id);
	delete [] records[i].loc;
	delete [] records[i].name;
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
    {
	fprintf(stderr, "Useage: findleak [ <pid> ] <debug trace file>\n");
	exit(-1);
    }
    int pid = 0;
    if (argc == 3) pid = atoi(argv[1]);
    FILE *fp = fopen(argv[argc-1], "r");
    if (fp == 0)
    {
	fprintf(stderr, "Failed to open file %s\n", argv[1]);
	exit(-1);
    }
    Init();
    while (!feof(fp))
	DoLine(fp, pid);
    fclose(fp);
    Report();
    printf("%d objects properly deallocated\n", count);
    return 0;
}

-------- end----------------------------------------------------------------

Hope this helps!

-- 
Dr Graham Wheeler                          E-mail: gram@cdsec.com
Citadel Data Security                      Phone:  +27(21)23-6065/6/7
Internet/Intranet Network Specialists      Mobile: +27(83)-253-9864
Firewalls/Virtual Private Networks         Fax:    +27(21)24-3656
Data Security Products                     WWW:    http://www.cdsec.com/




To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199804081622.SAA06201>