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>
