Detecting Increase in Heap Allocations with UMDH

The user-mode dump heap (UMDH) utility works with the Windows operating system to analyze the heap allocations for a specific process. UMDH utility is used to locate which routine in a specific process is leaking memory.

Note: The UMDH utility works on Windows OS only.

Prerequisites

  • Download and install the UMDH utility for Windows OS. For more information, see Debugging Tools for Windows from Microsoft documentation.
  • Enable "Create user mode stack trace database" with gflags.exe -i bwappnode-umdh.exe +ust command. Get the process name from task manager.

    C:\Program Files (x86)\Windows Kits\10\Debuggers\x64>gflags.exe -i bwappnode-umdh.exe +ust
    Current Registry Settings for bwappnode-umdh.exe executable are: 00001000
        	ust - Create user mode stack trace database
    
  • Before using UMDH, you must have access to the proper symbols for your application. UMDH uses the symbol path specified by the environment variable _NT_SYMBOL_PATH. Set the variable to a path containing symbols for your application.

    If you also include a path to Windows symbols, the analysis is more complete. The syntax for this symbol path is the same as that used by the debugger.

    For more information, see Symbol Path for Windows Debugger from Microsoft documentation.

    For example, if the symbols for your application are located at C:\MySymbols, then to use the public Microsoft symbol store for your Windows symbols, using C:\MyCache as your downstream store, run the following command to set your symbol path:

    C:\Program Files (x86)\Windows Kits\10\Debuggers\x64>set _NT_SYMBOL_PATH=c:\mysymbols;srv*c:\mycache*https://msdl.microsoft.com/download/symbols

Procedure

  1. Determine the process ID (PID) for the process to investigate.
    For more information, see Finding the process ID from Microsoft documentation.
  2. Analyze the heap memory allocations for the process before the memory leak is detected, and save it to a log file.
  3. Collect the data at application start up before sending load.

    For example, if the PID is 5872, and name of the log file is log_before.txt, use the following command:

    umdh.exe -p:5872 -f:log_before.txt

  4. Use the UMDH utility to analyze the heap memory allocations for this process after the memory starts increasing, and save it to a log file.
  5. Collect this data at regular intervals when an application starts leaking memory.

    For example, if the PID is 5872, and name the log file is log_after.txt, use the following command:

    umdh.exe -p:5872 -f:log_after.txt

  6. The UMDH utility can compare two different log files and display the change in their respective allocation sizes. To redirect the results into a third text file, use the greater-than symbol (>). To convert the byte and allocation counts from hexadecimal to decimal, use the -d option. For example, to compare log_before.txt and log_after.txt files, and save the results to the file log_compare.txt, use the following command:

    umdh.exe -d log_before.txt log_after.txt > log_compare.txt

  7. For each call stack that is labeled as "BackTrace" in the UMDH log files, there is a comparison made between the two log files. The snippet of the output is as follows:
     // where:                                                                   
    //     BYTES_DELTA - increase in bytes between before and after log         
    //     NEW_BYTES - bytes in after log                                       
    //     OLD_BYTES - bytes in before log                                      
    //     COUNT_DELTA - increase in allocations between before and after log   
    //     NEW_COUNT - number of allocations in after log                       
    //     OLD_COUNT - number of allocations in before log                      
    //     TRACEID - decimal index of the stack trace in the trace database     
    //         (can be used to search for allocation instances in the original  
    //         UMDH logs).                                                      
    
    +    8856 (  18400 -   9544)     12 allocs	BackTrace5
    +       3 (     12 -      9)	BackTrace5	allocations
    
    	ntdll!RtlpAllocateHeap+2298
    	ntdll!RtlpAllocateHeapInternal+727
    	MSVCR100!malloc+5B
    	jvm!JVM_ResolveClass+387AE
    	jvm!???+0 : 53C415D6
    	jvm!JVM_GetManagementExt+6A5FF
    	jvm!JVM_GetManagementExt+786B1
    	jvm!JVM_GetManagementExt+7A162
    	jvm!JVM_GetManagementExt+CA4E
    	jvm!JVM_FindSignal+178329
    	jvm!JVM_FindSignal+1792E4
    	jvm!JVM_FindSignal+179491
    	jvm!JVM_FindSignal+17969F
    	jvm!JVM_GetManagementExt+82712
    	jvm!JVM_GetManagementExt+8305F
    	jvm!JVM_ResolveClass+5F5FF
    	jvm!JVM_FindSignal+68FA
    	MSVCR100!endthreadex+43
    	MSVCR100!endthreadex+DF
    	KERNEL32!BaseThreadInitThunk+14
    	ntdll!RtlUserThreadStart+21
    

    This UMDH output shows that there were 8856 total bytes allocated from the call stack.