You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3058 lines
103 KiB
3058 lines
103 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// File: aqdbgext.cpp
|
|
//
|
|
// Description: Advanced Queuing Debug Extensions.
|
|
//
|
|
// Author: mikeswa
|
|
//
|
|
// Copyright (C) 1998 Microsoft Corporation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define _ANSI_UNICODE_STRINGS_DEFINED_
|
|
#include "aqincs.h"
|
|
#ifdef PLATINUM
|
|
#include "phatqdbg.h"
|
|
#include <ptrwinst.h>
|
|
#include <ptntintf.h>
|
|
#else
|
|
#include "aqdbgext.h"
|
|
#include <rwinst.h>
|
|
#endif //PLATINUM
|
|
#include <aqinst.h>
|
|
#include <domhash.h>
|
|
#include <destmsgq.h>
|
|
#include <linkmsgq.h>
|
|
#include <hashentr.h>
|
|
#include <fifoqdbg.h>
|
|
#include <dsnevent.h>
|
|
|
|
extern DWORD g_cbClasses;
|
|
extern DWORD g_dwFlavorSignature;
|
|
|
|
BOOL g_fVersionChecked = FALSE;
|
|
|
|
#define AQ_MIN(x, y) ((x) > (y) ? (y) : (x))
|
|
HANDLE g_hTransHeap; //Needed for to link because of transmem.h
|
|
|
|
const DWORD MAX_DOM_PATH_SIZE = 512;
|
|
|
|
const CHAR _LINK_STATE_UP[] = "UP ";
|
|
const CHAR _LINK_STATE_DOWN[] = "DOWN ";
|
|
const CHAR _LINK_STATE_ACTIVE[] = "ACTIVE ";
|
|
const CHAR _LINK_STATE_TURN[] = "TURN ";
|
|
const CHAR _LINK_STATE_RETRY[] = "RETRY ";
|
|
const CHAR _LINK_STATE_DSN[] = "DSN ";
|
|
const CHAR _LINK_STATE_SPECIAL[] = "SPECIAL ";
|
|
|
|
#define LINK_STATE_UP (LPSTR) _LINK_STATE_UP
|
|
#define LINK_STATE_DOWN (LPSTR) _LINK_STATE_DOWN
|
|
#define LINK_STATE_ACTIVE (LPSTR) _LINK_STATE_ACTIVE
|
|
#define LINK_STATE_TURN (LPSTR) _LINK_STATE_TURN
|
|
#define LINK_STATE_RETRY (LPSTR) _LINK_STATE_RETRY
|
|
#define LINK_STATE_DSN (LPSTR) _LINK_STATE_DSN
|
|
#define LINK_STATE_SPECIAL (LPSTR) _LINK_STATE_SPECIAL
|
|
|
|
//lower case function names
|
|
AQ_DEBUG_EXTENSION_IMP(dumpservers) {DumpServers(DebugArgs);}
|
|
AQ_DEBUG_EXTENSION_IMP(offsets) {Offsets(DebugArgs);}
|
|
AQ_DEBUG_EXTENSION_IMP(dumpdnt) {DumpDNT(DebugArgs);}
|
|
|
|
AQ_DEBUG_EXTENSION_IMP(Offsets)
|
|
{
|
|
dprintf("CDestMsgQueue m_liDomainEntryDMQs - 0x%X\n", FIELD_OFFSET(CDestMsgQueue, m_liDomainEntryDMQs));
|
|
dprintf("CDestMsgQueue m_liEmptyDMQs - 0x%X\n", FIELD_OFFSET(CDestMsgQueue, m_liEmptyDMQs));
|
|
dprintf("CLinkMsgQueue m_liLinks - 0x%X\n", FIELD_OFFSET(CLinkMsgQueue, m_liLinks));
|
|
dprintf("CLinkMsgQueue m_liConnections - 0x%X\n", FIELD_OFFSET(CLinkMsgQueue, m_liConnections));
|
|
dprintf("CAQSvrInst m_liVirtualServers - 0x%X\n", FIELD_OFFSET(CAQSvrInst, m_liVirtualServers));
|
|
dprintf("CRETRY_HASH_ENTRY m_QLEntry - 0x%X\n", FIELD_OFFSET(CRETRY_HASH_ENTRY, m_QLEntry));
|
|
dprintf("CRETRY_HASH_ENTRY m_HLEntry - 0x%X\n", FIELD_OFFSET(CRETRY_HASH_ENTRY, m_HLEntry));
|
|
dprintf("CShareLockInst m_liLocks - 0x%X\n", FIELD_OFFSET(CShareLockInst, m_liLocks));
|
|
}
|
|
|
|
|
|
|
|
AQ_DEBUG_EXTENSION_IMP(dumpoffsets)
|
|
{
|
|
_dumpoffsets(hCurrentProcess, hCurrentThread,
|
|
dwCurrentPc, pExtensionApis, szArg);
|
|
}
|
|
|
|
//---[ cpoolusage ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps the CPool usage for our known CPools.
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 5/31/2000 - Mikeswa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(cpoolusage)
|
|
{
|
|
CHAR rgKnownCPools[][200] = {
|
|
// "pttrace!g_pFreePool",
|
|
"exstrace!g_pFreePool",
|
|
// "phatcat!CPoolBuffer__sm_PoolNHeapBuffersPool",
|
|
"aqueue!CQuickList__s_QuickListPool",
|
|
"aqueue!CSMTPConn__s_SMTPConnPool",
|
|
"aqueue!CMsgRef__s_MsgRefPool",
|
|
"aqueue!CAQMsgGuidListEntry__s_MsgGuidListEntryPool",
|
|
"aqueue!CAsyncWorkQueueItem__s_CAsyncWorkQueueItemPool",
|
|
"aqueue!CRETRY_HASH_ENTRY__PoolForHashEntries",
|
|
// "drviis!CIMsgWrapper__m_CIMsgWrapperPool",
|
|
// "drviis!CQueueItem__m_CQueueItemPool",
|
|
"mailmsg!CBlockMemoryAccess__m_Pool",
|
|
"mailmsg!CMsg__m_Pool",
|
|
"mailmsg!CMailMsgRecipientsAdd__m_Pool",
|
|
"smtpsvc!SMTP_CONNECTION__Pool",
|
|
"smtpsvc!SMTP_CONNOUT__Pool",
|
|
"smtpsvc!CAddr__Pool",
|
|
"smtpsvc!CAsyncMx__Pool",
|
|
"smtpsvc!CAsyncSmtpDns__Pool",
|
|
"smtpsvc!CBuffer__Pool",
|
|
"smtpsvc!CIoBuffer__Pool",
|
|
"smtpsvc!CBlockMemoryAccess__m_Pool",
|
|
"smtpsvc!CDropDir__m_Pool",
|
|
""
|
|
};
|
|
|
|
DWORD rgdwPool[5];
|
|
DWORD cTotalBytes = 0;
|
|
DWORD cCurrentBytes = 0;
|
|
DWORD cInstanceBytes = 0;
|
|
DWORD cInstances = 0;
|
|
DWORD dwSignature = 0;
|
|
CHAR *pch = NULL;
|
|
DWORD i = 0;
|
|
PVOID pvPool = NULL;
|
|
|
|
//
|
|
// Loop over all known pools and display data
|
|
//
|
|
dprintf("Total Bytes\t# Instances \tInstance Size \tSignature\tName\n");
|
|
dprintf("=================================================================\n");
|
|
while (rgKnownCPools[i] && rgKnownCPools[i][0]) {
|
|
pvPool = (PVOID) GetExpression(rgKnownCPools[i]);
|
|
|
|
if (!pvPool ||
|
|
!ReadMemory(pvPool, rgdwPool, sizeof(rgdwPool), NULL)) {
|
|
dprintf("Unable to read pool %s at %p\n", rgKnownCPools[i], pvPool);
|
|
} else {
|
|
cInstances = rgdwPool[3];
|
|
cInstanceBytes = rgdwPool[2];
|
|
dwSignature = rgdwPool[0];
|
|
pch = (CHAR *) &dwSignature;
|
|
dprintf("%d\t\t%d\t\t%d\t\t0x%08X\t%s\n",
|
|
cInstanceBytes*cInstances, cInstances,
|
|
cInstanceBytes, rgdwPool[0], rgKnownCPools[i]);
|
|
cTotalBytes += cInstanceBytes*cInstances;
|
|
}
|
|
i++;
|
|
}
|
|
dprintf("=================================================================\n");
|
|
dprintf("\tTotal Bytes = %d\n\n", cTotalBytes);
|
|
|
|
}
|
|
|
|
//---[ remotecmd ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// start a remote cmd window
|
|
// Parameters:
|
|
// name of the pipe
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 5/31/2000 - AWetmore Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(remotecmd)
|
|
{
|
|
char szParameters[1024];
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si;
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
goto Usage;
|
|
|
|
_snprintf(szParameters, 1024, "remote /s cmd %s", szArg);
|
|
dprintf("\nRunning %s\n", szParameters);
|
|
|
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
|
|
|
if (!CreateProcess(NULL,
|
|
szParameters,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi))
|
|
{
|
|
dprintf("CreateProcess failed with %u\n", GetLastError());
|
|
} else {
|
|
dprintf("Started process %i\n", pi.dwProcessId);
|
|
}
|
|
|
|
|
|
Exit:
|
|
dprintf("\n");
|
|
return;
|
|
|
|
Usage:
|
|
//
|
|
// Display usage message
|
|
//
|
|
dprintf("\nUsage:\n");
|
|
dprintf("\tremotecmd <pipename>\n");
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//---[ findbytes ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Searches for a given byte-pattern in a memory address sapce
|
|
// Parameters:
|
|
// Pattern of bytes to search for. Expected format is a sequence of
|
|
// space separated hex digits.
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 5/9/2000 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(findbytes)
|
|
{
|
|
#ifdef WIN64
|
|
const DWORD_PTR cbVMSize = 0xFFFFFFFFFFFFFFFF;
|
|
#else //not WIN64
|
|
const DWORD_PTR cbVMSize = 0xFFFFFFFF;
|
|
#endif //WIN64
|
|
BYTE rgbBytesToFind[200];
|
|
LONG lCurrentValue = 0;
|
|
CHAR rgchCurrentValue[3] = "00";
|
|
LPSTR szStop = NULL;
|
|
DWORD_PTR cBytesToFind = 0;
|
|
DWORD_PTR cChunksChecked = 0;
|
|
DWORD_PTR cChunkSize = 0;
|
|
DWORD_PTR iChunk = 0;
|
|
BYTE pbChunk[0x1000];
|
|
PBYTE pbStopAddr = pbChunk + sizeof(pbChunk);
|
|
PBYTE pbCurrent = NULL;
|
|
DWORD_PTR cChunks = cbVMSize/sizeof(pbChunk);
|
|
DWORD_PTR cChunksInPercent = 1;
|
|
DWORD_PTR pvEffectiveAddressOtherProc = NULL;
|
|
DWORD cComplaints = 0;
|
|
DWORD cMemchkMatches = 0;
|
|
DWORD cFullSigMatches = 0;
|
|
LPCSTR szCurrentArg = szArg;
|
|
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
goto Usage;
|
|
|
|
//
|
|
// Parse command line args
|
|
//
|
|
while (*szCurrentArg)
|
|
{
|
|
//
|
|
// Loop over whitespace
|
|
//
|
|
while (*szCurrentArg && isspace(*szCurrentArg)) szCurrentArg++;
|
|
|
|
//
|
|
// Make sure we have at least pair of characters as expected
|
|
//
|
|
if (!*(szCurrentArg+1))
|
|
break;
|
|
|
|
//
|
|
// Convert from hex characters to binary
|
|
//
|
|
lCurrentValue = strtol(szCurrentArg, &szStop, 16);
|
|
if ((lCurrentValue > 0xFF) || (lCurrentValue < 0))
|
|
goto Usage;
|
|
|
|
//
|
|
// Copy to our search buffer
|
|
//
|
|
rgbBytesToFind[cBytesToFind] = (BYTE) lCurrentValue;
|
|
cBytesToFind++;
|
|
|
|
//
|
|
// Make sure our search buffer is big enough for the next byte
|
|
//
|
|
if (cBytesToFind >= sizeof(rgbBytesToFind))
|
|
{
|
|
dprintf("Search for max pattern of %d bytes\n", cBytesToFind);
|
|
break;
|
|
}
|
|
|
|
szCurrentArg += 2; //Skip to next known whitespace
|
|
}
|
|
|
|
if (!cBytesToFind)
|
|
{
|
|
dprintf("\nYou must specify at least one byte to search for\n");
|
|
goto Usage;
|
|
}
|
|
|
|
//
|
|
// Used to display progress
|
|
//
|
|
cChunksInPercent = cChunks/100;
|
|
|
|
//
|
|
// Calculate memory size for 32-bit machines
|
|
//
|
|
cChunkSize = cbVMSize/cChunks;
|
|
|
|
if (cChunkSize < 1024)
|
|
{
|
|
dprintf("ERROR: Chunk size of 0x%p is too small", cChunkSize);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Make sure we are cool wrt to buffer size
|
|
//
|
|
if (cChunkSize > sizeof(pbChunk))
|
|
{
|
|
dprintf("ERROR: Chunksize of 0x%p is larger than max size of 0x%p",
|
|
cChunkSize, sizeof(pbChunk));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Loop over chunks --
|
|
// $$REVIEW - does not find patterns that span 1K chunks...
|
|
// this is probably OK, since this is an unlikely scenario. Most
|
|
// byte patterns will be DWORD (signatures) or pointer sized.
|
|
//
|
|
for (iChunk = 0; iChunk < cChunks; iChunk++)
|
|
{
|
|
|
|
//
|
|
// Check to see if the user pressed ctrl-c
|
|
//
|
|
if (CheckControlC())
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Give some status
|
|
//
|
|
if ((iChunk % cChunksInPercent) == 0)
|
|
dprintf(".");
|
|
|
|
//
|
|
// Address should be page aligned
|
|
//
|
|
if (((iChunk*cChunkSize) & 0xFFF) && (cComplaints < 100))
|
|
{
|
|
cComplaints++;
|
|
dprintf("0x%p not alligned at index %d", (iChunk*cChunkSize), iChunk);
|
|
}
|
|
|
|
//
|
|
// Do a memory search for the first byte
|
|
//
|
|
if (!ReadMemory(iChunk*cChunkSize, pbChunk, (DWORD)cChunkSize, NULL))
|
|
continue; //on to the next buffer chunk
|
|
|
|
//
|
|
// Now that we have a chunk... look for our sig
|
|
//
|
|
pbCurrent = pbChunk;
|
|
while (pbCurrent < pbStopAddr-cBytesToFind)
|
|
{
|
|
pbCurrent = (PBYTE) memchr(pbCurrent,
|
|
rgbBytesToFind[0],
|
|
pbStopAddr-pbCurrent);
|
|
|
|
//
|
|
// See if we have a match
|
|
if (!pbCurrent)
|
|
break;
|
|
|
|
cMemchkMatches++;
|
|
|
|
pvEffectiveAddressOtherProc = iChunk*cChunkSize+(pbCurrent-pbChunk);
|
|
|
|
//
|
|
// See if the full pattern matches
|
|
//
|
|
if (!memcmp(rgbBytesToFind, pbCurrent, cBytesToFind))
|
|
{
|
|
cFullSigMatches++;
|
|
dprintf("\nFound match at 0x%p\n", pvEffectiveAddressOtherProc);
|
|
}
|
|
|
|
if (0 != memcmp(rgbBytesToFind, pbCurrent, 1))
|
|
{
|
|
cComplaints++;
|
|
if (cComplaints < 100)
|
|
dprintf("Messed up %02X %02X - %02X %02X\n",
|
|
rgbBytesToFind[0], rgbBytesToFind[1],
|
|
pbCurrent[0], pbCurrent[1]);
|
|
}
|
|
|
|
|
|
pbCurrent++;
|
|
|
|
}
|
|
|
|
cChunksChecked++;
|
|
}
|
|
|
|
//
|
|
// Give some summary information
|
|
//
|
|
dprintf("\nChecked 0x%p chunks (%d%%) searching from 0x%p to 0x%p",
|
|
cChunksChecked,
|
|
(DWORD)(100*cChunksChecked/cChunks), NULL,
|
|
cChunkSize*(cChunks+1)-1);
|
|
dprintf("\nFound %d partial matches and %d full matches",
|
|
cMemchkMatches, cFullSigMatches);
|
|
|
|
Exit:
|
|
dprintf("\n");
|
|
return;
|
|
|
|
Usage:
|
|
//
|
|
// Display usage message
|
|
//
|
|
if (szCurrentArg && *szCurrentArg)
|
|
dprintf("Error at %s\n", szCurrentArg);
|
|
|
|
dprintf("\nUsage:\n");
|
|
dprintf("\tfindbytes <aa> [<bb> ...]\n");
|
|
dprintf("\t\tBytes should be specifed as 2 hexadecimal characters\n");
|
|
dprintf("\nExamples:\n");
|
|
dprintf("\tTo search for the signature \"LMQ \"\n");
|
|
dprintf("\t\tfindbytes %02X %02X %02X %02X\n", 'L', 'M', 'Q', ' ');
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
|
|
//---[ findsig ]---------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Searches for a given class signature in a memory address sapce
|
|
// Parameters:
|
|
// The Siganature to look for.
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 5/3/2000 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(findsig)
|
|
{
|
|
CHAR szNewArg[200];
|
|
LPCSTR szCurrentArg = szArg;
|
|
CHAR szSig[5] = " ";
|
|
DWORD iChar = 0;
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
goto Usage;
|
|
|
|
|
|
//
|
|
// Loop over whitespace
|
|
//
|
|
while (*szCurrentArg && isspace(*szCurrentArg)) szCurrentArg++;
|
|
|
|
//
|
|
// Grab the first 4 characters and convert them to binary
|
|
//
|
|
for( iChar = 0; iChar < 4; iChar++)
|
|
{
|
|
if (!szCurrentArg[iChar])
|
|
break;
|
|
|
|
szSig[iChar] = szCurrentArg[iChar];
|
|
}
|
|
|
|
dprintf("Searching for Signature \"%s\"...\n", szSig);
|
|
|
|
sprintf(szNewArg, "%02X %02X %02X %02X", szSig[0], szSig[1], szSig[2], szSig[3]);
|
|
|
|
//
|
|
// Just use the code in findbytes to do the actual search
|
|
//
|
|
dprintf("Calling findbytes %s\n", szNewArg);
|
|
findbytes(hCurrentProcess, hCurrentThread, dwCurrentPc,
|
|
pExtensionApis, szNewArg);
|
|
|
|
Exit:
|
|
return;
|
|
|
|
Usage:
|
|
dprintf("\nUsage:\n");
|
|
dprintf("\tfindsig <XXXX>\n");
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
//---[ hashthread ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Uses the CThreadIdBlock hashing mechanism to return the hashed value
|
|
// for a thread.
|
|
// Parameters:
|
|
// Thread Id to hash
|
|
// Max hash value
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 8/9/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(hashthread)
|
|
{
|
|
//Arguement should be thread Id
|
|
DWORD dwThreadId = GetCurrentThreadId();
|
|
DWORD dwMax = 1000;
|
|
DWORD dwThreadHash = 0;
|
|
CHAR szArgBuffer[200];
|
|
LPSTR szCurrentArg = NULL;
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
dprintf("Warning... using default thead id and max\n");
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
dwThreadId = (DWORD)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
dwMax = (DWORD) GetExpression(szCurrentArg);
|
|
else
|
|
dprintf("Warning... using default max hash\n");
|
|
}
|
|
}
|
|
|
|
//Try hashing the ID
|
|
dwThreadHash = dwHashThreadId(dwThreadId, dwMax);
|
|
dprintf("Thread Id 0x%0X hashes to index 0x%0X (%d) with max 0x%08X (%d)\n", dwThreadId,
|
|
dwThreadHash, dwThreadHash, dwMax, dwMax);
|
|
}
|
|
|
|
//---[ dumplock ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps all of the information in the CThreadIdBlocks for a given
|
|
// CShareLockInst.
|
|
// Parameters:
|
|
// Address of CShareLockInst
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 8/9/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(dumplock)
|
|
{
|
|
BYTE pbBuffer[sizeof(CShareLockInst)];
|
|
BYTE pbThreadBlocks[1000*sizeof(CThreadIdBlock)];
|
|
PVOID pvLock = NULL;
|
|
PVOID pvNextBlock = NULL;
|
|
CThreadIdBlock tblkCurrent;
|
|
CThreadIdBlock *ptblkCurrent = NULL;
|
|
CThreadIdBlock *ptblkArray = NULL;
|
|
DWORD cNumBlocks = 0;
|
|
DWORD iBlock = 0;
|
|
DWORD cThreads = 0;
|
|
DWORD cLockCount = 0;
|
|
DWORD cLockedThreads = 0;
|
|
BOOL fDisplayedHashHeader = FALSE;
|
|
|
|
ZeroMemory(pbBuffer, sizeof(pbBuffer));
|
|
ZeroMemory(pbThreadBlocks, sizeof(pbThreadBlocks));
|
|
|
|
if (!szArg || ('\0' == szArg[0]) || !(pvLock = (PVOID) GetExpression(szArg)))
|
|
{
|
|
dprintf("You must specify a lock address\n");
|
|
return;
|
|
}
|
|
|
|
//read the whole lock into our buffer
|
|
if (!ReadMemory(pvLock, &pbBuffer, sizeof(pbBuffer), NULL))
|
|
{
|
|
dprintf("Error unable read memory at 0x%0X\n", pvLock);
|
|
return;
|
|
}
|
|
|
|
cNumBlocks = ((CShareLockInst *)pbBuffer)->m_cMaxTrackedSharedThreadIDs;
|
|
pvNextBlock = ((CShareLockInst *)pbBuffer)->m_rgtblkSharedThreadIDs;
|
|
|
|
if (!cNumBlocks || !pvNextBlock)
|
|
{
|
|
dprintf("Thread tracking is not enabled for this lock");
|
|
return;
|
|
}
|
|
|
|
if (cNumBlocks > sizeof(pbThreadBlocks)/sizeof(CThreadIdBlock))
|
|
cNumBlocks = sizeof(pbThreadBlocks)/sizeof(CThreadIdBlock);
|
|
|
|
if (!ReadMemory(pvNextBlock, &pbThreadBlocks,
|
|
cNumBlocks*sizeof(CThreadIdBlock), NULL))
|
|
{
|
|
dprintf("Error, unable to read %d blocks at 0x%0X", cNumBlocks, pvNextBlock);
|
|
return;
|
|
}
|
|
|
|
ptblkArray = (CThreadIdBlock *) pbThreadBlocks;
|
|
for (iBlock = 0; iBlock < cNumBlocks; iBlock++ && ptblkArray++)
|
|
{
|
|
ptblkCurrent = ptblkArray;
|
|
fDisplayedHashHeader = FALSE;
|
|
while (ptblkCurrent)
|
|
{
|
|
if (ptblkCurrent != ptblkArray)
|
|
{
|
|
//Read into this process
|
|
if (!ReadMemory(ptblkCurrent, &tblkCurrent,
|
|
sizeof(CThreadIdBlock), NULL))
|
|
{
|
|
dprintf("Error reading block at 0x%0X", ptblkCurrent);
|
|
break;
|
|
}
|
|
ptblkCurrent = &tblkCurrent;
|
|
}
|
|
|
|
if (THREAD_ID_BLOCK_SIG != ptblkCurrent->m_dwSignature)
|
|
{
|
|
dprintf("Warning... bad signature on block 0x%0X\n",
|
|
((BYTE *)pvNextBlock) + iBlock*sizeof(CThreadIdBlock));
|
|
break;
|
|
}
|
|
|
|
//See if this block has any data
|
|
if (THREAD_ID_BLOCK_UNUSED != ptblkCurrent->m_dwThreadId)
|
|
{
|
|
|
|
//Only dump info if the recursion count is non-zero
|
|
if (ptblkCurrent->m_cThreadRecursionCount)
|
|
{
|
|
if (!fDisplayedHashHeader)
|
|
{
|
|
fDisplayedHashHeader = TRUE;
|
|
dprintf("Thread Hash 0x%0X (%d)\n", iBlock, iBlock);
|
|
}
|
|
dprintf("%s\tThread 0x%08X has count of %d - Next link of 0x%08X\n",
|
|
(ptblkCurrent == ptblkArray) ? "+" : "",
|
|
ptblkCurrent->m_dwThreadId,
|
|
ptblkCurrent->m_cThreadRecursionCount,
|
|
ptblkCurrent->m_ptblkNext);
|
|
|
|
cLockedThreads++;
|
|
}
|
|
|
|
cThreads++;
|
|
cLockCount += ptblkCurrent->m_cThreadRecursionCount;
|
|
}
|
|
ptblkCurrent = ptblkCurrent->m_ptblkNext;
|
|
}
|
|
}
|
|
|
|
dprintf("===================================================================\n");
|
|
dprintf("%d threads with %d total lock count (%d threads holding locks)\n",
|
|
cThreads, cLockCount, cLockedThreads);
|
|
}
|
|
|
|
//---[ workqueue ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps a summary of items in the async work queue
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// History:
|
|
// 9/13/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(workqueue)
|
|
{
|
|
SETCALLBACKS();
|
|
const DWORD MAX_COMPLETION_FUNCTIONS = 10;
|
|
PVOID rgpvFnName[MAX_COMPLETION_FUNCTIONS];
|
|
DWORD rgcFnCount[MAX_COMPLETION_FUNCTIONS];
|
|
BYTE pbWorkItem[sizeof(CAsyncWorkQueueItem)];
|
|
PVOID pvQueue = NULL;
|
|
PVOID pvWorkItem = NULL;
|
|
PVOID pvFn = NULL;
|
|
DWORD i = 0;
|
|
DWORD cItems = 0;
|
|
UCHAR SymbolName[ 200 ];
|
|
ULONG_PTR Displacement;
|
|
CFifoQueueDbgIterator fifoqdbg(pExtensionApis);
|
|
|
|
ZeroMemory(&rgpvFnName, sizeof(rgpvFnName));
|
|
ZeroMemory(&rgcFnCount, sizeof(rgcFnCount));
|
|
ZeroMemory(&pbWorkItem, sizeof(pbWorkItem));
|
|
ZeroMemory(&SymbolName, sizeof(SymbolName));
|
|
|
|
if (!szArg || ('\0' == szArg[0]) ||
|
|
!(pvQueue = (PVOID) GetExpression(szArg)))
|
|
{
|
|
dprintf("You must specify a queue address\n");
|
|
return;
|
|
}
|
|
|
|
|
|
//Get FifoqOffset
|
|
pvQueue = (PVOID) &(((CAsyncWorkQueue *)pvQueue)->m_asyncq.m_fqQueue);
|
|
|
|
if (!fifoqdbg.fInit(hCurrentProcess, pvQueue))
|
|
{
|
|
dprintf("Error initializing queue iterator for address 0x%08X\n", pvQueue);
|
|
return;
|
|
}
|
|
|
|
while (pvWorkItem = fifoqdbg.pvGetNext())
|
|
{
|
|
cItems++;
|
|
if (!ReadMemory(pvWorkItem, &pbWorkItem, sizeof(pbWorkItem), NULL))
|
|
{
|
|
dprintf("Error reading memory at 0x%0X\n", pvWorkItem);
|
|
continue;
|
|
}
|
|
|
|
pvFn = ((CAsyncWorkQueueItem *)pbWorkItem)->m_pfnCompletion;
|
|
|
|
for (i = 0; i < MAX_COMPLETION_FUNCTIONS; i++)
|
|
{
|
|
if (pvFn == rgpvFnName[i])
|
|
{
|
|
rgcFnCount[i]++;
|
|
break;
|
|
}
|
|
else if (!rgpvFnName[i])
|
|
{
|
|
rgpvFnName[i] = pvFn;
|
|
rgcFnCount[i] = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf("# Calls\t| Address\t\t| Function Name\n");
|
|
dprintf("------------------------------------------------------------\n");
|
|
for (i = 0; i < MAX_COMPLETION_FUNCTIONS; i++)
|
|
{
|
|
if (!rgpvFnName[i])
|
|
break;
|
|
|
|
g_lpGetSymbolRoutine( rgpvFnName[i], (PCHAR)SymbolName, &Displacement );
|
|
dprintf( "%d\t| 0x%08X\t| %s\n", rgcFnCount[i], rgpvFnName[i], SymbolName);
|
|
}
|
|
dprintf("------------------------------------------------------------\n");
|
|
dprintf("Total %d pending work queue items\n", cItems);
|
|
|
|
#ifdef NEVER
|
|
//Dump fifoqdbg
|
|
dprintf("CFifoQueueDbgIterator: page %d, index %d, pages %d\n ",
|
|
fifoqdbg.m_iCurrentPage, fifoqdbg.m_iCurrentIndexInPage,
|
|
fifoqdbg.m_cPagesLoaded);
|
|
#endif
|
|
}
|
|
|
|
//---[ dumpqueue ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps the *entire* contents of a queue
|
|
// Parameters:
|
|
// szArg
|
|
// - String-ized address of CFifoQ to dump
|
|
// - [optional] msg to search for
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/21/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION(dumpqueue)
|
|
{
|
|
const DWORD cStoppingRule = 10000;
|
|
CQueueDbgIterator qdbg(pExtensionApis);
|
|
BYTE pbMsgRef[sizeof(CMsgRef)];
|
|
PVOID pvMsgRef = NULL;
|
|
PVOID pvMailMsg = NULL;
|
|
PVOID pvQueue = NULL;
|
|
DWORD cItems = 0;
|
|
BOOL fIsMsgRef = FALSE;
|
|
CHAR szArgBuffer[200];
|
|
LPSTR szCurrentArg = NULL;
|
|
PVOID pvSearch = NULL;
|
|
DWORD cMatchSearch = 0;
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
dprintf("You must specify a queue address\n");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
pvQueue = (PVOID)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
pvSearch = (PVOID) GetExpression(szCurrentArg);
|
|
}
|
|
else
|
|
{
|
|
pvQueue = (PVOID) GetExpression(szArg);
|
|
}
|
|
|
|
}
|
|
|
|
if (!pvQueue)
|
|
{
|
|
dprintf("You must specify a queue address\n");
|
|
return;
|
|
}
|
|
|
|
if (!qdbg.fInit(hCurrentProcess, pvQueue))
|
|
{
|
|
dprintf("Unable to get the a queue for address 0x%X\n", pvQueue);
|
|
return;
|
|
}
|
|
|
|
while ((pvMsgRef = qdbg.pvGetNext()) && (cItems++ < cStoppingRule))
|
|
{
|
|
fIsMsgRef = FALSE;
|
|
if (cItems > qdbg.cGetCount())
|
|
{
|
|
cItems--;
|
|
break;
|
|
}
|
|
|
|
|
|
//Try to read it as a CMsgRef
|
|
if (ReadMemory(pvMsgRef, pbMsgRef, sizeof(pbMsgRef), NULL))
|
|
{
|
|
if (MSGREF_SIG == ((CMsgRef *)pbMsgRef)->m_dwSignature)
|
|
{
|
|
fIsMsgRef = TRUE;
|
|
pvMailMsg = ((CMsgRef *)pbMsgRef)->m_pIMailMsgProperties;
|
|
}
|
|
}
|
|
|
|
//Print it out if it matches our search (or we have no search)
|
|
if (!pvSearch || (pvSearch == pvMsgRef) || (pvSearch == pvMailMsg))
|
|
{
|
|
cMatchSearch++;
|
|
if (pvSearch)
|
|
dprintf("\n****\n");
|
|
|
|
if (fIsMsgRef)
|
|
dprintf("\t0x%08X\t0x%08X\n", pvMsgRef, pvMailMsg);
|
|
else
|
|
dprintf("\t0x%08X\n", pvMsgRef);
|
|
|
|
if (pvSearch)
|
|
dprintf("****\n\n");
|
|
}
|
|
|
|
}
|
|
|
|
if (pvSearch)
|
|
dprintf("Found %d matches to search\n", cMatchSearch);
|
|
}
|
|
|
|
//---[ displaytickcount ]------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Converts a tick count to a readable time
|
|
// Parameters:
|
|
// szArg - String-ized tick count in hex
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/29/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(displaytickcount)
|
|
{
|
|
DWORD dwTickCountToDisplay = (DWORD)GetExpression(szArg);
|
|
DWORD dwCurrentTickCount = GetTickCount();
|
|
DWORD dwTickDifference = dwCurrentTickCount - dwTickCountToDisplay;
|
|
FILETIME ftCurrentUTC;
|
|
FILETIME ftDisplayUTC;
|
|
FILETIME ftDisplayLocal;
|
|
ULARGE_INTEGER uliTimeAdjust;
|
|
SYSTEMTIME stDisplayLocal;
|
|
|
|
static char *s_rgszMonth[ 12 ] =
|
|
{
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
|
};
|
|
|
|
static char *s_rgszWeekDays[7] =
|
|
{
|
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
|
};
|
|
|
|
GetSystemTimeAsFileTime(&ftCurrentUTC);
|
|
|
|
//Adjust the current filetime to local
|
|
memcpy(&uliTimeAdjust, &ftCurrentUTC, sizeof(FILETIME));
|
|
uliTimeAdjust.QuadPart -= (((ULONGLONG)dwTickDifference)*((ULONGLONG)10000));
|
|
memcpy(&ftDisplayUTC, &uliTimeAdjust, sizeof(FILETIME));
|
|
|
|
FileTimeToLocalFileTime(&ftDisplayUTC, &ftDisplayLocal);
|
|
|
|
ZeroMemory(&stDisplayLocal, sizeof(stDisplayLocal));
|
|
FileTimeToSystemTime(&ftDisplayLocal, &stDisplayLocal);
|
|
|
|
dprintf("\n%s, %d %s %04d %02d:%02d:%02d (localized)\n",
|
|
s_rgszWeekDays[stDisplayLocal.wDayOfWeek],
|
|
stDisplayLocal.wDay, s_rgszMonth[ stDisplayLocal.wMonth - 1 ],
|
|
stDisplayLocal.wYear, stDisplayLocal.wHour,
|
|
stDisplayLocal.wMinute, stDisplayLocal.wSecond);
|
|
|
|
}
|
|
|
|
//---[ queueusage ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps the usage count averages for a given fifoq. If we are dumping
|
|
// CMsgRefs, it will dump the pointers to the various MailMsg interfaces
|
|
// as well.
|
|
// Parameters:
|
|
// szArg String-ized address of CFifoQ to dump
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/15/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(queueusage)
|
|
{
|
|
const DWORD cbUsageCountOffset = 0x20;
|
|
const DWORD cbContentHandleOffset = 0x90+cbUsageCountOffset;
|
|
const DWORD cbStreamHandleOffset = 0x8+cbContentHandleOffset;
|
|
const DWORD cStoppingRule = 10000;
|
|
const DWORD cMaxUsageCountToTrack = 6;
|
|
CFifoQueueDbgIterator fifoqdbg(pExtensionApis);
|
|
BYTE pbMsgRef[4*sizeof(CMsgRef)]; //leave room for bitmaps
|
|
BYTE pbMailMsg[cbStreamHandleOffset+sizeof(PVOID)];
|
|
PVOID pvMsgRef = NULL;
|
|
PVOID pvMailMsg = NULL;
|
|
PVOID pvQueue = NULL;
|
|
DWORD cItems = 0;
|
|
DWORD cCurrentUsageCount = 0;
|
|
DWORD cTotalUsageCount = 0;
|
|
DWORD cMaxUsageCount = 0;
|
|
DWORD cMinUsageCount = 200;
|
|
DWORD rgcUsageCounts[cMaxUsageCountToTrack];
|
|
PVOID pvHandle = NULL;
|
|
DWORD cMsgsWithOpenContentHandles = 0;
|
|
DWORD cMsgsWithOpenStreamHandles = 0;
|
|
BOOL fVerbose = FALSE;
|
|
|
|
ZeroMemory(rgcUsageCounts, sizeof(rgcUsageCounts));
|
|
|
|
if (!szArg || ('\0' == szArg[0]) ||
|
|
!(pvQueue = (PVOID) GetExpression(szArg)))
|
|
{
|
|
dprintf("You must specify a queue address\n");
|
|
return;
|
|
}
|
|
|
|
if (!fifoqdbg.fInit(hCurrentProcess, pvQueue))
|
|
{
|
|
dprintf("Unable to get the a queue for address 0x%X\n", pvQueue);
|
|
return;
|
|
}
|
|
|
|
while ((pvMsgRef = fifoqdbg.pvGetNext()) && (cItems++ < cStoppingRule))
|
|
{
|
|
if (cItems > fifoqdbg.cGetCount())
|
|
{
|
|
cItems--;
|
|
break;
|
|
}
|
|
|
|
//Read CMsgRef into this process
|
|
if (!ReadMemory(pvMsgRef, pbMsgRef, sizeof(pbMsgRef), NULL))
|
|
{
|
|
dprintf("Unable to read MsgRef at address 0x%X, index %d\n",
|
|
pvMsgRef, cItems);
|
|
cItems--;
|
|
break;
|
|
}
|
|
|
|
//Get inteface ptr for mailmsg from CMsgRef
|
|
pvMailMsg = ((CMsgRef *)pbMsgRef)->m_pIMailMsgQM;
|
|
|
|
if (!ReadMemory(pvMailMsg, pbMailMsg, sizeof(pbMailMsg), NULL))
|
|
{
|
|
dprintf("Unable to read MailMsg Ptr at address 0x%X for MsgRef 0x%X, index %d\n",
|
|
pvMailMsg, pvMsgRef, cItems);
|
|
cItems--;
|
|
break;
|
|
}
|
|
|
|
//Check and see if this message has a content (P2) handle open
|
|
if (*(pbMailMsg + cbContentHandleOffset))
|
|
cMsgsWithOpenContentHandles++;
|
|
|
|
//Check and see if this message has a stream (P1) handle open
|
|
if (*(pbMailMsg + cbStreamHandleOffset))
|
|
cMsgsWithOpenStreamHandles++;
|
|
|
|
if (fVerbose &&
|
|
((*(pbMailMsg + cbStreamHandleOffset)) ||
|
|
(*(pbMailMsg + cbStreamHandleOffset))))
|
|
{
|
|
dprintf("Message at address 0x%X has open handles\n", pvMsgRef);
|
|
}
|
|
|
|
cCurrentUsageCount = (DWORD) *(pbMailMsg + cbUsageCountOffset);
|
|
cTotalUsageCount += cCurrentUsageCount;
|
|
|
|
if (cCurrentUsageCount > cMaxUsageCount)
|
|
cMaxUsageCount = cCurrentUsageCount;
|
|
|
|
if (cCurrentUsageCount < cMinUsageCount)
|
|
cMinUsageCount = cCurrentUsageCount;
|
|
|
|
if (cCurrentUsageCount >= cMaxUsageCountToTrack)
|
|
{
|
|
dprintf("\n****\n");
|
|
dprintf("High usage count of %d found on MailMsg 0x%X, MsgRef 0x%X, item %d\n",
|
|
cCurrentUsageCount, pvMailMsg, pvMsgRef, cItems);
|
|
dprintf("\n****\n");
|
|
cCurrentUsageCount = cMaxUsageCountToTrack-1;
|
|
}
|
|
|
|
//Save count for summaries
|
|
rgcUsageCounts[cCurrentUsageCount]++;
|
|
}
|
|
|
|
//Generate and display summary information
|
|
if (!cItems)
|
|
{
|
|
dprintf("No Messages found in queue 0x%X\n", pvQueue);
|
|
}
|
|
else
|
|
{
|
|
dprintf("\n==================================================================\n");
|
|
dprintf("Usage Count Summary\n");
|
|
dprintf("------------------------------------------------------------------\n");
|
|
dprintf("\t%d\t\tTotal Message\n", cItems);
|
|
dprintf("\t%d\t\tTotal Messages with open content handles\n", cMsgsWithOpenContentHandles);
|
|
dprintf("\t%d\t\tTotal Messages with open stream handles\n", cMsgsWithOpenStreamHandles);
|
|
dprintf("\t%d\t\tTotal Usage Count\n", cTotalUsageCount);
|
|
dprintf("\t%d\t\tMax Usage Count\n", cMaxUsageCount);
|
|
dprintf("\t%d\t\tMin Usage Count\n", cMinUsageCount);
|
|
dprintf("\t%f\tAverage Usage Count\n", ((float)cTotalUsageCount)/((float)cItems));
|
|
for (DWORD i = 0; i < cMaxUsageCountToTrack-1; i++)
|
|
{
|
|
dprintf("\t%d\t\tMessages with Usage count of %d\n", rgcUsageCounts[i], i);
|
|
}
|
|
dprintf("\t%d\t\tMessages with Usage count of %d or greater\n",
|
|
rgcUsageCounts[cMaxUsageCountToTrack-1], cMaxUsageCountToTrack-1);
|
|
dprintf("==================================================================\n");
|
|
}
|
|
}
|
|
|
|
//---[ dmqusage ]--------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Debugger extension that wraps the queue usage debugger extension
|
|
// to display the usage counts for all queues
|
|
// Parameters:
|
|
// szArg String-ized address of DMQ to dump
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/15/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(dmqusage)
|
|
{
|
|
PVOID pvQueue = NULL;
|
|
PVOID pvDMQ = NULL;
|
|
BYTE pbDMQ[sizeof(CDestMsgQueue)];
|
|
CHAR szQueueAddress[30];
|
|
DWORD iQueue = 0;
|
|
|
|
if (!szArg || ('\0' == szArg[0]) ||
|
|
!(pvDMQ = (PVOID) GetExpression(szArg)))
|
|
{
|
|
dprintf("You must specify a queue address\n");
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pvDMQ, pbDMQ, sizeof(pbDMQ), NULL))
|
|
{
|
|
dprintf("Unable to read DMQ at address 0x%X\n", pvDMQ);
|
|
return;
|
|
}
|
|
|
|
dprintf("\n\n******************************************************************\n");
|
|
dprintf("Start USAGE COUNT STATS for DMQ 0x%0X\n", pvDMQ);
|
|
dprintf("******************************************************************\n");
|
|
|
|
for (iQueue = 0; iQueue < NUM_PRIORITIES; iQueue++)
|
|
{
|
|
pvQueue = ((CDestMsgQueue *)pbDMQ)->m_rgpfqQueues[iQueue];
|
|
if (!pvQueue)
|
|
continue; //nothing as every been queued to this queue
|
|
|
|
//Only display the queue if we think we have messages
|
|
//$$TODO - We could actual read this queue into memory and check it,
|
|
//but since we currently only support 1 priority, this will do.
|
|
if (((CDestMsgQueue *)pbDMQ)->m_aqstats.m_cMsgs ||((CDestMsgQueue *)pbDMQ)->m_aqstats.m_cRetryMsgs)
|
|
{
|
|
wsprintf(szQueueAddress, "0x%X", pvQueue);
|
|
queueusage(hCurrentProcess, hCurrentThread, dwCurrentPc,
|
|
pExtensionApis, szQueueAddress);
|
|
}
|
|
}
|
|
|
|
//Display retry queue, if there are messages there
|
|
if (((CDestMsgQueue *)pbDMQ)->m_fqRetryQueue.m_cQueueEntries)
|
|
{
|
|
pvQueue = ((PBYTE)pvDMQ) + FIELD_OFFSET(CDestMsgQueue, m_fqRetryQueue);
|
|
wsprintf(szQueueAddress, "0x%X", pvQueue);
|
|
queueusage(hCurrentProcess, hCurrentThread, dwCurrentPc,
|
|
pExtensionApis, szQueueAddress);
|
|
}
|
|
|
|
dprintf("\n\n******************************************************************\n");
|
|
dprintf("End USAGE COUNT STATS for DMQ 0x%0X\n", pvDMQ);
|
|
dprintf("******************************************************************\n");
|
|
}
|
|
|
|
//---[ dntusage ]--------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Debugger extension that wrap dmqusage. Call dmqusage for every DMQ
|
|
// in the DNT.
|
|
// Parameters:
|
|
// szArg string-ize address of dnt (DOMAIN_NAME_TABLE)
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/15/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(dntusage)
|
|
{
|
|
BYTE pbBuffer[sizeof(DOMAIN_NAME_TABLE)];
|
|
PDOMAIN_NAME_TABLE pdnt = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
|
|
BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
|
|
CHAR pBuffer[MAX_DOM_PATH_SIZE] = "Root Entry";
|
|
LPSTR pEntryBuffer = NULL;
|
|
LPSTR pEntryBufferStop = NULL;
|
|
DWORD dwLength = 0;
|
|
DWORD dwSig = 0;
|
|
CHAR szFinalDest[MAX_DOM_PATH_SIZE];
|
|
BYTE pbDomainEntry[sizeof(CDomainEntry)];
|
|
CDomainEntry *pdentry = (CDomainEntry *) pbDomainEntry;
|
|
CHAR szDMQAddress[30];
|
|
DWORD cQueuesPerEntry = 0;
|
|
DWORD cMaxQueuesPerEntry = 1000;
|
|
PLIST_ENTRY pliHead = NULL;
|
|
PLIST_ENTRY pliCurrent = NULL;
|
|
LIST_ENTRY liCurrent;
|
|
|
|
|
|
//Define buffers for parsing addresses... the sizes are clearly overkill, and
|
|
//I'm not too worried about overflow in a debugger extension
|
|
CHAR szAddress[MAX_DOM_PATH_SIZE];
|
|
CHAR szDumpArg[MAX_DOM_PATH_SIZE] = "";
|
|
LPSTR szParsedArg = (LPSTR) szArg;
|
|
LPSTR szCurrentDest = NULL;
|
|
|
|
//Allow people who are used to typeing dump CFoo@Address... keep using the @ sign
|
|
if ('@' == *szParsedArg)
|
|
szParsedArg++;
|
|
|
|
//Get Address of DomainNameTable
|
|
szCurrentDest = szAddress;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szParsedArg-szArg <= MAX_DOM_PATH_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '\0';
|
|
|
|
|
|
//Eat white space
|
|
while (('\0' != *szParsedArg) && isspace(*szParsedArg))
|
|
szParsedArg++;
|
|
|
|
//Copy name of struct to dump at each node
|
|
szCurrentDest = szDumpArg;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_DOM_PATH_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '@';
|
|
szCurrentDest++; //szCurrentDest now points to place to copy address to
|
|
|
|
pdnt = (PDOMAIN_NAME_TABLE) GetExpression(szAddress);
|
|
|
|
if (!pdnt)
|
|
{
|
|
dprintf("ERROR: Unable to Get DOMAIN_NAME_TABLE from argument %s\n", szArg);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pdnt, pbBuffer, sizeof(DOMAIN_NAME_TABLE), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory\n");
|
|
return;
|
|
}
|
|
|
|
pdnt = (PDOMAIN_NAME_TABLE)pbBuffer;
|
|
pEntry = &(pdnt->RootEntry);
|
|
|
|
while(pEntry)
|
|
{
|
|
//We are not interested in wildcard data
|
|
if (pEntry->pData)
|
|
{
|
|
//Display link state information
|
|
if (!ReadMemory(pEntry->pData, pbDomainEntry, sizeof(CDomainEntry), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read domain entry from @0x%08X\n", pEntry->pData);
|
|
return;
|
|
}
|
|
|
|
pliHead = (PLIST_ENTRY) (((BYTE *)pEntry->pData) + FIELD_OFFSET(CDomainEntry, m_liDestQueues));
|
|
pliCurrent = pdentry->m_liDestQueues.Flink;
|
|
|
|
//Get final destination string
|
|
if (!ReadMemory(pdentry->m_szDomainName, szFinalDest, pdentry->m_cbDomainName, NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read final destination name from @0x%08X\n",
|
|
pdentry->m_szDomainName);
|
|
return;
|
|
}
|
|
|
|
szFinalDest[pdentry->m_cbDomainName] = '\0';
|
|
|
|
//Loop and display each DMQ
|
|
cQueuesPerEntry = 0;
|
|
while (pliHead != pliCurrent)
|
|
{
|
|
cQueuesPerEntry++;
|
|
|
|
if (cQueuesPerEntry > cMaxQueuesPerEntry)
|
|
{
|
|
dprintf("ERROR: More than %d queues for this entry\n", cQueuesPerEntry);
|
|
return;
|
|
}
|
|
if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read link LIST_ENTRY @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
|
|
wsprintf(szDMQAddress, "0x%X",
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
|
|
dmqusage(hCurrentProcess, hCurrentThread, dwCurrentPc,
|
|
pExtensionApis, szDMQAddress);
|
|
|
|
pliCurrent = liCurrent.Flink;
|
|
}
|
|
}
|
|
|
|
|
|
//Now determine what the "next" entry is
|
|
if (pEntry->pFirstChildEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pFirstChildEntry;
|
|
}
|
|
else if (pEntry->pSiblingEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
else
|
|
{
|
|
for (pEntryRealAddress = pEntry->pParentEntry;
|
|
pEntryRealAddress != NULL;
|
|
pEntryRealAddress = pEntry->pParentEntry)
|
|
{
|
|
//must read parent entry into our buffer
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
|
|
if (pEntry->pSiblingEntry != NULL)
|
|
break;
|
|
|
|
}
|
|
if (pEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
}
|
|
|
|
if (pEntryRealAddress)
|
|
{
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
|
|
pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
}
|
|
else
|
|
{
|
|
pEntry = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---[ walkcpool ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Will walk a given CPool object. Validate headers, and dump currently
|
|
// used objects.
|
|
//
|
|
// ***NOTE*** This version only works on DBG CPool implementations (since
|
|
// RTL does not have the headerinfo). I could write a more complex
|
|
// version that checks and sees if this each pool object is in the
|
|
// freelist, but I will leave that as an exercise to the reader.
|
|
// Parameters:
|
|
// szArg - String containing arguments
|
|
// Address of CPool object to dump
|
|
// Offset of additional address to dump
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/30/1999 - MikeSwa Created
|
|
//
|
|
#define HEAD_SIGNATURE (DWORD)'daeH'
|
|
#define TAIL_SIGNATURE (DWORD)'liaT'
|
|
|
|
#define FREE_STATE (DWORD)'eerF'
|
|
#define USED_STATE (DWORD)'desU'
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(walkcpool)
|
|
{
|
|
PVOID pvCPool = NULL;
|
|
DWORD cbCPoolData = 0;
|
|
DWORD cCommited = 0;
|
|
DWORD cFragments = 0;
|
|
DWORD cBuffersPerFragment = 0;
|
|
DWORD iCurrentBufferInFragment = 0;
|
|
DWORD iCurrentFragment = 0;
|
|
PVOID *pvFragment = NULL;
|
|
PVOID pvCPoolData = NULL;
|
|
BYTE pbCPoolBuffer[sizeof(CPool)];
|
|
BYTE pbCPoolDataBuffer[100];
|
|
LPSTR szCurrentArg = NULL;
|
|
CHAR szArgBuffer[200];
|
|
DWORD_PTR cbOffset = 0;
|
|
DWORD_PTR dwptrData = 0;
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
dprintf("You must specify a Pool address\n");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
pvCPool = (PVOID)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
cbOffset = (DWORD_PTR) GetExpression(szCurrentArg);
|
|
}
|
|
else
|
|
{
|
|
pvCPool = (PVOID) GetExpression(szArg);
|
|
}
|
|
|
|
}
|
|
|
|
if (!ReadMemory(pvCPool, pbCPoolBuffer, sizeof(CPool), NULL))
|
|
{
|
|
dprintf("Unable to read memory at 0x%x\n", pvCPool);
|
|
return;
|
|
}
|
|
|
|
dprintf("Dumping CPool at address 0x%08X\n", pvCPool);
|
|
|
|
//Get interesting values from CPool
|
|
cbCPoolData = *((PDWORD)(pbCPoolBuffer + 0x8));
|
|
cCommited = *((PDWORD)(pbCPoolBuffer + 0xc));
|
|
cFragments = *((PDWORD)(pbCPoolBuffer + 0x54));
|
|
cBuffersPerFragment = *((PDWORD)(pbCPoolBuffer + 0x50));
|
|
|
|
dprintf("CPool data size is %d bytes (0x%x)\n", cbCPoolData, cbCPoolData);
|
|
dprintf("CPool fragment count is %d\n", cFragments);
|
|
dprintf("CPool has %d buffers per fragment\n", cBuffersPerFragment);
|
|
dprintf("CPool has %d commited buffers\n", cCommited);
|
|
|
|
if (!cbCPoolData)
|
|
{
|
|
dprintf("Invalid CPool\n");
|
|
return;
|
|
}
|
|
|
|
//Loop over the fragment and dump each one
|
|
pvFragment = (PVOID *) (pbCPoolBuffer + 0x58);
|
|
for (iCurrentFragment = 0;
|
|
iCurrentFragment < cFragments;
|
|
iCurrentFragment++ || pvFragment++)
|
|
{
|
|
pvCPoolData = *pvFragment;
|
|
|
|
if (!pvCPoolData)
|
|
continue;
|
|
|
|
dprintf("CPool Fragment #%d at 0x%08X\n", iCurrentFragment, pvCPoolData);
|
|
|
|
for (iCurrentBufferInFragment = 0;
|
|
iCurrentBufferInFragment < cBuffersPerFragment;
|
|
iCurrentBufferInFragment++)
|
|
{
|
|
if (!ReadMemory(pvCPoolData, pbCPoolDataBuffer, 100, NULL))
|
|
{
|
|
dprintf("\tUnable to read CPool buffer data at 0x%x\n", pvCPoolData);
|
|
break;
|
|
}
|
|
|
|
if (HEAD_SIGNATURE != ((DWORD *)pbCPoolDataBuffer)[1])
|
|
{
|
|
dprintf("\tHit bad signature at 0x%08X\n", pvCPoolData);
|
|
break; //bad signature bail
|
|
}
|
|
|
|
if (USED_STATE == ((DWORD *)pbCPoolDataBuffer)[2])
|
|
{
|
|
dprintf("\tAllocated block found at offset %d (0x%08X)\n",
|
|
iCurrentBufferInFragment, pvCPoolData);
|
|
if (cbOffset)
|
|
{
|
|
if (ReadMemory(((PBYTE)pvCPoolData)+cbOffset, &dwptrData,
|
|
sizeof(DWORD_PTR), NULL))
|
|
{
|
|
dprintf("\t\tData 0x%X found at address 0x%X\n",
|
|
dwptrData, ((PBYTE)pvCPoolData)+cbOffset);
|
|
}
|
|
}
|
|
}
|
|
pvCPoolData = ((BYTE *)pvCPoolData) + cbCPoolData;
|
|
|
|
if (!(--cCommited))
|
|
{
|
|
dprintf("\tLast block is in fragment at offset %d (0x%08X)\n",
|
|
iCurrentBufferInFragment, pvCPoolData);
|
|
break; //We're done
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//---[ CheckVersion ]----------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Checks the AQ version to make sure that this debugger extension will
|
|
// work with it.
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// History:
|
|
// 2/5/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(CheckVersion)
|
|
{
|
|
DWORD cbAQClasses = 0;
|
|
DWORD dwAQFlavorSignature = ' ';
|
|
PVOID pcbAQClasses = (PVOID) GetExpression("aqueue!g_cbClasses");
|
|
PVOID pdwAQFlavorSignature = (PVOID) GetExpression("aqueue!g_dwFlavorSignature");
|
|
PCHAR pch = NULL;
|
|
|
|
//Read the version information stamped in AQ
|
|
ReadMemory(pcbAQClasses, &cbAQClasses, sizeof(DWORD), NULL);
|
|
ReadMemory(pdwAQFlavorSignature, &dwAQFlavorSignature, sizeof(DWORD), NULL);
|
|
|
|
if (!g_fVersionChecked)
|
|
{
|
|
dprintf("AQueue Internal Version Info (#'s should match):\n");
|
|
pch = (PCHAR) &g_dwFlavorSignature;
|
|
dprintf("\taqdbgext %c%c%c%c 0x%08X\n", *(pch), *(pch+1), *(pch+2), *(pch+3), g_cbClasses);
|
|
pch = (PCHAR) &dwAQFlavorSignature;
|
|
dprintf("\taqueue %c%c%c%c 0x%08X\n\n", *(pch), *(pch+1), *(pch+2), *(pch+3), cbAQClasses);
|
|
}
|
|
|
|
g_fVersionChecked = FALSE;
|
|
if (dwAQFlavorSignature != g_dwFlavorSignature)
|
|
dprintf("\n\nWARNING: DBG/RTL aqueue.dll & aqdbgext.dll mismatch\n\n");
|
|
else if (g_cbClasses != cbAQClasses)
|
|
dprintf("\n\nWARNING: aqueue.dll & aqdbgext.dll version mismatch\n\n");
|
|
else
|
|
g_fVersionChecked = TRUE;
|
|
|
|
}
|
|
|
|
//---[ DumpServers ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps pointers to the CAQSvrInst for each virtual server
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(DumpServers)
|
|
{
|
|
PVOID pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
DWORD *pcInstances = (DWORD *) GetExpression("aqueue!g_cInstances");
|
|
DWORD cInstances = 0;
|
|
LIST_ENTRY liCurrent;
|
|
BYTE pbBuffer[sizeof(CAQSvrInst)];
|
|
CAQSvrInst *paqinst = (CAQSvrInst *) pbBuffer;
|
|
PVOID pCMQAddress = NULL;
|
|
DWORD dwInstance = 0;
|
|
CHAR szDumpArg[40] = "";
|
|
CHAR szArgBuffer[200];
|
|
LPSTR szCurrentArg = NULL;
|
|
|
|
CheckVersion(DebugArgs);
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
dwInstance = 0;
|
|
pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
dwInstance = (DWORD)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
pvListHead = (PVOID) GetExpression(szCurrentArg);
|
|
else
|
|
pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
}
|
|
|
|
if (!pvListHead)
|
|
{
|
|
dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
|
|
dprintf(" If you are using windbg, you should specify the value as the\n");
|
|
dprintf(" 2nd argument. You can determine the address value by typeing:\n");
|
|
dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
|
|
dprintf(" You may also have bad symbols for aqueue.dll.\n");
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pvListHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @ aqueue!g_liVirtualServers 0x%08X", pvListHead);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pcInstances, &cInstances, sizeof(DWORD), NULL))
|
|
{
|
|
//For you windbg users out there
|
|
dprintf("\n\n%Virtual Server Instance(s)\n\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("\n\n%d Virtual Server Instance(s)\n\n", cInstances);
|
|
}
|
|
|
|
dprintf("Class@Address Server Instance\n");
|
|
dprintf("==========================================\n");
|
|
while (liCurrent.Flink != pvListHead)
|
|
{
|
|
pCMQAddress = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
|
|
|
|
|
|
if (!ReadMemory(pCMQAddress, paqinst, sizeof(CAQSvrInst), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pCMQAddress);
|
|
return;
|
|
}
|
|
|
|
if (CATMSGQ_SIG != paqinst->m_dwSignature)
|
|
{
|
|
dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pCMQAddress, liCurrent.Flink);
|
|
}
|
|
else
|
|
{
|
|
dprintf("CAQSvrInst@0x%08X %d\n", pCMQAddress, paqinst->m_dwServerInstance);
|
|
if (paqinst->m_dwServerInstance == dwInstance)
|
|
wsprintf(szDumpArg, "CAQSvrInst@0x%08X", pCMQAddress);
|
|
}
|
|
|
|
|
|
if (!ReadMemory(liCurrent.Flink, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @0x%08X", liCurrent.Flink);
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//Dump the interesting instance
|
|
if ('\0' != szDumpArg[0])
|
|
_dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
|
|
|
|
}
|
|
|
|
//---[ handlemgmt ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Caclulates a handle management score for a given virtual server.
|
|
//
|
|
// Calculates score based on the number of messages closed and messages
|
|
// delivered / pending delivery... the lower the score... the better.
|
|
// Score = Closes /
|
|
// (m_cCurrentMsgsPendingSubmit + m_cCurrentMsgsPendingCat*2 +
|
|
// m_cCurrentMsgsPendingRouting*3 + m_cCurrentMsgsPendingLocal*4 +
|
|
// m_cMsgsDeliveredLocal*5)
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(handlemgmt)
|
|
{
|
|
#define MAILMSG_CLOSES_SYMBOL \
|
|
"mailmsg!CMailMsg__g_cTotalExternalReleaseUsageZero"
|
|
|
|
#define MAILMSG_CURRENT_CLOSED_SYMBOL \
|
|
"mailmsg!CMailMsg__g_cCurrentMsgsClosedByExternalReleaseUsage"
|
|
|
|
#define MAILMSG_CURRENT_ALLOCATED \
|
|
"mailmsg!CMsg__m_Pool+0x10"
|
|
|
|
#define MAILMSG_TOTAL_ALLOCATED \
|
|
"mailmsg!CMsg__m_Pool+0x3c"
|
|
|
|
PVOID pvCloses = (PVOID) GetExpression(MAILMSG_CLOSES_SYMBOL);
|
|
DWORD cCloses = 1;
|
|
PVOID pvCurrentMsgsThatHaveBeenClosed = (PVOID) GetExpression(MAILMSG_CURRENT_CLOSED_SYMBOL);
|
|
DWORD cCurrentMsgsThatHaveBeenClosed = 1;
|
|
PVOID pvCurrentMsgsAllocated = (PVOID) GetExpression(MAILMSG_CURRENT_ALLOCATED);
|
|
DWORD cCurrentMsgsAllocated = 1;
|
|
PVOID pvTotalMsgsAllocated = (PVOID) GetExpression(MAILMSG_TOTAL_ALLOCATED);
|
|
DWORD cTotalMsgsAllocated = 1;
|
|
DWORD dwPercentCurrentMessagesClosed = 0;
|
|
DWORD dwPercentTotalMessagesBacklogged = 0;
|
|
DWORD dwPercentCurrentMessagesQueueInternally = 0;
|
|
PVOID pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
DWORD *pcInstances = (DWORD *) GetExpression("phatq!g_cInstances");
|
|
DWORD cInstances = 0;
|
|
LIST_ENTRY liCurrent;
|
|
BYTE pbBuffer[sizeof(CAQSvrInst)];
|
|
CAQSvrInst *paqinst = (CAQSvrInst *) pbBuffer;
|
|
PVOID pCMQAddress = NULL;
|
|
DWORD dwInstance = 1;
|
|
CHAR szDumpArg[40] = "";
|
|
CHAR szArgBuffer[200];
|
|
LPSTR szCurrentArg = NULL;
|
|
DWORD dwQueueScore = 0;
|
|
DWORD dwWeightedScore = 0;
|
|
DWORD dwDeliveredScore = 0;
|
|
DWORD dwSubmittedScore = 0;
|
|
DWORD dwWeightedQueueLength = 0;
|
|
DWORD dwTotalQueueLength = 0;
|
|
BOOL fFoundInstance = FALSE;
|
|
|
|
|
|
//
|
|
// Read the data we need from mailmsg
|
|
//
|
|
if (!ReadMemory(pvCloses, &cCloses, sizeof(cCloses), NULL))
|
|
{
|
|
dprintf("Unable to read %s at address %p\n",
|
|
MAILMSG_CLOSES_SYMBOL, pvCloses);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pvCurrentMsgsThatHaveBeenClosed,
|
|
&cCurrentMsgsThatHaveBeenClosed, sizeof(cCloses), NULL))
|
|
{
|
|
dprintf("Unable to read %s at address %p\n",
|
|
MAILMSG_CLOSES_SYMBOL, pvCloses);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pvCurrentMsgsAllocated, &cCurrentMsgsAllocated,
|
|
sizeof(cCloses), NULL))
|
|
{
|
|
dprintf("Unable to read %s at address %p\n",
|
|
MAILMSG_CLOSES_SYMBOL, pvCloses);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pvTotalMsgsAllocated, &cTotalMsgsAllocated,
|
|
sizeof(cCloses), NULL))
|
|
{
|
|
dprintf("Unable to read %s at address %p\n",
|
|
MAILMSG_CLOSES_SYMBOL, pvCloses);
|
|
return;
|
|
}
|
|
|
|
if (cCurrentMsgsAllocated)
|
|
{
|
|
dwPercentCurrentMessagesClosed =
|
|
(100*cCurrentMsgsThatHaveBeenClosed)/cCurrentMsgsAllocated;
|
|
}
|
|
|
|
if (cTotalMsgsAllocated)
|
|
{
|
|
dwPercentTotalMessagesBacklogged =
|
|
(100*cCurrentMsgsAllocated)/cTotalMsgsAllocated;
|
|
}
|
|
|
|
//
|
|
// Get the instance object we want to get data from
|
|
//
|
|
CheckVersion(DebugArgs);
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
dwInstance = 1;
|
|
pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
dwInstance = (DWORD)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
pvListHead = (PVOID) GetExpression(szCurrentArg);
|
|
else
|
|
pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
}
|
|
|
|
if (!pvListHead)
|
|
{
|
|
dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
|
|
dprintf(" If you are using windbg, you should specify the value as the\n");
|
|
dprintf(" 2nd argument. You can determine the address value by typeing:\n");
|
|
dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
|
|
dprintf(" You may also have bad symbols for phatq.dll.\n");
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pvListHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @ phatq!g_liVirtualServers 0x%08X", pvListHead);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pcInstances, &cInstances, sizeof(DWORD), NULL))
|
|
{
|
|
//For you windbg users out there
|
|
dprintf("\n\n%Virtual Server Instance(s)\n\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("\n\n%d Virtual Server Instance(s)\n\n", cInstances);
|
|
}
|
|
|
|
while (liCurrent.Flink != pvListHead)
|
|
{
|
|
pCMQAddress = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
|
|
|
|
|
|
if (!ReadMemory(pCMQAddress, paqinst, sizeof(CAQSvrInst), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pCMQAddress);
|
|
return;
|
|
}
|
|
|
|
if (CATMSGQ_SIG != paqinst->m_dwSignature)
|
|
{
|
|
dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pCMQAddress, liCurrent.Flink);
|
|
}
|
|
else if (paqinst->m_dwServerInstance == dwInstance)
|
|
{
|
|
fFoundInstance = TRUE;
|
|
dprintf("Using CAQSvrInst@0x%08X %d\n", pCMQAddress, paqinst->m_dwServerInstance);
|
|
break;
|
|
}
|
|
|
|
|
|
if (!ReadMemory(liCurrent.Flink, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @0x%08X", liCurrent.Flink);
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Did we find the instance
|
|
//
|
|
if (!fFoundInstance)
|
|
{
|
|
dprintf("We did not find instance %d\n", dwInstance);
|
|
return;
|
|
}
|
|
dwWeightedQueueLength = paqinst->m_cCurrentMsgsPendingSubmit +
|
|
paqinst->m_cCurrentMsgsPendingCat*2 +
|
|
paqinst->m_cCurrentMsgsPendingRouting*3 +
|
|
paqinst->m_cCurrentMsgsPendingLocal*4 +
|
|
paqinst->m_cMsgsDeliveredLocal*5;
|
|
|
|
dwTotalQueueLength = paqinst->m_cCurrentMsgsPendingSubmit +
|
|
paqinst->m_cCurrentMsgsPendingCat +
|
|
paqinst->m_cCurrentMsgsPendingRouting +
|
|
paqinst->m_cCurrentMsgsPendingLocal +
|
|
paqinst->m_cMsgsDeliveredLocal;
|
|
|
|
if (cTotalMsgsAllocated)
|
|
{
|
|
dwPercentCurrentMessagesQueueInternally =
|
|
(100*(dwTotalQueueLength-paqinst->m_cMsgsDeliveredLocal))
|
|
/cCurrentMsgsAllocated;
|
|
}
|
|
|
|
if (dwTotalQueueLength)
|
|
dwQueueScore = (cCloses*1000)/dwTotalQueueLength;
|
|
|
|
if (dwWeightedQueueLength)
|
|
dwWeightedScore = (cCloses*1000)/dwWeightedQueueLength;
|
|
|
|
if (paqinst->m_cMsgsDeliveredLocal)
|
|
dwDeliveredScore = (cCloses*1000)/paqinst->m_cMsgsDeliveredLocal;
|
|
|
|
if (paqinst->m_cTotalExternalMsgsSubmitted)
|
|
dwSubmittedScore = (cCloses*1000)/paqinst->m_cTotalExternalMsgsSubmitted;
|
|
|
|
dprintf("\n\nHandle Managment scores:\n");
|
|
dprintf("========================\n");
|
|
dprintf("Non-Weighted Score: %d\n", dwQueueScore);
|
|
dprintf("Weighted Score: %d\n", dwWeightedScore);
|
|
dprintf("Delivery Score: %d\n", dwDeliveredScore);
|
|
dprintf("Submitted Score: %d\n", dwSubmittedScore);
|
|
dprintf("Current Messsages Allocated That have been closed: %d%%\n",
|
|
dwPercentCurrentMessagesClosed);
|
|
dprintf("\nThe following are useful in correlating different test runs...\n");
|
|
dprintf("Messages Backlogged: %d%%\n", dwPercentTotalMessagesBacklogged);
|
|
dprintf("Backlogged Messsages Queued internally: %d%%\n",
|
|
dwPercentCurrentMessagesQueueInternally);
|
|
dprintf("\n%d Total message closures.. %d total deliveries\n\n",
|
|
cCloses, paqinst->m_cMsgsDeliveredLocal);
|
|
|
|
}
|
|
|
|
//---[ DumpDNT ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps the contents of a DOMAIN_NAME_TABLE
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(DumpDNT)
|
|
{
|
|
BYTE pbBuffer[sizeof(DOMAIN_NAME_TABLE)];
|
|
PDOMAIN_NAME_TABLE pdnt = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
|
|
BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
|
|
BYTE pbPathEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)]; //buffer for putter path name entries in
|
|
BYTE pbPathEntryBuffer[MAX_DOM_PATH_SIZE];
|
|
CHAR pBuffer[MAX_DOM_PATH_SIZE] = "Root Entry";
|
|
LPSTR pPathBuffer = NULL;
|
|
LPSTR pPathBufferStop = NULL;
|
|
LPSTR pEntryBuffer = NULL;
|
|
LPSTR pEntryBufferStop = NULL;
|
|
DWORD dwLength = 0;
|
|
DWORD dwSig = 0;
|
|
|
|
//Define buffers for parsing addresses... the sizes are clearly overkill, and
|
|
//I'm not too worried about overflow in a debugger extension
|
|
CHAR szAddress[MAX_DOM_PATH_SIZE];
|
|
CHAR szDumpArg[MAX_DOM_PATH_SIZE] = "";
|
|
LPSTR szParsedArg = (LPSTR) szArg;
|
|
LPSTR szCurrentDest = NULL;
|
|
|
|
//Allow people who are used to typeing dump CFoo@Address... keep using the @ sign
|
|
if ('@' == *szParsedArg)
|
|
szParsedArg++;
|
|
|
|
//Get Address of DomainNameTable
|
|
szCurrentDest = szAddress;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szParsedArg-szArg <= MAX_DOM_PATH_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '\0';
|
|
|
|
|
|
//Eat white space
|
|
while (('\0' != *szParsedArg) && isspace(*szParsedArg))
|
|
szParsedArg++;
|
|
|
|
//Copy name of struct to dump at each node
|
|
szCurrentDest = szDumpArg;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_DOM_PATH_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '@';
|
|
szCurrentDest++; //szCurrentDest now points to place to copy address to
|
|
|
|
pdnt = (PDOMAIN_NAME_TABLE) GetExpression(szAddress);
|
|
|
|
if (!pdnt)
|
|
{
|
|
dprintf("ERROR: Unable to Get DOMAIN_NAME_TABLE from argument %s\n", szArg);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pdnt, pbBuffer, sizeof(DOMAIN_NAME_TABLE), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory\n");
|
|
return;
|
|
}
|
|
|
|
pPathBuffer = pBuffer;
|
|
pPathBufferStop = pPathBuffer + (MAX_DOM_PATH_SIZE / sizeof(CHAR) -1 );
|
|
|
|
pEntryRealAddress = (PDOMAIN_NAME_TABLE_ENTRY)
|
|
((BYTE *)pdnt + FIELD_OFFSET(DOMAIN_NAME_TABLE, RootEntry));
|
|
pdnt = (PDOMAIN_NAME_TABLE) pbBuffer;
|
|
|
|
pEntry = &(pdnt->RootEntry);
|
|
dprintf("Entry ID # Children pData pWildCard Path\n");
|
|
dprintf("===========================================================================\n");
|
|
while(pEntry)
|
|
{
|
|
//only display interesting entries
|
|
if (pEntry->pData || pEntry->pWildCardData)
|
|
{
|
|
//Get full path name of this domain entry
|
|
pPathEntry = pEntry;
|
|
pPathBuffer = pBuffer;
|
|
while (pPathEntry && pPathEntry->pParentEntry && pPathBuffer < pPathBufferStop)
|
|
{
|
|
//dump current entries portion of the string
|
|
if (pPathBuffer != pBuffer) //already made first pass -- Add delimter
|
|
{
|
|
*pPathBuffer++ = '.';
|
|
}
|
|
|
|
//read partial path name from debuggee
|
|
if (!ReadMemory(pPathEntry->PathSegment.Buffer, pbPathEntryBuffer,
|
|
AQ_MIN(MAX_DOM_PATH_SIZE, pPathEntry->PathSegment.Length), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory for path segment 0x%08X\n",
|
|
pPathEntry->PathSegment.Buffer);
|
|
break;
|
|
}
|
|
|
|
pEntryBuffer = (CHAR *) pbPathEntryBuffer;
|
|
pEntryBufferStop = pEntryBuffer;
|
|
pEntryBuffer += (pPathEntry->PathSegment.Length / sizeof(CHAR) -1 );
|
|
|
|
while (pPathBuffer < pPathBufferStop && pEntryBuffer >= pEntryBufferStop)
|
|
{
|
|
*pPathBuffer++ = *pEntryBuffer--;
|
|
}
|
|
*pPathBuffer = '\0'; //make sure we terminate
|
|
pPathEntry = pPathEntry->pParentEntry;
|
|
|
|
//read next part of path name from debuggee
|
|
if (!ReadMemory(pPathEntry, pbPathEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory for path entry 0x%08x\n", pPathEntry);
|
|
pPathEntry = NULL;
|
|
}
|
|
else
|
|
{
|
|
pPathEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbPathEntry;
|
|
}
|
|
}
|
|
|
|
dprintf("0x%08.8X %10.10d 0x%08.8X 0x%08.8X %s\n", pEntryRealAddress,
|
|
pEntry->NoOfChildren, pEntry->pData, pEntry->pWildCardData, pBuffer);
|
|
|
|
//Dump structs if requested
|
|
if ('@' != *szDumpArg)
|
|
{
|
|
if (pEntry->pData)
|
|
{
|
|
//Write address string
|
|
wsprintf(szCurrentDest, "0x%08X", pEntry->pData);
|
|
|
|
//Call ptdbgext dump function
|
|
_dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
|
|
}
|
|
|
|
if (pEntry->pWildCardData)
|
|
{
|
|
//Write address string
|
|
wsprintf(szCurrentDest, "0x%08X", pEntry->pWildCardData);
|
|
|
|
//Call ptdbgext dump function
|
|
_dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//Get the next entry... in order of child, sibling, closest ancestor with sibling
|
|
if (pEntry->pFirstChildEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pFirstChildEntry;
|
|
}
|
|
else if (pEntry->pSiblingEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
else
|
|
{
|
|
for (pEntryRealAddress = pEntry->pParentEntry;
|
|
pEntryRealAddress != NULL;
|
|
pEntryRealAddress = pEntry->pParentEntry)
|
|
{
|
|
//must read parent entry into our buffer
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
|
|
if (pEntry->pSiblingEntry != NULL)
|
|
break;
|
|
|
|
}
|
|
if (pEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
}
|
|
|
|
if (pEntryRealAddress)
|
|
{
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
|
|
pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
}
|
|
else
|
|
{
|
|
pEntry = NULL;
|
|
}
|
|
}
|
|
dprintf("===========================================================================\n");
|
|
}
|
|
|
|
|
|
//---[ DumpList ]--------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Function to walk a set of LIST_ENTRY's and dump their contenxts
|
|
// Parameters:
|
|
// szArg - space separated list of the following
|
|
// Address of head list entry
|
|
// Offset of object address [optional]
|
|
// Name of object to dump [optional]
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/15/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(dumplist)
|
|
{
|
|
const DWORD MAX_ARG_SIZE = 200;
|
|
const DWORD MAX_ENTRIES = 3000;
|
|
LIST_ENTRY liCurrent;
|
|
PLIST_ENTRY pliHead = NULL;
|
|
PLIST_ENTRY pliCurrent = NULL;
|
|
DWORD_PTR dwOffsetOfEntry = 0;
|
|
CHAR szAddress[MAX_ARG_SIZE];
|
|
CHAR szDumpArg[MAX_ARG_SIZE];
|
|
LPSTR szParsedArg = (LPSTR) szArg;
|
|
LPSTR szCurrentDest = NULL;
|
|
DWORD cEntries = 0;
|
|
|
|
//Get Address of DomainNameTable
|
|
szCurrentDest = szAddress;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szParsedArg-szArg <= MAX_ARG_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '\0';
|
|
|
|
|
|
//Eat white space
|
|
while (('\0' != *szParsedArg) && isspace(*szParsedArg))
|
|
szParsedArg++;
|
|
|
|
//Get offset of data
|
|
szCurrentDest = szDumpArg;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_ARG_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '\0';
|
|
dwOffsetOfEntry = GetExpression(szDumpArg);
|
|
|
|
//Eat white more space
|
|
while (('\0' != *szParsedArg) && isspace(*szParsedArg))
|
|
szParsedArg++;
|
|
|
|
//Copy name of struct to dump at each node
|
|
szCurrentDest = szDumpArg;
|
|
while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_ARG_SIZE))
|
|
{
|
|
*szCurrentDest = *szParsedArg;
|
|
szParsedArg++;
|
|
szCurrentDest++;
|
|
}
|
|
*szCurrentDest = '@';
|
|
szCurrentDest++; //szCurrentDest now points to place to copy address to
|
|
|
|
pliHead = (PLIST_ENTRY) GetExpression(szAddress);
|
|
if (!ReadMemory(pliHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("Error reading head entry at 0x%08X\n", pliHead);
|
|
return;
|
|
}
|
|
|
|
pliCurrent = pliHead;
|
|
dprintf("LIST ENTRY DATA OFFSET\n");
|
|
dprintf("==============================================\n");
|
|
dprintf(" 0x%08X 0x%08X (HEAD)\n", pliCurrent, pliCurrent-dwOffsetOfEntry);
|
|
dprintf("----------------------------------------------\n");
|
|
//OK... start walking list using Flink
|
|
pliCurrent = liCurrent.Flink;
|
|
while(pliCurrent != NULL && pliHead != pliCurrent)
|
|
{
|
|
// There have been some problems with this.
|
|
#ifdef NEVER
|
|
if (pliCurrent != liCurrent.Blink)
|
|
{
|
|
dprintf(" %p %p (WARNING does Flink/Blink mismatch)\n", pliCurrent,
|
|
((DWORD_PTR) pliCurrent)-dwOffsetOfEntry);
|
|
}
|
|
else
|
|
#else
|
|
if (TRUE)
|
|
#endif //NEVER
|
|
{
|
|
dprintf(" %p %p\n", pliCurrent,
|
|
((DWORD_PTR) pliCurrent)-dwOffsetOfEntry);
|
|
}
|
|
|
|
if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("Error reading LIST_ENTRY at 0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
|
|
//dump the struct if we were asked to
|
|
if ('@' != *szDumpArg)
|
|
{
|
|
//Write address string
|
|
wsprintf(szCurrentDest, "%p", ((DWORD_PTR) pliCurrent)-dwOffsetOfEntry);
|
|
|
|
//Call ptdbgext dump function
|
|
_dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
|
|
}
|
|
|
|
cEntries++;
|
|
if (cEntries > MAX_ENTRIES)
|
|
{
|
|
dprintf("ERROR: Max number of entries exceeded\n");
|
|
return;
|
|
}
|
|
pliCurrent = liCurrent.Flink;
|
|
}
|
|
dprintf("----------------------------------------------\n");
|
|
dprintf(" %d Total Entries\n", cEntries);
|
|
dprintf("==============================================\n");
|
|
|
|
|
|
}
|
|
|
|
//---[ linkstate ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Dumps the current link state (including routing information) of a
|
|
// virtual server.
|
|
// Parameters:
|
|
// Virtual Server Instance - virtual server ID of server to dump
|
|
// Global Server list (optional) - Head of virtual server list
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/30/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(linkstate)
|
|
{
|
|
DWORD dwInstance = 0;
|
|
PLIST_ENTRY pliHead = NULL;
|
|
PLIST_ENTRY pliCurrent = NULL;
|
|
BYTE pBuffer[sizeof(CAQSvrInst)] = {'\0'};
|
|
CAQSvrInst *paqinst = (CAQSvrInst *) pBuffer;
|
|
DOMAIN_NAME_TABLE *pdnt = NULL;
|
|
PVOID pvAQueue = NULL;
|
|
LIST_ENTRY liCurrent;
|
|
BOOL fFound = FALSE;
|
|
CHAR szArgBuffer[20];
|
|
LPSTR szCurrentArg = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
|
|
BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
|
|
CHAR szNextHop[MAX_DOM_PATH_SIZE];
|
|
CHAR szFinalDest[MAX_DOM_PATH_SIZE];
|
|
BYTE pbLMQ[sizeof(CLinkMsgQueue)];
|
|
BYTE pbDomainEntry[sizeof(CDomainEntry)];
|
|
BYTE pbDMQ[sizeof(CDestMsgQueue)];
|
|
CLinkMsgQueue *plmq = (CLinkMsgQueue *) pbLMQ;
|
|
CDomainEntry *pdentry = (CDomainEntry *) pbDomainEntry;
|
|
CDestMsgQueue *pdmq = (CDestMsgQueue *) pbDMQ;
|
|
DWORD *pdwGuid = NULL;
|
|
LPSTR szLinkState = LINK_STATE_UP;
|
|
CHAR szError[100];
|
|
HINSTANCE hModule = GetModuleHandle("aqdbgext.dll");
|
|
DWORD dwMsgId = 0;
|
|
DWORD dwFacility = 0;
|
|
|
|
CheckVersion(DebugArgs);
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
//Assume the first instance
|
|
dwInstance = 1;
|
|
pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
dwInstance = (DWORD)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
pliHead = (PLIST_ENTRY) GetExpression(szCurrentArg);
|
|
else
|
|
pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
}
|
|
|
|
|
|
if (!pliHead)
|
|
{
|
|
dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
|
|
dprintf(" If you are using windbg, you should specify the value as the\n");
|
|
dprintf(" 2nd argument. You can determine the address value by typeing:\n");
|
|
dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pliHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @0x%08X\n", pliHead);
|
|
return;
|
|
}
|
|
|
|
while (liCurrent.Flink != pliHead)
|
|
{
|
|
pvAQueue = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
|
|
|
|
if (!ReadMemory(pvAQueue, paqinst, sizeof(CAQSvrInst), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pvAQueue);
|
|
return;
|
|
}
|
|
|
|
//Check the signature
|
|
if (CATMSGQ_SIG != paqinst->m_dwSignature)
|
|
{
|
|
dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pvAQueue, liCurrent.Flink);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (paqinst->m_dwServerInstance == dwInstance)
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pliCurrent = liCurrent.Flink;
|
|
|
|
if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
|
|
if (pliCurrent == liCurrent.Flink)
|
|
{
|
|
dprintf("ERROR: Loop in LIST_ENTRY @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!fFound)
|
|
{
|
|
dprintf("Requested instance not found.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("Using Server instance %d @0x%08X\n", dwInstance, pvAQueue);
|
|
//Use our current instance to dump all of the interesting bits
|
|
|
|
pdnt = &(paqinst->m_dmt.m_dnt);
|
|
pEntry = &(pdnt->RootEntry);
|
|
|
|
while(pEntry)
|
|
{
|
|
//We are not interested in wildcard data
|
|
if (pEntry->pData)
|
|
{
|
|
//Display link state information
|
|
if (!ReadMemory(pEntry->pData, pbDomainEntry, sizeof(CDomainEntry), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read domain entry from @0x%08X\n", pEntry->pData);
|
|
return;
|
|
}
|
|
|
|
pliHead = (PLIST_ENTRY) (((BYTE *)pEntry->pData) + FIELD_OFFSET(CDomainEntry, m_liDestQueues));
|
|
pliCurrent = pdentry->m_liDestQueues.Flink;
|
|
|
|
//Get final destination string
|
|
if (!ReadMemory(pdentry->m_szDomainName, szFinalDest, pdentry->m_cbDomainName, NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read final destination name from @0x%08X\n",
|
|
pdentry->m_szDomainName);
|
|
return;
|
|
}
|
|
|
|
szFinalDest[pdentry->m_cbDomainName] = '\0';
|
|
|
|
//Loop and display each DMQ
|
|
while (pliHead != pliCurrent)
|
|
{
|
|
if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read link LIST_ENTRY @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
|
|
pbDMQ, sizeof(CDestMsgQueue), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read DMQ @0x%08X\n",
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
|
|
return;
|
|
}
|
|
|
|
//Verify DMQ Signature
|
|
if (DESTMSGQ_SIG != pdmq->m_dwSignature)
|
|
{
|
|
dprintf("ERROR: Invalid DMQ signature for CDestMsgQueue@0x%08X (from LIST_ENTRY) @0x%08X\n",
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
|
|
pliCurrent);
|
|
return;
|
|
}
|
|
|
|
//Read link
|
|
if (!ReadMemory(pdmq->m_plmq, pbLMQ, sizeof(CLinkMsgQueue), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read LMQ @0x%08X\n",
|
|
pdmq->m_plmq);
|
|
return;
|
|
}
|
|
|
|
//Now print off next hop info
|
|
if (!ReadMemory(plmq->m_szSMTPDomain, szNextHop, plmq->m_cbSMTPDomain, NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read next hop name from @0x%08X\n",
|
|
plmq->m_szSMTPDomain);
|
|
return;
|
|
}
|
|
szNextHop[plmq->m_cbSMTPDomain] = '\0';
|
|
|
|
pdwGuid = (DWORD *) &(plmq->m_aqsched.m_guidRouter);
|
|
|
|
//Determine the state of the link
|
|
if (plmq->m_dwLinkFlags & LINK_STATE_PRIV_GENERATING_DSNS)
|
|
{
|
|
szLinkState = LINK_STATE_DSN;
|
|
}
|
|
if (CLinkMsgQueue::fFlagsAllowConnection(plmq->m_dwLinkStateFlags))
|
|
{
|
|
//If we can connect... are we?
|
|
if (plmq->m_cConnections)
|
|
szLinkState = LINK_STATE_ACTIVE;
|
|
else
|
|
szLinkState = LINK_STATE_UP;
|
|
}
|
|
else
|
|
{
|
|
//If we're down... why?
|
|
szLinkState = LINK_STATE_DOWN;
|
|
if (!(plmq->m_dwLinkStateFlags & LINK_STATE_RETRY_ENABLED))
|
|
szLinkState = LINK_STATE_RETRY;
|
|
else if (plmq->m_dwLinkStateFlags & LINK_STATE_PRIV_CONFIG_TURN_ETRN)
|
|
szLinkState = LINK_STATE_TURN;
|
|
else if (plmq->m_dwLinkStateFlags & LINK_STATE_PRIV_NO_CONNECTION)
|
|
szLinkState = LINK_STATE_SPECIAL;
|
|
}
|
|
|
|
//Print some interesting data
|
|
dprintf("==============================================================================\n");
|
|
dprintf("| Link State | Final Destination | Next Hop |\n");
|
|
dprintf("| %s | %-29s | %-29s |\n", szLinkState, szFinalDest, szNextHop);
|
|
dprintf("------------------------------------------------------------------------------\n");
|
|
dprintf("| Route Details: |\n");
|
|
dprintf("| Router GUID: %08X-%08X-%08X-%08X |\n",
|
|
pdwGuid[0], pdwGuid[1], pdwGuid[2], pdwGuid[3]);
|
|
dprintf("| Message Type: %08X Schedule ID:%08X |\n",
|
|
pdmq->m_aqmt.m_dwMessageType, plmq->m_aqsched.m_dwScheduleID);
|
|
dprintf("| Link State Flags 0x%08X |\n",
|
|
plmq->m_dwLinkStateFlags);
|
|
dprintf("| Current # of connections: %-8d |\n",
|
|
plmq->m_cConnections);
|
|
dprintf("| Current # of Msgs (on link): %-8d |\n",
|
|
plmq->m_aqstats.m_cMsgs);
|
|
dprintf("| Current # of Msgs (on DMQ): %-8d |\n",
|
|
pdmq->m_aqstats.m_cMsgs);
|
|
dprintf("| Current # of Msgs (on DMQ/retry): %-8d |\n",
|
|
pdmq->m_aqstats.m_cRetryMsgs);
|
|
dprintf("| CLinkMsgQueue@0x%08X |\n",
|
|
pdmq->m_plmq);
|
|
dprintf("| CDestMsgQueue@0x%08X |\n",
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
|
|
|
|
//print out the diagnostic information if in retry
|
|
//or a failure has been recorded and there are no msgs.
|
|
if ((LINK_STATE_RETRY == szLinkState) ||
|
|
(FAILED(plmq->m_hrDiagnosticError) && !plmq->m_aqstats.m_cMsgs))
|
|
{
|
|
//Get and format the error message
|
|
szError[0] = '\0';
|
|
dwMsgId = plmq->m_hrDiagnosticError;
|
|
dwFacility = ((0x0FFF0000 & dwMsgId) >> 16);
|
|
|
|
//If it is not ours... then "un-HRESULT" it
|
|
if (dwFacility != FACILITY_ITF)
|
|
dwMsgId &= 0x0000FFFF;
|
|
|
|
|
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_HMODULE,
|
|
hModule,
|
|
dwMsgId,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
szError,
|
|
sizeof(szError),
|
|
NULL );
|
|
|
|
dprintf("------------------------------------------------------------------------------\n");
|
|
dprintf("| Failure Details: |\n");
|
|
dprintf("| Diagnostic HRESULT 0x%08X |\n",
|
|
plmq->m_hrDiagnosticError);
|
|
if (szError && *szError)
|
|
{
|
|
dprintf("| Diagnostic string: %s\n",
|
|
szError);
|
|
}
|
|
dprintf("| Protocol Verb: %-20.20s |\n",
|
|
plmq->m_szDiagnosticVerb);
|
|
dprintf("| Protocol Response: %s\n",
|
|
plmq->m_szDiagnosticResponse);
|
|
}
|
|
pliCurrent = liCurrent.Flink;
|
|
}
|
|
}
|
|
|
|
//Now determine what the "next" entry is
|
|
if (pEntry->pFirstChildEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pFirstChildEntry;
|
|
}
|
|
else if (pEntry->pSiblingEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
else
|
|
{
|
|
for (pEntryRealAddress = pEntry->pParentEntry;
|
|
pEntryRealAddress != NULL;
|
|
pEntryRealAddress = pEntry->pParentEntry)
|
|
{
|
|
//must read parent entry into our buffer
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
|
|
if (pEntry->pSiblingEntry != NULL)
|
|
break;
|
|
|
|
}
|
|
if (pEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
}
|
|
|
|
if (pEntryRealAddress)
|
|
{
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
|
|
pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
}
|
|
else
|
|
{
|
|
pEntry = NULL;
|
|
}
|
|
}
|
|
dprintf("==============================================================================\n");
|
|
}
|
|
|
|
//---[ zombieq ]-------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Trolls the DNT for queues that are marked as empty, yet are not in
|
|
// in the empty list
|
|
// Parameters:
|
|
// Virtual Server Instance - virtual server ID of server to dump
|
|
// Global Server list (optional) - Head of virtual server list
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/30/98 - MikeSwa Created
|
|
// 3/19/2001 - MikeSwa Modified from linkstate
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(zombieq)
|
|
{
|
|
DWORD dwInstance = 0;
|
|
PLIST_ENTRY pliHead = NULL;
|
|
PLIST_ENTRY pliCurrent = NULL;
|
|
BYTE pBuffer[sizeof(CAQSvrInst)] = {'\0'};
|
|
CAQSvrInst *paqinst = (CAQSvrInst *) pBuffer;
|
|
DOMAIN_NAME_TABLE *pdnt = NULL;
|
|
PVOID pvAQueue = NULL;
|
|
LIST_ENTRY liCurrent;
|
|
BOOL fFound = FALSE;
|
|
CHAR szArgBuffer[20];
|
|
LPSTR szCurrentArg = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
|
|
PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
|
|
BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
|
|
CHAR szFinalDest[MAX_DOM_PATH_SIZE];
|
|
BYTE pbLMQ[sizeof(CLinkMsgQueue)];
|
|
BYTE pbDomainEntry[sizeof(CDomainEntry)];
|
|
BYTE pbDMQ[sizeof(CDestMsgQueue)];
|
|
CLinkMsgQueue *plmq = (CLinkMsgQueue *) pbLMQ;
|
|
CDomainEntry *pdentry = (CDomainEntry *) pbDomainEntry;
|
|
CDestMsgQueue *pdmq = (CDestMsgQueue *) pbDMQ;
|
|
DWORD *pdwGuid = NULL;
|
|
DWORD dwMsgId = 0;
|
|
DWORD dwFacility = 0;
|
|
DWORD cZombieQueues = 0; //Queues that are marked as empty but not in empty list
|
|
DWORD cPristineZombieQueues = 0; //Zombie queues that have never had a message on them
|
|
DWORD cZombieQueuesInUse = 0; //Zombie queues that have a refcount
|
|
DWORD cEntries = 0;
|
|
DWORD cZombieEntries = 0;
|
|
DWORD cQueues = 0;
|
|
const DWORD MAX_DBG_MESSAGE_TYPES = 1000;
|
|
DWORD rgdwMessageTypes[MAX_DBG_MESSAGE_TYPES]; //array of message types we have found
|
|
DWORD cMessageTypes = 0;
|
|
DWORD iLastMessageType = 0;
|
|
DWORD iCurrentMessageType = 0;
|
|
DWORD iCurrentPri = 0;
|
|
BOOL fFoundFifoQ = FALSE;
|
|
BOOL fZombieQueueInUse = FALSE;
|
|
LPSTR szScanStatus = "FAILED";
|
|
|
|
ZeroMemory(rgdwMessageTypes, sizeof(rgdwMessageTypes));
|
|
|
|
CheckVersion(DebugArgs);
|
|
|
|
if (!szArg || ('\0' == szArg[0]))
|
|
{
|
|
//Assume the first instance
|
|
dwInstance = 1;
|
|
pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
else
|
|
{
|
|
strcpy(szArgBuffer, szArg);
|
|
|
|
szCurrentArg = strtok(szArgBuffer, " ");
|
|
|
|
if (szCurrentArg)
|
|
{
|
|
dwInstance = (DWORD)GetExpression(szCurrentArg);
|
|
|
|
szCurrentArg = strtok(NULL, " ");
|
|
if (szCurrentArg)
|
|
pliHead = (PLIST_ENTRY) GetExpression(szCurrentArg);
|
|
else
|
|
pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
|
|
}
|
|
}
|
|
|
|
|
|
if (!pliHead)
|
|
{
|
|
dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
|
|
dprintf(" If you are using windbg, you should specify the value as the\n");
|
|
dprintf(" 2nd argument. You can determine the address value by typeing:\n");
|
|
dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(pliHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @0x%08X\n", pliHead);
|
|
return;
|
|
}
|
|
|
|
while (liCurrent.Flink != pliHead)
|
|
{
|
|
pvAQueue = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
|
|
|
|
if (!ReadMemory(pvAQueue, paqinst, sizeof(CAQSvrInst), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pvAQueue);
|
|
return;
|
|
}
|
|
|
|
//Check the signature
|
|
if (CATMSGQ_SIG != paqinst->m_dwSignature)
|
|
{
|
|
dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pvAQueue, liCurrent.Flink);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (paqinst->m_dwServerInstance == dwInstance)
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pliCurrent = liCurrent.Flink;
|
|
|
|
if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read entry @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
|
|
if (pliCurrent == liCurrent.Flink)
|
|
{
|
|
dprintf("ERROR: Loop in LIST_ENTRY @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!fFound)
|
|
{
|
|
dprintf("Requested instance not found.\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("Using Server instance %d @0x%08X\n", dwInstance, pvAQueue);
|
|
//Use our current instance to dump all of the interesting bits
|
|
|
|
pdnt = &(paqinst->m_dmt.m_dnt);
|
|
pEntry = &(pdnt->RootEntry);
|
|
|
|
while(pEntry)
|
|
{
|
|
cEntries++;
|
|
//
|
|
// Check to see if the user pressed ctrl-C
|
|
//
|
|
if (CheckControlC())
|
|
{
|
|
szScanStatus = "FAILED - User ctrl-c";
|
|
goto Exit;
|
|
}
|
|
|
|
//We are not interested in wildcard data
|
|
if (pEntry->pData)
|
|
{
|
|
//Display link state information
|
|
if (!ReadMemory(pEntry->pData, pbDomainEntry, sizeof(CDomainEntry), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read domain entry from @0x%08X\n", pEntry->pData);
|
|
return;
|
|
}
|
|
|
|
pliHead = (PLIST_ENTRY) (((BYTE *)pEntry->pData) + FIELD_OFFSET(CDomainEntry, m_liDestQueues));
|
|
pliCurrent = pdentry->m_liDestQueues.Flink;
|
|
|
|
//Get final destination string
|
|
if (!ReadMemory(pdentry->m_szDomainName, szFinalDest, pdentry->m_cbDomainName, NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read final destination name from @0x%08X\n",
|
|
pdentry->m_szDomainName);
|
|
return;
|
|
}
|
|
|
|
szFinalDest[pdentry->m_cbDomainName] = '\0';
|
|
|
|
//
|
|
// Does this entry have any queues or links
|
|
//
|
|
if (!pdentry->m_cQueues && !pdentry->m_cLinks)
|
|
cZombieEntries++;
|
|
|
|
//Loop and display each DMQ
|
|
while (pliHead != pliCurrent)
|
|
{
|
|
cQueues++;
|
|
//
|
|
// Check to see if the user pressed ctrl-C
|
|
//
|
|
if (CheckControlC())
|
|
{
|
|
szScanStatus = "FAILED - User ctrl-c";
|
|
goto Exit;
|
|
}
|
|
|
|
if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read link LIST_ENTRY @0x%08X\n", pliCurrent);
|
|
return;
|
|
}
|
|
|
|
if (!ReadMemory(CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
|
|
pbDMQ, sizeof(CDestMsgQueue), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read DMQ @0x%08X\n",
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
|
|
return;
|
|
}
|
|
|
|
//Verify DMQ Signature
|
|
if (DESTMSGQ_SIG != pdmq->m_dwSignature)
|
|
{
|
|
dprintf("ERROR: Invalid DMQ signature for CDestMsgQueue@0x%08X (from LIST_ENTRY) @0x%08X\n",
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
|
|
pliCurrent);
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// It is a zombie if it is marked as empty, but not in the empty list.
|
|
//
|
|
if ((pdmq->m_dwFlags & CDestMsgQueue::DMQ_EMPTY) &&
|
|
!pdmq->m_liEmptyDMQs.Flink &&
|
|
!pdmq->m_liEmptyDMQs.Blink &&
|
|
!pdmq->m_aqstats.m_cMsgs &&
|
|
!pdmq->m_aqstats.m_cRetryMsgs)
|
|
{
|
|
cZombieQueues++;
|
|
|
|
//
|
|
// Look at the refcount. If it is 1 (or 2 with an LMQ) then
|
|
// it is unlikley that it is currently in use
|
|
//
|
|
fZombieQueueInUse = FALSE;
|
|
if (!((1 == *(((DWORD *)pdmq) + 3)) ||
|
|
((2 == *(((DWORD *)pdmq) + 3)) && pdmq->m_plmq)))
|
|
{
|
|
cZombieQueuesInUse++;
|
|
fZombieQueueInUse = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check and see if this has *ever* had a message queued on it.
|
|
//
|
|
fFoundFifoQ = FALSE;
|
|
for (iCurrentPri = 0; iCurrentPri < NUM_PRIORITIES; iCurrentPri++)
|
|
{
|
|
if (pdmq->m_rgpfqQueues[iCurrentPri])
|
|
{
|
|
fFoundFifoQ = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fFoundFifoQ)
|
|
cPristineZombieQueues++;
|
|
//
|
|
// Have we see this message type before?
|
|
//
|
|
if (rgdwMessageTypes[iLastMessageType] != pdmq->m_aqmt.m_dwMessageType)
|
|
{
|
|
for (iCurrentMessageType = 0;
|
|
iCurrentMessageType < MAX_DBG_MESSAGE_TYPES;
|
|
iCurrentMessageType++)
|
|
{
|
|
if (!rgdwMessageTypes[iCurrentMessageType])
|
|
{
|
|
rgdwMessageTypes[iCurrentMessageType] = pdmq->m_aqmt.m_dwMessageType;
|
|
cMessageTypes++;
|
|
break;
|
|
}
|
|
|
|
if (rgdwMessageTypes[iCurrentMessageType] == pdmq->m_aqmt.m_dwMessageType)
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//Print some interesting data
|
|
dprintf("%s%s| %-29s | CDestMsgQueue@0x%08X | 0x%08X\n",
|
|
fZombieQueueInUse ? "!" : "",
|
|
fFoundFifoQ ? "*" : "",
|
|
szFinalDest,
|
|
CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
|
|
pdmq->m_aqmt.m_dwMessageType);
|
|
}
|
|
pliCurrent = liCurrent.Flink;
|
|
}
|
|
}
|
|
|
|
//Now determine what the "next" entry is
|
|
if (pEntry->pFirstChildEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pFirstChildEntry;
|
|
}
|
|
else if (pEntry->pSiblingEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
else
|
|
{
|
|
for (pEntryRealAddress = pEntry->pParentEntry;
|
|
pEntryRealAddress != NULL;
|
|
pEntryRealAddress = pEntry->pParentEntry)
|
|
{
|
|
//must read parent entry into our buffer
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
|
|
if (pEntry->pSiblingEntry != NULL)
|
|
break;
|
|
|
|
}
|
|
if (pEntry != NULL)
|
|
{
|
|
pEntryRealAddress = pEntry->pSiblingEntry;
|
|
}
|
|
}
|
|
|
|
if (pEntryRealAddress)
|
|
{
|
|
if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
|
|
{
|
|
dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
|
|
pEntryRealAddress);
|
|
pEntry = NULL;
|
|
break;
|
|
}
|
|
pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
|
|
}
|
|
else
|
|
{
|
|
pEntry = NULL;
|
|
}
|
|
}
|
|
szScanStatus = "COMPLETED";
|
|
Exit:
|
|
dprintf("==============================================================================\n");
|
|
dprintf("SCAN %s\n", szScanStatus);
|
|
dprintf("==============================================================================\n");
|
|
dprintf("%d Total Zombie Queues (%d bytes) \n", cZombieQueues,
|
|
cZombieQueues*sizeof(CDestMsgQueue));
|
|
dprintf("%d Total Zombie Queues that have never had a message queued\n", cPristineZombieQueues);
|
|
dprintf("%d Total Zombie Queues that may be in use \n", cZombieQueuesInUse);
|
|
dprintf("%d Total Zombie Message Types\n", cMessageTypes);
|
|
dprintf("%d Total Queues\n", cQueues);
|
|
dprintf("%d Total Domain Entires\n", cEntries);
|
|
dprintf("%d Total Zombie Domain Entires (%d bytes) \n", cZombieEntries,
|
|
cZombieEntries*sizeof(CDomainEntry));
|
|
}
|
|
|
|
//---[ dsncontexthash ]--------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Calculates the dsncontexthash for a given filename. Will also dump
|
|
// common hash names
|
|
// Parameters:
|
|
// filename to dump
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/30/98 - MikeSwa Created
|
|
// 3/19/2001 - MikeSwa Modified from linkstate
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
AQ_DEBUG_EXTENSION_IMP(dsncontexthash)
|
|
{
|
|
DWORD dwHash = 0;
|
|
const DWORD MAX_DSN_HASH_FILES = 10;
|
|
CHAR rgszWellKnown[MAX_DSN_HASH_FILES][20] =
|
|
{
|
|
"msgref.cpp",
|
|
"aqinst.cpp",
|
|
"mailadmq.cpp",
|
|
"dsnevent.h"
|
|
""
|
|
};
|
|
DWORD i = 0;
|
|
LPSTR szCurrentWellKnown = rgszWellKnown[0];
|
|
|
|
if (szArg && ('\0' != szArg[0]))
|
|
{
|
|
dwHash = dwDSNContextHash(szArg,strlen(szArg));
|
|
dprintf ("DSNContext has for %s is 0x%08X\n",
|
|
szArg, dwHash);
|
|
}
|
|
|
|
//
|
|
// If no arg just dump the well known file names.
|
|
//
|
|
for (DWORD i = 0; i < MAX_DSN_HASH_FILES; i++)
|
|
{
|
|
szCurrentWellKnown = rgszWellKnown[i];
|
|
if (!szCurrentWellKnown || !*szCurrentWellKnown)
|
|
break;
|
|
dwHash = dwDSNContextHash(szCurrentWellKnown,
|
|
strlen(szCurrentWellKnown));
|
|
dprintf ("DSNContext has for %s is 0x%08X\n",
|
|
szCurrentWellKnown, dwHash);
|
|
szCurrentWellKnown++;
|
|
}
|
|
|
|
}
|