Performance Meter and Memory Leaks Detector

Environment: [eg VC6 SP4, NT4 SP3, winCE 2.0]-->

This article describes how to monitor program heap for memory leaks even if you load DLL's or use a CRT-functions for memory allocating. The code provided with this article works on Windows NT. It should work on Windows2K system as well, although it has not been tested on it. It has been written with MS Visual C/C++ 6.0

Usage

While working in QA team I got a task "Measure product-components performance, i.e. CPU/Memory usage during their work and detect possible memory leaks". Detecting of memory leaks isn't so simple procedure. At first because there is no any definite technique for making it. The first idea was viewing the process memory space at the page level. But this method is rough enough (I guess), apart Windows doesn't always free allocated blocks immediately. So I decided to calculate PROCESS_HEAP_ENTRY_BUSY nodes in program heap before using all components (loading dll's) and after it.

About CPU usage - the first idea wasn't so bright again :) I've tried to measure CPU usage by Windows NT Pdh-functions, but necessity of loading "pdh.lib" call in question clearness of experiment. So I used NtQuerySystemInformation technique (unfortunately I don't know the author's name, because I get sources through third person).

Well now, to detect memory leaks in the components of your application and to get system resources info during work, you can define a monitoring thread function like this:

#define PERFORMANCE_FILENAME "DocProcPerf.log"

typedef struct EVENTS
{
  HANDLE StartEvent;
  HANDLE StopEvent;
};

DWORD WINAPI UserThreadProc(LPVOID lpParameter)
{
  EVENTS* hWait = (EVENTS *)lpParameter;
  DWORD  dwStartTime = GetTickCount();
  
  CCompInfo* hInfo = new CCompInfo(PERFORMANCE_FILENAME);

  hInfo->HeapMakeSnapShot();
  hInfo->HeapStoreDumpToFile();

  SetEvent((HANDLE)hWait->StartEvent);
	
  hInfo->m_log->print( "DocProcTest started at %s\n", 
                       hInfo->GetTimeString() );

  while (1)
  {
    if(WaitForSingleObject((HANDLE)hWait->StopEvent,0) == WAIT_OBJECT_0)
      break;
    hInfo->m_log->print( "%s CPU[%d%%] Memory[%dKb]\n", 
                         hInfo->GetTimeString(), 
                         hInfo->GetCPUInfo(),
                         hInfo->HeapCommitedBytes()/1024);
    Sleep(1000);
  };

  hInfo->m_log->print( "%s CPU[%d%%] Memory[%dKb]\n",
                       hInfo->GetTimeString(), 
                       hInfo->GetCPUInfo(),
                       hInfo->HeapCommitedBytes()/1024);

  hInfo->m_log->print( "DocProcTest finished at %s\n",
                       hInfo->GetTimeString() );
  hInfo->m_log->print( "Elapsed time %d sec\n",
                       (GetTickCount() - dwStartTime)/1000 );
  hInfo->m_log->print( "Total memory difference: %dKb\n\n",
                       hInfo->HeapCompareSnapShots()/1024 );
 
  CloseHandle((HANDLE)hWait->StopEvent);
  CloseHandle((HANDLE)hWait->StartEvent);

  if (NULL != hWait)
    delete hWait;

  hInfo->HeapCompareDumpWithFile(FALSE); // basic report
  hInfo->HeapCompareDumpWithFile(TRUE);  // extended report

  if (NULL != hInfo)
    delete hInfo;

  return 0;
}

Thus we just have to add our keep-an-eye-thread initialization lines in the "main" application function like this:

printf(  "\nTest started.\n");
  EVENTS *hEvent = new EVENTS;
  hEvent->StartEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  hEvent->StopEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	
  HANDLE hTread = CreateThread( NULL,
                                NULL,
                                UserThreadProc,
                                hEvent,
                                NULL,
                                NULL); 
  WaitForSingleObject((HANDLE)hEvent->StartEvent,15000);
  ...
  program body goes here
  ...
  if (NULL != hEvent)
    SetEvent((HANDLE)hEvent->StopEvent);
  while (WaitForSingleObject(hTread,1000) != WAIT_OBJECT_0)
    Sleep(1000);
  printf(  "\nTest finished.\n");

In conclusion I want to add that this method is under research now so feel free to ask me for new stuff or modifications of it. I'll be grateful for your comments and suggestions.

Downloads

Download source - 7 Kb


Comments

  • designed to detect 1KB or more leaks

    Posted by Legacy on 01/12/2004 12:00am

    Originally posted by: Matthew Read

    If you look at the source, you see Kb and /1024 in many places, that means, any value less than 1024 will not be displayed.

    If you want to monitor more accurately, simple change "Kb" to "Bytes" and replace "/1024" with nothing

    magic, huh

    Reply
  • it doesn't detect the memory leaks if I load dll with leak

    Posted by Legacy on 08/21/2002 12:00am

    Originally posted by: rob

    Hi,
    Nice code, I just started experimented with it but
    it doesn't detect the memory leaks if I load dll with leak.
    eg: method called from dll:
    // exported function.
    WINDLL_API int fnWindll(void)
    {
    char* pArray;
    pArray = new char[1024];
    return 79;
    //delete [] pArray;
    }

    any suggestion why? I also working on it, because it would be nice tool for testing dll for leaks.

    Reply
  • Ending the thread

    Posted by Legacy on 12/06/2001 12:00am

    Originally posted by: P. Kramer

    The following loop code
    
    

    while (1)
    {
    if(WaitForSingleObject((HANDLE)hWait->StopEvent,0) == WAIT_OBJECT_0)
    break;
    hInfo->m_log->print( "%s CPU[%d%%] Memory[%dKb]\n",
    hInfo->GetTimeString(),
    hInfo->GetCPUInfo(),
    hInfo->HeapCommitedBytes()/1024);
    Sleep(1000);
    };

    Can be replaced with (and in my opinion should be)

    do
    {
    hInfo->m_log->print( "%s CPU[%d%%] Memory[%dKb]\n",
    hInfo->GetTimeString(),
    hInfo->GetCPUInfo(),
    hInfo->HeapCommitedBytes()/1024);

    retval = ::WaitForSingleObject(
    (HANDLE)hWait->StopEvent,
    1000
    );

    } while( retval == WAIT_TIMEOUT );


    This makes the has the thread died within 1000ms
    check at the end of your program unnecessary as
    well.

    Reply
  • <981 bytes goes undetected

    Posted by Legacy on 12/05/2001 12:00am

    Originally posted by: JD

    For some reason, when I change the number of bytes
    being allocated from 1024 to any number less than
    981, it does not detect the memory leak.

    pArray = new char[980]; // 981 or less will not be detected.

    Got any idea why that is?

    Otherwise, thanks for the code. It is useful information.


    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds