Advertisement
Guest User

Untitled

a guest
Feb 25th, 2020
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.66 KB | None | 0 0
  1. // ConsoleApplication6.cpp: определяет точку входа для консольного приложения.
  2. //
  3.  
  4. #include "stdafx.h"
  5. #ifndef UNICODE
  6. #define UNICODE
  7. #endif
  8.  
  9. #include <windows.h>
  10. #include <wct.h>
  11. #include <psapi.h>
  12. #include <tlhelp32.h>
  13. #include <wchar.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string>
  17. #include <iostream>
  18.  
  19. #pragma comment(lib, "Psapi.lib")
  20. #pragma comment(lib, "Advapi32.lib")
  21.  
  22. typedef struct _STR_ARRAY
  23. {
  24. CHAR Desc[32];
  25. } STR_ARRAY;
  26.  
  27. // Human-readable names for the different synchronization types.
  28. STR_ARRAY STR_OBJECT_TYPE[] =
  29. {
  30. { "CriticalSection" },
  31. { "SendMessage" },
  32. { "Mutex" },
  33. { "Alpc" },
  34. { "Com" },
  35. { "ThreadWait" },
  36. { "ProcWait" },
  37. { "Thread" },
  38. { "ComActivation" },
  39. { "Unknown" },
  40. { "Max" }
  41. };
  42.  
  43. // Global variable to store the WCT session handle
  44. HWCT g_WctHandle = NULL;
  45.  
  46. // Global variable to store OLE32.DLL module handle.
  47. HMODULE g_Ole32Hnd = NULL;
  48.  
  49. //
  50. // Function prototypes
  51. //
  52.  
  53. void
  54. PrintWaitChain(
  55. __in DWORD ThreadId
  56. );
  57.  
  58.  
  59. BOOL
  60. GrantDebugPrivilege()
  61. /*++
  62.  
  63. Routine Description:
  64.  
  65. Enables the debug privilege (SE_DEBUG_NAME) for this process.
  66. This is necessary if we want to retrieve wait chains for processes
  67. not owned by the current user.
  68.  
  69. Arguments:
  70.  
  71. None.
  72.  
  73. Return Value:
  74.  
  75. TRUE if this privilege could be enabled; FALSE otherwise.
  76.  
  77. --*/
  78. {
  79. BOOL fSuccess = FALSE;
  80. HANDLE TokenHandle = NULL;
  81. TOKEN_PRIVILEGES TokenPrivileges;
  82.  
  83. /*
  84. Process Token contains:
  85. - ID
  86. - Session ID
  87. - User ID
  88. - Group ID
  89. - Privileges: sets some property for a process for operating with it (as SE_DEBUG_NAME)
  90. */
  91.  
  92. if (!OpenProcessToken(GetCurrentProcess(),
  93. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  94. &TokenHandle))
  95. {
  96. printf("Could not get the process token");
  97. goto Cleanup;
  98. }
  99.  
  100. TokenPrivileges.PrivilegeCount = 1;
  101.  
  102. if (!LookupPrivilegeValue(NULL,
  103. SE_DEBUG_NAME,
  104. &TokenPrivileges.Privileges[0].Luid))
  105. {
  106. printf("Couldn't lookup SeDebugPrivilege name");
  107. goto Cleanup;
  108. }
  109.  
  110. TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  111.  
  112. if (!AdjustTokenPrivileges(TokenHandle,
  113. FALSE,
  114. &TokenPrivileges,
  115. sizeof(TokenPrivileges),
  116. NULL,
  117. NULL))
  118. {
  119. printf("Could not revoke the debug privilege");
  120. goto Cleanup;
  121. }
  122.  
  123. fSuccess = TRUE;
  124.  
  125. Cleanup:
  126.  
  127. if (TokenHandle)
  128. {
  129. CloseHandle(TokenHandle);
  130. }
  131.  
  132. return fSuccess;
  133. }
  134.  
  135. BOOL CheckThreadsOfProcess(__in DWORD ProcId)
  136. {
  137. //spdlog::info("Traverse wait chain of the process with ID: " + std::string(ProcId));
  138.  
  139. if (!GrantDebugPrivilege())
  140. {
  141. //printf("Could not enable the debug privilege");
  142. //spdlog::error("Module-name failed, could not enable the debug privilege for the process");
  143. }
  144.  
  145. auto process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcId);
  146.  
  147. if (process)
  148. {
  149. WCHAR file[MAX_PATH];
  150.  
  151. //printf("Process %d - ", processes[i]);
  152. //spdlog::info("");
  153.  
  154. // Retrieve the executable name and print it.
  155. if (GetProcessImageFileName(process, file, ARRAYSIZE(file)) > 0)
  156. {
  157. PCWSTR filePart = wcsrchr(file, L'\\');
  158. if (filePart)
  159. {
  160. filePart++;
  161. }
  162. else
  163. {
  164. filePart = file;
  165. }
  166.  
  167. //spdlog::info("Retrieved process has name" + std::string(filePart));
  168. }
  169.  
  170. // Get a snapshot of this process. This enables us to
  171. // enumerate its threads.
  172. auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, ProcId);
  173.  
  174. if (snapshot)
  175. {
  176. THREADENTRY32 thread;
  177. thread.dwSize = sizeof(thread);
  178.  
  179. // Walk the thread list and print each wait chain
  180. if (Thread32First(snapshot, &thread))
  181. {
  182. do
  183. {
  184. /*Get all threads that belong to the process that is being considered*/
  185. if (thread.th32OwnerProcessID == ProcId)
  186. {
  187. // Open a handle to this specific thread
  188. HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS,
  189. FALSE,
  190. thread.th32ThreadID);
  191. if (threadHandle)
  192. {
  193. // Check whether the thread is still running
  194. DWORD exitCode;
  195. GetExitCodeThread(threadHandle, &exitCode);
  196.  
  197. if (exitCode == STILL_ACTIVE)
  198. {
  199. // Print the wait chain.
  200. PrintWaitChain(thread.th32ThreadID);
  201. }
  202.  
  203. CloseHandle(threadHandle);
  204. }
  205. }
  206. } while (Thread32Next(snapshot, &thread));
  207. }
  208. CloseHandle(snapshot);
  209. }
  210. CloseHandle(process);
  211. }
  212. return true;
  213. }
  214.  
  215. BOOL
  216. CheckThreads(
  217. __in DWORD ProcId
  218. )
  219. /*++
  220.  
  221. Routine Description:
  222.  
  223. Enumerates all threads (or optionally only threads for one
  224. process) in the system. It the calls the WCT API on each of them.
  225.  
  226. Arguments:
  227.  
  228. ProcId--Specifies the process ID to analyze. If '0' all processes
  229. in the system will be checked.
  230.  
  231. Return Value:
  232.  
  233. TRUE if processes could be checked; FALSE if a general failure
  234. occurred.
  235.  
  236. --*/
  237. {
  238. DWORD processes[1024];
  239. DWORD numProcesses;
  240. DWORD i;
  241.  
  242. // Try to enable the SE_DEBUG_NAME privilege for this process.
  243. // Continue even if this fails--we just won't be able to retrieve
  244. // wait chains for processes not owned by the current user.
  245. if (!GrantDebugPrivilege())
  246. {
  247. printf("Could not enable the debug privilege");
  248. }
  249.  
  250. // Get a list of all processes currently running.
  251. if (EnumProcesses(processes, sizeof(processes), &numProcesses) == FALSE)
  252. {
  253. printf("Could not enumerate processes");
  254. return FALSE;
  255. }
  256.  
  257. for (i = 0; i < numProcesses / sizeof(DWORD); i++)
  258. {
  259. HANDLE process;
  260. HANDLE snapshot;
  261.  
  262. if (processes[i] == GetCurrentProcessId())
  263. {
  264. continue;
  265. }
  266.  
  267. // If the caller specified a Process ID, check if we have a match.
  268. if (ProcId != 0)
  269. {
  270. if (processes[i] != ProcId)
  271. {
  272. continue;
  273. }
  274. }
  275.  
  276. // Get a handle to this process.
  277. process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processes[i]);
  278. if (process)
  279. {
  280. WCHAR file[MAX_PATH];
  281.  
  282. printf("Process %d - ", processes[i]);
  283.  
  284. // Retrieve the executable name and print it.
  285. if (GetProcessImageFileName(process, file, ARRAYSIZE(file)) > 0)
  286. {
  287. PCWSTR filePart = wcsrchr(file, L'\\');
  288. if (filePart)
  289. {
  290. filePart++;
  291. }
  292. else
  293. {
  294. filePart = file;
  295. }
  296.  
  297. printf("%S", filePart);
  298. }
  299.  
  300. printf("\n----------------------------------\n");
  301.  
  302. // Get a snapshot of this process. This enables us to
  303. // enumerate its threads.
  304. snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,
  305. processes[i]);
  306. if (snapshot)
  307. {
  308. THREADENTRY32 thread;
  309. thread.dwSize = sizeof(thread);
  310.  
  311. // Walk the thread list and print each wait chain
  312. if (Thread32First(snapshot, &thread))
  313. {
  314. do
  315. {
  316. /*Get all threads that belong to the process that is being considered*/
  317. if (thread.th32OwnerProcessID == processes[i])
  318. {
  319. // Open a handle to this specific thread
  320. HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS,
  321. FALSE,
  322. thread.th32ThreadID);
  323. if (threadHandle)
  324. {
  325. // Check whether the thread is still running
  326. DWORD exitCode;
  327. GetExitCodeThread(threadHandle, &exitCode);
  328.  
  329. if (exitCode == STILL_ACTIVE)
  330. {
  331. // Print the wait chain.
  332. PrintWaitChain(thread.th32ThreadID);
  333. }
  334.  
  335. CloseHandle(threadHandle);
  336. }
  337. }
  338.  
  339.  
  340.  
  341.  
  342.  
  343. } while (Thread32Next(snapshot, &thread));
  344. }
  345. CloseHandle(snapshot);
  346. }
  347.  
  348. CloseHandle(process);
  349. printf("\n");
  350. }
  351. }
  352. return TRUE;
  353. }
  354.  
  355. void TraverseWaitChain(__in DWORD ThreadId) {
  356. WAITCHAIN_NODE_INFO NodeInfoArray[WCT_MAX_NODE_COUNT];
  357. DWORD Count, i;
  358. BOOL IsCycle;
  359.  
  360. //spdlog::info("Checking thread with id" + std::string(ThreadId));
  361. Count = WCT_MAX_NODE_COUNT;
  362.  
  363. /*WTC API*/
  364. if (!GetThreadWaitChain(g_WctHandle,
  365. NULL,
  366. WCTP_GETINFO_ALL_FLAGS,
  367. ThreadId,
  368. &Count,
  369. NodeInfoArray,
  370. &IsCycle))
  371. {
  372. //printf("Error (0X%x)\n", GetLastError());
  373. //spdlog::error("could not traverse the thread [" + std::string(ThreadId) + "]. Reason: " + std::string(GetLastError()));
  374. return;
  375. }
  376.  
  377. if (Count > WCT_MAX_NODE_COUNT)
  378. {
  379. printf("Found additional nodes %d\n", Count);
  380. Count = WCT_MAX_NODE_COUNT;
  381. }
  382.  
  383. for (i = 0; i < Count; i++)
  384. {
  385. switch (NodeInfoArray[i].ObjectType)
  386. {
  387. case WctThreadType:
  388. // A thread node contains process and thread ID.
  389. printf("[%d:%d:%s]->",
  390. NodeInfoArray[i].ThreadObject.ProcessId,
  391. NodeInfoArray[i].ThreadObject.ThreadId,
  392. ((NodeInfoArray[i].ObjectStatus == WctStatusBlocked) ? "b" : "r"));
  393. break;
  394.  
  395. default:
  396. // A synchronization object.
  397.  
  398. // Some objects have names...
  399. if (NodeInfoArray[i].LockObject.ObjectName[0] != L'\0')
  400. {
  401. printf("[%s:%S]->",
  402. STR_OBJECT_TYPE[NodeInfoArray[i].ObjectType - 1].Desc,
  403. NodeInfoArray[i].LockObject.ObjectName);
  404. }
  405. else
  406. {
  407. printf("[%s]->",
  408. STR_OBJECT_TYPE[NodeInfoArray[i].ObjectType - 1].Desc);
  409. }
  410. if (NodeInfoArray[i].ObjectStatus == WctStatusAbandoned)
  411. {
  412. printf("<abandoned>");
  413. }
  414. break;
  415. }
  416. }
  417.  
  418. /**/
  419. if (IsCycle)
  420. {
  421. printf(" !!!Deadlock!!!");
  422. }
  423. }
  424.  
  425. void
  426. PrintWaitChain(
  427. __in DWORD ThreadId
  428. )
  429. /*++
  430.  
  431. Routine Description:
  432.  
  433. Enumerates all threads (or optionally only threads for one
  434. process) in the system. It the calls the WCT API on each thread.
  435.  
  436. Arguments:
  437.  
  438. ThreadId--Specifies the thread ID to analyze.
  439.  
  440. Return Value:
  441.  
  442. (none)
  443.  
  444. --*/
  445. {
  446. WAITCHAIN_NODE_INFO NodeInfoArray[WCT_MAX_NODE_COUNT];
  447. DWORD Count, i;
  448. BOOL IsCycle;
  449.  
  450. //printf("%d: ", ThreadId);
  451.  
  452. Count = WCT_MAX_NODE_COUNT;
  453.  
  454. // Make a synchronous WCT call to retrieve the wait chain.
  455. if (!GetThreadWaitChain(g_WctHandle,
  456. NULL,
  457. WCTP_GETINFO_ALL_FLAGS,
  458. ThreadId,
  459. &Count,
  460. NodeInfoArray,
  461. &IsCycle))
  462. {
  463. printf("Error (0X%x)\n", GetLastError());
  464. return;
  465. }
  466.  
  467. // Check if the wait chain is too big for the array we passed in.
  468. if (Count > WCT_MAX_NODE_COUNT)
  469. {
  470. printf("Found additional nodes %d\n", Count);
  471. Count = WCT_MAX_NODE_COUNT;
  472. }
  473.  
  474. // Loop over all the nodes returned and print useful information.
  475. for (i = 0; i < Count; i++)
  476. {
  477. std::string logMessage;
  478.  
  479. logMessage = "[process:" + std::to_string(NodeInfoArray[i].ThreadObject.ProcessId) + "]";
  480. logMessage += "[thread:" + std::to_string(NodeInfoArray[i].ThreadObject.ThreadId) + "]";
  481. logMessage += std::string("[status:") + std::string((NodeInfoArray[i].ObjectStatus == WctStatusBlocked) ? "BLOCKED]" : "RUNNING]");
  482.  
  483. switch (NodeInfoArray[i].ObjectType)
  484. {
  485. case WctThreadType:
  486. // A thread node contains process and thread ID.
  487. /*printf("[%d:%d:%s]->",
  488. NodeInfoArray[i].ThreadObject.ProcessId,
  489. NodeInfoArray[i].ThreadObject.ThreadId,
  490. ((NodeInfoArray[i].ObjectStatus == WctStatusBlocked) ? "b" : "r"));*/
  491.  
  492. //std::cout << "[process:" << NodeInfoArray[i].ThreadObject.ProcessId << "]"
  493.  
  494. if (NodeInfoArray[i].ObjectStatus != WctStatusBlocked)
  495. break;
  496. else
  497. std::cout << logMessage << std::endl;
  498. default:
  499. // A synchronization object.
  500.  
  501. // Some objects have names...
  502. if (NodeInfoArray[i].LockObject.ObjectName[0] != L'\0')
  503. {
  504. std::string objectDescription(STR_OBJECT_TYPE[NodeInfoArray[i].ObjectType - 1].Desc);
  505.  
  506. if (objectDescription == "Thread")
  507. {
  508. logMessage += " waiting another thread ";
  509. std::cout << logMessage << std::endl;
  510. //printf("process is waiting another thread ");
  511. }
  512. else
  513. {
  514. logMessage += std::string(STR_OBJECT_TYPE[NodeInfoArray[i].ObjectType - 1].Desc) + " " ;
  515. std::cout << logMessage << std::endl;
  516. printf("%S\n", NodeInfoArray[i].LockObject.ObjectName);
  517. //printf("[%s:%S]->",
  518. // STR_OBJECT_TYPE[NodeInfoArray[i].ObjectType - 1].Desc,
  519. // NodeInfoArray[i].LockObject.ObjectName);
  520. }
  521.  
  522. }
  523. else
  524. {
  525. printf("[%s]->",
  526. STR_OBJECT_TYPE[NodeInfoArray[i].ObjectType - 1].Desc);
  527. }
  528. if (NodeInfoArray[i].ObjectStatus == WctStatusAbandoned)
  529. {
  530. printf("<abandoned>");
  531. }
  532. break;
  533. }
  534. //std::cout << logMessage << endl;
  535. }
  536.  
  537. //printf("\r[End]");
  538.  
  539. // Did we find a deadlock?
  540. if (IsCycle)
  541. {
  542. printf(" !!!Deadlock!!!");
  543. }
  544.  
  545. printf("\n");
  546. }
  547.  
  548. void
  549. Usage()
  550. /*++
  551.  
  552. Routine Description:
  553.  
  554. Print usage information to stdout.
  555.  
  556. --*/
  557. {
  558. printf("\nPrints the thread wait chains for one or all processes in the system.\n\n");
  559. printf("\nUsage:\tWctEnum [ProcId]\n");
  560. printf("\t (no params) -- get the wait chains for all processes\n");
  561. printf("\t ProcId -- get the wait chains for the specified process\n\n");
  562. }
  563.  
  564. BOOL
  565. InitCOMAccess()
  566. /*++
  567.  
  568. Routine Description:
  569.  
  570. Register COM interfaces with WCT. This enables WCT to provide wait
  571. information if a thread is blocked on a COM call.
  572.  
  573. --*/
  574. {
  575. PCOGETCALLSTATE CallStateCallback;
  576. PCOGETACTIVATIONSTATE ActivationStateCallback;
  577.  
  578. // Get a handle to OLE32.DLL. You must keep this handle around
  579. // for the life time for any WCT session.
  580. g_Ole32Hnd = LoadLibrary(L"ole32.dll");
  581. if (!g_Ole32Hnd)
  582. {
  583. printf("ERROR: GetModuleHandle failed: 0x%X\n", GetLastError());
  584. return FALSE;
  585. }
  586.  
  587. // Retrieve the function addresses for the COM helper APIs.
  588. CallStateCallback = (PCOGETCALLSTATE)
  589. GetProcAddress(g_Ole32Hnd, "CoGetCallState");
  590. if (!CallStateCallback)
  591. {
  592. printf("ERROR: GetProcAddress failed: 0x%X\n", GetLastError());
  593. return FALSE;
  594. }
  595.  
  596. ActivationStateCallback = (PCOGETACTIVATIONSTATE)
  597. GetProcAddress(g_Ole32Hnd, "CoGetActivationState");
  598. if (!ActivationStateCallback)
  599. {
  600. printf("ERROR: GetProcAddress failed: 0x%X\n", GetLastError());
  601. return FALSE;
  602. }
  603.  
  604. // Register these functions with WCT.
  605. RegisterWaitChainCOMCallback(CallStateCallback,
  606. ActivationStateCallback);
  607. return TRUE;
  608. }
  609.  
  610. int _cdecl
  611. wmain(
  612. __in int argc,
  613. __in_ecount(argc) PWSTR* argv
  614. )
  615. /*++
  616.  
  617. Routine Description:
  618.  
  619. Main entry point for this application.
  620.  
  621. --*/
  622. {
  623. int rc = 1;
  624.  
  625. // Initialize the WCT interface to COM. Continue if this
  626. // fails--there just will not be COM information.
  627. if (!InitCOMAccess())
  628. {
  629. printf("Could not enable COM access\n");
  630. }
  631.  
  632. // Open a synchronous WCT session.
  633. g_WctHandle = OpenThreadWaitChainSession(0, NULL);
  634. if (NULL == g_WctHandle)
  635. {
  636. printf("ERROR: OpenThreadWaitChainSession failed\n");
  637. goto Cleanup;
  638. }
  639.  
  640. if (argc < 2)
  641. {
  642. // Enumerate all threads in the system.
  643. CheckThreads(0);
  644. }
  645. else
  646. {
  647. // Only enumerate threads in the specified process.
  648. //
  649. // Take the first command line parameter as the process ID.
  650. DWORD ProcId = 0;
  651.  
  652. ProcId = _wtoi(argv[1]);
  653. if (ProcId == 0)
  654. {
  655. Usage();
  656. goto Cleanup;
  657. }
  658.  
  659. CheckThreads(ProcId);
  660. }
  661.  
  662. // Close the WCT session.
  663. CloseThreadWaitChainSession(g_WctHandle);
  664.  
  665. rc = 0;
  666.  
  667. Cleanup:
  668.  
  669. if (NULL != g_Ole32Hnd)
  670. {
  671. FreeLibrary(g_Ole32Hnd);
  672. }
  673.  
  674. return rc;
  675. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement