The testHash program provided with Purify shows you how to find and correct memory leaks.
If you have not yet built and run the testHash example program, do so now. For instructions, click
The memory address information in the example is compiler and system dependent. You will see different output and debugging information depending on whether you are running the example on Solaris, HP-UX, or IRIX.
Run the Purify'd version of testHash.
% testHash.pure
Expand
the memory leaked summary, then expand the MLK error messages.
Purify reports two MLK errors: a 12-byte leak and a 2-byte leak, each
occurring multiple times. This results in a total of 182 bytes of leaked
memory.
Expand the putHash function. Notice that each leaked block is a hashEntry or its associated key.
To track down a memory leak, you need to know how the memory blocks are used and where they are stored, and you need to understand where they are lost.
Run the program again and look for a section of code that loses the last pointer to a block of memory. The last pointer to a block of memory can be lost if:
The pointer is reassigned to a new value
The pointer goes out of scope
A memory block containing the pointer is freed or becomes a leak itself
To understand this memory leak message, you need to review how to store and use hashEntry type memory blocks. It is possible that a pointer to a hashEntry is being lost when a new one is inserted in putHash, or when the old one in remHash is removed. It is also possible that they are being lost when they are removed in delHashTable.
Notice that only 13 blocks are lost, even though many more are added or removed as the test program runs. Therefore, do not expect to see a leak on every hashEntry that is added, removed, or deleted.
Start your debugger and set breakpoints in the testHash program after calls to PutHash, RemHash, and DelHashTable.
% dbx testHash.pure
(dbx) file testHash.c
(dbx) stop at 99
(1) stop at "testHash.c":99
(dbx) stop at 183
(2) stop at "testHash.c":183
(dbx) stop at 194
(3) stop at "testHash.c":194
(dbx) run
Note: For an explanation of how to use
purify_xdb, click
% purify_xdb testHash.pure
>v testHash.c
>b 99
Overall breakpoints state: ACTIVE
Added:
1: count: 1 Active testPutHash: 99: }
>b 183
Overall breakpoints state: ACTIVE
Added:
2: count: 1 Active testRemHash: 183: }
>b 194
Overall breakpoints state: ACTIVE
Added:
3: count: 1 Active testDelHashTable: 194: }
>r
Starting process 796: "testHash.pure"
.
.
.
Notes:
For more information about calling Purify API functions from a debugger, click
For information about using Purify's just-in-time debugging feature, click
Rerun the testhash program.
Each
time the program stops (at the end of testPutHash,
testRemHash, and testDelHashTable),
call purify_new_leaks.
By calling purify_new_leaks, you can get a
message showing the new leaks that occurred since the last call to purify_new_leaks. If no leaks are reported, continue
running the program to the next breakpoint.
stopped
in testPutHash at line 99 in file "testHash.c"
99 }
(dbx) print purify_new_leaks()
.
.
.
purify_new_leaks() = 0
(dbx) cont
.
.
.
stopped in testPutHash at line 194 in file "testHash.c"
194 }
(dbx) print purify_new_leaks()
.
.
.
purify_new_leaks() = 182
Note:
In your own longer running programs, you can use the new leaks button to generate a new leaks summary while the program is running.
Use your debugger to take a closer look at the last hashEntry that was leaked.
(dbx)
print *((hashEntry *)0x4f400)
*(hashEntry *) 0x4f400 = {
key =
0x83328 "49"
value = 0xf7fff524
next =
(nil)
}
(dbx)
The debugger confirms that this is the last hashEntry in the list because its next field is NULL. Now look at the source code for delHashTable, looking for errors relating to the edge case of handling
the last member of the list.
void
delHashTable(ht)
hashtable* ht;
{
int index;
hashEntry* last_entry;
hashEntry* entry;
for (index = 0;
index < HASHTABLE_SIZE; index++) {
for
(last_entry = NULL, entry = ht[index];
entry;
last_entry
= entry, entry = entry->next) {
if
(last_entry) {
free(last_entry->key);
free(last_entry);
}
}
}
free(ht);
}
Notice that the inner loop is off by one. The loop deallocates the entry prior to the present position, thereby failing to deallocate the last entry in the list. This means that the pointer to the last hashEntry in each list is being dropped. This happens because the loop is terminated prematurely while last_entry still points to an entry, and the memory is never freed.
To correct this error, add a free for the last hashEntry and its key at the end of the loop.
void delHashTable(ht)
hashtable* ht;
{
int index;
hashEntry* last_entry;
hashEntry* entry;
for (index = 0; index < HASHTABLE_SIZE; index++) {
for (last_entry = NULL, entry = ht[index];
entry;
last_entry
= entry, entry = entry->next) {
if (last_entry) {
free(last_entry->key);
free(last_entry);
}
}
if
(last_entry) { /*
last_entry left dangling */
free(last_entry->key);
/* classic off-by-one error */
free(last_entry); /*
free the last one */
}
}
free(ht);
}
Recompile the program and run it again.
This time Purify should indicate that there are no leaks. You have successfully fixed the problem.