MORE INFORMATION
Before You Use UMDH
If you think that you are experiencing a memory leak, be aware
that memory leaks may not be what they appear to be. You may discover that a
memory leak is not a true memory leak, but is a performance enhancement. For
example, the Microsoft Jet database engine can consume large amounts of memory
(up to 128 MB on a 256-MB computer) because it retrieves data and writes
caches. The cache permits the Jet engine to get fast read-ahead and write-ahead
buffering.
To determine whether or not a process is experiencing
memory leaks, use Windows Performance Monitor (Perfmon.exe) and monitor Private
Bytes under the Process category for your application. Private bytes is the
total memory that the process has allocated, but is not sharing with other
processes. Note that this is different from Virtual Bytes, which is also
interesting to monitor. Virtual Bytes is the current size in bytes of the
virtual address space that the process uses. An application can leak virtual
memory, but may not see a difference in the private bytes that are allocated.
If you do not see memory increase when you monitor private bytes, but you
suspect that you are still running out of memory, monitor virtual bytes to see
if you are using up virtual memory. For additional information about detecting memory leaks and the overview of Windows Performance Monitor (Perfmon.exe), visit the following Microsoft Web site:
To make sure that your application is leaking memory, put the
suspect code in a loop with many iterations, and then monitor private and
virtual bytes for any increases of memory. Watch to make sure that the number
of private bytes and virtual bytes does not eventually stay the same and the
number stops increasing. If there is a point at which the memory stops
increasing, (for example, it does not continue to climb indefinitely) you do
not see a memory leak but more likely, you see a cache that is growing to its
maximum size.
If you determine that you see a memory leak, before you
use UMDH, follow these steps:
- Install the UMDH utility.
- Set the System PATH environment variable to include the
folder where you installed UMDH.
- Set the _NT_SYMBOL_PATH environment variable to the
Microsoft symbol server path so that UMDH can locate debug symbol
files.
The UMDH utility is included with the Debugging Tools for
Windows products at the following Microsoft Web site:
Download and install the utility, and then set the PATH system
environment variable to the path where the debugging tools were installed.
Before you use UMDH, you must install the correct debug symbols for
the components of your application and your operating system. Use the Microsoft
Symbol Server to acquire debug symbols for Microsoft components.
For additional information about the
Microsoft Symbol Server, click the article number below to view the article in
the Microsoft Knowledge Base:
311503 Use the Microsoft Symbol Server to Get Debug Symbol Files
UMDH tries to find the symbol files by using the
_NT_SYMBOL_PATH environment variable. The command to set the path from a
command prompt might look similar to the following:
set _NT_SYMBOL_PATH=SRV*c:\LocalSymbolCache
For additional information about setting up symbolic debugging
information, see the "Debug Symbols" section later in this article.
After you complete these steps, you are ready to use the UMDH utility.
Capturing Heap Dumps with UMDH
UMDH is a utility that dumps information about the heap
allocations of a process. This information includes the callstack for each
allocation, the number of allocations that are made through that callstack, and
the number of bytes that are consumed through that callstack. For example:
00005320 bytes in 0x14 allocations (@ 0x00000428) by: BackTrace00053
ntdll!RtlDebugAllocateHeap+0x000000FD
ntdll!RtlAllocateHeapSlowly+0x0000005A
ntdll!RtlAllocateHeap+0x00000808
MyApp!_heap_alloc_base+0x00000069
MyApp!_heap_alloc_dbg+0x000001A2
MyApp!_nh_malloc_dbg+0x00000023
MyApp!_nh_malloc+0x00000016
MyApp!operator new+0x0000000E
MyApp!LeakyFunc+0x0000001E
MyApp!main+0x0000002C
MyApp!mainCRTStartup+0x000000FC
KERNEL32!BaseProcessStart+0x0000003D
This UMDH output shows that there were 21280 (0x5320) bytes allocated
total from the callstack. The 21280 bytes were allocated from 20 (0x14)
separate allocations of 1064 bytes (0x428). The callstack is given an
identifier of BackTrace00053.
To produce a dump file of the heap
allocations, you must use the Gflags.exe utility, which is also included with
the Debugging Tools for Windows products, to let the operating system know that
you want the kernel to track the allocations.
Assume that you want
to dump the heap(s) contents for Notepad.exe. First you must enable stack trace
acquisition for the application that you want to test. By default, this feature
is not enabled. The command to enable this feature is as follows:
gflags -i notepad.exe +ust
The command does not enable stack tracing for processes that are
already running, but it enables stack tracing for all future executions of
Notepad.exe. Alternatively, you can set the flag through the GFLAGS user
interface (run Gflags.exe without any arguments to get the user interface). Use
the
-ust option for gflags to disable the stack tracing when you are
finished debugging.
When you set the image flags through Gflags.exe,
and you set up the debug symbols, you are ready to start Notepad (the
application that is using UMDH). After the program is started, you must
determine the Process ID (PID) of the Notepad process that was just started.
The command for this is as follows:
You can find the PID from the output of the TLIST application.
The PID information can also be obtained from Task Manager. Assume the PID for
the Notepad process that you just started is 124. You can use UMDH to get a
heap dump with the following command:
umdh -p:124 -f:notepad124.log
Results: You have a complete heap dump of the Notepad process in the
Notepad124.log file. This file shows all of the allocations that were made and
the callstacks where the allocations were made.
Use Umdh.exe to Compare UMDH Logs
While the UMDH log file contains valuable information about the
current state of the heaps for a process, if you are concerned with finding a
memory leak, it may be more valuable to compare the outputs of two logs and
find out what callstack has seen the largest growth between the two dump files.
The Umdh.exe utility helps compare two UMDH logs to provide an analysis of the
difference between them. Once you have two logs captured at different
intervals, you can then use the following command:
UMDH dh1.log dh2.log > cmp12.txt
-or-
UMDH -d dh1.log dh2.log > cmp12.txt
The
-d command line option tells UMDH to display in decimal instead of
hexadecimal. The output of the command compares the differences of the
allocations between the two logs and provides information that is similar to
the following:
+ 5320 ( f110 - 9df0) 3a allocs BackTrace00053
Total increase == 5320
For each BackTrace in the UMDH log files, there is a comparison
made between the two logs files. This case shows that the last log file that is
specified in the UMDH command line had 0xF110 bytes allocated while the first
log in the UMDH command line had 0x9DF0 bytes allocated for the same BackTrace
(callstack). The "5320" is the difference in the number of bytes allocated. In
this case, there were 0x5320 more bytes allocated between the times that the
two logs were captured. The bytes came from the callstack that is identified by
"BackTrace00053".
The next step is to find out what is in that
backtrace. If you open the second log file and search for
BackTrace00053, you may find something that similar to the following:
00005320 bytes in 0x14 allocations (@ 0x00000428) by: BackTrace00053
ntdll!RtlDebugAllocateHeap+0x000000FD
ntdll!RtlAllocateHeapSlowly+0x0000005A
ntdll!RtlAllocateHeap+0x00000808
MyApp!_heap_alloc_base+0x00000069
MyApp!_heap_alloc_dbg+0x000001A2
MyApp!_nh_malloc_dbg+0x00000023
MyApp!_nh_malloc+0x00000016
MyApp!operator new+0x0000000E
MyApp!LeakyFunc+0x0000001E
MyApp!main+0x0000002C
MyApp!mainCRTStartup+0x000000FC
KERNEL32!BaseProcessStart+0x0000003D
When you view the callstack, you can see that the
LeakyFunc function allocates memory through the Visual C++ run-time library
operator new function. If you find that the number of allocations grows as you
take more dump files, you may conclude that memory is not being freed.
Enabling Stack Traces
The most important information in UMDH logs is the stack traces
of the heap allocations. You can analyze them to verify if a process leaks heap
memory. By default, these stack traces are not acquired. You can enable this
feature per-process or system-wide. Use the following command to enable stack
tracing system-wide:
Restart the computer after this command. For per-process
enabling, the command is as follows:
Where
APPNAME is the file name of the executable including the extension (For
example, Services.exe, Lsass.exe). The command does not enable stack tracing
for a process that is already running. Therefore, for processes that you cannot
restart (for example, services, lsass, winlogon), you must restart your test
computer.
Use the following commands to verify what settings have
been set system-wide or for a specific process: System-wide:
Specific process:
By default, the maximum stack trace depth is 16. If you want to
see deeper callstacks, you can increase this by running GFLAGS. Click to select
System Registry, and then type a new depth in the
Max. Stack Trace Capture Depth edit control. Click
Apply, and then restart the computer.
IMPORTANT: If you are using Windows NT 4.0 Service Pack 6, you must use
Umdh_nt4.exe, instead of Umdh.exe, and you must use the
gflags
-r command to set system-wide stack tracing. Make sure that you
restart your computer. Umdh_nt4 stack tracing does not work on a per process
basis on Windows NT version 4. It must be set for the whole system.
Debug Symbols
One of the most important steps to using UMDH is to make sure
that you have good symbol files (.dbg or .pdb file) to get a good stack trace.
At a minimum, you need the Kernel32.dbg and Ntdll.dbg symbol files. You can
acquire additional debugging symbols that you may need as you find out more
about which components leak memory.
For additional information about how to get debug symbol files
for Microsoft components, click the article number below to view the article in
the Microsoft Knowledge Base:
311503 INFO: Use the Microsoft Symbol Server to Acquire Debug Symbol Files
For additional information about how to use the
Microsoft Symbol Server and how to obtain Windows symbol packages, visit the
following Microsoft Web site:
When you build components with Visual C++, it is important that
you not have Program Database for Edit and Continue selected for the C++
compiler options. Instead, select Program Database. To set the symbol path,
initialize the _NT_SYMBOL_PATH environment variable to the path to be used. You
can use the Microsoft Symbol Server to acquire symbols for Microsoft
components.
311503 INFO: Use the Microsoft Symbol Server to Obtain Debug Symbol Files
Follow these steps to set the _NT_SYMBOL_PATH
environment variable:
- In Control Panel, double-click System.
- Click the Advanced tab, and then click Environmental Variables.
Or you can set the _NT_SYMBOL_PATH environment variable in a
command window before UMDH is run.
NOTE: Also include the path to the PDBs for the components of your
application. For example, set the path for _NT_SYMBOL_PATH to the following:
SRV*c:\symbols*http://msdl.microsoft.com/download/symbols;c:\myapplicationssymbols
The first part of this path points to the Microsoft Symbol Server
and states that the symbols that are used will be downloaded in the c:\symbols
folder. The part that follows the semi-colon is the path to the PDB files
(symbol files) specifically for the leaking application.
Invoking UMDH
The only required command-line parameter for UMDH is the
-p option, which specifies the PID of the process from which a heap
dump will be obtained. The PID can be obtained by using Task Manager or the
Tlist.exe program. For a command similar to the following, the log will be
dumped to the standard output:
UMDH also displays various informational messages to standard
error, and therefore if you do not redirect it, it is mixed with the real log.
To gather the UMDH informational messages in a file, use the following command:
If you want to gather the log that is dumped by UMDH in a file,
use one of the following commands:
-or-
These commands are equivalent.
The default log that is
obtained by UMDH contains an enumeration of heap consumers that are sorted by
allocation count. If, for debugging purposes, you also need a dump file of all
of the allocated blocks with their corresponding stack traces, the
-d option can be used:
If you use this command, you might see the following in the UMDH
log file:
Allocations for trace BackTrace00046: 005F69A0
005F6150
These are the memory addresses of the allocations for that
callstack. If your debugger is attached to the process, you can dump the
contents of the memory at these addresses to see what has been allocated.
If the log contains too much information, it can be limited only to
big users who have the allocation count above a certain threshold. Use the
following command:
All of the command-line options (for example,
-p, -f, -t, -d) can be specified simultaneously in any order. The following is a
more difficult command-line example:
umdh -p:123 -t:1000 -f:umdh.log -d
This command dumps the heaps for the process with PID 123 into
the Umdh.log file. It dumps only stack traces that account for more than 1000
allocations and it also dumps the addresses of the heap blocks that are
allocated through each stack trace.
Another useful UMDH option is
the
-l option. This causes file and line numbers to be printed in the
callstack whenever possible.
UMDH Output Explained
If you redirected the log to a file (
umdh -p:PID -f:umdh.log), the contents are similar to the following, which was obtained
from a running Notepad process:
UMDH: Logtime 2000-06-28 10:54 - Machine=MYMachine - PID=704
*********** Heap 00270000 Information ********************
Flags: 58000062
Number Of Entries: 87
Number Of Tags: <unknown>
Bytes Allocated: 00008DF0
Bytes Committed: 0000A000
Total FreeSpace: 00001210
Number of Virtual Address chunks used: 1
Address Space Used: <unknown>
Entry Overhead: 8
Creator: (Backtrace00007)
ntdll!RtlDebugCreateHeap+0x00000196
ntdll!RtlCreateHeap+0x0000023F
ntdll!LdrpInitializeProcess+0x00000369
ntdll!LdrpInitialize+0x0000028D
ntdll!KiUserApcDispatcher+0x00000007
*********** Heap 00270000 Hogs ********************
000001A0 bytes in 0x4 allocations (@ 0x00000068) by: BackTrace00031
ntdll!RtlDebugAllocateHeap+0x000000FB
ntdll!RtlAllocateHeapSlowly+0x0000005B
ntdll!RtlAllocateHeap+0x00000D81
ntdll!LdrpAllocateDataTableEntry+0x00000039
ntdll!LdrpMapDll+0x000002A4
ntdll!LdrpLoadImportModule+0x0000010D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpInitializeProcess+0x000009DC
ntdll!LdrpInitialize+0x0000028D
ntdll!KiUserApcDispatcher+0x00000007
000001A0 bytes in 0x4 allocations (@ 0x00000068) by: BackTrace00034
ntdll!RtlDebugAllocateHeap+0x000000FB
ntdll!RtlAllocateHeapSlowly+0x0000005B
ntdll!RtlAllocateHeap+0x00000D81
ntdll!LdrpAllocateDataTableEntry+0x00000039
ntdll!LdrpMapDll+0x000002A4
ntdll!LdrpLoadImportModule+0x0000010D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpInitializeProcess+0x000009DC
ntdll!LdrpInitialize+0x0000028D
ntdll!KiUserApcDispatcher+0x00000007
The log contains a dump of every heap in the process. In this example,
the log starts with a heap at address 270000. After a few global counters for
the heap, the log contains a dump in decreasing sorted order of stack traces
that are responsible for the most allocations. When you compare the dynamics of
memory that are used at different moments, you can deduce what occurred in the
process and if any heap use is similar to a leak.
Problems That You Can Experience When You Use UMDH
The most common errors when you use UMDH occur because stack
tracing is not enabled. Also, incorrect symbols for Ntdll.dll prevent UMDH from
running. For the other symbols files, UMDH runs but the log file contains stack
traces that do not have function names but instead have relative addresses
inside modules. A distant third error is specifying a wrong PID. The following
error message results when you try to run UMDH for a process that does not have
stack tracing enabled:
C:\>umdh -p:1140 UMDH: Logtime
2000-06-28 12:43 - Machine=MyMachine - PID=1140 Connecting.....Module
enumeration complete. SymGetSymFromName(process, ntdll!RtlpStackTraceDataBase,
xxx) failed, LastError = 126 UmdhGetAddrFromName couldn't find Stack Trace DB
pointer (ntdll!RtlpStackTraceDataBase). ntdll.dll symbols are incorrect; we
must be able to see non-import symbols.
Use the following command to
double-check the settings for the process that you are investigating:
Use the following command when you rely on system-wide stack
tracing:
These commands show the list of flags set for the application.
Note that in the case of system-wide stack tracing, the feature might appear as
active but if you did not restart the computer after running the
gflags -r +ust command, it is not actually activated. If you want to know every
application that has stack tracing enabled, you can view the USTEnabled key
under the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File
Execution Options
If you run UMDH on a process that has stack tracing enabled, but
you have not restarted the application since you set the flags, you may receive
the following message in your logs:
A stack trace was
not saved for this allocation (Index == 0)
If you run do not set the
correct symbol path or the symbols are incorrect, and you run UMDH, you may
receive an error message in the log. However, you may only receive incorrect or
misleading callstacks. To verify that you have the correct symbols, start the
NTSD system debugger for a process, for example:
Then, from the debugger console, run the
LD command to load the symbol information for the module and the
LM command to list what modules have their symbols loaded. If the
output of the
LM command shows export symbols loaded, the symbols are not good. If
you have PDB symbols loaded, the symbols are good. You may receive the
following error message if you specified the wrong PID:
C:\>umdh -p:1000 UMDH: Logtime 2000-06-28 09:45 - Machine=MyMachine -
PID=1000 Connecting... OpenProcess failed, LastError = 0x57
Call UMDH from Visual Basic
It may be useful sometimes to dump a number of logs over time
because the leak may not be very noticeable at first. For example, if you
suspect that your Active Server Pages (ASP) Web application is leaking memory,
it may be helpful to write a COM component in Visual Basic that shells out to
UMDH. You can then call that component from your ASP page.
The
following is some Visual Basic code that invokes UMDH and creates a log file
that is based on the current time:
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Public Function GetProcessID()
GetProcessID = GetCurrentProcessId()
End Function
.
.
.
Dim strTime As String
Dim sProcID As String
sProcID = GetProcessID()
strTime = "MYLOG_" & Format(Now(), "hhmm")
Shell ("C:\UMDH\umdh -p:" & sProcID & " -f:d:\logs\" & strTime & ".txt")
Use UMDH with Windows NT 4.0 Service Pack 6a (SP6a)
The UMDH utility that is included with the Debugging Tools for
Windows products does not work on Windows NT 4.0. A self-extracting executable
(Umdhnt4tools.exe) is included with this article and contains the following
tools to be used with NT 4.0:
- Umdh_nt4.exe and Dbghelp.dll
This is the Windows NT
4.0 SP6 version of the UMDH utility. - Dhcmp.exe
This utility is used to compare two UMDH
dumps to determine where a possible memory leak occurrs.
The
following file is available for download from the Microsoft Download
Center:
Release Date: August
28, 2002
For additional information about how to download Microsoft
Support files, click the following article number to view the article in the
Microsoft Knowledge Base:
119591 How to Obtain Microsoft Support Files from Online Services
Microsoft scanned this file for viruses. Microsoft used the most
current virus-detection software that was available on the date that the file
was posted. The file is stored on security-enhanced servers that help to
prevent any unauthorized changes to the file.
Put Umdh_nt4.exe and Dbghelp.dll in a folder, and then
put them first in your PATH environment variable. Use Umdh_nt4.exe instead of
UMDH.
On a computer that is running Windows NT 4.0, you must use
Gflags.exe to set system-wide stack tracing. For example:
Make sure that you restart your computer. Umdh_nt4 stack tracing
does not work on a per-process basis on Windows NT version 4.0. It is set for
the whole system.
UMDH_NT4 is unlike UMDH in that it does not
compare log files. For example, you cannot do the following:
UMDH_NT4 dh1.log dh2.log > cmp12.txt
Instead you must use the Dhcmp.exe utility that is included with
this article. The command looks similar to the following:
DHCMP dh1.log dh2.log > cmp12.txt