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.

1308 lines
32 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. processdetails.cxx
  5. Abstract:
  6. implementation of processdetails.h
  7. Author:
  8. Hamid Mahmood (t-hamidm) 08-13-2001
  9. Revision History:
  10. --*/
  11. #include <precomp.hxx>
  12. BOOL ProcessDetails::sm_fIsOldMode;
  13. ProcessDetails::ProcessDetails()
  14. /*++
  15. Routine Description:
  16. Constructor.
  17. Arguments:
  18. none
  19. Return Value:
  20. none
  21. --*/
  22. {
  23. sm_fIsOldMode = FALSE;
  24. m_fIsInetinfo = FALSE;
  25. m_dwPID = 0;
  26. m_pszAppPoolId = NULL;
  27. m_fListMode = FALSE;
  28. m_pProcessParameters = NULL;
  29. m_dwRequestsServed = 0;
  30. m_chVerbosity = 0;
  31. m_dwErrorCode = ERROR_SUCCESS;
  32. InitializeListHead(&m_localRequestListHead);
  33. m_w3wpListInfoStruct.m_pListHead = NULL;
  34. m_w3wpListInfoStruct.m_pRequestsServed = NULL;
  35. m_dwSignature = PROCESS_DETAILS_SIGNATURE;
  36. }
  37. ProcessDetails::~ProcessDetails()
  38. /*++
  39. Routine Description:
  40. Destructor.
  41. Arguments:
  42. none
  43. Return Value:
  44. none
  45. --*/
  46. {
  47. DBG_ASSERT (m_dwSignature == PROCESS_DETAILS_SIGNATURE);
  48. m_dwSignature = PROCESS_DETAILS_SIGNATURE_FREE;
  49. DBG_ASSERT (m_pszAppPoolId == NULL);
  50. DBG_ASSERT (m_pProcessParameters == NULL);
  51. DBG_ASSERT (IsListEmpty(&m_localRequestListHead) == TRUE);
  52. DBG_ASSERT (m_dwPID == 0);
  53. DBG_ASSERT (m_fListMode == FALSE);
  54. DBG_ASSERT (m_fIsInetinfo == FALSE);
  55. DBG_ASSERT (m_chVerbosity == 0);
  56. DBG_ASSERT (m_dwErrorCode == ERROR_SUCCESS);
  57. DBG_ASSERT (m_dwRequestsServed == 0);
  58. }
  59. VOID
  60. ProcessDetails::Init( DWORD dwPID,
  61. CHAR chVerbosity,
  62. BOOL fIsListMode
  63. )
  64. /*++
  65. Routine Description:
  66. Initializes all the class members.
  67. Arguments:
  68. dwPID -- input PID
  69. chVerbosity -- verbosity level
  70. fIsListMode -- TRUE if just enumerating all the
  71. worker processes
  72. Return Value:
  73. none
  74. --*/
  75. {
  76. m_dwPID = dwPID;
  77. m_chVerbosity = chVerbosity;
  78. m_fListMode = fIsListMode;
  79. }
  80. VOID ProcessDetails::Terminate()
  81. /*++
  82. Routine Description:
  83. deallocates memory.
  84. Arguments:
  85. none
  86. Return Value:
  87. none
  88. --*/
  89. {
  90. PLIST_ENTRY pListEntry;
  91. REQUESTLIST_NODE* pRequestListNode;
  92. m_fListMode = FALSE;
  93. m_fIsInetinfo = FALSE;
  94. m_dwPID = 0;
  95. m_dwErrorCode = ERROR_SUCCESS;
  96. m_dwRequestsServed = 0;
  97. m_chVerbosity = 0;
  98. delete[] m_pszAppPoolId;
  99. m_pszAppPoolId = NULL;
  100. m_pProcessParameters = NULL;
  101. //
  102. // w3wplist info struct
  103. //
  104. m_w3wpListInfoStruct.m_pListHead = NULL;
  105. m_w3wpListInfoStruct.m_pRequestsServed = NULL;
  106. //
  107. // deleting the link list
  108. //
  109. while ( ! IsListEmpty (&m_localRequestListHead) )
  110. {
  111. pListEntry = RemoveHeadList(&m_localRequestListHead);
  112. pRequestListNode = CONTAINING_RECORD ( pListEntry,
  113. REQUESTLIST_NODE,
  114. m_listEntry
  115. );
  116. DBG_ASSERT (pRequestListNode->CheckSignature());
  117. pRequestListNode->m_w3wpListHttpReq.Terminate();
  118. pRequestListNode->m_fIsHeaderPresent = FALSE;
  119. //
  120. // delete the current node
  121. //
  122. delete pRequestListNode;
  123. pRequestListNode = NULL;
  124. pListEntry = NULL;
  125. }
  126. }
  127. HRESULT
  128. ProcessDetails::GetProcessDetails( WCHAR* pszInputAppPoolId )
  129. /*++
  130. Routine Description:
  131. Walks through all the processes and calls
  132. GetModuleInfo on all of them.
  133. If pszInputAppPoolId is null, this method
  134. looks at all app pools, otherwise it will
  135. only look at the inputted one.
  136. If the process name is EXE_NAME
  137. then calls ReadEnvVarInfo and DebugProcess
  138. to populate the list with current headers.
  139. DumpRequests is then called to print out
  140. info.
  141. Arguments:
  142. pszInputAppPoolId -- App pool id user input
  143. Return Value:
  144. HRESULT hr -- S_FALSE, if exe name did not match
  145. S_OK if name matched even if. S_OK
  146. is returned even if any function
  147. fails, but the error code is logged.
  148. --*/
  149. {
  150. DWORD dwBytesReceived;
  151. HANDLE hProcess;
  152. HRESULT hr = S_FALSE;
  153. hProcess = OpenProcess ( PROCESS_ALL_ACCESS,
  154. FALSE,
  155. m_dwPID
  156. );
  157. //
  158. // No error generated if OpenProcess
  159. // fails because processes may have
  160. // exited between EnumProcess and
  161. // OpenProcess function calls.
  162. //
  163. if ( hProcess == NULL )
  164. {
  165. goto end;
  166. }
  167. //
  168. // GetModuleInfo() compares the
  169. // exe name to EXE_NAME. If yes,
  170. // then gets the app pool id
  171. // and updates m_pProcessParameters
  172. // (does not allocate memory) and
  173. // m_pszAppPoolId (allocates
  174. // mem for) class members
  175. //
  176. if ( FAILED (GetModuleInfo(hProcess)) )
  177. {
  178. //
  179. // exit if we are in old mode,
  180. // The thread that set fIsOldMode
  181. // found the inetinfo.exe process
  182. // and did all the stuff
  183. //
  184. if ( sm_fIsOldMode == TRUE )
  185. {
  186. ExitThread( CONSOLE_HANDLER_VAR::g_THREAD_EXIT_CODE );
  187. }
  188. goto end;
  189. }
  190. //
  191. // if it is a worker process &&
  192. // if we are enumerating by app pool id
  193. // then make sure they match
  194. //
  195. if ((m_fIsInetinfo == FALSE) &&
  196. (pszInputAppPoolId != NULL) &&
  197. (_wcsicmp ( m_pszAppPoolId,
  198. pszInputAppPoolId) != 0)
  199. )
  200. {
  201. goto end;
  202. }
  203. //
  204. // if successful ReadEnvironmentVar
  205. // reads in W3WPLIST_INFO struct
  206. // into m_w3wpListInfoStruct from
  207. // the worker process, or inetinfo
  208. // in old mode
  209. //
  210. hr = ReadEnvironmentVar ( hProcess );
  211. if ( FAILED (hr) )
  212. {
  213. m_dwErrorCode = HRESULT_FROM_WIN32(GetLastError());
  214. hr = S_FALSE;
  215. goto end;
  216. }
  217. //
  218. // if we're in old mode, set the static
  219. // flag so that other threads should exit.
  220. // We are not entering any critical section
  221. // because there is only one inetinfo
  222. // process and thus only one thread
  223. // will set this variable
  224. //
  225. if ( m_fIsInetinfo == TRUE )
  226. {
  227. sm_fIsOldMode = TRUE;
  228. }
  229. if ( (m_chVerbosity <= 0) ||
  230. ( m_fListMode == TRUE )
  231. )
  232. {
  233. hr = S_OK;
  234. goto end;
  235. }
  236. //
  237. // check for Ctrl+C, Ctrl+Break, etc
  238. //
  239. if ( CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED == TRUE )
  240. {
  241. ExitThread(CONSOLE_HANDLER_VAR::g_THREAD_EXIT_CODE);
  242. }
  243. //
  244. // attaches itself to the worker process,
  245. // traverses the list,
  246. // detaches it self.
  247. //
  248. hr = DebugProcess( hProcess );
  249. if ( FAILED (hr) )
  250. {
  251. m_dwErrorCode = HRESULT_FROM_WIN32(GetLastError());
  252. hr = S_FALSE;
  253. goto end;
  254. }
  255. CloseHandle(hProcess);
  256. hProcess = NULL;
  257. hr = S_OK;
  258. end:
  259. return hr;
  260. }
  261. HRESULT
  262. ProcessDetails::GetModuleInfo(IN HANDLE hProcess)
  263. /*++
  264. Routine Description:
  265. Gets the base module info. If the
  266. exe name == EXE_NAME, then calls
  267. GetAppPoolID to get the application
  268. pool id.
  269. Arguments:
  270. IN hProcess -- Handle to Process to manipulate
  271. Return Value:
  272. HRESULT hr -- E_FAIL, if exe name does not match
  273. or if error occurs
  274. S_OK if name matched and other funcitons
  275. called from within also passed
  276. Error code: if anything else failed
  277. --*/
  278. {
  279. HMODULE hModule;
  280. DWORD dwBytesReceived;
  281. WCHAR szProcessName[MAX_PATH];
  282. HRESULT hr = S_OK;
  283. //
  284. // EnumProcessModules gets all the module
  285. // handles if the buffer is big enough.
  286. // We are only interested in the first
  287. // module here. First module is the exe name.
  288. //
  289. if ( EnumProcessModules ( hProcess,
  290. &hModule,
  291. sizeof(HMODULE),
  292. &dwBytesReceived
  293. ) == FALSE)
  294. {
  295. hr = HRESULT_FROM_WIN32(GetLastError());
  296. goto end;
  297. }
  298. dwBytesReceived = 0;
  299. //
  300. // Get the exe name from the first
  301. // module handle.
  302. //
  303. dwBytesReceived = GetModuleBaseName ( hProcess,
  304. hModule,
  305. szProcessName,
  306. sizeof(szProcessName) / sizeof(WCHAR) - 1
  307. );
  308. if ( dwBytesReceived == 0)
  309. {
  310. hr = HRESULT_FROM_WIN32(GetLastError());
  311. goto end;
  312. }
  313. //
  314. // pad a null at the end of the string
  315. //
  316. szProcessName[dwBytesReceived] = L'\0';
  317. //
  318. // check whether this process is inetinfo,
  319. //
  320. if (_wcsicmp (szProcessName,
  321. INETINFO_NAME
  322. ) == 0
  323. )
  324. {
  325. m_fIsInetinfo = TRUE;
  326. }
  327. //
  328. // check whether it is the
  329. // worker process, if yes
  330. // get the app pool id.
  331. //
  332. else if (_wcsicmp (szProcessName,
  333. EXE_NAME
  334. ) != 0
  335. )
  336. {
  337. hr = E_FAIL;
  338. goto end;
  339. }
  340. //
  341. // we want to call ReadPEB if it is either
  342. // inetinfo or worker process.
  343. //
  344. hr = ReadPEB( hProcess );
  345. if ( FAILED (hr) )
  346. {
  347. goto end;
  348. }
  349. //
  350. // read app pool id only if it is a
  351. // worker process
  352. //
  353. if ( m_fIsInetinfo == FALSE )
  354. {
  355. //
  356. // gets app pool id and initializes
  357. // m_currappPoolId class var
  358. //
  359. hr = GetAppPoolID(hProcess);
  360. }
  361. else
  362. {
  363. //
  364. // we will output inetinfo
  365. // in place of app Pool id
  366. m_pszAppPoolId = new WCHAR[wcslen(INETINFO_NAME) + 1];
  367. if ( m_pszAppPoolId == NULL )
  368. {
  369. hr = HRESULT_FROM_WIN32(GetLastError());
  370. }
  371. else
  372. {
  373. wcscpy ( m_pszAppPoolId,
  374. INETINFO_NAME
  375. );
  376. }
  377. }
  378. end:
  379. return hr;
  380. }
  381. HRESULT
  382. ProcessDetails::ReadPEB(IN HANDLE hProcess )
  383. /*++
  384. Routine Description:
  385. Read in the PEB an initializes some class
  386. vars/
  387. Arguments:
  388. IN hProcess -- Handle to open process to
  389. get app pool id.
  390. Return Value:
  391. HRESULT hr -- hr error code , if error occurs
  392. S_OK if success
  393. --*/
  394. {
  395. NTSTATUS ntStatus;
  396. PROCESS_BASIC_INFORMATION basicProcessInfo;
  397. DWORD dwBytesReceived;
  398. SIZE_T stNumBytesRead;
  399. PEB processPEB;
  400. BOOL fReadMemStatus = FALSE;
  401. HRESULT hr = S_OK;
  402. //
  403. // Gets the basic process info for the
  404. // process handle hProcess
  405. //
  406. ntStatus = NtQueryInformationProcess( hProcess,
  407. ProcessBasicInformation,
  408. &basicProcessInfo,
  409. sizeof(PROCESS_BASIC_INFORMATION),
  410. &dwBytesReceived
  411. );
  412. if ( ntStatus != STATUS_SUCCESS )
  413. {
  414. hr = HRESULT_FROM_WIN32(GetLastError());
  415. goto end;
  416. }
  417. //
  418. // Read the Process PEB from
  419. // the PEB base address retrieved
  420. // earlier as part of the
  421. // ProcessBasicInfo
  422. //
  423. fReadMemStatus = ReadProcessMemory( hProcess,
  424. basicProcessInfo.PebBaseAddress,
  425. &processPEB,
  426. sizeof(PEB),
  427. &stNumBytesRead
  428. );
  429. if (fReadMemStatus == FALSE)
  430. {
  431. hr = HRESULT_FROM_WIN32(GetLastError());
  432. goto end;
  433. }
  434. //
  435. // PEB.ProcessParameters contains
  436. // info or the proces params.
  437. // We are interested only in the
  438. // command line param.Therefore,
  439. // pCommandLineStartAddress points
  440. // to beginning of the command line
  441. // param which is of type UNICODE_STRING
  442. //
  443. m_pProcessParameters = processPEB.ProcessParameters;
  444. end:
  445. return hr;
  446. }
  447. HRESULT
  448. ProcessDetails::GetAppPoolID(IN HANDLE hProcess )
  449. /*++
  450. Routine Description:
  451. Gets the App Pool ID of the current open Process.
  452. Also initializes the m_pProcessParameters class var
  453. Arguments:
  454. IN hProcess -- Handle to open process to
  455. get app pool id.
  456. Return Value:
  457. HRESULT hr -- S_FALSE, if exe name does not match
  458. S_OK if name matched and other funcitons
  459. called from within also passed
  460. Error code: if anything else failed
  461. --*/
  462. {
  463. SIZE_T stNumBytesRead;
  464. BOOL fReadMemStatus = FALSE;
  465. UNICODE_STRING unistrCommandLine;
  466. WCHAR *pszCmdLine = NULL;
  467. WCHAR *pszTempAppPoolId;
  468. BYTE* pCommandLineStartAddress;
  469. HRESULT hr = S_OK;
  470. DBG_ASSERT ( m_pProcessParameters != NULL );
  471. pCommandLineStartAddress = (BYTE*)m_pProcessParameters + CMD_LINE_OFFSET;
  472. fReadMemStatus = ReadProcessMemory( hProcess,
  473. pCommandLineStartAddress,
  474. &unistrCommandLine,
  475. sizeof(UNICODE_STRING),
  476. &stNumBytesRead
  477. );
  478. if (fReadMemStatus == FALSE)
  479. {
  480. hr = HRESULT_FROM_WIN32(GetLastError());
  481. goto cleanup;
  482. }
  483. //
  484. // initialize the WCHAR * to the
  485. // size of the command line string.
  486. // This length (in bytes) is stored in the
  487. // UNICODE_STRING struct. Then call
  488. // ReadProcessMemory to read in the
  489. // string to the local var pszCmdLine.
  490. //
  491. int sizeCmdLine = unistrCommandLine.Length / 2;
  492. pszCmdLine = new WCHAR[sizeCmdLine + 1];
  493. if (pszCmdLine == NULL)
  494. {
  495. hr = HRESULT_FROM_WIN32(GetLastError());
  496. goto cleanup;
  497. }
  498. fReadMemStatus = ReadProcessMemory( hProcess,
  499. unistrCommandLine.Buffer,
  500. pszCmdLine,
  501. unistrCommandLine.Length,
  502. &stNumBytesRead
  503. );
  504. if (fReadMemStatus == FALSE)
  505. {
  506. hr = HRESULT_FROM_WIN32(GetLastError());
  507. goto cleanup;
  508. }
  509. //
  510. // Assure that the string is zero terminated
  511. //
  512. pszCmdLine[sizeCmdLine] = L'\0';
  513. //
  514. // parses the cmd line to get app pool id. if found
  515. // initializes the m_pszAppPoolId class var
  516. //
  517. hr = ParseAppPoolID(pszCmdLine);
  518. cleanup:
  519. delete [] pszCmdLine;
  520. pszCmdLine = NULL;
  521. return hr;
  522. }
  523. HRESULT
  524. ProcessDetails::ParseAppPoolID( IN WCHAR* pszCmdLine)
  525. /*++
  526. Routine Description:
  527. Parses the input command line. Looks for
  528. "-ap" in the command line. The next token
  529. is the app pool id
  530. Arguments:
  531. IN pszCmdLine -- pointer to command line to parse
  532. Return Value:
  533. HRESULT hr -- S_FALSE, if exe name does not match
  534. S_OK if name matched and other funcitons
  535. called from within also passed
  536. Error code: if anything else failed
  537. --*/
  538. {
  539. WCHAR* pszAppPoolTag = NULL;
  540. WCHAR* pszStart = NULL;
  541. DWORD dwSize;
  542. HRESULT hr = S_OK;
  543. DBG_ASSERT( pszCmdLine != NULL );
  544. //
  545. // find "-ap" in the cmd line
  546. //
  547. pszAppPoolTag = wcsstr( pszCmdLine,
  548. L"-ap"
  549. );
  550. if (pszAppPoolTag == NULL)
  551. {
  552. return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  553. }
  554. //
  555. // move to the start of app pool id
  556. // which is in quotes
  557. //
  558. while ( *(++pszAppPoolTag) != L'"');
  559. pszStart = ++pszAppPoolTag;
  560. //
  561. // locate the end of app pool id
  562. //
  563. while ( *(++pszAppPoolTag) != L'"');
  564. //
  565. // calculate size and initialize class var
  566. //
  567. dwSize = DIFF(pszAppPoolTag - pszStart);
  568. m_pszAppPoolId = new WCHAR[dwSize + 1];
  569. if (m_pszAppPoolId == NULL)
  570. {
  571. hr = HRESULT_FROM_WIN32(GetLastError());
  572. goto end;
  573. }
  574. wcsncpy( m_pszAppPoolId,
  575. pszStart,
  576. dwSize
  577. );
  578. m_pszAppPoolId[dwSize] = L'\0';
  579. end:
  580. return hr;
  581. }
  582. HRESULT
  583. ProcessDetails::ReadEnvironmentVar( IN HANDLE hProcess)
  584. /*++
  585. Routine Description:
  586. Reads the environment block of the process
  587. associated with hProcess. Looks for the specific
  588. env var associated with the worker process
  589. and reads in the memory location of the
  590. struct. Then reads in the struct into the class
  591. var m_w3wpListInfoStruct; and the LIST_ENTRY* and
  592. num request served*.
  593. Arguments:
  594. hProcess -- handle to the process
  595. Return Value:
  596. HRESULT hr -- S_FALSE, if exe name does not match
  597. S_OK if name matched and other funcitons
  598. called from within also passed
  599. Error code: if anything else failed
  600. --*/
  601. {
  602. BOOL fReadMemStatus;
  603. SIZE_T stNumBytesRead;
  604. WCHAR szBuf[200];
  605. WCHAR szVarName[ENV_VAR_NAME_MAX_SIZE];
  606. DWORD dwPos;
  607. DWORD_PTR pdwStructAddr = NULL;
  608. HRESULT hr = S_OK;
  609. LONG lRet = ERROR_SUCCESS;
  610. HKEY hkey = 0;
  611. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  612. REGISTRY_KEY_W3SVC_PARAMETERS_W,
  613. 0,
  614. KEY_READ,
  615. &hkey);
  616. if (ERROR_SUCCESS != lRet)
  617. {
  618. hr = HRESULT_FROM_WIN32(lRet);
  619. goto end;
  620. }
  621. DWORD dwType;
  622. DWORD dwSize = sizeof(pdwStructAddr);
  623. lRet = RegQueryValueEx(hkey,
  624. REGISTRY_VALUE_W3WPLIST_INFO_ADDR,
  625. NULL,
  626. &dwType,
  627. (LPBYTE)&pdwStructAddr,
  628. &dwSize);
  629. if (ERROR_SUCCESS != lRet)
  630. {
  631. hr = HRESULT_FROM_WIN32(lRet);
  632. goto end;
  633. }
  634. if ( pdwStructAddr == NULL ) // may need check for overflow or underflow error
  635. {
  636. hr = E_FAIL;
  637. goto end;
  638. }
  639. //
  640. // read in the struct in to the class var m_w3wpListInfoStruct
  641. //
  642. fReadMemStatus = ReadProcessMemory( hProcess,
  643. (LPCVOID)pdwStructAddr,
  644. &m_w3wpListInfoStruct,
  645. sizeof(W3WPLIST_INFO),
  646. &stNumBytesRead
  647. );
  648. if ( fReadMemStatus == FALSE )
  649. {
  650. hr = HRESULT_FROM_WIN32(GetLastError());
  651. goto end;
  652. }
  653. //
  654. // Read the remote list head in the class var.
  655. //
  656. fReadMemStatus = ReadProcessMemory( hProcess,
  657. m_w3wpListInfoStruct.m_pListHead,
  658. &m_remoteListHead,
  659. sizeof(LIST_ENTRY),
  660. &stNumBytesRead
  661. );
  662. if ( fReadMemStatus == FALSE )
  663. {
  664. hr = HRESULT_FROM_WIN32(GetLastError());
  665. goto end;
  666. }
  667. //
  668. // read in the num of requests served
  669. //
  670. fReadMemStatus = ReadProcessMemory( hProcess,
  671. m_w3wpListInfoStruct.m_pRequestsServed,
  672. &m_dwRequestsServed,
  673. sizeof(ULONG),
  674. &stNumBytesRead
  675. );
  676. if ( fReadMemStatus == FALSE )
  677. {
  678. hr = HRESULT_FROM_WIN32(GetLastError());
  679. goto end;
  680. }
  681. end:
  682. if (hkey)
  683. {
  684. RegCloseKey(hkey);
  685. hkey = 0;
  686. }
  687. return hr;
  688. }
  689. HRESULT
  690. ProcessDetails::DebugProcess( IN HANDLE hProcess)
  691. /*++
  692. Routine Description:
  693. This funciton is called if verbosity level >= 1. For
  694. lower verbosity level, we do not need to call this.
  695. Attaches it self to the worker process as a debugger.
  696. Creates a debugging thread in it and waits
  697. for the debug break event. The remote thread created
  698. enters the critical section before breaking.
  699. Calls Traverse list which traverses the list
  700. and copies the relevant info in this process'
  701. memory.
  702. Detaches itseft from the worker process
  703. Arguments:
  704. hProcess -- handle to the process
  705. Return Value:
  706. HRESULT hr -- S_FALSE, if exe name does not match
  707. S_OK if name matched and other funcitons
  708. called from within also passed
  709. Error code: if anything else failed
  710. --*/
  711. {
  712. BOOL bIsListTraversed = FALSE;
  713. DEBUG_EVENT DebugEv; // debugging event information
  714. DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
  715. HRESULT hr = S_OK;
  716. __try
  717. {
  718. //
  719. // attach to the worker process
  720. //
  721. if ( ! DebugActiveProcess( m_dwPID ) )
  722. {
  723. hr = HRESULT_FROM_WIN32(GetLastError());
  724. __leave;
  725. }
  726. for(;;)
  727. {
  728. // Wait for a debugging event to occur. The second parameter indicates
  729. // that the function does not return until a debugging event occurs.
  730. WaitForDebugEvent(&DebugEv, INFINITE);
  731. // Process the debugging event code.
  732. switch (DebugEv.dwDebugEventCode)
  733. {
  734. case EXCEPTION_DEBUG_EVENT:
  735. {
  736. // Process the exception code. When handling
  737. // exceptions, remember to set the continuation
  738. // status parameter (dwContinueStatus). This value
  739. // is used by the ContinueDebugEvent function.
  740. switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
  741. {
  742. case EXCEPTION_BREAKPOINT:
  743. {
  744. // only occurs from the thread
  745. // we created remotely. This
  746. // means that the worker process
  747. // is in stable state, and its in
  748. // a critical section. We can
  749. // start traversing the list now.
  750. hr = TraverseList( hProcess);
  751. bIsListTraversed = TRUE;
  752. dwContinueStatus = DBG_CONTINUE;
  753. break;
  754. } // case EXCEPTION_BREAKPOINT
  755. default:
  756. {
  757. dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
  758. break;
  759. }
  760. } // switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
  761. break;
  762. } // case EXCEPTION_DEBUG_EVENT
  763. default:
  764. break;
  765. } // switch (DebugEv.dwDebugEventCode)
  766. // Resume executing the thread that reported the debugging event.
  767. if ( ContinueDebugEvent( DebugEv.dwProcessId,
  768. DebugEv.dwThreadId,
  769. dwContinueStatus
  770. ) == 0
  771. )
  772. {
  773. hr = HRESULT_FROM_WIN32(GetLastError());
  774. __leave;
  775. }
  776. if ( bIsListTraversed == TRUE ) // traversed the list
  777. {
  778. break;
  779. }
  780. }
  781. } // try
  782. __finally
  783. {
  784. if ( DebugActiveProcessStop( m_dwPID ) == FALSE )
  785. {
  786. hr = HRESULT_FROM_WIN32(GetLastError());
  787. }
  788. } // __finally
  789. //
  790. // check for Ctrl+C, Ctrl+Break, etc
  791. //
  792. if ( CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED == TRUE )
  793. {
  794. ExitThread(CONSOLE_HANDLER_VAR::g_THREAD_EXIT_CODE);
  795. }
  796. return hr;
  797. }
  798. HRESULT
  799. ProcessDetails::TraverseList( IN HANDLE hProcess)
  800. /*++
  801. Routine Description:
  802. Traverses the list in the worker process
  803. and copies the relevant info (dependent on the
  804. verbosity level) to the list in this process.
  805. This function is only called if the verbosity
  806. level is >= 1. For lower verbostiy level,
  807. we don't need to call this
  808. Arguments:
  809. hProcess -- handle to the process
  810. Return Value:
  811. HRESULT hr -- E_FAIL, if any error occurs
  812. else S_OK
  813. --*/
  814. {
  815. BOOL fReadMemStatus = FALSE;
  816. SIZE_T stNumBytesRead;
  817. PVOID pvMemReadAddr = NULL;
  818. REQUESTLIST_NODE* pRequestListNode = NULL;
  819. HTTP_REQUEST remoteHttpRequest;
  820. BYTE pNativeRequestBuffer[sizeof(UL_NATIVE_REQUEST)];
  821. UL_NATIVE_REQUEST* pUlNativeRequest = NULL;
  822. LIST_ENTRY* pNextRemoteListEntry = NULL;
  823. HRESULT hr = S_OK;
  824. DWORD dwError;
  825. DWORD dwCount = 1;
  826. BOOL fIsListCorrupt = FALSE;
  827. LIST_ENTRY* pPossibleBadRemoteListEntry = NULL;
  828. pUlNativeRequest = (UL_NATIVE_REQUEST*) pNativeRequestBuffer;
  829. // move to the first native request from the head and get the
  830. // address of native request
  831. pNextRemoteListEntry = m_remoteListHead.Flink;
  832. pPossibleBadRemoteListEntry = m_remoteListHead.Flink;
  833. while ((pNextRemoteListEntry != m_w3wpListInfoStruct.m_pListHead) &&
  834. (pNextRemoteListEntry != NULL )
  835. )
  836. {
  837. // traversal complete
  838. if ( ( fIsListCorrupt == TRUE ) &&
  839. ( pNextRemoteListEntry == pPossibleBadRemoteListEntry )
  840. )
  841. {
  842. goto end;
  843. }
  844. //
  845. // check for Ctrl+C, Ctrl+Break, etc
  846. //
  847. if ( CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED == TRUE )
  848. {
  849. hr = E_FAIL;
  850. goto end;
  851. }
  852. pvMemReadAddr = CONTAINING_RECORD ( pNextRemoteListEntry,
  853. UL_NATIVE_REQUEST,
  854. _ListEntry
  855. );
  856. //
  857. // read in the UL_NATIVE_REQUEST obj
  858. //
  859. fReadMemStatus = ReadProcessMemory( hProcess,
  860. pvMemReadAddr,
  861. pUlNativeRequest,
  862. sizeof(UL_NATIVE_REQUEST),
  863. &stNumBytesRead
  864. );
  865. if ( ( fReadMemStatus == FALSE ) ||
  866. ( pUlNativeRequest->_dwSignature != UL_NATIVE_REQUEST_SIGNATURE)
  867. )
  868. {
  869. dwError = GetLastError();
  870. if ( (dwError != ERROR_PARTIAL_COPY) &&
  871. (dwError != ERROR_INVALID_ADDRESS) &&
  872. (dwError != ERROR_NOACCESS)
  873. )
  874. {
  875. hr = E_FAIL;
  876. goto end;
  877. }
  878. //
  879. // this should never happen,
  880. // the only scenario is when the list
  881. // is corrupt at two diff locations
  882. //
  883. if ( fIsListCorrupt == TRUE )
  884. {
  885. goto end;
  886. }
  887. fIsListCorrupt = TRUE;
  888. goto NextRemoteNode;
  889. }
  890. //
  891. // read _ExecState var of UL_NATIVE_REQUEST and
  892. // make sure that it is in NREQ_STATE_PROCESS
  893. //
  894. if ( pUlNativeRequest->_ExecState != NREQ_STATE_PROCESS )
  895. {
  896. goto NextRemoteNode;
  897. }
  898. //
  899. // read in the HTTP_REQUEST struct
  900. //
  901. pvMemReadAddr = pUlNativeRequest->_pbBuffer;
  902. fReadMemStatus = ReadProcessMemory( hProcess,
  903. pvMemReadAddr,
  904. &remoteHttpRequest,
  905. sizeof(HTTP_REQUEST),
  906. &stNumBytesRead
  907. );
  908. if ( fReadMemStatus == FALSE )
  909. {
  910. hr = E_FAIL;
  911. goto cleanup;
  912. }
  913. pRequestListNode = new REQUESTLIST_NODE();
  914. if ( pRequestListNode == NULL ) // new failed
  915. {
  916. hr = E_FAIL;
  917. goto end;
  918. }
  919. hr = pRequestListNode->m_w3wpListHttpReq.ReadFromWorkerProcess( hProcess,
  920. &remoteHttpRequest,
  921. m_chVerbosity
  922. );
  923. if ( hr == E_FAIL )
  924. {
  925. goto cleanup;
  926. }
  927. //
  928. // Insert node in the local list
  929. //
  930. InsertTailList( &m_localRequestListHead,
  931. &(pRequestListNode->m_listEntry)
  932. );
  933. pRequestListNode = NULL;
  934. NextRemoteNode:
  935. //
  936. // Get address of next element in the remote list
  937. //
  938. if ( fIsListCorrupt == FALSE )
  939. {
  940. //
  941. // save address of the current node
  942. // just in case so that we can check for this
  943. // address coming from the back-
  944. // side of the list
  945. //
  946. pPossibleBadRemoteListEntry = pNextRemoteListEntry;
  947. pNextRemoteListEntry = pUlNativeRequest->_ListEntry.Flink;
  948. }
  949. //
  950. // first time, we have to go back from the head
  951. //
  952. else if ( dwCount == 1 )
  953. {
  954. pNextRemoteListEntry = m_remoteListHead.Blink;
  955. dwCount = 2;
  956. }
  957. //
  958. // all other times just follow the back link
  959. //
  960. else
  961. {
  962. pNextRemoteListEntry = pUlNativeRequest->_ListEntry.Blink;
  963. }
  964. } // while (pNextRemoteListEntry != m_pRemoteListHead);
  965. goto end;
  966. cleanup:
  967. delete pRequestListNode;
  968. pRequestListNode = NULL;
  969. end:
  970. return hr;
  971. }
  972. VOID
  973. ProcessDetails::DumpRequests()
  974. /*++
  975. Routine Description:
  976. Traverses the local list and prints out the stuff
  977. depending on the current verbosity level chosen
  978. Arguments:
  979. None
  980. Return Value:
  981. None
  982. --*/
  983. {
  984. REQUESTLIST_NODE* pListNode;
  985. PLIST_ENTRY pListEntry;
  986. if ( m_dwErrorCode != ERROR_SUCCESS )
  987. {
  988. wprintf( L"%-10ufailed. Error Code %u",
  989. m_dwPID,
  990. m_dwErrorCode
  991. );
  992. goto end;
  993. }
  994. //
  995. // output stuff for verbosity level 0
  996. //
  997. wprintf ( L"%-10u %-28s %-28s",
  998. m_dwPID,
  999. m_pszAppPoolId,
  1000. L"" // this should be app pool friendly name
  1001. );
  1002. if ( m_fListMode == TRUE )
  1003. {
  1004. wprintf( L"\n" );
  1005. goto end;
  1006. }
  1007. wprintf( L"%-10u\n",
  1008. m_dwRequestsServed
  1009. );
  1010. if ( m_chVerbosity == 0 )
  1011. {
  1012. goto end;
  1013. }
  1014. //
  1015. // traversing the list
  1016. //
  1017. for ( pListEntry = m_localRequestListHead.Flink;
  1018. pListEntry != &m_localRequestListHead;
  1019. pListEntry = pListEntry->Flink
  1020. )
  1021. {
  1022. pListNode = CONTAINING_RECORD( pListEntry,
  1023. REQUESTLIST_NODE,
  1024. m_listEntry
  1025. );
  1026. DBG_ASSERT(pListNode->CheckSignature());
  1027. pListNode->m_w3wpListHttpReq.Print(m_chVerbosity);
  1028. }
  1029. end:
  1030. return;
  1031. }