Leaked source code of windows server 2003
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

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: aqdbgext.cpp
  5. //
  6. // Description: Advanced Queuing Debug Extensions.
  7. //
  8. // Author: mikeswa
  9. //
  10. // Copyright (C) 1998 Microsoft Corporation
  11. //
  12. //-----------------------------------------------------------------------------
  13. #define _ANSI_UNICODE_STRINGS_DEFINED_
  14. #include "aqincs.h"
  15. #ifdef PLATINUM
  16. #include "phatqdbg.h"
  17. #include <ptrwinst.h>
  18. #include <ptntintf.h>
  19. #else
  20. #include "aqdbgext.h"
  21. #include <rwinst.h>
  22. #endif //PLATINUM
  23. #include <aqinst.h>
  24. #include <domhash.h>
  25. #include <destmsgq.h>
  26. #include <linkmsgq.h>
  27. #include <hashentr.h>
  28. #include <fifoqdbg.h>
  29. #include <dsnevent.h>
  30. extern DWORD g_cbClasses;
  31. extern DWORD g_dwFlavorSignature;
  32. BOOL g_fVersionChecked = FALSE;
  33. #define AQ_MIN(x, y) ((x) > (y) ? (y) : (x))
  34. HANDLE g_hTransHeap; //Needed for to link because of transmem.h
  35. const DWORD MAX_DOM_PATH_SIZE = 512;
  36. const CHAR _LINK_STATE_UP[] = "UP ";
  37. const CHAR _LINK_STATE_DOWN[] = "DOWN ";
  38. const CHAR _LINK_STATE_ACTIVE[] = "ACTIVE ";
  39. const CHAR _LINK_STATE_TURN[] = "TURN ";
  40. const CHAR _LINK_STATE_RETRY[] = "RETRY ";
  41. const CHAR _LINK_STATE_DSN[] = "DSN ";
  42. const CHAR _LINK_STATE_SPECIAL[] = "SPECIAL ";
  43. #define LINK_STATE_UP (LPSTR) _LINK_STATE_UP
  44. #define LINK_STATE_DOWN (LPSTR) _LINK_STATE_DOWN
  45. #define LINK_STATE_ACTIVE (LPSTR) _LINK_STATE_ACTIVE
  46. #define LINK_STATE_TURN (LPSTR) _LINK_STATE_TURN
  47. #define LINK_STATE_RETRY (LPSTR) _LINK_STATE_RETRY
  48. #define LINK_STATE_DSN (LPSTR) _LINK_STATE_DSN
  49. #define LINK_STATE_SPECIAL (LPSTR) _LINK_STATE_SPECIAL
  50. //lower case function names
  51. AQ_DEBUG_EXTENSION_IMP(dumpservers) {DumpServers(DebugArgs);}
  52. AQ_DEBUG_EXTENSION_IMP(offsets) {Offsets(DebugArgs);}
  53. AQ_DEBUG_EXTENSION_IMP(dumpdnt) {DumpDNT(DebugArgs);}
  54. AQ_DEBUG_EXTENSION_IMP(Offsets)
  55. {
  56. dprintf("CDestMsgQueue m_liDomainEntryDMQs - 0x%X\n", FIELD_OFFSET(CDestMsgQueue, m_liDomainEntryDMQs));
  57. dprintf("CDestMsgQueue m_liEmptyDMQs - 0x%X\n", FIELD_OFFSET(CDestMsgQueue, m_liEmptyDMQs));
  58. dprintf("CLinkMsgQueue m_liLinks - 0x%X\n", FIELD_OFFSET(CLinkMsgQueue, m_liLinks));
  59. dprintf("CLinkMsgQueue m_liConnections - 0x%X\n", FIELD_OFFSET(CLinkMsgQueue, m_liConnections));
  60. dprintf("CAQSvrInst m_liVirtualServers - 0x%X\n", FIELD_OFFSET(CAQSvrInst, m_liVirtualServers));
  61. dprintf("CRETRY_HASH_ENTRY m_QLEntry - 0x%X\n", FIELD_OFFSET(CRETRY_HASH_ENTRY, m_QLEntry));
  62. dprintf("CRETRY_HASH_ENTRY m_HLEntry - 0x%X\n", FIELD_OFFSET(CRETRY_HASH_ENTRY, m_HLEntry));
  63. dprintf("CShareLockInst m_liLocks - 0x%X\n", FIELD_OFFSET(CShareLockInst, m_liLocks));
  64. }
  65. AQ_DEBUG_EXTENSION_IMP(dumpoffsets)
  66. {
  67. _dumpoffsets(hCurrentProcess, hCurrentThread,
  68. dwCurrentPc, pExtensionApis, szArg);
  69. }
  70. //---[ cpoolusage ]------------------------------------------------------------
  71. //
  72. //
  73. // Description:
  74. // Dumps the CPool usage for our known CPools.
  75. // Parameters:
  76. // -
  77. // Returns:
  78. // -
  79. // History:
  80. // 5/31/2000 - Mikeswa Created
  81. //
  82. //-----------------------------------------------------------------------------
  83. AQ_DEBUG_EXTENSION_IMP(cpoolusage)
  84. {
  85. CHAR rgKnownCPools[][200] = {
  86. // "pttrace!g_pFreePool",
  87. "exstrace!g_pFreePool",
  88. // "phatcat!CPoolBuffer__sm_PoolNHeapBuffersPool",
  89. "aqueue!CQuickList__s_QuickListPool",
  90. "aqueue!CSMTPConn__s_SMTPConnPool",
  91. "aqueue!CMsgRef__s_MsgRefPool",
  92. "aqueue!CAQMsgGuidListEntry__s_MsgGuidListEntryPool",
  93. "aqueue!CAsyncWorkQueueItem__s_CAsyncWorkQueueItemPool",
  94. "aqueue!CRETRY_HASH_ENTRY__PoolForHashEntries",
  95. // "drviis!CIMsgWrapper__m_CIMsgWrapperPool",
  96. // "drviis!CQueueItem__m_CQueueItemPool",
  97. "mailmsg!CBlockMemoryAccess__m_Pool",
  98. "mailmsg!CMsg__m_Pool",
  99. "mailmsg!CMailMsgRecipientsAdd__m_Pool",
  100. "smtpsvc!SMTP_CONNECTION__Pool",
  101. "smtpsvc!SMTP_CONNOUT__Pool",
  102. "smtpsvc!CAddr__Pool",
  103. "smtpsvc!CAsyncMx__Pool",
  104. "smtpsvc!CAsyncSmtpDns__Pool",
  105. "smtpsvc!CBuffer__Pool",
  106. "smtpsvc!CIoBuffer__Pool",
  107. "smtpsvc!CBlockMemoryAccess__m_Pool",
  108. "smtpsvc!CDropDir__m_Pool",
  109. ""
  110. };
  111. DWORD rgdwPool[5];
  112. DWORD cTotalBytes = 0;
  113. DWORD cCurrentBytes = 0;
  114. DWORD cInstanceBytes = 0;
  115. DWORD cInstances = 0;
  116. DWORD dwSignature = 0;
  117. CHAR *pch = NULL;
  118. DWORD i = 0;
  119. PVOID pvPool = NULL;
  120. //
  121. // Loop over all known pools and display data
  122. //
  123. dprintf("Total Bytes\t# Instances \tInstance Size \tSignature\tName\n");
  124. dprintf("=================================================================\n");
  125. while (rgKnownCPools[i] && rgKnownCPools[i][0]) {
  126. pvPool = (PVOID) GetExpression(rgKnownCPools[i]);
  127. if (!pvPool ||
  128. !ReadMemory(pvPool, rgdwPool, sizeof(rgdwPool), NULL)) {
  129. dprintf("Unable to read pool %s at %p\n", rgKnownCPools[i], pvPool);
  130. } else {
  131. cInstances = rgdwPool[3];
  132. cInstanceBytes = rgdwPool[2];
  133. dwSignature = rgdwPool[0];
  134. pch = (CHAR *) &dwSignature;
  135. dprintf("%d\t\t%d\t\t%d\t\t0x%08X\t%s\n",
  136. cInstanceBytes*cInstances, cInstances,
  137. cInstanceBytes, rgdwPool[0], rgKnownCPools[i]);
  138. cTotalBytes += cInstanceBytes*cInstances;
  139. }
  140. i++;
  141. }
  142. dprintf("=================================================================\n");
  143. dprintf("\tTotal Bytes = %d\n\n", cTotalBytes);
  144. }
  145. //---[ remotecmd ]------------------------------------------------------------
  146. //
  147. //
  148. // Description:
  149. // start a remote cmd window
  150. // Parameters:
  151. // name of the pipe
  152. // Returns:
  153. // -
  154. // History:
  155. // 5/31/2000 - AWetmore Created
  156. //
  157. //-----------------------------------------------------------------------------
  158. AQ_DEBUG_EXTENSION_IMP(remotecmd)
  159. {
  160. char szParameters[1024];
  161. PROCESS_INFORMATION pi;
  162. STARTUPINFO si;
  163. if (!szArg || ('\0' == szArg[0]))
  164. goto Usage;
  165. _snprintf(szParameters, 1024, "remote /s cmd %s", szArg);
  166. dprintf("\nRunning %s\n", szParameters);
  167. ZeroMemory(&si, sizeof(STARTUPINFO));
  168. si.cb = sizeof(STARTUPINFO);
  169. ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
  170. if (!CreateProcess(NULL,
  171. szParameters,
  172. NULL,
  173. NULL,
  174. FALSE,
  175. CREATE_NEW_CONSOLE,
  176. NULL,
  177. NULL,
  178. &si,
  179. &pi))
  180. {
  181. dprintf("CreateProcess failed with %u\n", GetLastError());
  182. } else {
  183. dprintf("Started process %i\n", pi.dwProcessId);
  184. }
  185. Exit:
  186. dprintf("\n");
  187. return;
  188. Usage:
  189. //
  190. // Display usage message
  191. //
  192. dprintf("\nUsage:\n");
  193. dprintf("\tremotecmd <pipename>\n");
  194. goto Exit;
  195. }
  196. //---[ findbytes ]-------------------------------------------------------------
  197. //
  198. //
  199. // Description:
  200. // Searches for a given byte-pattern in a memory address sapce
  201. // Parameters:
  202. // Pattern of bytes to search for. Expected format is a sequence of
  203. // space separated hex digits.
  204. // Returns:
  205. // -
  206. // History:
  207. // 5/9/2000 - MikeSwa Created
  208. //
  209. //-----------------------------------------------------------------------------
  210. AQ_DEBUG_EXTENSION_IMP(findbytes)
  211. {
  212. #ifdef WIN64
  213. const DWORD_PTR cbVMSize = 0xFFFFFFFFFFFFFFFF;
  214. #else //not WIN64
  215. const DWORD_PTR cbVMSize = 0xFFFFFFFF;
  216. #endif //WIN64
  217. BYTE rgbBytesToFind[200];
  218. LONG lCurrentValue = 0;
  219. CHAR rgchCurrentValue[3] = "00";
  220. LPSTR szStop = NULL;
  221. DWORD_PTR cBytesToFind = 0;
  222. DWORD_PTR cChunksChecked = 0;
  223. DWORD_PTR cChunkSize = 0;
  224. DWORD_PTR iChunk = 0;
  225. BYTE pbChunk[0x1000];
  226. PBYTE pbStopAddr = pbChunk + sizeof(pbChunk);
  227. PBYTE pbCurrent = NULL;
  228. DWORD_PTR cChunks = cbVMSize/sizeof(pbChunk);
  229. DWORD_PTR cChunksInPercent = 1;
  230. DWORD_PTR pvEffectiveAddressOtherProc = NULL;
  231. DWORD cComplaints = 0;
  232. DWORD cMemchkMatches = 0;
  233. DWORD cFullSigMatches = 0;
  234. LPCSTR szCurrentArg = szArg;
  235. if (!szArg || ('\0' == szArg[0]))
  236. goto Usage;
  237. //
  238. // Parse command line args
  239. //
  240. while (*szCurrentArg)
  241. {
  242. //
  243. // Loop over whitespace
  244. //
  245. while (*szCurrentArg && isspace(*szCurrentArg)) szCurrentArg++;
  246. //
  247. // Make sure we have at least pair of characters as expected
  248. //
  249. if (!*(szCurrentArg+1))
  250. break;
  251. //
  252. // Convert from hex characters to binary
  253. //
  254. lCurrentValue = strtol(szCurrentArg, &szStop, 16);
  255. if ((lCurrentValue > 0xFF) || (lCurrentValue < 0))
  256. goto Usage;
  257. //
  258. // Copy to our search buffer
  259. //
  260. rgbBytesToFind[cBytesToFind] = (BYTE) lCurrentValue;
  261. cBytesToFind++;
  262. //
  263. // Make sure our search buffer is big enough for the next byte
  264. //
  265. if (cBytesToFind >= sizeof(rgbBytesToFind))
  266. {
  267. dprintf("Search for max pattern of %d bytes\n", cBytesToFind);
  268. break;
  269. }
  270. szCurrentArg += 2; //Skip to next known whitespace
  271. }
  272. if (!cBytesToFind)
  273. {
  274. dprintf("\nYou must specify at least one byte to search for\n");
  275. goto Usage;
  276. }
  277. //
  278. // Used to display progress
  279. //
  280. cChunksInPercent = cChunks/100;
  281. //
  282. // Calculate memory size for 32-bit machines
  283. //
  284. cChunkSize = cbVMSize/cChunks;
  285. if (cChunkSize < 1024)
  286. {
  287. dprintf("ERROR: Chunk size of 0x%p is too small", cChunkSize);
  288. goto Exit;
  289. }
  290. //
  291. // Make sure we are cool wrt to buffer size
  292. //
  293. if (cChunkSize > sizeof(pbChunk))
  294. {
  295. dprintf("ERROR: Chunksize of 0x%p is larger than max size of 0x%p",
  296. cChunkSize, sizeof(pbChunk));
  297. goto Exit;
  298. }
  299. //
  300. // Loop over chunks --
  301. // $$REVIEW - does not find patterns that span 1K chunks...
  302. // this is probably OK, since this is an unlikely scenario. Most
  303. // byte patterns will be DWORD (signatures) or pointer sized.
  304. //
  305. for (iChunk = 0; iChunk < cChunks; iChunk++)
  306. {
  307. //
  308. // Check to see if the user pressed ctrl-c
  309. //
  310. if (CheckControlC())
  311. {
  312. goto Exit;
  313. }
  314. //
  315. // Give some status
  316. //
  317. if ((iChunk % cChunksInPercent) == 0)
  318. dprintf(".");
  319. //
  320. // Address should be page aligned
  321. //
  322. if (((iChunk*cChunkSize) & 0xFFF) && (cComplaints < 100))
  323. {
  324. cComplaints++;
  325. dprintf("0x%p not alligned at index %d", (iChunk*cChunkSize), iChunk);
  326. }
  327. //
  328. // Do a memory search for the first byte
  329. //
  330. if (!ReadMemory(iChunk*cChunkSize, pbChunk, (DWORD)cChunkSize, NULL))
  331. continue; //on to the next buffer chunk
  332. //
  333. // Now that we have a chunk... look for our sig
  334. //
  335. pbCurrent = pbChunk;
  336. while (pbCurrent < pbStopAddr-cBytesToFind)
  337. {
  338. pbCurrent = (PBYTE) memchr(pbCurrent,
  339. rgbBytesToFind[0],
  340. pbStopAddr-pbCurrent);
  341. //
  342. // See if we have a match
  343. if (!pbCurrent)
  344. break;
  345. cMemchkMatches++;
  346. pvEffectiveAddressOtherProc = iChunk*cChunkSize+(pbCurrent-pbChunk);
  347. //
  348. // See if the full pattern matches
  349. //
  350. if (!memcmp(rgbBytesToFind, pbCurrent, cBytesToFind))
  351. {
  352. cFullSigMatches++;
  353. dprintf("\nFound match at 0x%p\n", pvEffectiveAddressOtherProc);
  354. }
  355. if (0 != memcmp(rgbBytesToFind, pbCurrent, 1))
  356. {
  357. cComplaints++;
  358. if (cComplaints < 100)
  359. dprintf("Messed up %02X %02X - %02X %02X\n",
  360. rgbBytesToFind[0], rgbBytesToFind[1],
  361. pbCurrent[0], pbCurrent[1]);
  362. }
  363. pbCurrent++;
  364. }
  365. cChunksChecked++;
  366. }
  367. //
  368. // Give some summary information
  369. //
  370. dprintf("\nChecked 0x%p chunks (%d%%) searching from 0x%p to 0x%p",
  371. cChunksChecked,
  372. (DWORD)(100*cChunksChecked/cChunks), NULL,
  373. cChunkSize*(cChunks+1)-1);
  374. dprintf("\nFound %d partial matches and %d full matches",
  375. cMemchkMatches, cFullSigMatches);
  376. Exit:
  377. dprintf("\n");
  378. return;
  379. Usage:
  380. //
  381. // Display usage message
  382. //
  383. if (szCurrentArg && *szCurrentArg)
  384. dprintf("Error at %s\n", szCurrentArg);
  385. dprintf("\nUsage:\n");
  386. dprintf("\tfindbytes <aa> [<bb> ...]\n");
  387. dprintf("\t\tBytes should be specifed as 2 hexadecimal characters\n");
  388. dprintf("\nExamples:\n");
  389. dprintf("\tTo search for the signature \"LMQ \"\n");
  390. dprintf("\t\tfindbytes %02X %02X %02X %02X\n", 'L', 'M', 'Q', ' ');
  391. goto Exit;
  392. }
  393. //---[ findsig ]---------------------------------------------------------------
  394. //
  395. //
  396. // Description:
  397. // Searches for a given class signature in a memory address sapce
  398. // Parameters:
  399. // The Siganature to look for.
  400. // Returns:
  401. // -
  402. // History:
  403. // 5/3/2000 - MikeSwa Created
  404. //
  405. //-----------------------------------------------------------------------------
  406. AQ_DEBUG_EXTENSION_IMP(findsig)
  407. {
  408. CHAR szNewArg[200];
  409. LPCSTR szCurrentArg = szArg;
  410. CHAR szSig[5] = " ";
  411. DWORD iChar = 0;
  412. if (!szArg || ('\0' == szArg[0]))
  413. goto Usage;
  414. //
  415. // Loop over whitespace
  416. //
  417. while (*szCurrentArg && isspace(*szCurrentArg)) szCurrentArg++;
  418. //
  419. // Grab the first 4 characters and convert them to binary
  420. //
  421. for( iChar = 0; iChar < 4; iChar++)
  422. {
  423. if (!szCurrentArg[iChar])
  424. break;
  425. szSig[iChar] = szCurrentArg[iChar];
  426. }
  427. dprintf("Searching for Signature \"%s\"...\n", szSig);
  428. sprintf(szNewArg, "%02X %02X %02X %02X", szSig[0], szSig[1], szSig[2], szSig[3]);
  429. //
  430. // Just use the code in findbytes to do the actual search
  431. //
  432. dprintf("Calling findbytes %s\n", szNewArg);
  433. findbytes(hCurrentProcess, hCurrentThread, dwCurrentPc,
  434. pExtensionApis, szNewArg);
  435. Exit:
  436. return;
  437. Usage:
  438. dprintf("\nUsage:\n");
  439. dprintf("\tfindsig <XXXX>\n");
  440. goto Exit;
  441. }
  442. //---[ hashthread ]------------------------------------------------------------
  443. //
  444. //
  445. // Description:
  446. // Uses the CThreadIdBlock hashing mechanism to return the hashed value
  447. // for a thread.
  448. // Parameters:
  449. // Thread Id to hash
  450. // Max hash value
  451. // Returns:
  452. // -
  453. // History:
  454. // 8/9/99 - MikeSwa Created
  455. //
  456. //-----------------------------------------------------------------------------
  457. AQ_DEBUG_EXTENSION_IMP(hashthread)
  458. {
  459. //Arguement should be thread Id
  460. DWORD dwThreadId = GetCurrentThreadId();
  461. DWORD dwMax = 1000;
  462. DWORD dwThreadHash = 0;
  463. CHAR szArgBuffer[200];
  464. LPSTR szCurrentArg = NULL;
  465. if (!szArg || ('\0' == szArg[0]))
  466. {
  467. dprintf("Warning... using default thead id and max\n");
  468. }
  469. else
  470. {
  471. strcpy(szArgBuffer, szArg);
  472. szCurrentArg = strtok(szArgBuffer, " ");
  473. if (szCurrentArg)
  474. {
  475. dwThreadId = (DWORD)GetExpression(szCurrentArg);
  476. szCurrentArg = strtok(NULL, " ");
  477. if (szCurrentArg)
  478. dwMax = (DWORD) GetExpression(szCurrentArg);
  479. else
  480. dprintf("Warning... using default max hash\n");
  481. }
  482. }
  483. //Try hashing the ID
  484. dwThreadHash = dwHashThreadId(dwThreadId, dwMax);
  485. dprintf("Thread Id 0x%0X hashes to index 0x%0X (%d) with max 0x%08X (%d)\n", dwThreadId,
  486. dwThreadHash, dwThreadHash, dwMax, dwMax);
  487. }
  488. //---[ dumplock ]-------------------------------------------------------------
  489. //
  490. //
  491. // Description:
  492. // Dumps all of the information in the CThreadIdBlocks for a given
  493. // CShareLockInst.
  494. // Parameters:
  495. // Address of CShareLockInst
  496. // Returns:
  497. // -
  498. // History:
  499. // 8/9/99 - MikeSwa Created
  500. //
  501. //-----------------------------------------------------------------------------
  502. AQ_DEBUG_EXTENSION_IMP(dumplock)
  503. {
  504. BYTE pbBuffer[sizeof(CShareLockInst)];
  505. BYTE pbThreadBlocks[1000*sizeof(CThreadIdBlock)];
  506. PVOID pvLock = NULL;
  507. PVOID pvNextBlock = NULL;
  508. CThreadIdBlock tblkCurrent;
  509. CThreadIdBlock *ptblkCurrent = NULL;
  510. CThreadIdBlock *ptblkArray = NULL;
  511. DWORD cNumBlocks = 0;
  512. DWORD iBlock = 0;
  513. DWORD cThreads = 0;
  514. DWORD cLockCount = 0;
  515. DWORD cLockedThreads = 0;
  516. BOOL fDisplayedHashHeader = FALSE;
  517. ZeroMemory(pbBuffer, sizeof(pbBuffer));
  518. ZeroMemory(pbThreadBlocks, sizeof(pbThreadBlocks));
  519. if (!szArg || ('\0' == szArg[0]) || !(pvLock = (PVOID) GetExpression(szArg)))
  520. {
  521. dprintf("You must specify a lock address\n");
  522. return;
  523. }
  524. //read the whole lock into our buffer
  525. if (!ReadMemory(pvLock, &pbBuffer, sizeof(pbBuffer), NULL))
  526. {
  527. dprintf("Error unable read memory at 0x%0X\n", pvLock);
  528. return;
  529. }
  530. cNumBlocks = ((CShareLockInst *)pbBuffer)->m_cMaxTrackedSharedThreadIDs;
  531. pvNextBlock = ((CShareLockInst *)pbBuffer)->m_rgtblkSharedThreadIDs;
  532. if (!cNumBlocks || !pvNextBlock)
  533. {
  534. dprintf("Thread tracking is not enabled for this lock");
  535. return;
  536. }
  537. if (cNumBlocks > sizeof(pbThreadBlocks)/sizeof(CThreadIdBlock))
  538. cNumBlocks = sizeof(pbThreadBlocks)/sizeof(CThreadIdBlock);
  539. if (!ReadMemory(pvNextBlock, &pbThreadBlocks,
  540. cNumBlocks*sizeof(CThreadIdBlock), NULL))
  541. {
  542. dprintf("Error, unable to read %d blocks at 0x%0X", cNumBlocks, pvNextBlock);
  543. return;
  544. }
  545. ptblkArray = (CThreadIdBlock *) pbThreadBlocks;
  546. for (iBlock = 0; iBlock < cNumBlocks; iBlock++ && ptblkArray++)
  547. {
  548. ptblkCurrent = ptblkArray;
  549. fDisplayedHashHeader = FALSE;
  550. while (ptblkCurrent)
  551. {
  552. if (ptblkCurrent != ptblkArray)
  553. {
  554. //Read into this process
  555. if (!ReadMemory(ptblkCurrent, &tblkCurrent,
  556. sizeof(CThreadIdBlock), NULL))
  557. {
  558. dprintf("Error reading block at 0x%0X", ptblkCurrent);
  559. break;
  560. }
  561. ptblkCurrent = &tblkCurrent;
  562. }
  563. if (THREAD_ID_BLOCK_SIG != ptblkCurrent->m_dwSignature)
  564. {
  565. dprintf("Warning... bad signature on block 0x%0X\n",
  566. ((BYTE *)pvNextBlock) + iBlock*sizeof(CThreadIdBlock));
  567. break;
  568. }
  569. //See if this block has any data
  570. if (THREAD_ID_BLOCK_UNUSED != ptblkCurrent->m_dwThreadId)
  571. {
  572. //Only dump info if the recursion count is non-zero
  573. if (ptblkCurrent->m_cThreadRecursionCount)
  574. {
  575. if (!fDisplayedHashHeader)
  576. {
  577. fDisplayedHashHeader = TRUE;
  578. dprintf("Thread Hash 0x%0X (%d)\n", iBlock, iBlock);
  579. }
  580. dprintf("%s\tThread 0x%08X has count of %d - Next link of 0x%08X\n",
  581. (ptblkCurrent == ptblkArray) ? "+" : "",
  582. ptblkCurrent->m_dwThreadId,
  583. ptblkCurrent->m_cThreadRecursionCount,
  584. ptblkCurrent->m_ptblkNext);
  585. cLockedThreads++;
  586. }
  587. cThreads++;
  588. cLockCount += ptblkCurrent->m_cThreadRecursionCount;
  589. }
  590. ptblkCurrent = ptblkCurrent->m_ptblkNext;
  591. }
  592. }
  593. dprintf("===================================================================\n");
  594. dprintf("%d threads with %d total lock count (%d threads holding locks)\n",
  595. cThreads, cLockCount, cLockedThreads);
  596. }
  597. //---[ workqueue ]-------------------------------------------------------------
  598. //
  599. //
  600. // Description:
  601. // Dumps a summary of items in the async work queue
  602. // Parameters:
  603. //
  604. // Returns:
  605. //
  606. // History:
  607. // 9/13/99 - MikeSwa Created
  608. //
  609. //-----------------------------------------------------------------------------
  610. AQ_DEBUG_EXTENSION_IMP(workqueue)
  611. {
  612. SETCALLBACKS();
  613. const DWORD MAX_COMPLETION_FUNCTIONS = 10;
  614. PVOID rgpvFnName[MAX_COMPLETION_FUNCTIONS];
  615. DWORD rgcFnCount[MAX_COMPLETION_FUNCTIONS];
  616. BYTE pbWorkItem[sizeof(CAsyncWorkQueueItem)];
  617. PVOID pvQueue = NULL;
  618. PVOID pvWorkItem = NULL;
  619. PVOID pvFn = NULL;
  620. DWORD i = 0;
  621. DWORD cItems = 0;
  622. UCHAR SymbolName[ 200 ];
  623. ULONG_PTR Displacement;
  624. CFifoQueueDbgIterator fifoqdbg(pExtensionApis);
  625. ZeroMemory(&rgpvFnName, sizeof(rgpvFnName));
  626. ZeroMemory(&rgcFnCount, sizeof(rgcFnCount));
  627. ZeroMemory(&pbWorkItem, sizeof(pbWorkItem));
  628. ZeroMemory(&SymbolName, sizeof(SymbolName));
  629. if (!szArg || ('\0' == szArg[0]) ||
  630. !(pvQueue = (PVOID) GetExpression(szArg)))
  631. {
  632. dprintf("You must specify a queue address\n");
  633. return;
  634. }
  635. //Get FifoqOffset
  636. pvQueue = (PVOID) &(((CAsyncWorkQueue *)pvQueue)->m_asyncq.m_fqQueue);
  637. if (!fifoqdbg.fInit(hCurrentProcess, pvQueue))
  638. {
  639. dprintf("Error initializing queue iterator for address 0x%08X\n", pvQueue);
  640. return;
  641. }
  642. while (pvWorkItem = fifoqdbg.pvGetNext())
  643. {
  644. cItems++;
  645. if (!ReadMemory(pvWorkItem, &pbWorkItem, sizeof(pbWorkItem), NULL))
  646. {
  647. dprintf("Error reading memory at 0x%0X\n", pvWorkItem);
  648. continue;
  649. }
  650. pvFn = ((CAsyncWorkQueueItem *)pbWorkItem)->m_pfnCompletion;
  651. for (i = 0; i < MAX_COMPLETION_FUNCTIONS; i++)
  652. {
  653. if (pvFn == rgpvFnName[i])
  654. {
  655. rgcFnCount[i]++;
  656. break;
  657. }
  658. else if (!rgpvFnName[i])
  659. {
  660. rgpvFnName[i] = pvFn;
  661. rgcFnCount[i] = 1;
  662. break;
  663. }
  664. }
  665. }
  666. dprintf("# Calls\t| Address\t\t| Function Name\n");
  667. dprintf("------------------------------------------------------------\n");
  668. for (i = 0; i < MAX_COMPLETION_FUNCTIONS; i++)
  669. {
  670. if (!rgpvFnName[i])
  671. break;
  672. g_lpGetSymbolRoutine( rgpvFnName[i], (PCHAR)SymbolName, &Displacement );
  673. dprintf( "%d\t| 0x%08X\t| %s\n", rgcFnCount[i], rgpvFnName[i], SymbolName);
  674. }
  675. dprintf("------------------------------------------------------------\n");
  676. dprintf("Total %d pending work queue items\n", cItems);
  677. #ifdef NEVER
  678. //Dump fifoqdbg
  679. dprintf("CFifoQueueDbgIterator: page %d, index %d, pages %d\n ",
  680. fifoqdbg.m_iCurrentPage, fifoqdbg.m_iCurrentIndexInPage,
  681. fifoqdbg.m_cPagesLoaded);
  682. #endif
  683. }
  684. //---[ dumpqueue ]-------------------------------------------------------------
  685. //
  686. //
  687. // Description:
  688. // Dumps the *entire* contents of a queue
  689. // Parameters:
  690. // szArg
  691. // - String-ized address of CFifoQ to dump
  692. // - [optional] msg to search for
  693. // Returns:
  694. // -
  695. // History:
  696. // 10/21/1999 - MikeSwa Created
  697. //
  698. //-----------------------------------------------------------------------------
  699. AQ_DEBUG_EXTENSION(dumpqueue)
  700. {
  701. const DWORD cStoppingRule = 10000;
  702. CQueueDbgIterator qdbg(pExtensionApis);
  703. BYTE pbMsgRef[sizeof(CMsgRef)];
  704. PVOID pvMsgRef = NULL;
  705. PVOID pvMailMsg = NULL;
  706. PVOID pvQueue = NULL;
  707. DWORD cItems = 0;
  708. BOOL fIsMsgRef = FALSE;
  709. CHAR szArgBuffer[200];
  710. LPSTR szCurrentArg = NULL;
  711. PVOID pvSearch = NULL;
  712. DWORD cMatchSearch = 0;
  713. if (!szArg || ('\0' == szArg[0]))
  714. {
  715. dprintf("You must specify a queue address\n");
  716. return;
  717. }
  718. else
  719. {
  720. strcpy(szArgBuffer, szArg);
  721. szCurrentArg = strtok(szArgBuffer, " ");
  722. if (szCurrentArg)
  723. {
  724. pvQueue = (PVOID)GetExpression(szCurrentArg);
  725. szCurrentArg = strtok(NULL, " ");
  726. if (szCurrentArg)
  727. pvSearch = (PVOID) GetExpression(szCurrentArg);
  728. }
  729. else
  730. {
  731. pvQueue = (PVOID) GetExpression(szArg);
  732. }
  733. }
  734. if (!pvQueue)
  735. {
  736. dprintf("You must specify a queue address\n");
  737. return;
  738. }
  739. if (!qdbg.fInit(hCurrentProcess, pvQueue))
  740. {
  741. dprintf("Unable to get the a queue for address 0x%X\n", pvQueue);
  742. return;
  743. }
  744. while ((pvMsgRef = qdbg.pvGetNext()) && (cItems++ < cStoppingRule))
  745. {
  746. fIsMsgRef = FALSE;
  747. if (cItems > qdbg.cGetCount())
  748. {
  749. cItems--;
  750. break;
  751. }
  752. //Try to read it as a CMsgRef
  753. if (ReadMemory(pvMsgRef, pbMsgRef, sizeof(pbMsgRef), NULL))
  754. {
  755. if (MSGREF_SIG == ((CMsgRef *)pbMsgRef)->m_dwSignature)
  756. {
  757. fIsMsgRef = TRUE;
  758. pvMailMsg = ((CMsgRef *)pbMsgRef)->m_pIMailMsgProperties;
  759. }
  760. }
  761. //Print it out if it matches our search (or we have no search)
  762. if (!pvSearch || (pvSearch == pvMsgRef) || (pvSearch == pvMailMsg))
  763. {
  764. cMatchSearch++;
  765. if (pvSearch)
  766. dprintf("\n****\n");
  767. if (fIsMsgRef)
  768. dprintf("\t0x%08X\t0x%08X\n", pvMsgRef, pvMailMsg);
  769. else
  770. dprintf("\t0x%08X\n", pvMsgRef);
  771. if (pvSearch)
  772. dprintf("****\n\n");
  773. }
  774. }
  775. if (pvSearch)
  776. dprintf("Found %d matches to search\n", cMatchSearch);
  777. }
  778. //---[ displaytickcount ]------------------------------------------------------
  779. //
  780. //
  781. // Description:
  782. // Converts a tick count to a readable time
  783. // Parameters:
  784. // szArg - String-ized tick count in hex
  785. // Returns:
  786. // -
  787. // History:
  788. // 10/29/1999 - MikeSwa Created
  789. //
  790. //-----------------------------------------------------------------------------
  791. AQ_DEBUG_EXTENSION_IMP(displaytickcount)
  792. {
  793. DWORD dwTickCountToDisplay = (DWORD)GetExpression(szArg);
  794. DWORD dwCurrentTickCount = GetTickCount();
  795. DWORD dwTickDifference = dwCurrentTickCount - dwTickCountToDisplay;
  796. FILETIME ftCurrentUTC;
  797. FILETIME ftDisplayUTC;
  798. FILETIME ftDisplayLocal;
  799. ULARGE_INTEGER uliTimeAdjust;
  800. SYSTEMTIME stDisplayLocal;
  801. static char *s_rgszMonth[ 12 ] =
  802. {
  803. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  804. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  805. };
  806. static char *s_rgszWeekDays[7] =
  807. {
  808. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  809. };
  810. GetSystemTimeAsFileTime(&ftCurrentUTC);
  811. //Adjust the current filetime to local
  812. memcpy(&uliTimeAdjust, &ftCurrentUTC, sizeof(FILETIME));
  813. uliTimeAdjust.QuadPart -= (((ULONGLONG)dwTickDifference)*((ULONGLONG)10000));
  814. memcpy(&ftDisplayUTC, &uliTimeAdjust, sizeof(FILETIME));
  815. FileTimeToLocalFileTime(&ftDisplayUTC, &ftDisplayLocal);
  816. ZeroMemory(&stDisplayLocal, sizeof(stDisplayLocal));
  817. FileTimeToSystemTime(&ftDisplayLocal, &stDisplayLocal);
  818. dprintf("\n%s, %d %s %04d %02d:%02d:%02d (localized)\n",
  819. s_rgszWeekDays[stDisplayLocal.wDayOfWeek],
  820. stDisplayLocal.wDay, s_rgszMonth[ stDisplayLocal.wMonth - 1 ],
  821. stDisplayLocal.wYear, stDisplayLocal.wHour,
  822. stDisplayLocal.wMinute, stDisplayLocal.wSecond);
  823. }
  824. //---[ queueusage ]------------------------------------------------------------
  825. //
  826. //
  827. // Description:
  828. // Dumps the usage count averages for a given fifoq. If we are dumping
  829. // CMsgRefs, it will dump the pointers to the various MailMsg interfaces
  830. // as well.
  831. // Parameters:
  832. // szArg String-ized address of CFifoQ to dump
  833. // Returns:
  834. // -
  835. // History:
  836. // 10/15/1999 - MikeSwa Created
  837. //
  838. //-----------------------------------------------------------------------------
  839. AQ_DEBUG_EXTENSION_IMP(queueusage)
  840. {
  841. const DWORD cbUsageCountOffset = 0x20;
  842. const DWORD cbContentHandleOffset = 0x90+cbUsageCountOffset;
  843. const DWORD cbStreamHandleOffset = 0x8+cbContentHandleOffset;
  844. const DWORD cStoppingRule = 10000;
  845. const DWORD cMaxUsageCountToTrack = 6;
  846. CFifoQueueDbgIterator fifoqdbg(pExtensionApis);
  847. BYTE pbMsgRef[4*sizeof(CMsgRef)]; //leave room for bitmaps
  848. BYTE pbMailMsg[cbStreamHandleOffset+sizeof(PVOID)];
  849. PVOID pvMsgRef = NULL;
  850. PVOID pvMailMsg = NULL;
  851. PVOID pvQueue = NULL;
  852. DWORD cItems = 0;
  853. DWORD cCurrentUsageCount = 0;
  854. DWORD cTotalUsageCount = 0;
  855. DWORD cMaxUsageCount = 0;
  856. DWORD cMinUsageCount = 200;
  857. DWORD rgcUsageCounts[cMaxUsageCountToTrack];
  858. PVOID pvHandle = NULL;
  859. DWORD cMsgsWithOpenContentHandles = 0;
  860. DWORD cMsgsWithOpenStreamHandles = 0;
  861. BOOL fVerbose = FALSE;
  862. ZeroMemory(rgcUsageCounts, sizeof(rgcUsageCounts));
  863. if (!szArg || ('\0' == szArg[0]) ||
  864. !(pvQueue = (PVOID) GetExpression(szArg)))
  865. {
  866. dprintf("You must specify a queue address\n");
  867. return;
  868. }
  869. if (!fifoqdbg.fInit(hCurrentProcess, pvQueue))
  870. {
  871. dprintf("Unable to get the a queue for address 0x%X\n", pvQueue);
  872. return;
  873. }
  874. while ((pvMsgRef = fifoqdbg.pvGetNext()) && (cItems++ < cStoppingRule))
  875. {
  876. if (cItems > fifoqdbg.cGetCount())
  877. {
  878. cItems--;
  879. break;
  880. }
  881. //Read CMsgRef into this process
  882. if (!ReadMemory(pvMsgRef, pbMsgRef, sizeof(pbMsgRef), NULL))
  883. {
  884. dprintf("Unable to read MsgRef at address 0x%X, index %d\n",
  885. pvMsgRef, cItems);
  886. cItems--;
  887. break;
  888. }
  889. //Get inteface ptr for mailmsg from CMsgRef
  890. pvMailMsg = ((CMsgRef *)pbMsgRef)->m_pIMailMsgQM;
  891. if (!ReadMemory(pvMailMsg, pbMailMsg, sizeof(pbMailMsg), NULL))
  892. {
  893. dprintf("Unable to read MailMsg Ptr at address 0x%X for MsgRef 0x%X, index %d\n",
  894. pvMailMsg, pvMsgRef, cItems);
  895. cItems--;
  896. break;
  897. }
  898. //Check and see if this message has a content (P2) handle open
  899. if (*(pbMailMsg + cbContentHandleOffset))
  900. cMsgsWithOpenContentHandles++;
  901. //Check and see if this message has a stream (P1) handle open
  902. if (*(pbMailMsg + cbStreamHandleOffset))
  903. cMsgsWithOpenStreamHandles++;
  904. if (fVerbose &&
  905. ((*(pbMailMsg + cbStreamHandleOffset)) ||
  906. (*(pbMailMsg + cbStreamHandleOffset))))
  907. {
  908. dprintf("Message at address 0x%X has open handles\n", pvMsgRef);
  909. }
  910. cCurrentUsageCount = (DWORD) *(pbMailMsg + cbUsageCountOffset);
  911. cTotalUsageCount += cCurrentUsageCount;
  912. if (cCurrentUsageCount > cMaxUsageCount)
  913. cMaxUsageCount = cCurrentUsageCount;
  914. if (cCurrentUsageCount < cMinUsageCount)
  915. cMinUsageCount = cCurrentUsageCount;
  916. if (cCurrentUsageCount >= cMaxUsageCountToTrack)
  917. {
  918. dprintf("\n****\n");
  919. dprintf("High usage count of %d found on MailMsg 0x%X, MsgRef 0x%X, item %d\n",
  920. cCurrentUsageCount, pvMailMsg, pvMsgRef, cItems);
  921. dprintf("\n****\n");
  922. cCurrentUsageCount = cMaxUsageCountToTrack-1;
  923. }
  924. //Save count for summaries
  925. rgcUsageCounts[cCurrentUsageCount]++;
  926. }
  927. //Generate and display summary information
  928. if (!cItems)
  929. {
  930. dprintf("No Messages found in queue 0x%X\n", pvQueue);
  931. }
  932. else
  933. {
  934. dprintf("\n==================================================================\n");
  935. dprintf("Usage Count Summary\n");
  936. dprintf("------------------------------------------------------------------\n");
  937. dprintf("\t%d\t\tTotal Message\n", cItems);
  938. dprintf("\t%d\t\tTotal Messages with open content handles\n", cMsgsWithOpenContentHandles);
  939. dprintf("\t%d\t\tTotal Messages with open stream handles\n", cMsgsWithOpenStreamHandles);
  940. dprintf("\t%d\t\tTotal Usage Count\n", cTotalUsageCount);
  941. dprintf("\t%d\t\tMax Usage Count\n", cMaxUsageCount);
  942. dprintf("\t%d\t\tMin Usage Count\n", cMinUsageCount);
  943. dprintf("\t%f\tAverage Usage Count\n", ((float)cTotalUsageCount)/((float)cItems));
  944. for (DWORD i = 0; i < cMaxUsageCountToTrack-1; i++)
  945. {
  946. dprintf("\t%d\t\tMessages with Usage count of %d\n", rgcUsageCounts[i], i);
  947. }
  948. dprintf("\t%d\t\tMessages with Usage count of %d or greater\n",
  949. rgcUsageCounts[cMaxUsageCountToTrack-1], cMaxUsageCountToTrack-1);
  950. dprintf("==================================================================\n");
  951. }
  952. }
  953. //---[ dmqusage ]--------------------------------------------------------------
  954. //
  955. //
  956. // Description:
  957. // Debugger extension that wraps the queue usage debugger extension
  958. // to display the usage counts for all queues
  959. // Parameters:
  960. // szArg String-ized address of DMQ to dump
  961. // Returns:
  962. // -
  963. // History:
  964. // 10/15/1999 - MikeSwa Created
  965. //
  966. //-----------------------------------------------------------------------------
  967. AQ_DEBUG_EXTENSION_IMP(dmqusage)
  968. {
  969. PVOID pvQueue = NULL;
  970. PVOID pvDMQ = NULL;
  971. BYTE pbDMQ[sizeof(CDestMsgQueue)];
  972. CHAR szQueueAddress[30];
  973. DWORD iQueue = 0;
  974. if (!szArg || ('\0' == szArg[0]) ||
  975. !(pvDMQ = (PVOID) GetExpression(szArg)))
  976. {
  977. dprintf("You must specify a queue address\n");
  978. return;
  979. }
  980. if (!ReadMemory(pvDMQ, pbDMQ, sizeof(pbDMQ), NULL))
  981. {
  982. dprintf("Unable to read DMQ at address 0x%X\n", pvDMQ);
  983. return;
  984. }
  985. dprintf("\n\n******************************************************************\n");
  986. dprintf("Start USAGE COUNT STATS for DMQ 0x%0X\n", pvDMQ);
  987. dprintf("******************************************************************\n");
  988. for (iQueue = 0; iQueue < NUM_PRIORITIES; iQueue++)
  989. {
  990. pvQueue = ((CDestMsgQueue *)pbDMQ)->m_rgpfqQueues[iQueue];
  991. if (!pvQueue)
  992. continue; //nothing as every been queued to this queue
  993. //Only display the queue if we think we have messages
  994. //$$TODO - We could actual read this queue into memory and check it,
  995. //but since we currently only support 1 priority, this will do.
  996. if (((CDestMsgQueue *)pbDMQ)->m_aqstats.m_cMsgs ||((CDestMsgQueue *)pbDMQ)->m_aqstats.m_cRetryMsgs)
  997. {
  998. wsprintf(szQueueAddress, "0x%X", pvQueue);
  999. queueusage(hCurrentProcess, hCurrentThread, dwCurrentPc,
  1000. pExtensionApis, szQueueAddress);
  1001. }
  1002. }
  1003. //Display retry queue, if there are messages there
  1004. if (((CDestMsgQueue *)pbDMQ)->m_fqRetryQueue.m_cQueueEntries)
  1005. {
  1006. pvQueue = ((PBYTE)pvDMQ) + FIELD_OFFSET(CDestMsgQueue, m_fqRetryQueue);
  1007. wsprintf(szQueueAddress, "0x%X", pvQueue);
  1008. queueusage(hCurrentProcess, hCurrentThread, dwCurrentPc,
  1009. pExtensionApis, szQueueAddress);
  1010. }
  1011. dprintf("\n\n******************************************************************\n");
  1012. dprintf("End USAGE COUNT STATS for DMQ 0x%0X\n", pvDMQ);
  1013. dprintf("******************************************************************\n");
  1014. }
  1015. //---[ dntusage ]--------------------------------------------------------------
  1016. //
  1017. //
  1018. // Description:
  1019. // Debugger extension that wrap dmqusage. Call dmqusage for every DMQ
  1020. // in the DNT.
  1021. // Parameters:
  1022. // szArg string-ize address of dnt (DOMAIN_NAME_TABLE)
  1023. // Returns:
  1024. // -
  1025. // History:
  1026. // 10/15/1999 - MikeSwa Created
  1027. //
  1028. //-----------------------------------------------------------------------------
  1029. AQ_DEBUG_EXTENSION_IMP(dntusage)
  1030. {
  1031. BYTE pbBuffer[sizeof(DOMAIN_NAME_TABLE)];
  1032. PDOMAIN_NAME_TABLE pdnt = NULL;
  1033. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  1034. PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
  1035. PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
  1036. BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
  1037. CHAR pBuffer[MAX_DOM_PATH_SIZE] = "Root Entry";
  1038. LPSTR pEntryBuffer = NULL;
  1039. LPSTR pEntryBufferStop = NULL;
  1040. DWORD dwLength = 0;
  1041. DWORD dwSig = 0;
  1042. CHAR szFinalDest[MAX_DOM_PATH_SIZE];
  1043. BYTE pbDomainEntry[sizeof(CDomainEntry)];
  1044. CDomainEntry *pdentry = (CDomainEntry *) pbDomainEntry;
  1045. CHAR szDMQAddress[30];
  1046. DWORD cQueuesPerEntry = 0;
  1047. DWORD cMaxQueuesPerEntry = 1000;
  1048. PLIST_ENTRY pliHead = NULL;
  1049. PLIST_ENTRY pliCurrent = NULL;
  1050. LIST_ENTRY liCurrent;
  1051. //Define buffers for parsing addresses... the sizes are clearly overkill, and
  1052. //I'm not too worried about overflow in a debugger extension
  1053. CHAR szAddress[MAX_DOM_PATH_SIZE];
  1054. CHAR szDumpArg[MAX_DOM_PATH_SIZE] = "";
  1055. LPSTR szParsedArg = (LPSTR) szArg;
  1056. LPSTR szCurrentDest = NULL;
  1057. //Allow people who are used to typeing dump CFoo@Address... keep using the @ sign
  1058. if ('@' == *szParsedArg)
  1059. szParsedArg++;
  1060. //Get Address of DomainNameTable
  1061. szCurrentDest = szAddress;
  1062. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szParsedArg-szArg <= MAX_DOM_PATH_SIZE))
  1063. {
  1064. *szCurrentDest = *szParsedArg;
  1065. szParsedArg++;
  1066. szCurrentDest++;
  1067. }
  1068. *szCurrentDest = '\0';
  1069. //Eat white space
  1070. while (('\0' != *szParsedArg) && isspace(*szParsedArg))
  1071. szParsedArg++;
  1072. //Copy name of struct to dump at each node
  1073. szCurrentDest = szDumpArg;
  1074. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_DOM_PATH_SIZE))
  1075. {
  1076. *szCurrentDest = *szParsedArg;
  1077. szParsedArg++;
  1078. szCurrentDest++;
  1079. }
  1080. *szCurrentDest = '@';
  1081. szCurrentDest++; //szCurrentDest now points to place to copy address to
  1082. pdnt = (PDOMAIN_NAME_TABLE) GetExpression(szAddress);
  1083. if (!pdnt)
  1084. {
  1085. dprintf("ERROR: Unable to Get DOMAIN_NAME_TABLE from argument %s\n", szArg);
  1086. return;
  1087. }
  1088. if (!ReadMemory(pdnt, pbBuffer, sizeof(DOMAIN_NAME_TABLE), NULL))
  1089. {
  1090. dprintf("ERROR: Unable to read process memory\n");
  1091. return;
  1092. }
  1093. pdnt = (PDOMAIN_NAME_TABLE)pbBuffer;
  1094. pEntry = &(pdnt->RootEntry);
  1095. while(pEntry)
  1096. {
  1097. //We are not interested in wildcard data
  1098. if (pEntry->pData)
  1099. {
  1100. //Display link state information
  1101. if (!ReadMemory(pEntry->pData, pbDomainEntry, sizeof(CDomainEntry), NULL))
  1102. {
  1103. dprintf("ERROR: Unable to read domain entry from @0x%08X\n", pEntry->pData);
  1104. return;
  1105. }
  1106. pliHead = (PLIST_ENTRY) (((BYTE *)pEntry->pData) + FIELD_OFFSET(CDomainEntry, m_liDestQueues));
  1107. pliCurrent = pdentry->m_liDestQueues.Flink;
  1108. //Get final destination string
  1109. if (!ReadMemory(pdentry->m_szDomainName, szFinalDest, pdentry->m_cbDomainName, NULL))
  1110. {
  1111. dprintf("ERROR: Unable to read final destination name from @0x%08X\n",
  1112. pdentry->m_szDomainName);
  1113. return;
  1114. }
  1115. szFinalDest[pdentry->m_cbDomainName] = '\0';
  1116. //Loop and display each DMQ
  1117. cQueuesPerEntry = 0;
  1118. while (pliHead != pliCurrent)
  1119. {
  1120. cQueuesPerEntry++;
  1121. if (cQueuesPerEntry > cMaxQueuesPerEntry)
  1122. {
  1123. dprintf("ERROR: More than %d queues for this entry\n", cQueuesPerEntry);
  1124. return;
  1125. }
  1126. if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1127. {
  1128. dprintf("ERROR: Unable to read link LIST_ENTRY @0x%08X\n", pliCurrent);
  1129. return;
  1130. }
  1131. wsprintf(szDMQAddress, "0x%X",
  1132. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
  1133. dmqusage(hCurrentProcess, hCurrentThread, dwCurrentPc,
  1134. pExtensionApis, szDMQAddress);
  1135. pliCurrent = liCurrent.Flink;
  1136. }
  1137. }
  1138. //Now determine what the "next" entry is
  1139. if (pEntry->pFirstChildEntry != NULL)
  1140. {
  1141. pEntryRealAddress = pEntry->pFirstChildEntry;
  1142. }
  1143. else if (pEntry->pSiblingEntry != NULL)
  1144. {
  1145. pEntryRealAddress = pEntry->pSiblingEntry;
  1146. }
  1147. else
  1148. {
  1149. for (pEntryRealAddress = pEntry->pParentEntry;
  1150. pEntryRealAddress != NULL;
  1151. pEntryRealAddress = pEntry->pParentEntry)
  1152. {
  1153. //must read parent entry into our buffer
  1154. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  1155. {
  1156. dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
  1157. pEntry = NULL;
  1158. break;
  1159. }
  1160. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  1161. if (pEntry->pSiblingEntry != NULL)
  1162. break;
  1163. }
  1164. if (pEntry != NULL)
  1165. {
  1166. pEntryRealAddress = pEntry->pSiblingEntry;
  1167. }
  1168. }
  1169. if (pEntryRealAddress)
  1170. {
  1171. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  1172. {
  1173. dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
  1174. pEntryRealAddress);
  1175. pEntry = NULL;
  1176. break;
  1177. }
  1178. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  1179. }
  1180. else
  1181. {
  1182. pEntry = NULL;
  1183. }
  1184. }
  1185. }
  1186. //---[ walkcpool ]-------------------------------------------------------------
  1187. //
  1188. //
  1189. // Description:
  1190. // Will walk a given CPool object. Validate headers, and dump currently
  1191. // used objects.
  1192. //
  1193. // ***NOTE*** This version only works on DBG CPool implementations (since
  1194. // RTL does not have the headerinfo). I could write a more complex
  1195. // version that checks and sees if this each pool object is in the
  1196. // freelist, but I will leave that as an exercise to the reader.
  1197. // Parameters:
  1198. // szArg - String containing arguments
  1199. // Address of CPool object to dump
  1200. // Offset of additional address to dump
  1201. // Returns:
  1202. // -
  1203. // History:
  1204. // 9/30/1999 - MikeSwa Created
  1205. //
  1206. #define HEAD_SIGNATURE (DWORD)'daeH'
  1207. #define TAIL_SIGNATURE (DWORD)'liaT'
  1208. #define FREE_STATE (DWORD)'eerF'
  1209. #define USED_STATE (DWORD)'desU'
  1210. //-----------------------------------------------------------------------------
  1211. AQ_DEBUG_EXTENSION_IMP(walkcpool)
  1212. {
  1213. PVOID pvCPool = NULL;
  1214. DWORD cbCPoolData = 0;
  1215. DWORD cCommited = 0;
  1216. DWORD cFragments = 0;
  1217. DWORD cBuffersPerFragment = 0;
  1218. DWORD iCurrentBufferInFragment = 0;
  1219. DWORD iCurrentFragment = 0;
  1220. PVOID *pvFragment = NULL;
  1221. PVOID pvCPoolData = NULL;
  1222. BYTE pbCPoolBuffer[sizeof(CPool)];
  1223. BYTE pbCPoolDataBuffer[100];
  1224. LPSTR szCurrentArg = NULL;
  1225. CHAR szArgBuffer[200];
  1226. DWORD_PTR cbOffset = 0;
  1227. DWORD_PTR dwptrData = 0;
  1228. if (!szArg || ('\0' == szArg[0]))
  1229. {
  1230. dprintf("You must specify a Pool address\n");
  1231. return;
  1232. }
  1233. else
  1234. {
  1235. strcpy(szArgBuffer, szArg);
  1236. szCurrentArg = strtok(szArgBuffer, " ");
  1237. if (szCurrentArg)
  1238. {
  1239. pvCPool = (PVOID)GetExpression(szCurrentArg);
  1240. szCurrentArg = strtok(NULL, " ");
  1241. if (szCurrentArg)
  1242. cbOffset = (DWORD_PTR) GetExpression(szCurrentArg);
  1243. }
  1244. else
  1245. {
  1246. pvCPool = (PVOID) GetExpression(szArg);
  1247. }
  1248. }
  1249. if (!ReadMemory(pvCPool, pbCPoolBuffer, sizeof(CPool), NULL))
  1250. {
  1251. dprintf("Unable to read memory at 0x%x\n", pvCPool);
  1252. return;
  1253. }
  1254. dprintf("Dumping CPool at address 0x%08X\n", pvCPool);
  1255. //Get interesting values from CPool
  1256. cbCPoolData = *((PDWORD)(pbCPoolBuffer + 0x8));
  1257. cCommited = *((PDWORD)(pbCPoolBuffer + 0xc));
  1258. cFragments = *((PDWORD)(pbCPoolBuffer + 0x54));
  1259. cBuffersPerFragment = *((PDWORD)(pbCPoolBuffer + 0x50));
  1260. dprintf("CPool data size is %d bytes (0x%x)\n", cbCPoolData, cbCPoolData);
  1261. dprintf("CPool fragment count is %d\n", cFragments);
  1262. dprintf("CPool has %d buffers per fragment\n", cBuffersPerFragment);
  1263. dprintf("CPool has %d commited buffers\n", cCommited);
  1264. if (!cbCPoolData)
  1265. {
  1266. dprintf("Invalid CPool\n");
  1267. return;
  1268. }
  1269. //Loop over the fragment and dump each one
  1270. pvFragment = (PVOID *) (pbCPoolBuffer + 0x58);
  1271. for (iCurrentFragment = 0;
  1272. iCurrentFragment < cFragments;
  1273. iCurrentFragment++ || pvFragment++)
  1274. {
  1275. pvCPoolData = *pvFragment;
  1276. if (!pvCPoolData)
  1277. continue;
  1278. dprintf("CPool Fragment #%d at 0x%08X\n", iCurrentFragment, pvCPoolData);
  1279. for (iCurrentBufferInFragment = 0;
  1280. iCurrentBufferInFragment < cBuffersPerFragment;
  1281. iCurrentBufferInFragment++)
  1282. {
  1283. if (!ReadMemory(pvCPoolData, pbCPoolDataBuffer, 100, NULL))
  1284. {
  1285. dprintf("\tUnable to read CPool buffer data at 0x%x\n", pvCPoolData);
  1286. break;
  1287. }
  1288. if (HEAD_SIGNATURE != ((DWORD *)pbCPoolDataBuffer)[1])
  1289. {
  1290. dprintf("\tHit bad signature at 0x%08X\n", pvCPoolData);
  1291. break; //bad signature bail
  1292. }
  1293. if (USED_STATE == ((DWORD *)pbCPoolDataBuffer)[2])
  1294. {
  1295. dprintf("\tAllocated block found at offset %d (0x%08X)\n",
  1296. iCurrentBufferInFragment, pvCPoolData);
  1297. if (cbOffset)
  1298. {
  1299. if (ReadMemory(((PBYTE)pvCPoolData)+cbOffset, &dwptrData,
  1300. sizeof(DWORD_PTR), NULL))
  1301. {
  1302. dprintf("\t\tData 0x%X found at address 0x%X\n",
  1303. dwptrData, ((PBYTE)pvCPoolData)+cbOffset);
  1304. }
  1305. }
  1306. }
  1307. pvCPoolData = ((BYTE *)pvCPoolData) + cbCPoolData;
  1308. if (!(--cCommited))
  1309. {
  1310. dprintf("\tLast block is in fragment at offset %d (0x%08X)\n",
  1311. iCurrentBufferInFragment, pvCPoolData);
  1312. break; //We're done
  1313. }
  1314. }
  1315. }
  1316. }
  1317. //---[ CheckVersion ]----------------------------------------------------------
  1318. //
  1319. //
  1320. // Description:
  1321. // Checks the AQ version to make sure that this debugger extension will
  1322. // work with it.
  1323. // Parameters:
  1324. //
  1325. // Returns:
  1326. //
  1327. // History:
  1328. // 2/5/99 - MikeSwa Created
  1329. //
  1330. //-----------------------------------------------------------------------------
  1331. AQ_DEBUG_EXTENSION_IMP(CheckVersion)
  1332. {
  1333. DWORD cbAQClasses = 0;
  1334. DWORD dwAQFlavorSignature = ' ';
  1335. PVOID pcbAQClasses = (PVOID) GetExpression("aqueue!g_cbClasses");
  1336. PVOID pdwAQFlavorSignature = (PVOID) GetExpression("aqueue!g_dwFlavorSignature");
  1337. PCHAR pch = NULL;
  1338. //Read the version information stamped in AQ
  1339. ReadMemory(pcbAQClasses, &cbAQClasses, sizeof(DWORD), NULL);
  1340. ReadMemory(pdwAQFlavorSignature, &dwAQFlavorSignature, sizeof(DWORD), NULL);
  1341. if (!g_fVersionChecked)
  1342. {
  1343. dprintf("AQueue Internal Version Info (#'s should match):\n");
  1344. pch = (PCHAR) &g_dwFlavorSignature;
  1345. dprintf("\taqdbgext %c%c%c%c 0x%08X\n", *(pch), *(pch+1), *(pch+2), *(pch+3), g_cbClasses);
  1346. pch = (PCHAR) &dwAQFlavorSignature;
  1347. dprintf("\taqueue %c%c%c%c 0x%08X\n\n", *(pch), *(pch+1), *(pch+2), *(pch+3), cbAQClasses);
  1348. }
  1349. g_fVersionChecked = FALSE;
  1350. if (dwAQFlavorSignature != g_dwFlavorSignature)
  1351. dprintf("\n\nWARNING: DBG/RTL aqueue.dll & aqdbgext.dll mismatch\n\n");
  1352. else if (g_cbClasses != cbAQClasses)
  1353. dprintf("\n\nWARNING: aqueue.dll & aqdbgext.dll version mismatch\n\n");
  1354. else
  1355. g_fVersionChecked = TRUE;
  1356. }
  1357. //---[ DumpServers ]------------------------------------------------------------
  1358. //
  1359. //
  1360. // Description:
  1361. // Dumps pointers to the CAQSvrInst for each virtual server
  1362. // Parameters:
  1363. //
  1364. // Returns:
  1365. //
  1366. //
  1367. //-----------------------------------------------------------------------------
  1368. AQ_DEBUG_EXTENSION_IMP(DumpServers)
  1369. {
  1370. PVOID pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  1371. DWORD *pcInstances = (DWORD *) GetExpression("aqueue!g_cInstances");
  1372. DWORD cInstances = 0;
  1373. LIST_ENTRY liCurrent;
  1374. BYTE pbBuffer[sizeof(CAQSvrInst)];
  1375. CAQSvrInst *paqinst = (CAQSvrInst *) pbBuffer;
  1376. PVOID pCMQAddress = NULL;
  1377. DWORD dwInstance = 0;
  1378. CHAR szDumpArg[40] = "";
  1379. CHAR szArgBuffer[200];
  1380. LPSTR szCurrentArg = NULL;
  1381. CheckVersion(DebugArgs);
  1382. if (!szArg || ('\0' == szArg[0]))
  1383. {
  1384. dwInstance = 0;
  1385. pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  1386. }
  1387. else
  1388. {
  1389. strcpy(szArgBuffer, szArg);
  1390. szCurrentArg = strtok(szArgBuffer, " ");
  1391. if (szCurrentArg)
  1392. {
  1393. dwInstance = (DWORD)GetExpression(szCurrentArg);
  1394. szCurrentArg = strtok(NULL, " ");
  1395. if (szCurrentArg)
  1396. pvListHead = (PVOID) GetExpression(szCurrentArg);
  1397. else
  1398. pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  1399. }
  1400. }
  1401. if (!pvListHead)
  1402. {
  1403. dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
  1404. dprintf(" If you are using windbg, you should specify the value as the\n");
  1405. dprintf(" 2nd argument. You can determine the address value by typeing:\n");
  1406. dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
  1407. dprintf(" You may also have bad symbols for aqueue.dll.\n");
  1408. return;
  1409. }
  1410. if (!ReadMemory(pvListHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1411. {
  1412. dprintf("ERROR: Unable to read entry @ aqueue!g_liVirtualServers 0x%08X", pvListHead);
  1413. return;
  1414. }
  1415. if (!ReadMemory(pcInstances, &cInstances, sizeof(DWORD), NULL))
  1416. {
  1417. //For you windbg users out there
  1418. dprintf("\n\n%Virtual Server Instance(s)\n\n");
  1419. }
  1420. else
  1421. {
  1422. dprintf("\n\n%d Virtual Server Instance(s)\n\n", cInstances);
  1423. }
  1424. dprintf("Class@Address Server Instance\n");
  1425. dprintf("==========================================\n");
  1426. while (liCurrent.Flink != pvListHead)
  1427. {
  1428. pCMQAddress = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
  1429. if (!ReadMemory(pCMQAddress, paqinst, sizeof(CAQSvrInst), NULL))
  1430. {
  1431. dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pCMQAddress);
  1432. return;
  1433. }
  1434. if (CATMSGQ_SIG != paqinst->m_dwSignature)
  1435. {
  1436. dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pCMQAddress, liCurrent.Flink);
  1437. }
  1438. else
  1439. {
  1440. dprintf("CAQSvrInst@0x%08X %d\n", pCMQAddress, paqinst->m_dwServerInstance);
  1441. if (paqinst->m_dwServerInstance == dwInstance)
  1442. wsprintf(szDumpArg, "CAQSvrInst@0x%08X", pCMQAddress);
  1443. }
  1444. if (!ReadMemory(liCurrent.Flink, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1445. {
  1446. dprintf("ERROR: Unable to read entry @0x%08X", liCurrent.Flink);
  1447. return;
  1448. }
  1449. }
  1450. //Dump the interesting instance
  1451. if ('\0' != szDumpArg[0])
  1452. _dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
  1453. }
  1454. //---[ handlemgmt ]------------------------------------------------------------
  1455. //
  1456. //
  1457. // Description:
  1458. // Caclulates a handle management score for a given virtual server.
  1459. //
  1460. // Calculates score based on the number of messages closed and messages
  1461. // delivered / pending delivery... the lower the score... the better.
  1462. // Score = Closes /
  1463. // (m_cCurrentMsgsPendingSubmit + m_cCurrentMsgsPendingCat*2 +
  1464. // m_cCurrentMsgsPendingRouting*3 + m_cCurrentMsgsPendingLocal*4 +
  1465. // m_cMsgsDeliveredLocal*5)
  1466. //
  1467. //-----------------------------------------------------------------------------
  1468. AQ_DEBUG_EXTENSION_IMP(handlemgmt)
  1469. {
  1470. #define MAILMSG_CLOSES_SYMBOL \
  1471. "mailmsg!CMailMsg__g_cTotalExternalReleaseUsageZero"
  1472. #define MAILMSG_CURRENT_CLOSED_SYMBOL \
  1473. "mailmsg!CMailMsg__g_cCurrentMsgsClosedByExternalReleaseUsage"
  1474. #define MAILMSG_CURRENT_ALLOCATED \
  1475. "mailmsg!CMsg__m_Pool+0x10"
  1476. #define MAILMSG_TOTAL_ALLOCATED \
  1477. "mailmsg!CMsg__m_Pool+0x3c"
  1478. PVOID pvCloses = (PVOID) GetExpression(MAILMSG_CLOSES_SYMBOL);
  1479. DWORD cCloses = 1;
  1480. PVOID pvCurrentMsgsThatHaveBeenClosed = (PVOID) GetExpression(MAILMSG_CURRENT_CLOSED_SYMBOL);
  1481. DWORD cCurrentMsgsThatHaveBeenClosed = 1;
  1482. PVOID pvCurrentMsgsAllocated = (PVOID) GetExpression(MAILMSG_CURRENT_ALLOCATED);
  1483. DWORD cCurrentMsgsAllocated = 1;
  1484. PVOID pvTotalMsgsAllocated = (PVOID) GetExpression(MAILMSG_TOTAL_ALLOCATED);
  1485. DWORD cTotalMsgsAllocated = 1;
  1486. DWORD dwPercentCurrentMessagesClosed = 0;
  1487. DWORD dwPercentTotalMessagesBacklogged = 0;
  1488. DWORD dwPercentCurrentMessagesQueueInternally = 0;
  1489. PVOID pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  1490. DWORD *pcInstances = (DWORD *) GetExpression("phatq!g_cInstances");
  1491. DWORD cInstances = 0;
  1492. LIST_ENTRY liCurrent;
  1493. BYTE pbBuffer[sizeof(CAQSvrInst)];
  1494. CAQSvrInst *paqinst = (CAQSvrInst *) pbBuffer;
  1495. PVOID pCMQAddress = NULL;
  1496. DWORD dwInstance = 1;
  1497. CHAR szDumpArg[40] = "";
  1498. CHAR szArgBuffer[200];
  1499. LPSTR szCurrentArg = NULL;
  1500. DWORD dwQueueScore = 0;
  1501. DWORD dwWeightedScore = 0;
  1502. DWORD dwDeliveredScore = 0;
  1503. DWORD dwSubmittedScore = 0;
  1504. DWORD dwWeightedQueueLength = 0;
  1505. DWORD dwTotalQueueLength = 0;
  1506. BOOL fFoundInstance = FALSE;
  1507. //
  1508. // Read the data we need from mailmsg
  1509. //
  1510. if (!ReadMemory(pvCloses, &cCloses, sizeof(cCloses), NULL))
  1511. {
  1512. dprintf("Unable to read %s at address %p\n",
  1513. MAILMSG_CLOSES_SYMBOL, pvCloses);
  1514. return;
  1515. }
  1516. if (!ReadMemory(pvCurrentMsgsThatHaveBeenClosed,
  1517. &cCurrentMsgsThatHaveBeenClosed, sizeof(cCloses), NULL))
  1518. {
  1519. dprintf("Unable to read %s at address %p\n",
  1520. MAILMSG_CLOSES_SYMBOL, pvCloses);
  1521. return;
  1522. }
  1523. if (!ReadMemory(pvCurrentMsgsAllocated, &cCurrentMsgsAllocated,
  1524. sizeof(cCloses), NULL))
  1525. {
  1526. dprintf("Unable to read %s at address %p\n",
  1527. MAILMSG_CLOSES_SYMBOL, pvCloses);
  1528. return;
  1529. }
  1530. if (!ReadMemory(pvTotalMsgsAllocated, &cTotalMsgsAllocated,
  1531. sizeof(cCloses), NULL))
  1532. {
  1533. dprintf("Unable to read %s at address %p\n",
  1534. MAILMSG_CLOSES_SYMBOL, pvCloses);
  1535. return;
  1536. }
  1537. if (cCurrentMsgsAllocated)
  1538. {
  1539. dwPercentCurrentMessagesClosed =
  1540. (100*cCurrentMsgsThatHaveBeenClosed)/cCurrentMsgsAllocated;
  1541. }
  1542. if (cTotalMsgsAllocated)
  1543. {
  1544. dwPercentTotalMessagesBacklogged =
  1545. (100*cCurrentMsgsAllocated)/cTotalMsgsAllocated;
  1546. }
  1547. //
  1548. // Get the instance object we want to get data from
  1549. //
  1550. CheckVersion(DebugArgs);
  1551. if (!szArg || ('\0' == szArg[0]))
  1552. {
  1553. dwInstance = 1;
  1554. pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  1555. }
  1556. else
  1557. {
  1558. strcpy(szArgBuffer, szArg);
  1559. szCurrentArg = strtok(szArgBuffer, " ");
  1560. if (szCurrentArg)
  1561. {
  1562. dwInstance = (DWORD)GetExpression(szCurrentArg);
  1563. szCurrentArg = strtok(NULL, " ");
  1564. if (szCurrentArg)
  1565. pvListHead = (PVOID) GetExpression(szCurrentArg);
  1566. else
  1567. pvListHead = (PVOID) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  1568. }
  1569. }
  1570. if (!pvListHead)
  1571. {
  1572. dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
  1573. dprintf(" If you are using windbg, you should specify the value as the\n");
  1574. dprintf(" 2nd argument. You can determine the address value by typeing:\n");
  1575. dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
  1576. dprintf(" You may also have bad symbols for phatq.dll.\n");
  1577. return;
  1578. }
  1579. if (!ReadMemory(pvListHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1580. {
  1581. dprintf("ERROR: Unable to read entry @ phatq!g_liVirtualServers 0x%08X", pvListHead);
  1582. return;
  1583. }
  1584. if (!ReadMemory(pcInstances, &cInstances, sizeof(DWORD), NULL))
  1585. {
  1586. //For you windbg users out there
  1587. dprintf("\n\n%Virtual Server Instance(s)\n\n");
  1588. }
  1589. else
  1590. {
  1591. dprintf("\n\n%d Virtual Server Instance(s)\n\n", cInstances);
  1592. }
  1593. while (liCurrent.Flink != pvListHead)
  1594. {
  1595. pCMQAddress = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
  1596. if (!ReadMemory(pCMQAddress, paqinst, sizeof(CAQSvrInst), NULL))
  1597. {
  1598. dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pCMQAddress);
  1599. return;
  1600. }
  1601. if (CATMSGQ_SIG != paqinst->m_dwSignature)
  1602. {
  1603. dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pCMQAddress, liCurrent.Flink);
  1604. }
  1605. else if (paqinst->m_dwServerInstance == dwInstance)
  1606. {
  1607. fFoundInstance = TRUE;
  1608. dprintf("Using CAQSvrInst@0x%08X %d\n", pCMQAddress, paqinst->m_dwServerInstance);
  1609. break;
  1610. }
  1611. if (!ReadMemory(liCurrent.Flink, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1612. {
  1613. dprintf("ERROR: Unable to read entry @0x%08X", liCurrent.Flink);
  1614. return;
  1615. }
  1616. }
  1617. //
  1618. // Did we find the instance
  1619. //
  1620. if (!fFoundInstance)
  1621. {
  1622. dprintf("We did not find instance %d\n", dwInstance);
  1623. return;
  1624. }
  1625. dwWeightedQueueLength = paqinst->m_cCurrentMsgsPendingSubmit +
  1626. paqinst->m_cCurrentMsgsPendingCat*2 +
  1627. paqinst->m_cCurrentMsgsPendingRouting*3 +
  1628. paqinst->m_cCurrentMsgsPendingLocal*4 +
  1629. paqinst->m_cMsgsDeliveredLocal*5;
  1630. dwTotalQueueLength = paqinst->m_cCurrentMsgsPendingSubmit +
  1631. paqinst->m_cCurrentMsgsPendingCat +
  1632. paqinst->m_cCurrentMsgsPendingRouting +
  1633. paqinst->m_cCurrentMsgsPendingLocal +
  1634. paqinst->m_cMsgsDeliveredLocal;
  1635. if (cTotalMsgsAllocated)
  1636. {
  1637. dwPercentCurrentMessagesQueueInternally =
  1638. (100*(dwTotalQueueLength-paqinst->m_cMsgsDeliveredLocal))
  1639. /cCurrentMsgsAllocated;
  1640. }
  1641. if (dwTotalQueueLength)
  1642. dwQueueScore = (cCloses*1000)/dwTotalQueueLength;
  1643. if (dwWeightedQueueLength)
  1644. dwWeightedScore = (cCloses*1000)/dwWeightedQueueLength;
  1645. if (paqinst->m_cMsgsDeliveredLocal)
  1646. dwDeliveredScore = (cCloses*1000)/paqinst->m_cMsgsDeliveredLocal;
  1647. if (paqinst->m_cTotalExternalMsgsSubmitted)
  1648. dwSubmittedScore = (cCloses*1000)/paqinst->m_cTotalExternalMsgsSubmitted;
  1649. dprintf("\n\nHandle Managment scores:\n");
  1650. dprintf("========================\n");
  1651. dprintf("Non-Weighted Score: %d\n", dwQueueScore);
  1652. dprintf("Weighted Score: %d\n", dwWeightedScore);
  1653. dprintf("Delivery Score: %d\n", dwDeliveredScore);
  1654. dprintf("Submitted Score: %d\n", dwSubmittedScore);
  1655. dprintf("Current Messsages Allocated That have been closed: %d%%\n",
  1656. dwPercentCurrentMessagesClosed);
  1657. dprintf("\nThe following are useful in correlating different test runs...\n");
  1658. dprintf("Messages Backlogged: %d%%\n", dwPercentTotalMessagesBacklogged);
  1659. dprintf("Backlogged Messsages Queued internally: %d%%\n",
  1660. dwPercentCurrentMessagesQueueInternally);
  1661. dprintf("\n%d Total message closures.. %d total deliveries\n\n",
  1662. cCloses, paqinst->m_cMsgsDeliveredLocal);
  1663. }
  1664. //---[ DumpDNT ]------------------------------------------------------------
  1665. //
  1666. //
  1667. // Description:
  1668. // Dumps the contents of a DOMAIN_NAME_TABLE
  1669. // Parameters:
  1670. //
  1671. // Returns:
  1672. //
  1673. //
  1674. //-----------------------------------------------------------------------------
  1675. AQ_DEBUG_EXTENSION_IMP(DumpDNT)
  1676. {
  1677. BYTE pbBuffer[sizeof(DOMAIN_NAME_TABLE)];
  1678. PDOMAIN_NAME_TABLE pdnt = NULL;
  1679. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  1680. PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
  1681. PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
  1682. BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
  1683. BYTE pbPathEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)]; //buffer for putter path name entries in
  1684. BYTE pbPathEntryBuffer[MAX_DOM_PATH_SIZE];
  1685. CHAR pBuffer[MAX_DOM_PATH_SIZE] = "Root Entry";
  1686. LPSTR pPathBuffer = NULL;
  1687. LPSTR pPathBufferStop = NULL;
  1688. LPSTR pEntryBuffer = NULL;
  1689. LPSTR pEntryBufferStop = NULL;
  1690. DWORD dwLength = 0;
  1691. DWORD dwSig = 0;
  1692. //Define buffers for parsing addresses... the sizes are clearly overkill, and
  1693. //I'm not too worried about overflow in a debugger extension
  1694. CHAR szAddress[MAX_DOM_PATH_SIZE];
  1695. CHAR szDumpArg[MAX_DOM_PATH_SIZE] = "";
  1696. LPSTR szParsedArg = (LPSTR) szArg;
  1697. LPSTR szCurrentDest = NULL;
  1698. //Allow people who are used to typeing dump CFoo@Address... keep using the @ sign
  1699. if ('@' == *szParsedArg)
  1700. szParsedArg++;
  1701. //Get Address of DomainNameTable
  1702. szCurrentDest = szAddress;
  1703. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szParsedArg-szArg <= MAX_DOM_PATH_SIZE))
  1704. {
  1705. *szCurrentDest = *szParsedArg;
  1706. szParsedArg++;
  1707. szCurrentDest++;
  1708. }
  1709. *szCurrentDest = '\0';
  1710. //Eat white space
  1711. while (('\0' != *szParsedArg) && isspace(*szParsedArg))
  1712. szParsedArg++;
  1713. //Copy name of struct to dump at each node
  1714. szCurrentDest = szDumpArg;
  1715. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_DOM_PATH_SIZE))
  1716. {
  1717. *szCurrentDest = *szParsedArg;
  1718. szParsedArg++;
  1719. szCurrentDest++;
  1720. }
  1721. *szCurrentDest = '@';
  1722. szCurrentDest++; //szCurrentDest now points to place to copy address to
  1723. pdnt = (PDOMAIN_NAME_TABLE) GetExpression(szAddress);
  1724. if (!pdnt)
  1725. {
  1726. dprintf("ERROR: Unable to Get DOMAIN_NAME_TABLE from argument %s\n", szArg);
  1727. return;
  1728. }
  1729. if (!ReadMemory(pdnt, pbBuffer, sizeof(DOMAIN_NAME_TABLE), NULL))
  1730. {
  1731. dprintf("ERROR: Unable to read process memory\n");
  1732. return;
  1733. }
  1734. pPathBuffer = pBuffer;
  1735. pPathBufferStop = pPathBuffer + (MAX_DOM_PATH_SIZE / sizeof(CHAR) -1 );
  1736. pEntryRealAddress = (PDOMAIN_NAME_TABLE_ENTRY)
  1737. ((BYTE *)pdnt + FIELD_OFFSET(DOMAIN_NAME_TABLE, RootEntry));
  1738. pdnt = (PDOMAIN_NAME_TABLE) pbBuffer;
  1739. pEntry = &(pdnt->RootEntry);
  1740. dprintf("Entry ID # Children pData pWildCard Path\n");
  1741. dprintf("===========================================================================\n");
  1742. while(pEntry)
  1743. {
  1744. //only display interesting entries
  1745. if (pEntry->pData || pEntry->pWildCardData)
  1746. {
  1747. //Get full path name of this domain entry
  1748. pPathEntry = pEntry;
  1749. pPathBuffer = pBuffer;
  1750. while (pPathEntry && pPathEntry->pParentEntry && pPathBuffer < pPathBufferStop)
  1751. {
  1752. //dump current entries portion of the string
  1753. if (pPathBuffer != pBuffer) //already made first pass -- Add delimter
  1754. {
  1755. *pPathBuffer++ = '.';
  1756. }
  1757. //read partial path name from debuggee
  1758. if (!ReadMemory(pPathEntry->PathSegment.Buffer, pbPathEntryBuffer,
  1759. AQ_MIN(MAX_DOM_PATH_SIZE, pPathEntry->PathSegment.Length), NULL))
  1760. {
  1761. dprintf("ERROR: Unable to read process memory for path segment 0x%08X\n",
  1762. pPathEntry->PathSegment.Buffer);
  1763. break;
  1764. }
  1765. pEntryBuffer = (CHAR *) pbPathEntryBuffer;
  1766. pEntryBufferStop = pEntryBuffer;
  1767. pEntryBuffer += (pPathEntry->PathSegment.Length / sizeof(CHAR) -1 );
  1768. while (pPathBuffer < pPathBufferStop && pEntryBuffer >= pEntryBufferStop)
  1769. {
  1770. *pPathBuffer++ = *pEntryBuffer--;
  1771. }
  1772. *pPathBuffer = '\0'; //make sure we terminate
  1773. pPathEntry = pPathEntry->pParentEntry;
  1774. //read next part of path name from debuggee
  1775. if (!ReadMemory(pPathEntry, pbPathEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  1776. {
  1777. dprintf("ERROR: Unable to read process memory for path entry 0x%08x\n", pPathEntry);
  1778. pPathEntry = NULL;
  1779. }
  1780. else
  1781. {
  1782. pPathEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbPathEntry;
  1783. }
  1784. }
  1785. dprintf("0x%08.8X %10.10d 0x%08.8X 0x%08.8X %s\n", pEntryRealAddress,
  1786. pEntry->NoOfChildren, pEntry->pData, pEntry->pWildCardData, pBuffer);
  1787. //Dump structs if requested
  1788. if ('@' != *szDumpArg)
  1789. {
  1790. if (pEntry->pData)
  1791. {
  1792. //Write address string
  1793. wsprintf(szCurrentDest, "0x%08X", pEntry->pData);
  1794. //Call ptdbgext dump function
  1795. _dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
  1796. }
  1797. if (pEntry->pWildCardData)
  1798. {
  1799. //Write address string
  1800. wsprintf(szCurrentDest, "0x%08X", pEntry->pWildCardData);
  1801. //Call ptdbgext dump function
  1802. _dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
  1803. }
  1804. }
  1805. }
  1806. //Get the next entry... in order of child, sibling, closest ancestor with sibling
  1807. if (pEntry->pFirstChildEntry != NULL)
  1808. {
  1809. pEntryRealAddress = pEntry->pFirstChildEntry;
  1810. }
  1811. else if (pEntry->pSiblingEntry != NULL)
  1812. {
  1813. pEntryRealAddress = pEntry->pSiblingEntry;
  1814. }
  1815. else
  1816. {
  1817. for (pEntryRealAddress = pEntry->pParentEntry;
  1818. pEntryRealAddress != NULL;
  1819. pEntryRealAddress = pEntry->pParentEntry)
  1820. {
  1821. //must read parent entry into our buffer
  1822. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  1823. {
  1824. dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
  1825. pEntry = NULL;
  1826. break;
  1827. }
  1828. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  1829. if (pEntry->pSiblingEntry != NULL)
  1830. break;
  1831. }
  1832. if (pEntry != NULL)
  1833. {
  1834. pEntryRealAddress = pEntry->pSiblingEntry;
  1835. }
  1836. }
  1837. if (pEntryRealAddress)
  1838. {
  1839. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  1840. {
  1841. dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
  1842. pEntryRealAddress);
  1843. pEntry = NULL;
  1844. break;
  1845. }
  1846. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  1847. }
  1848. else
  1849. {
  1850. pEntry = NULL;
  1851. }
  1852. }
  1853. dprintf("===========================================================================\n");
  1854. }
  1855. //---[ DumpList ]--------------------------------------------------------------
  1856. //
  1857. //
  1858. // Description:
  1859. // Function to walk a set of LIST_ENTRY's and dump their contenxts
  1860. // Parameters:
  1861. // szArg - space separated list of the following
  1862. // Address of head list entry
  1863. // Offset of object address [optional]
  1864. // Name of object to dump [optional]
  1865. // Returns:
  1866. // -
  1867. // History:
  1868. // 9/15/98 - MikeSwa Created
  1869. //
  1870. //-----------------------------------------------------------------------------
  1871. AQ_DEBUG_EXTENSION_IMP(dumplist)
  1872. {
  1873. const DWORD MAX_ARG_SIZE = 200;
  1874. const DWORD MAX_ENTRIES = 3000;
  1875. LIST_ENTRY liCurrent;
  1876. PLIST_ENTRY pliHead = NULL;
  1877. PLIST_ENTRY pliCurrent = NULL;
  1878. DWORD_PTR dwOffsetOfEntry = 0;
  1879. CHAR szAddress[MAX_ARG_SIZE];
  1880. CHAR szDumpArg[MAX_ARG_SIZE];
  1881. LPSTR szParsedArg = (LPSTR) szArg;
  1882. LPSTR szCurrentDest = NULL;
  1883. DWORD cEntries = 0;
  1884. //Get Address of DomainNameTable
  1885. szCurrentDest = szAddress;
  1886. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szParsedArg-szArg <= MAX_ARG_SIZE))
  1887. {
  1888. *szCurrentDest = *szParsedArg;
  1889. szParsedArg++;
  1890. szCurrentDest++;
  1891. }
  1892. *szCurrentDest = '\0';
  1893. //Eat white space
  1894. while (('\0' != *szParsedArg) && isspace(*szParsedArg))
  1895. szParsedArg++;
  1896. //Get offset of data
  1897. szCurrentDest = szDumpArg;
  1898. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_ARG_SIZE))
  1899. {
  1900. *szCurrentDest = *szParsedArg;
  1901. szParsedArg++;
  1902. szCurrentDest++;
  1903. }
  1904. *szCurrentDest = '\0';
  1905. dwOffsetOfEntry = GetExpression(szDumpArg);
  1906. //Eat white more space
  1907. while (('\0' != *szParsedArg) && isspace(*szParsedArg))
  1908. szParsedArg++;
  1909. //Copy name of struct to dump at each node
  1910. szCurrentDest = szDumpArg;
  1911. while (('\0' != *szParsedArg) && !isspace(*szParsedArg) && (szCurrentDest-szDumpArg <= MAX_ARG_SIZE))
  1912. {
  1913. *szCurrentDest = *szParsedArg;
  1914. szParsedArg++;
  1915. szCurrentDest++;
  1916. }
  1917. *szCurrentDest = '@';
  1918. szCurrentDest++; //szCurrentDest now points to place to copy address to
  1919. pliHead = (PLIST_ENTRY) GetExpression(szAddress);
  1920. if (!ReadMemory(pliHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1921. {
  1922. dprintf("Error reading head entry at 0x%08X\n", pliHead);
  1923. return;
  1924. }
  1925. pliCurrent = pliHead;
  1926. dprintf("LIST ENTRY DATA OFFSET\n");
  1927. dprintf("==============================================\n");
  1928. dprintf(" 0x%08X 0x%08X (HEAD)\n", pliCurrent, pliCurrent-dwOffsetOfEntry);
  1929. dprintf("----------------------------------------------\n");
  1930. //OK... start walking list using Flink
  1931. pliCurrent = liCurrent.Flink;
  1932. while(pliCurrent != NULL && pliHead != pliCurrent)
  1933. {
  1934. // There have been some problems with this.
  1935. #ifdef NEVER
  1936. if (pliCurrent != liCurrent.Blink)
  1937. {
  1938. dprintf(" %p %p (WARNING does Flink/Blink mismatch)\n", pliCurrent,
  1939. ((DWORD_PTR) pliCurrent)-dwOffsetOfEntry);
  1940. }
  1941. else
  1942. #else
  1943. if (TRUE)
  1944. #endif //NEVER
  1945. {
  1946. dprintf(" %p %p\n", pliCurrent,
  1947. ((DWORD_PTR) pliCurrent)-dwOffsetOfEntry);
  1948. }
  1949. if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
  1950. {
  1951. dprintf("Error reading LIST_ENTRY at 0x%08X\n", pliCurrent);
  1952. return;
  1953. }
  1954. //dump the struct if we were asked to
  1955. if ('@' != *szDumpArg)
  1956. {
  1957. //Write address string
  1958. wsprintf(szCurrentDest, "%p", ((DWORD_PTR) pliCurrent)-dwOffsetOfEntry);
  1959. //Call ptdbgext dump function
  1960. _dump(hCurrentProcess, hCurrentThread, dwCurrentPc, pExtensionApis, szDumpArg);
  1961. }
  1962. cEntries++;
  1963. if (cEntries > MAX_ENTRIES)
  1964. {
  1965. dprintf("ERROR: Max number of entries exceeded\n");
  1966. return;
  1967. }
  1968. pliCurrent = liCurrent.Flink;
  1969. }
  1970. dprintf("----------------------------------------------\n");
  1971. dprintf(" %d Total Entries\n", cEntries);
  1972. dprintf("==============================================\n");
  1973. }
  1974. //---[ linkstate ]-------------------------------------------------------------
  1975. //
  1976. //
  1977. // Description:
  1978. // Dumps the current link state (including routing information) of a
  1979. // virtual server.
  1980. // Parameters:
  1981. // Virtual Server Instance - virtual server ID of server to dump
  1982. // Global Server list (optional) - Head of virtual server list
  1983. // Returns:
  1984. // -
  1985. // History:
  1986. // 9/30/98 - MikeSwa Created
  1987. //
  1988. //-----------------------------------------------------------------------------
  1989. AQ_DEBUG_EXTENSION_IMP(linkstate)
  1990. {
  1991. DWORD dwInstance = 0;
  1992. PLIST_ENTRY pliHead = NULL;
  1993. PLIST_ENTRY pliCurrent = NULL;
  1994. BYTE pBuffer[sizeof(CAQSvrInst)] = {'\0'};
  1995. CAQSvrInst *paqinst = (CAQSvrInst *) pBuffer;
  1996. DOMAIN_NAME_TABLE *pdnt = NULL;
  1997. PVOID pvAQueue = NULL;
  1998. LIST_ENTRY liCurrent;
  1999. BOOL fFound = FALSE;
  2000. CHAR szArgBuffer[20];
  2001. LPSTR szCurrentArg = NULL;
  2002. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  2003. PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
  2004. PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
  2005. BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
  2006. CHAR szNextHop[MAX_DOM_PATH_SIZE];
  2007. CHAR szFinalDest[MAX_DOM_PATH_SIZE];
  2008. BYTE pbLMQ[sizeof(CLinkMsgQueue)];
  2009. BYTE pbDomainEntry[sizeof(CDomainEntry)];
  2010. BYTE pbDMQ[sizeof(CDestMsgQueue)];
  2011. CLinkMsgQueue *plmq = (CLinkMsgQueue *) pbLMQ;
  2012. CDomainEntry *pdentry = (CDomainEntry *) pbDomainEntry;
  2013. CDestMsgQueue *pdmq = (CDestMsgQueue *) pbDMQ;
  2014. DWORD *pdwGuid = NULL;
  2015. LPSTR szLinkState = LINK_STATE_UP;
  2016. CHAR szError[100];
  2017. HINSTANCE hModule = GetModuleHandle("aqdbgext.dll");
  2018. DWORD dwMsgId = 0;
  2019. DWORD dwFacility = 0;
  2020. CheckVersion(DebugArgs);
  2021. if (!szArg || ('\0' == szArg[0]))
  2022. {
  2023. //Assume the first instance
  2024. dwInstance = 1;
  2025. pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  2026. }
  2027. else
  2028. {
  2029. strcpy(szArgBuffer, szArg);
  2030. szCurrentArg = strtok(szArgBuffer, " ");
  2031. if (szCurrentArg)
  2032. {
  2033. dwInstance = (DWORD)GetExpression(szCurrentArg);
  2034. szCurrentArg = strtok(NULL, " ");
  2035. if (szCurrentArg)
  2036. pliHead = (PLIST_ENTRY) GetExpression(szCurrentArg);
  2037. else
  2038. pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  2039. }
  2040. }
  2041. if (!pliHead)
  2042. {
  2043. dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
  2044. dprintf(" If you are using windbg, you should specify the value as the\n");
  2045. dprintf(" 2nd argument. You can determine the address value by typeing:\n");
  2046. dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
  2047. return;
  2048. }
  2049. if (!ReadMemory(pliHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
  2050. {
  2051. dprintf("ERROR: Unable to read entry @0x%08X\n", pliHead);
  2052. return;
  2053. }
  2054. while (liCurrent.Flink != pliHead)
  2055. {
  2056. pvAQueue = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
  2057. if (!ReadMemory(pvAQueue, paqinst, sizeof(CAQSvrInst), NULL))
  2058. {
  2059. dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pvAQueue);
  2060. return;
  2061. }
  2062. //Check the signature
  2063. if (CATMSGQ_SIG != paqinst->m_dwSignature)
  2064. {
  2065. dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pvAQueue, liCurrent.Flink);
  2066. return;
  2067. }
  2068. else
  2069. {
  2070. if (paqinst->m_dwServerInstance == dwInstance)
  2071. {
  2072. fFound = TRUE;
  2073. break;
  2074. }
  2075. }
  2076. pliCurrent = liCurrent.Flink;
  2077. if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
  2078. {
  2079. dprintf("ERROR: Unable to read entry @0x%08X\n", pliCurrent);
  2080. return;
  2081. }
  2082. if (pliCurrent == liCurrent.Flink)
  2083. {
  2084. dprintf("ERROR: Loop in LIST_ENTRY @0x%08X\n", pliCurrent);
  2085. return;
  2086. }
  2087. }
  2088. if (!fFound)
  2089. {
  2090. dprintf("Requested instance not found.\n");
  2091. return;
  2092. }
  2093. dprintf("Using Server instance %d @0x%08X\n", dwInstance, pvAQueue);
  2094. //Use our current instance to dump all of the interesting bits
  2095. pdnt = &(paqinst->m_dmt.m_dnt);
  2096. pEntry = &(pdnt->RootEntry);
  2097. while(pEntry)
  2098. {
  2099. //We are not interested in wildcard data
  2100. if (pEntry->pData)
  2101. {
  2102. //Display link state information
  2103. if (!ReadMemory(pEntry->pData, pbDomainEntry, sizeof(CDomainEntry), NULL))
  2104. {
  2105. dprintf("ERROR: Unable to read domain entry from @0x%08X\n", pEntry->pData);
  2106. return;
  2107. }
  2108. pliHead = (PLIST_ENTRY) (((BYTE *)pEntry->pData) + FIELD_OFFSET(CDomainEntry, m_liDestQueues));
  2109. pliCurrent = pdentry->m_liDestQueues.Flink;
  2110. //Get final destination string
  2111. if (!ReadMemory(pdentry->m_szDomainName, szFinalDest, pdentry->m_cbDomainName, NULL))
  2112. {
  2113. dprintf("ERROR: Unable to read final destination name from @0x%08X\n",
  2114. pdentry->m_szDomainName);
  2115. return;
  2116. }
  2117. szFinalDest[pdentry->m_cbDomainName] = '\0';
  2118. //Loop and display each DMQ
  2119. while (pliHead != pliCurrent)
  2120. {
  2121. if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
  2122. {
  2123. dprintf("ERROR: Unable to read link LIST_ENTRY @0x%08X\n", pliCurrent);
  2124. return;
  2125. }
  2126. if (!ReadMemory(CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
  2127. pbDMQ, sizeof(CDestMsgQueue), NULL))
  2128. {
  2129. dprintf("ERROR: Unable to read DMQ @0x%08X\n",
  2130. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
  2131. return;
  2132. }
  2133. //Verify DMQ Signature
  2134. if (DESTMSGQ_SIG != pdmq->m_dwSignature)
  2135. {
  2136. dprintf("ERROR: Invalid DMQ signature for CDestMsgQueue@0x%08X (from LIST_ENTRY) @0x%08X\n",
  2137. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
  2138. pliCurrent);
  2139. return;
  2140. }
  2141. //Read link
  2142. if (!ReadMemory(pdmq->m_plmq, pbLMQ, sizeof(CLinkMsgQueue), NULL))
  2143. {
  2144. dprintf("ERROR: Unable to read LMQ @0x%08X\n",
  2145. pdmq->m_plmq);
  2146. return;
  2147. }
  2148. //Now print off next hop info
  2149. if (!ReadMemory(plmq->m_szSMTPDomain, szNextHop, plmq->m_cbSMTPDomain, NULL))
  2150. {
  2151. dprintf("ERROR: Unable to read next hop name from @0x%08X\n",
  2152. plmq->m_szSMTPDomain);
  2153. return;
  2154. }
  2155. szNextHop[plmq->m_cbSMTPDomain] = '\0';
  2156. pdwGuid = (DWORD *) &(plmq->m_aqsched.m_guidRouter);
  2157. //Determine the state of the link
  2158. if (plmq->m_dwLinkFlags & LINK_STATE_PRIV_GENERATING_DSNS)
  2159. {
  2160. szLinkState = LINK_STATE_DSN;
  2161. }
  2162. if (CLinkMsgQueue::fFlagsAllowConnection(plmq->m_dwLinkStateFlags))
  2163. {
  2164. //If we can connect... are we?
  2165. if (plmq->m_cConnections)
  2166. szLinkState = LINK_STATE_ACTIVE;
  2167. else
  2168. szLinkState = LINK_STATE_UP;
  2169. }
  2170. else
  2171. {
  2172. //If we're down... why?
  2173. szLinkState = LINK_STATE_DOWN;
  2174. if (!(plmq->m_dwLinkStateFlags & LINK_STATE_RETRY_ENABLED))
  2175. szLinkState = LINK_STATE_RETRY;
  2176. else if (plmq->m_dwLinkStateFlags & LINK_STATE_PRIV_CONFIG_TURN_ETRN)
  2177. szLinkState = LINK_STATE_TURN;
  2178. else if (plmq->m_dwLinkStateFlags & LINK_STATE_PRIV_NO_CONNECTION)
  2179. szLinkState = LINK_STATE_SPECIAL;
  2180. }
  2181. //Print some interesting data
  2182. dprintf("==============================================================================\n");
  2183. dprintf("| Link State | Final Destination | Next Hop |\n");
  2184. dprintf("| %s | %-29s | %-29s |\n", szLinkState, szFinalDest, szNextHop);
  2185. dprintf("------------------------------------------------------------------------------\n");
  2186. dprintf("| Route Details: |\n");
  2187. dprintf("| Router GUID: %08X-%08X-%08X-%08X |\n",
  2188. pdwGuid[0], pdwGuid[1], pdwGuid[2], pdwGuid[3]);
  2189. dprintf("| Message Type: %08X Schedule ID:%08X |\n",
  2190. pdmq->m_aqmt.m_dwMessageType, plmq->m_aqsched.m_dwScheduleID);
  2191. dprintf("| Link State Flags 0x%08X |\n",
  2192. plmq->m_dwLinkStateFlags);
  2193. dprintf("| Current # of connections: %-8d |\n",
  2194. plmq->m_cConnections);
  2195. dprintf("| Current # of Msgs (on link): %-8d |\n",
  2196. plmq->m_aqstats.m_cMsgs);
  2197. dprintf("| Current # of Msgs (on DMQ): %-8d |\n",
  2198. pdmq->m_aqstats.m_cMsgs);
  2199. dprintf("| Current # of Msgs (on DMQ/retry): %-8d |\n",
  2200. pdmq->m_aqstats.m_cRetryMsgs);
  2201. dprintf("| CLinkMsgQueue@0x%08X |\n",
  2202. pdmq->m_plmq);
  2203. dprintf("| CDestMsgQueue@0x%08X |\n",
  2204. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
  2205. //print out the diagnostic information if in retry
  2206. //or a failure has been recorded and there are no msgs.
  2207. if ((LINK_STATE_RETRY == szLinkState) ||
  2208. (FAILED(plmq->m_hrDiagnosticError) && !plmq->m_aqstats.m_cMsgs))
  2209. {
  2210. //Get and format the error message
  2211. szError[0] = '\0';
  2212. dwMsgId = plmq->m_hrDiagnosticError;
  2213. dwFacility = ((0x0FFF0000 & dwMsgId) >> 16);
  2214. //If it is not ours... then "un-HRESULT" it
  2215. if (dwFacility != FACILITY_ITF)
  2216. dwMsgId &= 0x0000FFFF;
  2217. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
  2218. FORMAT_MESSAGE_IGNORE_INSERTS |
  2219. FORMAT_MESSAGE_FROM_HMODULE,
  2220. hModule,
  2221. dwMsgId,
  2222. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  2223. szError,
  2224. sizeof(szError),
  2225. NULL );
  2226. dprintf("------------------------------------------------------------------------------\n");
  2227. dprintf("| Failure Details: |\n");
  2228. dprintf("| Diagnostic HRESULT 0x%08X |\n",
  2229. plmq->m_hrDiagnosticError);
  2230. if (szError && *szError)
  2231. {
  2232. dprintf("| Diagnostic string: %s\n",
  2233. szError);
  2234. }
  2235. dprintf("| Protocol Verb: %-20.20s |\n",
  2236. plmq->m_szDiagnosticVerb);
  2237. dprintf("| Protocol Response: %s\n",
  2238. plmq->m_szDiagnosticResponse);
  2239. }
  2240. pliCurrent = liCurrent.Flink;
  2241. }
  2242. }
  2243. //Now determine what the "next" entry is
  2244. if (pEntry->pFirstChildEntry != NULL)
  2245. {
  2246. pEntryRealAddress = pEntry->pFirstChildEntry;
  2247. }
  2248. else if (pEntry->pSiblingEntry != NULL)
  2249. {
  2250. pEntryRealAddress = pEntry->pSiblingEntry;
  2251. }
  2252. else
  2253. {
  2254. for (pEntryRealAddress = pEntry->pParentEntry;
  2255. pEntryRealAddress != NULL;
  2256. pEntryRealAddress = pEntry->pParentEntry)
  2257. {
  2258. //must read parent entry into our buffer
  2259. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  2260. {
  2261. dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
  2262. pEntry = NULL;
  2263. break;
  2264. }
  2265. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  2266. if (pEntry->pSiblingEntry != NULL)
  2267. break;
  2268. }
  2269. if (pEntry != NULL)
  2270. {
  2271. pEntryRealAddress = pEntry->pSiblingEntry;
  2272. }
  2273. }
  2274. if (pEntryRealAddress)
  2275. {
  2276. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  2277. {
  2278. dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
  2279. pEntryRealAddress);
  2280. pEntry = NULL;
  2281. break;
  2282. }
  2283. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  2284. }
  2285. else
  2286. {
  2287. pEntry = NULL;
  2288. }
  2289. }
  2290. dprintf("==============================================================================\n");
  2291. }
  2292. //---[ zombieq ]-------------------------------------------------------------
  2293. //
  2294. //
  2295. // Description:
  2296. // Trolls the DNT for queues that are marked as empty, yet are not in
  2297. // in the empty list
  2298. // Parameters:
  2299. // Virtual Server Instance - virtual server ID of server to dump
  2300. // Global Server list (optional) - Head of virtual server list
  2301. // Returns:
  2302. // -
  2303. // History:
  2304. // 9/30/98 - MikeSwa Created
  2305. // 3/19/2001 - MikeSwa Modified from linkstate
  2306. //
  2307. //-----------------------------------------------------------------------------
  2308. AQ_DEBUG_EXTENSION_IMP(zombieq)
  2309. {
  2310. DWORD dwInstance = 0;
  2311. PLIST_ENTRY pliHead = NULL;
  2312. PLIST_ENTRY pliCurrent = NULL;
  2313. BYTE pBuffer[sizeof(CAQSvrInst)] = {'\0'};
  2314. CAQSvrInst *paqinst = (CAQSvrInst *) pBuffer;
  2315. DOMAIN_NAME_TABLE *pdnt = NULL;
  2316. PVOID pvAQueue = NULL;
  2317. LIST_ENTRY liCurrent;
  2318. BOOL fFound = FALSE;
  2319. CHAR szArgBuffer[20];
  2320. LPSTR szCurrentArg = NULL;
  2321. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  2322. PDOMAIN_NAME_TABLE_ENTRY pEntryRealAddress = NULL;
  2323. PDOMAIN_NAME_TABLE_ENTRY pPathEntry = NULL;
  2324. BYTE pbEntry[sizeof(DOMAIN_NAME_TABLE_ENTRY)];
  2325. CHAR szFinalDest[MAX_DOM_PATH_SIZE];
  2326. BYTE pbLMQ[sizeof(CLinkMsgQueue)];
  2327. BYTE pbDomainEntry[sizeof(CDomainEntry)];
  2328. BYTE pbDMQ[sizeof(CDestMsgQueue)];
  2329. CLinkMsgQueue *plmq = (CLinkMsgQueue *) pbLMQ;
  2330. CDomainEntry *pdentry = (CDomainEntry *) pbDomainEntry;
  2331. CDestMsgQueue *pdmq = (CDestMsgQueue *) pbDMQ;
  2332. DWORD *pdwGuid = NULL;
  2333. DWORD dwMsgId = 0;
  2334. DWORD dwFacility = 0;
  2335. DWORD cZombieQueues = 0; //Queues that are marked as empty but not in empty list
  2336. DWORD cPristineZombieQueues = 0; //Zombie queues that have never had a message on them
  2337. DWORD cZombieQueuesInUse = 0; //Zombie queues that have a refcount
  2338. DWORD cEntries = 0;
  2339. DWORD cZombieEntries = 0;
  2340. DWORD cQueues = 0;
  2341. const DWORD MAX_DBG_MESSAGE_TYPES = 1000;
  2342. DWORD rgdwMessageTypes[MAX_DBG_MESSAGE_TYPES]; //array of message types we have found
  2343. DWORD cMessageTypes = 0;
  2344. DWORD iLastMessageType = 0;
  2345. DWORD iCurrentMessageType = 0;
  2346. DWORD iCurrentPri = 0;
  2347. BOOL fFoundFifoQ = FALSE;
  2348. BOOL fZombieQueueInUse = FALSE;
  2349. LPSTR szScanStatus = "FAILED";
  2350. ZeroMemory(rgdwMessageTypes, sizeof(rgdwMessageTypes));
  2351. CheckVersion(DebugArgs);
  2352. if (!szArg || ('\0' == szArg[0]))
  2353. {
  2354. //Assume the first instance
  2355. dwInstance = 1;
  2356. pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  2357. }
  2358. else
  2359. {
  2360. strcpy(szArgBuffer, szArg);
  2361. szCurrentArg = strtok(szArgBuffer, " ");
  2362. if (szCurrentArg)
  2363. {
  2364. dwInstance = (DWORD)GetExpression(szCurrentArg);
  2365. szCurrentArg = strtok(NULL, " ");
  2366. if (szCurrentArg)
  2367. pliHead = (PLIST_ENTRY) GetExpression(szCurrentArg);
  2368. else
  2369. pliHead = (PLIST_ENTRY) GetExpression(AQUEUE_VIRTUAL_SERVER_SYMBOL);
  2370. }
  2371. }
  2372. if (!pliHead)
  2373. {
  2374. dprintf("ERROR: Unable to determine LIST_ENTRY for virtual servers\n");
  2375. dprintf(" If you are using windbg, you should specify the value as the\n");
  2376. dprintf(" 2nd argument. You can determine the address value by typeing:\n");
  2377. dprintf(" x " AQUEUE_VIRTUAL_SERVER_SYMBOL "\n");
  2378. return;
  2379. }
  2380. if (!ReadMemory(pliHead, &liCurrent, sizeof(LIST_ENTRY), NULL))
  2381. {
  2382. dprintf("ERROR: Unable to read entry @0x%08X\n", pliHead);
  2383. return;
  2384. }
  2385. while (liCurrent.Flink != pliHead)
  2386. {
  2387. pvAQueue = CONTAINING_RECORD(liCurrent.Flink, CAQSvrInst, m_liVirtualServers);
  2388. if (!ReadMemory(pvAQueue, paqinst, sizeof(CAQSvrInst), NULL))
  2389. {
  2390. dprintf("ERROR: Unable to CAQSvrInst @0x%08X", pvAQueue);
  2391. return;
  2392. }
  2393. //Check the signature
  2394. if (CATMSGQ_SIG != paqinst->m_dwSignature)
  2395. {
  2396. dprintf("@0x%08X INVALID SIGNATURE - list entry @0x%08X\n", pvAQueue, liCurrent.Flink);
  2397. return;
  2398. }
  2399. else
  2400. {
  2401. if (paqinst->m_dwServerInstance == dwInstance)
  2402. {
  2403. fFound = TRUE;
  2404. break;
  2405. }
  2406. }
  2407. pliCurrent = liCurrent.Flink;
  2408. if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
  2409. {
  2410. dprintf("ERROR: Unable to read entry @0x%08X\n", pliCurrent);
  2411. return;
  2412. }
  2413. if (pliCurrent == liCurrent.Flink)
  2414. {
  2415. dprintf("ERROR: Loop in LIST_ENTRY @0x%08X\n", pliCurrent);
  2416. return;
  2417. }
  2418. }
  2419. if (!fFound)
  2420. {
  2421. dprintf("Requested instance not found.\n");
  2422. return;
  2423. }
  2424. dprintf("Using Server instance %d @0x%08X\n", dwInstance, pvAQueue);
  2425. //Use our current instance to dump all of the interesting bits
  2426. pdnt = &(paqinst->m_dmt.m_dnt);
  2427. pEntry = &(pdnt->RootEntry);
  2428. while(pEntry)
  2429. {
  2430. cEntries++;
  2431. //
  2432. // Check to see if the user pressed ctrl-C
  2433. //
  2434. if (CheckControlC())
  2435. {
  2436. szScanStatus = "FAILED - User ctrl-c";
  2437. goto Exit;
  2438. }
  2439. //We are not interested in wildcard data
  2440. if (pEntry->pData)
  2441. {
  2442. //Display link state information
  2443. if (!ReadMemory(pEntry->pData, pbDomainEntry, sizeof(CDomainEntry), NULL))
  2444. {
  2445. dprintf("ERROR: Unable to read domain entry from @0x%08X\n", pEntry->pData);
  2446. return;
  2447. }
  2448. pliHead = (PLIST_ENTRY) (((BYTE *)pEntry->pData) + FIELD_OFFSET(CDomainEntry, m_liDestQueues));
  2449. pliCurrent = pdentry->m_liDestQueues.Flink;
  2450. //Get final destination string
  2451. if (!ReadMemory(pdentry->m_szDomainName, szFinalDest, pdentry->m_cbDomainName, NULL))
  2452. {
  2453. dprintf("ERROR: Unable to read final destination name from @0x%08X\n",
  2454. pdentry->m_szDomainName);
  2455. return;
  2456. }
  2457. szFinalDest[pdentry->m_cbDomainName] = '\0';
  2458. //
  2459. // Does this entry have any queues or links
  2460. //
  2461. if (!pdentry->m_cQueues && !pdentry->m_cLinks)
  2462. cZombieEntries++;
  2463. //Loop and display each DMQ
  2464. while (pliHead != pliCurrent)
  2465. {
  2466. cQueues++;
  2467. //
  2468. // Check to see if the user pressed ctrl-C
  2469. //
  2470. if (CheckControlC())
  2471. {
  2472. szScanStatus = "FAILED - User ctrl-c";
  2473. goto Exit;
  2474. }
  2475. if (!ReadMemory(pliCurrent, &liCurrent, sizeof(LIST_ENTRY), NULL))
  2476. {
  2477. dprintf("ERROR: Unable to read link LIST_ENTRY @0x%08X\n", pliCurrent);
  2478. return;
  2479. }
  2480. if (!ReadMemory(CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
  2481. pbDMQ, sizeof(CDestMsgQueue), NULL))
  2482. {
  2483. dprintf("ERROR: Unable to read DMQ @0x%08X\n",
  2484. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs));
  2485. return;
  2486. }
  2487. //Verify DMQ Signature
  2488. if (DESTMSGQ_SIG != pdmq->m_dwSignature)
  2489. {
  2490. dprintf("ERROR: Invalid DMQ signature for CDestMsgQueue@0x%08X (from LIST_ENTRY) @0x%08X\n",
  2491. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
  2492. pliCurrent);
  2493. return;
  2494. }
  2495. //
  2496. // It is a zombie if it is marked as empty, but not in the empty list.
  2497. //
  2498. if ((pdmq->m_dwFlags & CDestMsgQueue::DMQ_EMPTY) &&
  2499. !pdmq->m_liEmptyDMQs.Flink &&
  2500. !pdmq->m_liEmptyDMQs.Blink &&
  2501. !pdmq->m_aqstats.m_cMsgs &&
  2502. !pdmq->m_aqstats.m_cRetryMsgs)
  2503. {
  2504. cZombieQueues++;
  2505. //
  2506. // Look at the refcount. If it is 1 (or 2 with an LMQ) then
  2507. // it is unlikley that it is currently in use
  2508. //
  2509. fZombieQueueInUse = FALSE;
  2510. if (!((1 == *(((DWORD *)pdmq) + 3)) ||
  2511. ((2 == *(((DWORD *)pdmq) + 3)) && pdmq->m_plmq)))
  2512. {
  2513. cZombieQueuesInUse++;
  2514. fZombieQueueInUse = TRUE;
  2515. }
  2516. //
  2517. // Check and see if this has *ever* had a message queued on it.
  2518. //
  2519. fFoundFifoQ = FALSE;
  2520. for (iCurrentPri = 0; iCurrentPri < NUM_PRIORITIES; iCurrentPri++)
  2521. {
  2522. if (pdmq->m_rgpfqQueues[iCurrentPri])
  2523. {
  2524. fFoundFifoQ = TRUE;
  2525. break;
  2526. }
  2527. }
  2528. if (!fFoundFifoQ)
  2529. cPristineZombieQueues++;
  2530. //
  2531. // Have we see this message type before?
  2532. //
  2533. if (rgdwMessageTypes[iLastMessageType] != pdmq->m_aqmt.m_dwMessageType)
  2534. {
  2535. for (iCurrentMessageType = 0;
  2536. iCurrentMessageType < MAX_DBG_MESSAGE_TYPES;
  2537. iCurrentMessageType++)
  2538. {
  2539. if (!rgdwMessageTypes[iCurrentMessageType])
  2540. {
  2541. rgdwMessageTypes[iCurrentMessageType] = pdmq->m_aqmt.m_dwMessageType;
  2542. cMessageTypes++;
  2543. break;
  2544. }
  2545. if (rgdwMessageTypes[iCurrentMessageType] == pdmq->m_aqmt.m_dwMessageType)
  2546. break;
  2547. }
  2548. }
  2549. //Print some interesting data
  2550. dprintf("%s%s| %-29s | CDestMsgQueue@0x%08X | 0x%08X\n",
  2551. fZombieQueueInUse ? "!" : "",
  2552. fFoundFifoQ ? "*" : "",
  2553. szFinalDest,
  2554. CONTAINING_RECORD(pliCurrent, CDestMsgQueue, m_liDomainEntryDMQs),
  2555. pdmq->m_aqmt.m_dwMessageType);
  2556. }
  2557. pliCurrent = liCurrent.Flink;
  2558. }
  2559. }
  2560. //Now determine what the "next" entry is
  2561. if (pEntry->pFirstChildEntry != NULL)
  2562. {
  2563. pEntryRealAddress = pEntry->pFirstChildEntry;
  2564. }
  2565. else if (pEntry->pSiblingEntry != NULL)
  2566. {
  2567. pEntryRealAddress = pEntry->pSiblingEntry;
  2568. }
  2569. else
  2570. {
  2571. for (pEntryRealAddress = pEntry->pParentEntry;
  2572. pEntryRealAddress != NULL;
  2573. pEntryRealAddress = pEntry->pParentEntry)
  2574. {
  2575. //must read parent entry into our buffer
  2576. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  2577. {
  2578. dprintf("ERROR: Unable to read process memory of parent domain entry 0x%08X\n", pEntryRealAddress);
  2579. pEntry = NULL;
  2580. break;
  2581. }
  2582. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  2583. if (pEntry->pSiblingEntry != NULL)
  2584. break;
  2585. }
  2586. if (pEntry != NULL)
  2587. {
  2588. pEntryRealAddress = pEntry->pSiblingEntry;
  2589. }
  2590. }
  2591. if (pEntryRealAddress)
  2592. {
  2593. if (!ReadMemory(pEntryRealAddress, pbEntry, sizeof(DOMAIN_NAME_TABLE_ENTRY), NULL))
  2594. {
  2595. dprintf("ERROR: Unable to read process memory on domain entry 0x%08X\n",
  2596. pEntryRealAddress);
  2597. pEntry = NULL;
  2598. break;
  2599. }
  2600. pEntry = (PDOMAIN_NAME_TABLE_ENTRY) pbEntry;
  2601. }
  2602. else
  2603. {
  2604. pEntry = NULL;
  2605. }
  2606. }
  2607. szScanStatus = "COMPLETED";
  2608. Exit:
  2609. dprintf("==============================================================================\n");
  2610. dprintf("SCAN %s\n", szScanStatus);
  2611. dprintf("==============================================================================\n");
  2612. dprintf("%d Total Zombie Queues (%d bytes) \n", cZombieQueues,
  2613. cZombieQueues*sizeof(CDestMsgQueue));
  2614. dprintf("%d Total Zombie Queues that have never had a message queued\n", cPristineZombieQueues);
  2615. dprintf("%d Total Zombie Queues that may be in use \n", cZombieQueuesInUse);
  2616. dprintf("%d Total Zombie Message Types\n", cMessageTypes);
  2617. dprintf("%d Total Queues\n", cQueues);
  2618. dprintf("%d Total Domain Entires\n", cEntries);
  2619. dprintf("%d Total Zombie Domain Entires (%d bytes) \n", cZombieEntries,
  2620. cZombieEntries*sizeof(CDomainEntry));
  2621. }
  2622. //---[ dsncontexthash ]--------------------------------------------------------
  2623. //
  2624. //
  2625. // Description:
  2626. // Calculates the dsncontexthash for a given filename. Will also dump
  2627. // common hash names
  2628. // Parameters:
  2629. // filename to dump
  2630. // Returns:
  2631. // -
  2632. // History:
  2633. // 9/30/98 - MikeSwa Created
  2634. // 3/19/2001 - MikeSwa Modified from linkstate
  2635. //
  2636. //-----------------------------------------------------------------------------
  2637. AQ_DEBUG_EXTENSION_IMP(dsncontexthash)
  2638. {
  2639. DWORD dwHash = 0;
  2640. const DWORD MAX_DSN_HASH_FILES = 10;
  2641. CHAR rgszWellKnown[MAX_DSN_HASH_FILES][20] =
  2642. {
  2643. "msgref.cpp",
  2644. "aqinst.cpp",
  2645. "mailadmq.cpp",
  2646. "dsnevent.h"
  2647. ""
  2648. };
  2649. DWORD i = 0;
  2650. LPSTR szCurrentWellKnown = rgszWellKnown[0];
  2651. if (szArg && ('\0' != szArg[0]))
  2652. {
  2653. dwHash = dwDSNContextHash(szArg,strlen(szArg));
  2654. dprintf ("DSNContext has for %s is 0x%08X\n",
  2655. szArg, dwHash);
  2656. }
  2657. //
  2658. // If no arg just dump the well known file names.
  2659. //
  2660. for (DWORD i = 0; i < MAX_DSN_HASH_FILES; i++)
  2661. {
  2662. szCurrentWellKnown = rgszWellKnown[i];
  2663. if (!szCurrentWellKnown || !*szCurrentWellKnown)
  2664. break;
  2665. dwHash = dwDSNContextHash(szCurrentWellKnown,
  2666. strlen(szCurrentWellKnown));
  2667. dprintf ("DSNContext has for %s is 0x%08X\n",
  2668. szCurrentWellKnown, dwHash);
  2669. szCurrentWellKnown++;
  2670. }
  2671. }