Appendix Three: Tracing Objects and Memory in C

Our aim here is to take count.cpp and change it to trace not only objects but also allocation and deallocation by global operators new and delete . Toward this end we work with just one class YClass and have its only data member value be dynamically allocated. We therefore added a destructor to deallocate value . Here is the file memtrace.cpp :

 extern "C" {      #include <stdio.h>      #include <string.h>      #include <stdlib.h>      #include "trace.hpp"    }    #ifdef _OBTRACE_ON      #include "obtrace.hpp"    #endif    #include "memtrace.hpp"    // now redefine new    #ifdef _TRACE_ON      #define new new(__FILE__,__LINE__,REPORT_TRACE(3))    #else      #define new new(__FILE__,__LINE__,TRACELOC)    #endif    #ifdef _OBTRACE_ON      class XClass : private OBTRACE<XClass>    #else      class XClass    #endif    {    public:      XClass() { value=0; }      XClass(int i) {        value = new int;        *value = i;      }  ~  XClass() { if (value) delete value; }      #ifdef _OBTRACE_ON        // method ReportAll -------------------------------------        static void ReportAll() {          OBTRACE<XClass>::ReportAll(stdout,"XClass");        }//end ReportAll        // method ReportCount -----------------------------------        static void ReportCount() {          OBTRACE<XClass>::ReportCount(stdout,"XClass");        }//end ReportCount      #endif // _OBTRACE_ON    protected:      int* value;    };//end class XClass    #ifdef _OBTRACE_ON      char* OBTRACE<XClass>::init=NULL;    #endif    XClass ox1(1);    // function doit1 -------------------------------------------    int doit1()    {     TRACE(doit1)     XClass* ox2 = new XClass(2);     //delete ox2;     RETURN1(1)    }//end doit1    // function doit2 -------------------------------------------    void doit2()    {     TRACE(doit2)      XClass ox3(3);      XClass* ox4 = new XClass(4);      doit1();      //delete ox4;      #ifdef _OBTRACE_ON        XClass::ReportCount();      #endif      RETURN    }//end doit2    void Report()    {      #ifdef _OBTRACE_ON         XClass::ReportAll();      #endif      new_report(stdout);    }    // function main --------------------------------------------    int main()    {     TRACE(main)     #ifdef _OBTRACE_ON       atexit(Report);     #endif     doit2();     RETURN1(0)    }//end main 

The overloaded operators new and delete are in the file memtrace.hpp that is #included. That is where the reporting function new_report() is defined as well. Note that (a) we redefine new in two different ways, depending on whether or not we trace the functions, and (b) as discussed in Chapter 10, we do not redefine delete . Here is the file memtrace.hpp :

 #ifndef _MEMTRACE_HPP #define _MEMTRACE_HPP extern "C" {   #include <stdio.h>   #include <string.h> } struct NEW_STRUCT {  void *addr;  char* file;  int line;  char* local;  size_t size;  struct NEW_STRUCT* next; }; typedef NEW_STRUCT NEW; NEW* newlist = 0; void* operator new(size_t size,const char* file,                    int line,char* local) {    void *p;    NEW* newp;    // every allocation should be checked and exception    // thrown if error    newp = (NEW*) malloc(sizeof(NEW));    newp->addr = malloc(size);    newp->file = strdup(file);    newp->line = line;    newp->local = strdup(local);    newp->size = size;    newp->next = newlist;    newlist = newp;    return newp->addr; } void operator delete(void* p) {   NEW *newp, *newp1;   if (newlist == 0) {     // here we should throw, spurious deallocation     return;   }   if (newlist->addr == p) {     newp = newlist;     newlist = newlist->next;     free(newp->file);     free(newp->local);     free(newp);     return;   }   for(newp1 = newlist, newp = newlist->next;            newp != 0;                newp1 = newp, newp = newp->next) {     if (newp->addr == p) {       newp1->next = newp->next;       free(newp->file);       free(newp->local);       free(newp);       return;     }   } // here we should throw, spurious deallocation } void operator delete(void *p,const char* file,                      int line,char* local) {   operator delete(p); } void new_report(FILE *fp) {  NEW* newp;  if (newlist == 0) {    fprintf(fp,"all deallocated :-)\n");    fflush(fp);    return;  }  for(newp = newlist; newp != 0; newp = newp->next) {   fprintf(fp,"undealloc. segment at address %x from file=%s,\n",           newp->addr,newp->file);   fprintf(fp,"   line=%d, local=%s, size=%u\n",           newp->line,newp->local,new->size);  }  fflush(fp); } #endif     // _MEMTRACE_HPP 

Again, for keeping track of allocation and deallocation we chose a simple linked list, newlist . The files trace.hpp , trace.c , and obtrace.hpp are as before. The Makefile:

 CFLAGS= trace.o: trace.c        gcc -c trace.c $(CFLAGS) memtrace: memtrace.o trace.o        g++ -o memtrace memtrace.o trace.o $(CFLAGS) memtrace.o: memtrace.cpp memtrace.hpp trace.hpp obtrace.hpp        g++ -c -D_TRACE_ON -D_OBTRACE_ON memtrace.cpp $(CFLAGS) 

As in Appendix B, we first compile it as memtrace with both localization tracing and object tracing. We obtain the following output:

 [doit2:main:global] number of objects of class XClass = 4 undeallocated object of class XClass created in doit1:doit2:main undeallocated object of class XClass created in doit2:main:global undeallocated object of class XClass created in global undealloc. segment at address 22500 from file=memtrace.cpp,    line=31, local=doit1:doit2:main, size=4 undealloc. segment at address 224f0 from file=memtrace.cpp,    line=65, local=doit1:doit2:main, size=8 undealloc. segment at address 224d0 from file=memtrace.cpp,    line=31, local=doit2:main:global, size=4 undealloc. segment at address 224c0 from file=memtrace.cpp,    line=79, local=doit2:main:global, size=8 undealloc. segment at address 22460 from file=memtrace.cpp,    line=31, local=global, size=4 

The object count and undeallocated objects are clear (see the discussion in Appendix B). More interesting are the undeallocated segments. The one of size 4 at address 22500 is value of the global object ox1 . The one of size 8 at address 224f0 is the dynamic object *ox2 . The one of size 4 at address 224d0 is value of the object *ox2 . The one of size 8 at address 224c0 is the dynamic object *ox4 , while the one of size 4 at address 22460 is its value . There is no trace of the object ox3 and its value, for both were " destroyed " when it went out of scope. Now uncomment the deletes in memtrace.cpp and recompile. Running it again yields:

 [doit2:main:global] number of objects of class XClass = 2    undeallocated object of class XClass created in global    undealloc. segment at address 224b8 from file=memtrace.cpp,       line=31, local=global, size=4 

There is only one undeallocated object, the global ox1 , and only one un-deallocated segment of size 4 at address 224b8, its value . Now, let us comment out the global object ox1 , recompile, and run again:

 all deallocated :-) 

So there, a smiley!



Memory as a Programming Concept in C and C++
Memory as a Programming Concept in C and C++
ISBN: 0521520436
EAN: 2147483647
Year: 2003
Pages: 64

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net