Newsgroups: comp.os.vms Path: news.mitre.org!blanket.mitre.org!agate!newsgate.duke.edu!nntprelay.mathworks.com!howland.erols.net!cpk-news-hub1.bbnplanet.com!news.bbnplanet.com!ix.netcom.com!ejohnson From: ejohnson@netcom.com (Eric Johnson) Subject: Re: Memory leak and error detection. Message-ID: Organization: Netcom On-Line Services X-Newsreader: NN version 6.5.0 CURRENT #9 References: <01bcfe61$5a7c2720$afe0f6c6@agorelik.sungardams.com> Date: Tue, 2 Dec 1997 03:19:26 GMT Lines: 131 Sender: ejohnson@netcom3.netcom.com "Alexander Gorelik" writes: > I am looking for a VMS software that will detect memory corruption's > or leaks. There are many packages like that exists in the UNIX or > WINDOWS world, but I've never seen one for VMS. I know of no such commercial package, but my co-workers and I wrote one that we use in house for OpenVMS. Its not hard. You could do it in a few days, and I HIGHLY recommend it. Here's what we did. This works for both C and C++. If you're really into this type of thing, I suggest folding this into your debug mode options of your make system. You don't use a make system, you say. Here's yet another good reason to. The DEC C and C++ compiler automatically tacks on a DECC$ to the standard C library functions such as malloc, calloc, realloc, free, as well as the mangled new and delete operators. You'll want to turn this off during your debug compilations. Type in help cc /prefix for details. By turning this prefixing off, you will end up with unresolved symbols (duh!). Thus, you'll need to write some function stubs for malloc, calloc, etc. These function stubs should all be implemented in a shareable image (eg. debug_toolkit) and be exported in the transfer vector. The implementation of these functions is nothing more than allocating a little extra memory. The "extra memory" is nothing more than some extra information as well as room for a footer and header. The structure can also contain some pointers to chain these structures together. Consider something like... struct mem_block { unsigned char m_cMagicSignature[4]; // This should be first mem_block *m_pPrev; mem_block *m_pNext; unsigned long m_nRealSize; unsigned char *m_pFilename; // This will make more sense below unsigned long m_nLineNumber;// Same as above unsigned char m_cHeader[15]; char m_cBlock[1]; }; Your memory allocation will look like... void* malloc(size_t nbytes) { void *pRealBlock=DECC$MALLOC(sizeof(mem_block)+nbytes+15); ... } The reason for the extra 15 bytes on the end is because you'll want a footer as well as a header. After you allocate the memory, fix up the m_pPrev and m_pNext to form a chain of blocks with existing allocations. Your debug_toolkit should have some global pointers to point to that list. The value of the filname and line number fields will make sense shortly. I prmoise... You should initialize your header and footer region with magic values that would trigger an access violation should it be used as a pointer. We use 0xFDFDFDFD padded out. In addition, initialize the block region to another magic value. We use 0xCDCDCDCD. Initialize your magic sequence field too. In the end, return a pointer to m_cBlock of your structure. In your deallocation routine, your caller should hopefully have given you a valid pointer. The only way to know for sure is to back up the number of bytes consumed by your fields in your struct and then validate the magic signature field. If that pans out, then you know you have a valid structure. You should check the header and footer regions to make sure that they are okay. You should also pop the block from the master list. In addition, verify that your header and footer regions were unmolested. Should they be changed, your caller has a bug in their routines. They are modifying data that wasn't theirs. You should also wipe out their block with another bad chunk of hex values. We use 0xDD. When you're all said and done, call DECC$FREE on the original pointer. These hex values are important. Teach your development staff about them. If you get an access violation on 0xDDDDDDDD, you'll know that your are referencing something that was deleted. 0xCDCDCDCD will tell you that it was never initialized. Step two consists of a little C pre-processor magic. You'll want to create a header file that redefines malloc to be a macro like #define malloc(x) malloc_with_info(__FILE__,__LINE__,x); malloc_with_info will be another memory allocator that this time will populate those extra fields with the file and line number of the allocation. You should write some code in your debug_toolkit that will be executed upon process exit. This code will effectively dump all of the entries in the currently allocated list-- a memory leak list! We use a C++ global object whose dtor reports this information. This header file should be placed inside every compilation unit. Due to the funkiness with macros, we have a rule that it is the last header file of every compilation file. It is never included by another header file. If you pull a lot of tricks with operator new on your classes, this will be more of an issue. If you just do C development, this is less important. I realize that modifying an existing system is hard. We had over 300 source files to fix up. But doing so has enabled us to grow the system to over 2000. At the time, the system lit up like a Christmas tree. We had memory leaks and buffer write overruns all over the place. In a matter of weeks, the system was cleaned up and the bugs that could never quite be reproduced on a regular basis vanished. The system has worked so well that I was able to detect a bug in one version of the DEC CXX compiler that prevented it from calling the dtor in certains situations with template usage. A good memory tracking system is the single best good programming habit you can put into effect in your system. A good make system will make it an automatic process. What more could you want? Now you can tell those Java weenies to put this in their JITs and smoke it! :-> Questions? -Eric