I am trying to identify memory leak in a process(C++ Application) I am using Windbg tool to identify the memory leak.
0:000> !heap -stat -h 02e10000
heap @ 02e10000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
40 95857 - 25615c0 (60.70)
953130 1 - 953130 (15.14)
2040 16a - 2d9a80 (4.63)
0:000> !heap -flt s 40 -> filtered by size 40, as this is consuming 60%.
Below are the 2 most repeated call stacks I am seeing with !heap -p -a <usrptr>, with busy.
0:000> !heap -p -a 0ad77748
address 0ad77748 found in
_HEAP @ 2e10000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0ad77730 000b 0000 [00] 0ad77748 00040 - (busy)
76f9d483 ntdll!RtlpCallInterceptRoutine+0x00000026
76f56ad7 ntdll!RtlAllocateHeap+0x00045e87
74a5b356 combase!CRetailMalloc_Alloc+0x00000016
748d5174 oleaut32!APP_DATA::AllocCachedMem+0x0000005f
748d52cb oleaut32!SysAllocStringLen+0x0000005f
739ebe9c vbscript!rtReplace+0x0000005b
739ebe47 vbscript!VbsReplace+0x0000013a
739d54c7 vbscript!StaticEntryPoint::Call+0x0000002f
739d388f vbscript!CScriptRuntime::RunNoEH+0x0000267f
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739d388f vbscript!CScriptRuntime::RunNoEH+0x0000267f
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739d388f vbscript!CScriptRuntime::RunNoEH+0x0000267f
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739d3634 vbscript!CScriptRuntime::RunNoEH+0x000023d6
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739d3634 vbscript!CScriptRuntime::RunNoEH+0x000023d6
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739d3634 vbscript!CScriptRuntime::RunNoEH+0x000023d6
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739d3634 vbscript!CScriptRuntime::RunNoEH+0x000023d6
739d5dce vbscript!CScriptRuntime::Run+0x000000c3
739d5ceb vbscript!CScriptEntryPoint::Call+0x0000010b
739fd7cb vbscript!CSession::Execute+0x0000013d
739f76b2 vbscript!NameTbl::InvokeEx+0x00000794
73a22191 vbscript!LocalsNameTbl::Invoke+0x00000061
0:000> !heap -p -a 0ad76eb0
address 0ad76eb0 found in
_HEAP @ 2e10000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0ad76e98 000b 0000 [00] 0ad76eb0 00040 - (busy)
unknown!printable
76f9d483 ntdll!RtlpCallInterceptRoutine+0x00000026
76f56ad7 ntdll!RtlAllocateHeap+0x00045e87
6eb6fe25 webio!WapCreateSession+0x00000025
6eb702de webio!WapCreateSessionHandle+0x0000008d
6eb70215 webio!WebCreateSession+0x00000025
72870e35 winhttp!WinHttpSendRequest+0x000005d5
8fc1245 qNQCiscoCM!NQAxlConnection::sendRequest+0x00000255
8fc150e qNQCiscoCM!NQAxlConnection::processRequest+0x0000004e
8fc6982 qNQCiscoCM!NQAxlCommand::execute+0x00000062
8fb6e2c qNQCiscoCM!NQAxlUtil::getSessionHandle+0x000000ac
8fe3227 qNQCiscoCM!NQAxlSessionMgr::createNewSessionObj+0x00000077
8fe1236 qNQCiscoCM!AXLDispatchInterface::Invoke+0x00000396
8fd941d qNQCiscoCM!AXLCombinedDataPoint::Collect+0x0000012d
8fb6959 qNQCiscoCM!DataRecorder::CollectDataPoints+0x00000209
8ff16a1 qNQCiscoCM!DCCollector::CollectNow+0x00000051
8fe8a27 qNQCiscoCM!DCCollectorList::CollectNow+0x00000087
8fe4ebc qNQCiscoCM!DCController::CollectNow+0x000000ac
390031 SQLite3!sqlite3_vtab_config+0x00018d41
With the first call stack I don't have any idea to proceed further. Can someone explain, what is oleaut32!APP_DATA::AllocCachedMem, and why I am seeing this call stack multiple times.
With the second call stack, I looked NQAxlConnection::sendRequest function, which had one allocation, and proper de-allocation as well. Not sure, why I am seeing this call stack as well. Below is the code for sendRequest
DWORD
NQAxlConnection::sendRequest()
{
USES_CONVERSION;
CW2A soapMsgStrConverted(m_soapMsgStr.c_str());
std::string axlRequest = soapMsgStrConverted.m_psz;
if (axlRequest.length() <= 0 ) {
throw std::runtime_error("Empty request");
}
unsigned char *buffer = NULL;
// figure out request length and allocate a buffer
int requestLen = (int)axlRequest.length();
buffer = new unsigned char[requestLen + 1];
if (buffer == NULL) {
// memory error
throw std::runtime_error("Out of memory");
}
else {
memset(buffer, '\0', requestLen+1);
}
strncpy((char*)buffer, axlRequest.c_str(), requestLen);
buffer[requestLen] = '\0';
// Improve AXL throttling coverage and prevent erroneous events
// implement retry looping to better handle AXL throttle errors
int retryCount = 0;
const static int MAXRETRYCOUNT = 2;
bool applyAXLthrottle = false;
do {
try {
// set the security options based on the SSL flag
this->setSecurityOptions();
DWORD dwBytesWritten = (DWORD)m_HTTPHeader.length() + (DWORD)requestLen;
// Send the request
if (this->isSimCallManager()) {
this->LogPrintf(L"Sent following request to %s:%d \n %s \n %s \n",
this->getServerAddress().c_str(),
this->getHTTPPort(),
this->getHTTPHeader().c_str(), // contains base64 uid&pwd show dont log it
this->getSoapMessage().c_str());
}
else {
this->LogPrintf(L"Sent following request to %s:%d \n\n%s \n",
this->getServerAddress().c_str(),
this->getHTTPPort(),
//this->getHTTPHeader().c_str(), // contains base64 uid&pwd show dont log it
this->getSoapMessage().c_str());
}
BOOL bReturn = WinHttpSendRequest(m_hRequest, // HINTERNET hRequest,
m_HTTPHeader.c_str(), // LPCWSTR pwszHeaders,
(DWORD)m_HTTPHeader.length(), // DWORD dwHeadersLength,
(LPVOID)buffer, // LPVOID lpOptional,
(DWORD)axlRequest.length(), // DWORD dwOptionalLength,
dwBytesWritten, // DWORD dwTotalLength,
0); // DWORD_PTR dwContext
if (!bReturn) {
DWORD dwErr = GetLastError();
this->LogPrintf(L"Send request to %s failed - last error: %d",
this->getServerAddress().c_str(), dwErr);
throw dwErr;
}
// receive the response ...
bReturn = WinHttpReceiveResponse(m_hRequest, // HINTERNET hRequest,
NULL); // reserved
if (!bReturn) {
DWORD dwErr = GetLastError();
this->LogPrintf(L"Receive response from %s failed - last error: %d",
this->getServerAddress().c_str(), dwErr);
throw dwErr;
}
// retrieve the status oode from the reponse
this->retrieveStatusCode();
}
catch (...) {
delete [] buffer;
buffer = NULL;
throw;
}
this->LogPrintf(L"NQAxlConnection::sendRequest Response Status Code: %i", this->getStatusCode());
applyAXLthrottle = false;
if (this->getStatusCode() == 500) {
DWORD statusCode = this->retrieveStatusCode();
// we need to check fault code!
// note - status >= 500 is returned if the request is invalid and
// sometimes will have fault code in the response
// so look for a fault code and also return it
std::wstring response;
this->getResponse(response);
std::wstring faultCode;
std::wstring faultString;
std::wstring detailCode;
if ( this->responseHasFault(response, faultCode, faultString, detailCode)) {
this->LogPrintf(L"NQAxlConnection::sendRequest fault code of %s", faultCode.c_str());
if (faultCode.find(L"RateControl") != std::wstring::npos) {
applyAXLthrottle = true;
this->LogPrintf(L"NQAxlConnection::sendRequest AXL throttle; waiting 60 seconds");
retryCount++;
this->LogPrintf(L"NQAxlConnection::sendRequest retryCount: %i of %i", retryCount, MAXRETRYCOUNT);
Sleep(61100);
this->LogPrintf(L"NQAxlConnection::sendRequest AXL throttle; wait complete");
}
}
}
} while (applyAXLthrottle && (retryCount < MAXRETRYCOUNT));
// free up the buffer
delete [] buffer;
buffer = NULL;
return this->getStatusCode();
}
Can someone explain if there is any leak with these 2 call stacks, which I need to take care.
User contributions licensed under CC BY-SA 3.0