views:

739

answers:

5

What tools are available to attribute memory consumptions in VB6 application to it's multiple components? I can get the memory consumed by the entire application by watching various counters (Private Bytes, Working Set etc.), for example, in Process Explorer. I want to go level deeper than that and understand how much memory is consumed by various components or objects created at runtime. For example, figure out how much memory is consumed by large collection that's caching data at run time and how it changes based on number of elements in the collection.

A: 

In a pinch, you can use private bytes and create a test app that adds items to your collection one at a time.

Jon B
+1  A: 

There's another tool on the MS site called processmonitor.exe. It reports every request call and you can use its filtering capability to monitor only the process requests of your application.

http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

oosterwal
+2  A: 

I'm not sure any publicly available (free) tools will profile VB6 code down to the module level. There are several memory profilers available for C/C++ and .NET, but not a lot on VB6 that I saw. Looks like all the old vendors (IBM Purify, Compuware Devpartner/Boundschecker) in this area have either been bought out, or moved on to .NET support only.

You might try GlowCode. It states C++ support, but also stresses Win32 native x86 images.

Microsoft publishes DebugDiag, which has support for memory leak detection for .NET, or Win32, though I've never used it with VB. It might not show outstanding allocations to the module level, but I'd bet it will at least attribute which libraries/dlls have allocated the most memory.

Zach Bonham
+2  A: 

My favourite tool has to be DevPartner though at £1,500 a pop it is not cheap. It does a hell of a lot more than Memory leak checking though, but if that's all you need you might be carpet bombing ants.

If you want to see if your app is releasing resources correctly, use this Function I wrote to dump the memory at a given location. I would first cache the addresses of each of your variables then at shutdown you could call the DumpVariableMemory passing in references to those locations to see if they have been deallocated.

If you don't have one already, you'll need to add a declare fopr CopyMemory too :)

Public Function DumpVariableMemory(ByVal lngVariablePointer&, _
                               ByVal lngBufferSizeInBytes&) As String
'' * Object Name:   DumpVariableMemory
'' * Type:          Function
'' * Purpose:       Returns a memory dump of the variable or pointer location
'' * Created:       21/08/2006 - 17:41:32
'' * Coder:         Richard Pashley - NUPUK00008148
'' * Build Machine: W-XPRP-77
'' * Encapsulation: Full
'' * Parameters:    lngVariablePointer      -   Long    -   Pointer to the data to dump
'' *                lngBufferSizeInBytes    -   Long    -   Size of the dump to ouput in bytes
'' * Returns:       -                       -   String  -   Memory dump output as a string
'' *                This will dump the memory location starting at the pointer address and
'' *                ending at the address plus the offset (lngBufferSizeInBytes).
'' *                You can use LenB to determine the size of the variable for the
'' *                lngBufferSizeInBytes parameter if required.
'' *                Example: DebugPrint DumpVariableMemory(VarPtr(lngMyLongValue),LenB(lngMyLongValue)
'' * Modified By:   [Name]
'' * Date:          [Date]
'' * Reason:        [NUPUKxxxxxxxxx]
'' Declare locals
Dim lngBufferIterator&                  '' Buffer iterator
Dim lngBufferInnerIterator&             '' Buffer loop inner iterator
Dim bytHexDumpArray() As Byte           '' Received output buffer
Dim strDumpBuffer$                      '' Formatted hex dump construction buffer
Dim lngValidatedBufferSize&             '' Validated passed buffer size
'' Turn on error handling
On Error GoTo DumpVariableMemory_Err
'' Resize output buffer
ReDim bytHexDumpArray(0 To lngBufferSizeInBytes - 1) As Byte
'' Retrieve memory contents from supplied pointer
Call CopyMemory(bytHexDumpArray(0), _
   ByVal lngVariablePointer, _
   lngBufferSizeInBytes)
'' Format dump header
strDumpBuffer = String(81, "=") & vbCrLf & _
   "Pointer Address = &h" & Hex$(lngVariablePointer) & _
   "   Ouput Buffer Size = " & FormatBytes(lngBufferSizeInBytes)
'' Add header seperator
strDumpBuffer = strDumpBuffer & _
   vbCrLf & String(81, Chr$(61))
'' Validate buffer dimensions
If lngBufferSizeInBytes Mod 16 = 0 Then
    '' Validated ok so assign
    lngValidatedBufferSize = lngBufferSizeInBytes
Else
    '' Refactor to base 16
    lngValidatedBufferSize = _
       ((lngBufferSizeInBytes \ 16) + 1) * 16
End If
'' Iterate through buffer contents
For lngBufferIterator = 0 To (lngValidatedBufferSize - 1)
    '' Determine if first row
    If (lngBufferIterator Mod 16) = 0 Then
        '' Format dump output row
        strDumpBuffer = strDumpBuffer & vbCrLf & Right$(String(8, Chr$(48)) _
           & Hex$(lngVariablePointer + lngBufferIterator), 8) & Space(2) & _
           Right$(String(4, Chr$(48)) & Hex$(lngBufferIterator), 4) & Space(2)
    End If
    '' Determine required dump buffer padding
    If lngBufferIterator < lngBufferSizeInBytes Then
        '' Pad dump buffer
        strDumpBuffer = strDumpBuffer & Right$(Chr$(48) & _
           Hex(bytHexDumpArray(lngBufferIterator)), 2)
    Else
        '' Pad dump buffer
        strDumpBuffer = strDumpBuffer & Space(2)
    End If
    '' Determine required dump buffer padding
    If (lngBufferIterator Mod 16) = 15 Then
        '' Pad dump buffer
        strDumpBuffer = strDumpBuffer & Space(2)
        '' Iterate through buffer row
        For lngBufferInnerIterator = (lngBufferIterator - 15) To lngBufferIterator
            '' Validate row width
            If lngBufferInnerIterator < lngBufferSizeInBytes Then
                '' Validate buffer constraints
                If bytHexDumpArray(lngBufferInnerIterator) >= 32 And _
                   bytHexDumpArray(lngBufferInnerIterator) <= 126 Then
                    '' Ouput data to dump buffer row
                    strDumpBuffer = strDumpBuffer & _
                       Chr$(bytHexDumpArray(lngBufferInnerIterator))
                Else
                    '' Pad dump buffer
                    strDumpBuffer = strDumpBuffer & Chr$(45)
                End If
            End If
        Next
        '' Determine required dump buffer padding
    ElseIf (lngBufferIterator Mod 8) = 7 Then
        '' Pad dump buffer
        strDumpBuffer = strDumpBuffer & Chr$(45)
    Else
        '' Pad dump buffer
        strDumpBuffer = strDumpBuffer & Space(1)
    End If
Next
'' Assign result to function output
DumpVariableMemory = strDumpBuffer & _
   vbCrLf & String(81, Chr$(61)) & vbCrLf

Exit_Point: Exit Function '' Error Handling DumpVariableMemory_Err: LogError "modNYFixLibrary.DumpVariableMemory", Err.Number, Err.Description DumpVariableMemory = String(81, Chr$(61)) & vbCrLf & _ "DumpFailed!" & vbCrLf & String(81, Chr$(61)) GoTo Exit_Point Resume End Function

MaSuGaNa
A: 

Memory Validator can tell you where memory is allocated (and leaked) in VB6 programs (and C++, C, Delphi, Fortran 95...).

Stephen Kellett