[Demo] [Overview of Examples] [User's Guide]

Examples\Predefined\Memwatch

Memory Watch Predefined Probe: memwatch.dll

Memory Tracking with Aprobe

A common concern with large applications which may have to run is that we are leaking memory - allocating memory on the heap and then forgetting to free it. Sometimes we are unaware that particular routines are allocating it for us at all!

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.

A Simple Example

In the first example we detect a simple heap leak using Memwatch with no options. We will use the same executable for all our examples.

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_example
This 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

Tracking Memory Usage Over Time

Often it's useful to see a visual representation of the memory usage as the application is running. The Memwatch probe provides graphs showing how memory is being allocated over time. To show this we need to provide the `-g' option to memwatch:
aprobe -u memwatch -p -g memwatch_example
This 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.

Using Snapshots

Using the GUI we can take snapshots of the heap at a moment in time. Using snapshots effectively can really help you track down memory leaks. Let's assume we have an application which performs a lot of heap allocations at initialization and then goes into a steady state (obviously this is a very common scenario!) We suspect a leak but when we look at the list of outstanding allocations we have all those from initialization that we know will stay around for a while. If you take a snapshot after initialization is complete, those allocations made during initialization will not be shown when you take a snapshot later.

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.
[Demo] [Overview of Examples] [User's Guide]