|
|
//-----------------------------------------------------------------------------
//
//
// 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++; }
}
|