From owner-freebsd-hackers Wed Apr 8 09:18:33 1998 Return-Path: Received: (from majordom@localhost) by hub.freebsd.org (8.8.8/8.8.8) id JAA00953 for freebsd-hackers-outgoing; Wed, 8 Apr 1998 09:18:33 -0700 (PDT) (envelope-from owner-freebsd-hackers@FreeBSD.ORG) Received: from citadel.cdsec.com (citadel.cdsec.com [192.96.22.18]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id JAA00906 for ; Wed, 8 Apr 1998 09:18:03 -0700 (PDT) (envelope-from gram@cdsec.com) Received: (from nobody@localhost) by citadel.cdsec.com (8.8.5/8.6.9) id SAA28789 for ; Wed, 8 Apr 1998 18:23:10 +0200 (SAT) Received: by citadel via recvmail id 28787; Wed Apr 8 18:22:26 1998 From: Graham Wheeler Message-Id: <199804081622.SAA06201@cdsec.com> Subject: Re: Debugging new/delete for C++ To: hackers@FreeBSD.ORG Date: Wed, 8 Apr 1998 18:22:17 +0200 (SAT) X-Mailer: ELM [version 2.4 PL25-h4.1] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG > 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 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 #include #include #include #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 #include #include #include #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 [ ] \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