Aprobe provides the Memwatch predefined probe to help analyze use of allocated memory in your applications. It monitors and displays total heap usage, the rate of memory allocation, and reports data hasn't been deallocated.
While you can use the Aprobe Workspace to run the predefined probes as described in the Demo, we'll use the Aprobe command line to build the example application and run the probes, since the example program prompts for user input.
So choose Command Prompt from the Run menu, make sure
you're in the Examples\Predefined\Memwatch directory under Aprobe (or
your local copy of this directory), and type nmake
This compiles memwatch_example.c into
memwatch_example.exe.
Before we do anything else, let's just run it with the memwatch predefined probe:
aprobe -u memwatch memwatch_exampleThis will display a small menu of possible choices and wait for a reply. For now we want to enter option 1 which will allocate a small amount of heap using malloc and then quit without freeing it. Enter option 1 and then type
apformat memwatch_example to see the output.
The default output produced by Memwatch consists of two reports. The first groups allocations by size, and the second reports them by time or "age" since the start of the program. The point of allocation is expressed as a "call chain", which describes where the allocation action was done within the program. For our simple program, the report looks like this:
The following filters were applied during data collection:
Filter extern:"malloc()"
===> extern:"BigAlloc()"
Filter extern:"malloc()"
===> extern:"SimpleAlloc()"
Summary of snapshot ("program exit")
Taken at Fri Feb 02 16:10:02.496184 2001 covering 00:00:01.311601 seconds
Total allocations since program start = 1, since last snapshot = 1
Unallocated objects since last snapshot = 1
Report of outstanding allocations grouped by location (Size Report):
Total size Allocs Avg Size Allocation call chain ID
---------- ------ -------- ------------------------
16 1 16 #1
Report of outstanding allocations grouped by age (Age Report):
Address Size Age Allocation call chain ID
(bytes) (secs)
---------- ------ -------- -------------------------
0x02330f80 16 0.005994 #1
----------------------------------------------------------------------------
List of traceback IDs and their corresponding tracebacks:
----------------------------------------------------------------------------
#1 extern:"malloc()"
==> extern:"SimpleAlloc()" at line 17 (memwatch_example.c)
==> extern:"main()" at line 107 (memwatch_example.c)
==> extern:"mainCRTStartup()" + 0x00b2
(Note: the report is preceded by a note about "Filters" -- this is a feature that allows only certain allocations to be reported. It is used in this example to eliminate some system-dependent allocations done before the example program starts. If you're curious, go to the bottom of the memwatch_example.memwatch.cfg file, comment out the "Filter" lines, and re-run aprobe and apformat.)
This report is described in more detail in Appendix D of the User's Guide.
Sometimes it's useful to see what you get if there are no leaks in your program at all (that way you'll know what to change). Run aprobe as before but this time enter option 2. When you run apformat, there are no allocations shown as outstanding:
Summary of snapshot ("program exit")
Taken at Fri Feb 02 16:10:18.228063 2001 covering 00:00:03.049577 seconds
Total allocations since program start = 1, since last snapshot = 1
Unallocated objects since last snapshot = 0
aprobe -u memwatch -p -g memwatch_exampleThis will bring up the "Aprobe Memwatch Probe" window which graphically shows statistics about the executing program.
The default option is for the graph to update every second. Obviously there is nothing happening right now so select option 3 on the application to allocate a block of memory. On the next GUI update you should see that the "Heap Size" is now 1024, shown by the red line on the top graph; and the frequency of allocations is shown on the lower graph.
Now allocate a few more blocks and see the usage go up some more. Then deallocate a block and watch how the graph retains the maximum (High Water Mark) heap usage in blue but also shows the current usage in red.
A good example of this occurred when working with an application that had a web-browser style interface: You could move forward and backward between pages. Each time a new page was displayed, memory would be allocated to store the contents. Every time a page was destroyed that memory should be reclaimed. If the application was used long enough it started to run out of memory - there was a leak somewhere.
Using memwatch it was trivial to see that the memory utilization was going up when we displayed a new page and was going down when we deleted a page - but not going down quite enough! By using a snapshot to remove all current allocations, it was simple to get a page allocated and then freed and take another snapshot - the remaining allocations were the leaked objects and the problem was trivial to find and fix!
We can simulate this now using our example. Click the "Snapshot" button at the bottom of the MemWatch window to "clear" everything done before. Now select option 3 to allocate another block, and again click "Snapshot" in the MemWatch window. Finally, select example option 2 to quit the program.
When we run apformat memwatch_example we should see two
sections starting with
Summary of snapshot ("Snapshot from GUI")
In the second one, we should see the allocation caused by choosing
option 3 between the first and second snapshots:
Summary of snapshot ("Snapshot from GUI")
Taken at Thu Oct 12 17:17:43.155961 2000 covering 00:00:10.099780 seconds
Total allocations since program start = 3, since last snapshot = 1
Unallocated objects since last snapshot = 1
Report of outstanding allocations grouped by location (Size Report):
Total size Allocs Avg Size Allocation call chain ID
---------- ------ -------- ------------------------
2048 1 2048 #1
Report of outstanding allocations grouped by age (Age Report):
Address Size Age Allocation call chain ID
(bytes) (secs)
---------- ------ -------- -------------------------
0x00228f78 2048 7.167597 #1
----------------------------------------------------------------------------
Take some time to play around with allocating and deallocating memory,
taking snapshots and exploring the different options. When you've had
enough, use either option 1 or 2 to quit the program.
To find out more about the memwatch predefined probe you may want to look at
the reference section for it in "Memory Watch Probe: memwatch.dll"
in Appendix D of the User's Guide.