PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 1135 lines | 973 code | 82 blank | 80 comment | 95 complexity | c79d3ee5b8822bb915a6476c56c99448 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using Microsoft.Win32.SafeHandles;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Diagnostics.Contracts;
  8. using System.Globalization;
  9. using System.Runtime.InteropServices;
  10. using System.Text;
  11. using System.Threading;
  12. namespace System.Diagnostics
  13. {
  14. internal static partial class ProcessManager
  15. {
  16. /// <summary>Gets whether the process with the specified ID is currently running.</summary>
  17. /// <param name="processId">The process ID.</param>
  18. /// <returns>true if the process is running; otherwise, false.</returns>
  19. public static bool IsProcessRunning(int processId)
  20. {
  21. return IsProcessRunning(processId, GetProcessIds());
  22. }
  23. /// <summary>Gets whether the process with the specified ID on the specified machine is currently running.</summary>
  24. /// <param name="processId">The process ID.</param>
  25. /// <param name="machineName">The machine name.</param>
  26. /// <returns>true if the process is running; otherwise, false.</returns>
  27. public static bool IsProcessRunning(int processId, string machineName)
  28. {
  29. return IsProcessRunning(processId, GetProcessIds(machineName));
  30. }
  31. /// <summary>Gets the ProcessInfo for the specified process ID on the specified machine.</summary>
  32. /// <param name="processId">The process ID.</param>
  33. /// <param name="machineName">The machine name.</param>
  34. /// <returns>The ProcessInfo for the process if it could be found; otherwise, null.</returns>
  35. public static ProcessInfo GetProcessInfo(int processId, string machineName)
  36. {
  37. ProcessInfo[] processInfos = ProcessManager.GetProcessInfos(machineName);
  38. foreach (ProcessInfo processInfo in processInfos)
  39. {
  40. if (processInfo.ProcessId == processId)
  41. {
  42. return processInfo;
  43. }
  44. }
  45. return null;
  46. }
  47. /// <summary>Gets process infos for each process on the specified machine.</summary>
  48. /// <param name="machineName">The target machine.</param>
  49. /// <returns>An array of process infos, one per found process.</returns>
  50. public static ProcessInfo[] GetProcessInfos(string machineName)
  51. {
  52. return IsRemoteMachine(machineName) ?
  53. NtProcessManager.GetProcessInfos(machineName, true) :
  54. NtProcessInfoHelper.GetProcessInfos(); // Do not use performance counter for local machine
  55. }
  56. /// <summary>Gets the IDs of all processes on the specified machine.</summary>
  57. /// <param name="machineName">The machine to examine.</param>
  58. /// <returns>An array of process IDs from the specified machine.</returns>
  59. public static int[] GetProcessIds(string machineName)
  60. {
  61. // Due to the lack of support for EnumModules() on coresysserver, we rely
  62. // on PerformanceCounters to get the ProcessIds for both remote desktop
  63. // and the local machine, unlike Desktop on which we rely on PCs only for
  64. // remote machines.
  65. return IsRemoteMachine(machineName) ?
  66. NtProcessManager.GetProcessIds(machineName, true) :
  67. GetProcessIds();
  68. }
  69. /// <summary>Gets the IDs of all processes on the current machine.</summary>
  70. public static int[] GetProcessIds()
  71. {
  72. return NtProcessManager.GetProcessIds();
  73. }
  74. /// <summary>Gets the ID of a process from a handle to the process.</summary>
  75. /// <param name="processHandle">The handle.</param>
  76. /// <returns>The process ID.</returns>
  77. public static int GetProcessIdFromHandle(SafeProcessHandle processHandle)
  78. {
  79. return NtProcessManager.GetProcessIdFromHandle(processHandle);
  80. }
  81. /// <summary>Gets an array of module infos for the specified process.</summary>
  82. /// <param name="processId">The ID of the process whose modules should be enumerated.</param>
  83. /// <returns>The array of modules.</returns>
  84. public static ModuleInfo[] GetModuleInfos(int processId)
  85. {
  86. return NtProcessManager.GetModuleInfos(processId);
  87. }
  88. /// <summary>Gets whether the named machine is remote or local.</summary>
  89. /// <param name="machineName">The machine name.</param>
  90. /// <returns>true if the machine is remote; false if it's local.</returns>
  91. public static bool IsRemoteMachine(string machineName)
  92. {
  93. if (machineName == null)
  94. throw new ArgumentNullException(nameof(machineName));
  95. if (machineName.Length == 0)
  96. throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(machineName), machineName));
  97. string baseName;
  98. if (machineName.StartsWith("\\", StringComparison.Ordinal))
  99. baseName = machineName.Substring(2);
  100. else
  101. baseName = machineName;
  102. if (baseName.Equals(".")) return false;
  103. if (String.Compare(Interop.mincore.GetComputerName(), baseName, StringComparison.OrdinalIgnoreCase) == 0) return false;
  104. return true;
  105. }
  106. // -----------------------------
  107. // ---- PAL layer ends here ----
  108. // -----------------------------
  109. static ProcessManager()
  110. {
  111. // In order to query information (OpenProcess) on some protected processes
  112. // like csrss, we need SeDebugPrivilege privilege.
  113. // After removing the dependency on Performance Counter, we don't have a chance
  114. // to run the code in CLR performance counter to ask for this privilege.
  115. // So we will try to get the privilege here.
  116. // We could fail if the user account doesn't have right to do this, but that's fair.
  117. Interop.mincore.LUID luid = new Interop.mincore.LUID();
  118. if (!Interop.mincore.LookupPrivilegeValue(null, Interop.mincore.SeDebugPrivilege, out luid))
  119. {
  120. return;
  121. }
  122. SafeTokenHandle tokenHandle = null;
  123. try
  124. {
  125. if (!Interop.mincore.OpenProcessToken(
  126. Interop.mincore.GetCurrentProcess(),
  127. Interop.mincore.HandleOptions.TOKEN_ADJUST_PRIVILEGES,
  128. out tokenHandle))
  129. {
  130. return;
  131. }
  132. Interop.mincore.TokenPrivileges tp = new Interop.mincore.TokenPrivileges();
  133. tp.Luid = luid;
  134. tp.Attributes = Interop.mincore.SEPrivileges.SE_PRIVILEGE_ENABLED;
  135. // AdjustTokenPrivileges can return true even if it didn't succeed (when ERROR_NOT_ALL_ASSIGNED is returned).
  136. Interop.mincore.AdjustTokenPrivileges(tokenHandle, false, tp, 0, IntPtr.Zero, IntPtr.Zero);
  137. }
  138. finally
  139. {
  140. if (tokenHandle != null)
  141. {
  142. tokenHandle.Dispose();
  143. }
  144. }
  145. }
  146. private static bool IsProcessRunning(int processId, int[] processIds)
  147. {
  148. return Array.IndexOf(processIds, processId) >= 0;
  149. }
  150. public static SafeProcessHandle OpenProcess(int processId, int access, bool throwIfExited)
  151. {
  152. SafeProcessHandle processHandle = Interop.mincore.OpenProcess(access, false, processId);
  153. int result = Marshal.GetLastWin32Error();
  154. if (!processHandle.IsInvalid)
  155. {
  156. return processHandle;
  157. }
  158. if (processId == 0)
  159. {
  160. throw new Win32Exception(5);
  161. }
  162. // If the handle is invalid because the process has exited, only throw an exception if throwIfExited is true.
  163. if (!IsProcessRunning(processId))
  164. {
  165. if (throwIfExited)
  166. {
  167. throw new InvalidOperationException(SR.Format(SR.ProcessHasExited, processId.ToString(CultureInfo.CurrentCulture)));
  168. }
  169. else
  170. {
  171. return SafeProcessHandle.InvalidHandle;
  172. }
  173. }
  174. throw new Win32Exception(result);
  175. }
  176. public static SafeThreadHandle OpenThread(int threadId, int access)
  177. {
  178. SafeThreadHandle threadHandle = Interop.mincore.OpenThread(access, false, threadId);
  179. int result = Marshal.GetLastWin32Error();
  180. if (threadHandle.IsInvalid)
  181. {
  182. if (result == Interop.mincore.Errors.ERROR_INVALID_PARAMETER)
  183. throw new InvalidOperationException(SR.Format(SR.ThreadExited, threadId.ToString(CultureInfo.CurrentCulture)));
  184. throw new Win32Exception(result);
  185. }
  186. return threadHandle;
  187. }
  188. }
  189. /// <devdoc>
  190. /// This static class provides the process api for the WinNt platform.
  191. /// We use the performance counter api to query process and thread
  192. /// information. Module information is obtained using PSAPI.
  193. /// </devdoc>
  194. /// <internalonly/>
  195. internal static class NtProcessManager
  196. {
  197. private const int ProcessPerfCounterId = 230;
  198. private const int ThreadPerfCounterId = 232;
  199. private const string PerfCounterQueryString = "230 232";
  200. internal const int IdleProcessID = 0;
  201. private static readonly Dictionary<String, ValueId> s_valueIds = new Dictionary<string, ValueId>(19)
  202. {
  203. { "Pool Paged Bytes", ValueId.PoolPagedBytes },
  204. { "Pool Nonpaged Bytes", ValueId.PoolNonpagedBytes },
  205. { "Elapsed Time", ValueId.ElapsedTime },
  206. { "Virtual Bytes Peak", ValueId.VirtualBytesPeak },
  207. { "Virtual Bytes", ValueId.VirtualBytes },
  208. { "Private Bytes", ValueId.PrivateBytes },
  209. { "Page File Bytes", ValueId.PageFileBytes },
  210. { "Page File Bytes Peak", ValueId.PageFileBytesPeak },
  211. { "Working Set Peak", ValueId.WorkingSetPeak },
  212. { "Working Set", ValueId.WorkingSet },
  213. { "ID Thread", ValueId.ThreadId },
  214. { "ID Process", ValueId.ProcessId },
  215. { "Priority Base", ValueId.BasePriority },
  216. { "Priority Current", ValueId.CurrentPriority },
  217. { "% User Time", ValueId.UserTime },
  218. { "% Privileged Time", ValueId.PrivilegedTime },
  219. { "Start Address", ValueId.StartAddress },
  220. { "Thread State", ValueId.ThreadState },
  221. { "Thread Wait Reason", ValueId.ThreadWaitReason }
  222. };
  223. internal static int SystemProcessID
  224. {
  225. get
  226. {
  227. const int systemProcessIDOnXP = 4;
  228. return systemProcessIDOnXP;
  229. }
  230. }
  231. public static int[] GetProcessIds(string machineName, bool isRemoteMachine)
  232. {
  233. ProcessInfo[] infos = GetProcessInfos(machineName, isRemoteMachine);
  234. int[] ids = new int[infos.Length];
  235. for (int i = 0; i < infos.Length; i++)
  236. ids[i] = infos[i].ProcessId;
  237. return ids;
  238. }
  239. public static int[] GetProcessIds()
  240. {
  241. int[] processIds = new int[256];
  242. int size;
  243. for (; ; )
  244. {
  245. if (!Interop.mincore.EnumProcesses(processIds, processIds.Length * 4, out size))
  246. throw new Win32Exception();
  247. if (size == processIds.Length * 4)
  248. {
  249. processIds = new int[processIds.Length * 2];
  250. continue;
  251. }
  252. break;
  253. }
  254. int[] ids = new int[size / 4];
  255. Array.Copy(processIds, 0, ids, 0, ids.Length);
  256. return ids;
  257. }
  258. public static ModuleInfo[] GetModuleInfos(int processId)
  259. {
  260. return GetModuleInfos(processId, false);
  261. }
  262. public static ModuleInfo GetFirstModuleInfo(int processId)
  263. {
  264. ModuleInfo[] moduleInfos = GetModuleInfos(processId, true);
  265. if (moduleInfos.Length == 0)
  266. {
  267. return null;
  268. }
  269. else
  270. {
  271. return moduleInfos[0];
  272. }
  273. }
  274. private static ModuleInfo[] GetModuleInfos(int processId, bool firstModuleOnly)
  275. {
  276. // preserving Everett behavior.
  277. if (processId == SystemProcessID || processId == IdleProcessID)
  278. {
  279. // system process and idle process doesn't have any modules
  280. throw new Win32Exception(Interop.mincore.Errors.EFail, SR.EnumProcessModuleFailed);
  281. }
  282. SafeProcessHandle processHandle = SafeProcessHandle.InvalidHandle;
  283. try
  284. {
  285. processHandle = ProcessManager.OpenProcess(processId, Interop.mincore.ProcessOptions.PROCESS_QUERY_INFORMATION | Interop.mincore.ProcessOptions.PROCESS_VM_READ, true);
  286. IntPtr[] moduleHandles = new IntPtr[64];
  287. GCHandle moduleHandlesArrayHandle = new GCHandle();
  288. int moduleCount = 0;
  289. for (; ; )
  290. {
  291. bool enumResult = false;
  292. try
  293. {
  294. moduleHandlesArrayHandle = GCHandle.Alloc(moduleHandles, GCHandleType.Pinned);
  295. enumResult = Interop.mincore.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount);
  296. // The API we need to use to enumerate process modules differs on two factors:
  297. // 1) If our process is running in WOW64.
  298. // 2) The bitness of the process we wish to introspect.
  299. //
  300. // If we are not running in WOW64 or we ARE in WOW64 but want to inspect a 32 bit process
  301. // we can call psapi!EnumProcessModules.
  302. //
  303. // If we are running in WOW64 and we want to inspect the modules of a 64 bit process then
  304. // psapi!EnumProcessModules will return false with ERROR_PARTIAL_COPY (299). In this case we can't
  305. // do the enumeration at all. So we'll detect this case and bail out.
  306. //
  307. // Also, EnumProcessModules is not a reliable method to get the modules for a process.
  308. // If OS loader is touching module information, this method might fail and copy part of the data.
  309. // This is no easy solution to this problem. The only reliable way to fix this is to
  310. // suspend all the threads in target process. Of course we don't want to do this in Process class.
  311. // So we just to try avoid the race by calling the same method 50 (an arbitrary number) times.
  312. //
  313. if (!enumResult)
  314. {
  315. bool sourceProcessIsWow64 = false;
  316. bool targetProcessIsWow64 = false;
  317. SafeProcessHandle hCurProcess = SafeProcessHandle.InvalidHandle;
  318. try
  319. {
  320. hCurProcess = ProcessManager.OpenProcess(unchecked((int)Interop.mincore.GetCurrentProcessId()), Interop.mincore.ProcessOptions.PROCESS_QUERY_INFORMATION, true);
  321. bool wow64Ret;
  322. wow64Ret = Interop.mincore.IsWow64Process(hCurProcess, ref sourceProcessIsWow64);
  323. if (!wow64Ret)
  324. {
  325. throw new Win32Exception();
  326. }
  327. wow64Ret = Interop.mincore.IsWow64Process(processHandle, ref targetProcessIsWow64);
  328. if (!wow64Ret)
  329. {
  330. throw new Win32Exception();
  331. }
  332. if (sourceProcessIsWow64 && !targetProcessIsWow64)
  333. {
  334. // Wow64 isn't going to allow this to happen, the best we can do is give a descriptive error to the user.
  335. throw new Win32Exception(Interop.mincore.Errors.ERROR_PARTIAL_COPY, SR.EnumProcessModuleFailedDueToWow);
  336. }
  337. }
  338. finally
  339. {
  340. if (hCurProcess != SafeProcessHandle.InvalidHandle)
  341. {
  342. hCurProcess.Dispose();
  343. }
  344. }
  345. // If the failure wasn't due to Wow64, try again.
  346. for (int i = 0; i < 50; i++)
  347. {
  348. enumResult = Interop.mincore.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount);
  349. if (enumResult)
  350. {
  351. break;
  352. }
  353. Thread.Sleep(1);
  354. }
  355. }
  356. }
  357. finally
  358. {
  359. moduleHandlesArrayHandle.Free();
  360. }
  361. if (!enumResult)
  362. {
  363. throw new Win32Exception();
  364. }
  365. moduleCount /= IntPtr.Size;
  366. if (moduleCount <= moduleHandles.Length) break;
  367. moduleHandles = new IntPtr[moduleHandles.Length * 2];
  368. }
  369. List<ModuleInfo> moduleInfos = new List<ModuleInfo>(firstModuleOnly ? 1 : moduleCount);
  370. StringBuilder baseName = new StringBuilder(1024);
  371. StringBuilder fileName = new StringBuilder(1024);
  372. for (int i = 0; i < moduleCount; i++)
  373. {
  374. if (i > 0)
  375. {
  376. // If the user is only interested in the main module, break now.
  377. // This avoid some waste of time. In addition, if the application unloads a DLL
  378. // we will not get an exception.
  379. if (firstModuleOnly)
  380. {
  381. break;
  382. }
  383. baseName.Clear();
  384. fileName.Clear();
  385. }
  386. IntPtr moduleHandle = moduleHandles[i];
  387. Interop.mincore.NtModuleInfo ntModuleInfo = new Interop.mincore.NtModuleInfo();
  388. if (!Interop.mincore.GetModuleInformation(processHandle, moduleHandle, ntModuleInfo, Marshal.SizeOf(ntModuleInfo)))
  389. {
  390. HandleError();
  391. continue;
  392. }
  393. ModuleInfo moduleInfo = new ModuleInfo
  394. {
  395. _sizeOfImage = ntModuleInfo.SizeOfImage,
  396. _entryPoint = ntModuleInfo.EntryPoint,
  397. _baseOfDll = ntModuleInfo.BaseOfDll
  398. };
  399. int ret = Interop.mincore.GetModuleBaseName(processHandle, moduleHandle, baseName, baseName.Capacity);
  400. if (ret == 0)
  401. {
  402. HandleError();
  403. continue;
  404. }
  405. moduleInfo._baseName = baseName.ToString();
  406. ret = Interop.mincore.GetModuleFileNameEx(processHandle, moduleHandle, fileName, fileName.Capacity);
  407. if (ret == 0)
  408. {
  409. HandleError();
  410. continue;
  411. }
  412. moduleInfo._fileName = fileName.ToString();
  413. if (moduleInfo._fileName != null && moduleInfo._fileName.Length >= 4
  414. && moduleInfo._fileName.StartsWith(@"\\?\", StringComparison.Ordinal))
  415. {
  416. moduleInfo._fileName = fileName.ToString(4, fileName.Length - 4);
  417. }
  418. moduleInfos.Add(moduleInfo);
  419. }
  420. return moduleInfos.ToArray();
  421. }
  422. finally
  423. {
  424. #if FEATURE_TRACESWITCH
  425. Debug.WriteLineIf(Process._processTracing.TraceVerbose, "Process - CloseHandle(process)");
  426. #endif
  427. if (!processHandle.IsInvalid)
  428. {
  429. processHandle.Dispose();
  430. }
  431. }
  432. }
  433. private static void HandleError()
  434. {
  435. int lastError = Marshal.GetLastWin32Error();
  436. switch (lastError)
  437. {
  438. case Interop.mincore.Errors.ERROR_INVALID_HANDLE:
  439. case Interop.mincore.Errors.ERROR_PARTIAL_COPY:
  440. // It's possible that another thread caused this module to become
  441. // unloaded (e.g FreeLibrary was called on the module). Ignore it and
  442. // move on.
  443. break;
  444. default:
  445. throw new Win32Exception(lastError);
  446. }
  447. }
  448. public static int GetProcessIdFromHandle(SafeProcessHandle processHandle)
  449. {
  450. Interop.NtDll.NtProcessBasicInfo info = new Interop.NtDll.NtProcessBasicInfo();
  451. int status = Interop.NtDll.NtQueryInformationProcess(processHandle, Interop.NtDll.NtQueryProcessBasicInfo, info, (int)Marshal.SizeOf(info), null);
  452. if (status != 0)
  453. {
  454. throw new InvalidOperationException(SR.CantGetProcessId, new Win32Exception(status));
  455. }
  456. // We should change the signature of this function and ID property in process class.
  457. return info.UniqueProcessId.ToInt32();
  458. }
  459. public static ProcessInfo[] GetProcessInfos(string machineName, bool isRemoteMachine)
  460. {
  461. PerformanceCounterLib library = null;
  462. try
  463. {
  464. library = PerformanceCounterLib.GetPerformanceCounterLib(machineName, new CultureInfo("en"));
  465. return GetProcessInfos(library);
  466. }
  467. catch (Exception e)
  468. {
  469. if (isRemoteMachine)
  470. {
  471. throw new InvalidOperationException(SR.CouldntConnectToRemoteMachine, e);
  472. }
  473. else
  474. {
  475. throw e;
  476. }
  477. }
  478. // We don't want to call library.Close() here because that would cause us to unload all of the perflibs.
  479. // On the next call to GetProcessInfos, we'd have to load them all up again, which is SLOW!
  480. }
  481. static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library)
  482. {
  483. ProcessInfo[] processInfos;
  484. int retryCount = 5;
  485. do
  486. {
  487. try
  488. {
  489. byte[] dataPtr = library.GetPerformanceData(PerfCounterQueryString);
  490. processInfos = GetProcessInfos(library, ProcessPerfCounterId, ThreadPerfCounterId, dataPtr);
  491. }
  492. catch (Exception e)
  493. {
  494. throw new InvalidOperationException(SR.CouldntGetProcessInfos, e);
  495. }
  496. --retryCount;
  497. }
  498. while (processInfos.Length == 0 && retryCount != 0);
  499. if (processInfos.Length == 0)
  500. throw new InvalidOperationException(SR.ProcessDisabled);
  501. return processInfos;
  502. }
  503. static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int processIndex, int threadIndex, byte[] data)
  504. {
  505. #if FEATURE_TRACESWITCH
  506. Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos()");
  507. #endif
  508. Dictionary<int, ProcessInfo> processInfos = new Dictionary<int, ProcessInfo>();
  509. List<ThreadInfo> threadInfos = new List<ThreadInfo>();
  510. GCHandle dataHandle = new GCHandle();
  511. try
  512. {
  513. dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
  514. IntPtr dataBlockPtr = dataHandle.AddrOfPinnedObject();
  515. Interop.mincore.PERF_DATA_BLOCK dataBlock = new Interop.mincore.PERF_DATA_BLOCK();
  516. Marshal.PtrToStructure(dataBlockPtr, dataBlock);
  517. IntPtr typePtr = (IntPtr)((long)dataBlockPtr + dataBlock.HeaderLength);
  518. Interop.mincore.PERF_INSTANCE_DEFINITION instance = new Interop.mincore.PERF_INSTANCE_DEFINITION();
  519. Interop.mincore.PERF_COUNTER_BLOCK counterBlock = new Interop.mincore.PERF_COUNTER_BLOCK();
  520. for (int i = 0; i < dataBlock.NumObjectTypes; i++)
  521. {
  522. Interop.mincore.PERF_OBJECT_TYPE type = new Interop.mincore.PERF_OBJECT_TYPE();
  523. Marshal.PtrToStructure(typePtr, type);
  524. IntPtr instancePtr = (IntPtr)((long)typePtr + type.DefinitionLength);
  525. IntPtr counterPtr = (IntPtr)((long)typePtr + type.HeaderLength);
  526. List<Interop.mincore.PERF_COUNTER_DEFINITION> counterList = new List<Interop.mincore.PERF_COUNTER_DEFINITION>();
  527. for (int j = 0; j < type.NumCounters; j++)
  528. {
  529. Interop.mincore.PERF_COUNTER_DEFINITION counter = new Interop.mincore.PERF_COUNTER_DEFINITION();
  530. Marshal.PtrToStructure(counterPtr, counter);
  531. string counterName = library.GetCounterName(counter.CounterNameTitleIndex);
  532. if (type.ObjectNameTitleIndex == processIndex)
  533. counter.CounterNameTitlePtr = (int)GetValueId(counterName);
  534. else if (type.ObjectNameTitleIndex == threadIndex)
  535. counter.CounterNameTitlePtr = (int)GetValueId(counterName);
  536. counterList.Add(counter);
  537. counterPtr = (IntPtr)((long)counterPtr + counter.ByteLength);
  538. }
  539. Interop.mincore.PERF_COUNTER_DEFINITION[] counters = counterList.ToArray();
  540. for (int j = 0; j < type.NumInstances; j++)
  541. {
  542. Marshal.PtrToStructure(instancePtr, instance);
  543. IntPtr namePtr = (IntPtr)((long)instancePtr + instance.NameOffset);
  544. string instanceName = Marshal.PtrToStringUni(namePtr);
  545. if (instanceName.Equals("_Total")) continue;
  546. IntPtr counterBlockPtr = (IntPtr)((long)instancePtr + instance.ByteLength);
  547. Marshal.PtrToStructure(counterBlockPtr, counterBlock);
  548. if (type.ObjectNameTitleIndex == processIndex)
  549. {
  550. ProcessInfo processInfo = GetProcessInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
  551. if (processInfo.ProcessId == 0 && string.Compare(instanceName, "Idle", StringComparison.OrdinalIgnoreCase) != 0)
  552. {
  553. // Sometimes we'll get a process structure that is not completely filled in.
  554. // We can catch some of these by looking for non-"idle" processes that have id 0
  555. // and ignoring those.
  556. #if FEATURE_TRACESWITCH
  557. Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos() - found a non-idle process with id 0; ignoring.");
  558. #endif
  559. }
  560. else
  561. {
  562. if (processInfos.ContainsKey(processInfo.ProcessId))
  563. {
  564. // We've found two entries in the perfcounters that claim to be the
  565. // same process. We throw an exception. Is this really going to be
  566. // helpful to the user? Should we just ignore?
  567. #if FEATURE_TRACESWITCH
  568. Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos() - found a duplicate process id");
  569. #endif
  570. }
  571. else
  572. {
  573. // the performance counters keep a 15 character prefix of the exe name, and then delete the ".exe",
  574. // if it's in the first 15. The problem is that sometimes that will leave us with part of ".exe"
  575. // at the end. If instanceName ends in ".", ".e", or ".ex" we remove it.
  576. string processName = instanceName;
  577. if (processName.Length == 15)
  578. {
  579. if (instanceName.EndsWith(".", StringComparison.Ordinal)) processName = instanceName.Substring(0, 14);
  580. else if (instanceName.EndsWith(".e", StringComparison.Ordinal)) processName = instanceName.Substring(0, 13);
  581. else if (instanceName.EndsWith(".ex", StringComparison.Ordinal)) processName = instanceName.Substring(0, 12);
  582. }
  583. processInfo.ProcessName = processName;
  584. processInfos.Add(processInfo.ProcessId, processInfo);
  585. }
  586. }
  587. }
  588. else if (type.ObjectNameTitleIndex == threadIndex)
  589. {
  590. ThreadInfo threadInfo = GetThreadInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
  591. if (threadInfo._threadId != 0) threadInfos.Add(threadInfo);
  592. }
  593. instancePtr = (IntPtr)((long)instancePtr + instance.ByteLength + counterBlock.ByteLength);
  594. }
  595. typePtr = (IntPtr)((long)typePtr + type.TotalByteLength);
  596. }
  597. }
  598. finally
  599. {
  600. if (dataHandle.IsAllocated) dataHandle.Free();
  601. }
  602. for (int i = 0; i < threadInfos.Count; i++)
  603. {
  604. ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
  605. ProcessInfo processInfo;
  606. if (processInfos.TryGetValue(threadInfo._processId, out processInfo))
  607. {
  608. processInfo._threadInfoList.Add(threadInfo);
  609. }
  610. }
  611. ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
  612. processInfos.Values.CopyTo(temp, 0);
  613. return temp;
  614. }
  615. static ThreadInfo GetThreadInfo(Interop.mincore.PERF_OBJECT_TYPE type, IntPtr instancePtr, Interop.mincore.PERF_COUNTER_DEFINITION[] counters)
  616. {
  617. ThreadInfo threadInfo = new ThreadInfo();
  618. for (int i = 0; i < counters.Length; i++)
  619. {
  620. Interop.mincore.PERF_COUNTER_DEFINITION counter = counters[i];
  621. long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
  622. switch ((ValueId)counter.CounterNameTitlePtr)
  623. {
  624. case ValueId.ProcessId:
  625. threadInfo._processId = (int)value;
  626. break;
  627. case ValueId.ThreadId:
  628. threadInfo._threadId = (ulong)value;
  629. break;
  630. case ValueId.BasePriority:
  631. threadInfo._basePriority = (int)value;
  632. break;
  633. case ValueId.CurrentPriority:
  634. threadInfo._currentPriority = (int)value;
  635. break;
  636. case ValueId.StartAddress:
  637. threadInfo._startAddress = (IntPtr)value;
  638. break;
  639. case ValueId.ThreadState:
  640. threadInfo._threadState = (ThreadState)value;
  641. break;
  642. case ValueId.ThreadWaitReason:
  643. threadInfo._threadWaitReason = GetThreadWaitReason((int)value);
  644. break;
  645. }
  646. }
  647. return threadInfo;
  648. }
  649. internal static ThreadWaitReason GetThreadWaitReason(int value)
  650. {
  651. switch (value)
  652. {
  653. case 0:
  654. case 7: return ThreadWaitReason.Executive;
  655. case 1:
  656. case 8: return ThreadWaitReason.FreePage;
  657. case 2:
  658. case 9: return ThreadWaitReason.PageIn;
  659. case 3:
  660. case 10: return ThreadWaitReason.SystemAllocation;
  661. case 4:
  662. case 11: return ThreadWaitReason.ExecutionDelay;
  663. case 5:
  664. case 12: return ThreadWaitReason.Suspended;
  665. case 6:
  666. case 13: return ThreadWaitReason.UserRequest;
  667. case 14: return ThreadWaitReason.EventPairHigh; ;
  668. case 15: return ThreadWaitReason.EventPairLow;
  669. case 16: return ThreadWaitReason.LpcReceive;
  670. case 17: return ThreadWaitReason.LpcReply;
  671. case 18: return ThreadWaitReason.VirtualMemory;
  672. case 19: return ThreadWaitReason.PageOut;
  673. default: return ThreadWaitReason.Unknown;
  674. }
  675. }
  676. static ProcessInfo GetProcessInfo(Interop.mincore.PERF_OBJECT_TYPE type, IntPtr instancePtr, Interop.mincore.PERF_COUNTER_DEFINITION[] counters)
  677. {
  678. ProcessInfo processInfo = new ProcessInfo();
  679. for (int i = 0; i < counters.Length; i++)
  680. {
  681. Interop.mincore.PERF_COUNTER_DEFINITION counter = counters[i];
  682. long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
  683. switch ((ValueId)counter.CounterNameTitlePtr)
  684. {
  685. case ValueId.ProcessId:
  686. processInfo.ProcessId = (int)value;
  687. break;
  688. case ValueId.PoolPagedBytes:
  689. processInfo.PoolPagedBytes = value;
  690. break;
  691. case ValueId.PoolNonpagedBytes:
  692. processInfo.PoolNonPagedBytes = value;
  693. break;
  694. case ValueId.VirtualBytes:
  695. processInfo.VirtualBytes = value;
  696. break;
  697. case ValueId.VirtualBytesPeak:
  698. processInfo.VirtualBytesPeak = value;
  699. break;
  700. case ValueId.WorkingSetPeak:
  701. processInfo.WorkingSetPeak = value;
  702. break;
  703. case ValueId.WorkingSet:
  704. processInfo.WorkingSet = value;
  705. break;
  706. case ValueId.PageFileBytesPeak:
  707. processInfo.PageFileBytesPeak = value;
  708. break;
  709. case ValueId.PageFileBytes:
  710. processInfo.PageFileBytes = value;
  711. break;
  712. case ValueId.PrivateBytes:
  713. processInfo.PrivateBytes = value;
  714. break;
  715. case ValueId.BasePriority:
  716. processInfo.BasePriority = (int)value;
  717. break;
  718. }
  719. }
  720. return processInfo;
  721. }
  722. static ValueId GetValueId(string counterName)
  723. {
  724. if (counterName != null)
  725. {
  726. ValueId id;
  727. if (s_valueIds.TryGetValue(counterName, out id))
  728. return id;
  729. }
  730. return ValueId.Unknown;
  731. }
  732. static long ReadCounterValue(int counterType, IntPtr dataPtr)
  733. {
  734. if ((counterType & Interop.mincore.PerfCounterOptions.NtPerfCounterSizeLarge) != 0)
  735. return Marshal.ReadInt64(dataPtr);
  736. else
  737. return (long)Marshal.ReadInt32(dataPtr);
  738. }
  739. enum ValueId
  740. {
  741. Unknown = -1,
  742. PoolPagedBytes,
  743. PoolNonpagedBytes,
  744. ElapsedTime,
  745. VirtualBytesPeak,
  746. VirtualBytes,
  747. PrivateBytes,
  748. PageFileBytes,
  749. PageFileBytesPeak,
  750. WorkingSetPeak,
  751. WorkingSet,
  752. ThreadId,
  753. ProcessId,
  754. BasePriority,
  755. CurrentPriority,
  756. UserTime,
  757. PrivilegedTime,
  758. StartAddress,
  759. ThreadState,
  760. ThreadWaitReason
  761. }
  762. }
  763. internal static class NtProcessInfoHelper
  764. {
  765. private static int GetNewBufferSize(int existingBufferSize, int requiredSize)
  766. {
  767. if (requiredSize == 0)
  768. {
  769. //
  770. // On some old OS like win2000, requiredSize will not be set if the buffer
  771. // passed to NtQuerySystemInformation is not enough.
  772. //
  773. int newSize = existingBufferSize * 2;
  774. if (newSize < existingBufferSize)
  775. {
  776. // In reality, we should never overflow.
  777. // Adding the code here just in case it happens.
  778. throw new OutOfMemoryException();
  779. }
  780. return newSize;
  781. }
  782. else
  783. {
  784. // allocating a few more kilo bytes just in case there are some new process
  785. // kicked in since new call to NtQuerySystemInformation
  786. int newSize = requiredSize + 1024 * 10;
  787. if (newSize < requiredSize)
  788. {
  789. throw new OutOfMemoryException();
  790. }
  791. return newSize;
  792. }
  793. }
  794. public static ProcessInfo[] GetProcessInfos()
  795. {
  796. int requiredSize = 0;
  797. int status;
  798. ProcessInfo[] processInfos;
  799. GCHandle bufferHandle = new GCHandle();
  800. // Start with the default buffer size.
  801. int bufferSize = DefaultCachedBufferSize;
  802. // Get the cached buffer.
  803. long[] buffer = Interlocked.Exchange(ref CachedBuffer, null);
  804. try
  805. {
  806. do
  807. {
  808. if (buffer == null)
  809. {
  810. // Allocate buffer of longs since some platforms require the buffer to be 64-bit aligned.
  811. buffer = new long[(bufferSize + 7) / 8];
  812. }
  813. else
  814. {
  815. // If we have cached buffer, set the size properly.
  816. bufferSize = buffer.Length * sizeof(long);
  817. }
  818. bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
  819. status = Interop.NtDll.NtQuerySystemInformation(
  820. Interop.NtDll.NtQuerySystemProcessInformation,
  821. bufferHandle.AddrOfPinnedObject(),
  822. bufferSize,
  823. out requiredSize);
  824. if ((uint)status == Interop.NtDll.STATUS_INFO_LENGTH_MISMATCH)
  825. {
  826. if (bufferHandle.IsAllocated) bufferHandle.Free();
  827. buffer = null;
  828. bufferSize = GetNewBufferSize(bufferSize, requiredSize);
  829. }
  830. } while ((uint)status == Interop.NtDll.STATUS_INFO_LENGTH_MISMATCH);
  831. if (status < 0)
  832. { // see definition of NT_SUCCESS(Status) in SDK
  833. throw new InvalidOperationException(SR.CouldntGetProcessInfos, new Win32Exception(status));
  834. }
  835. // Parse the data block to get process information
  836. processInfos = GetProcessInfos(bufferHandle.AddrOfPinnedObject());
  837. }
  838. finally
  839. {
  840. // Cache the final buffer for use on the next call.
  841. Interlocked.Exchange(ref CachedBuffer, buffer);
  842. if (bufferHandle.IsAllocated) bufferHandle.Free();
  843. }
  844. return processInfos;
  845. }
  846. // Use a smaller buffer size on debug to ensure we hit the retry path.
  847. #if DEBUG
  848. private const int DefaultCachedBufferSize = 1024;
  849. #else
  850. private const int DefaultCachedBufferSize = 128 * 1024;
  851. #endif
  852. // Cache a single buffer for use in GetProcessInfos().
  853. private static long[] CachedBuffer;
  854. static ProcessInfo[] GetProcessInfos(IntPtr dataPtr)
  855. {
  856. // 60 is a reasonable number for processes on a normal machine.
  857. Dictionary<int, ProcessInfo> processInfos = new Dictionary<int, ProcessInfo>(60);
  858. long totalOffset = 0;
  859. while (true)
  860. {
  861. IntPtr currentPtr = (IntPtr)((long)dataPtr + totalOffset);
  862. SystemProcessInformation pi = new SystemProcessInformation();
  863. Marshal.PtrToStructure(currentPtr, pi);
  864. // get information for a process
  865. ProcessInfo processInfo = new ProcessInfo();
  866. // Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.
  867. processInfo.ProcessId = pi.UniqueProcessId.ToInt32();
  868. processInfo.SessionId = (int)pi.SessionId;
  869. processInfo.PoolPagedBytes = (long)pi.QuotaPagedPoolUsage; ;
  870. processInfo.PoolNonPagedBytes = (long)pi.QuotaNonPagedPoolUsage;
  871. processInfo.VirtualBytes = (long)pi.VirtualSize;
  872. processInfo.VirtualBytesPeak = (long)pi.PeakVirtualSize;
  873. processInfo.WorkingSetPeak = (long)pi.PeakWorkingSetSize;
  874. processInfo.WorkingSet = (long)pi.WorkingSetSize;
  875. processInfo.PageFileBytesPeak = (long)pi.PeakPagefileUsage;
  876. processInfo.PageFileBytes = (long)pi.PagefileUsage;
  877. processInfo.PrivateBytes = (long)pi.PrivatePageCount;
  878. processInfo.BasePriority = pi.BasePriority;
  879. if (pi.NamePtr == IntPtr.Zero)
  880. {
  881. if (processInfo.ProcessId == NtProcessManager.SystemProcessID)
  882. {
  883. processInfo.ProcessName = "System";
  884. }
  885. else if (processInfo.ProcessId == NtProcessManager.IdleProcessID)
  886. {
  887. processInfo.ProcessName = "Idle";
  888. }
  889. else
  890. {
  891. // for normal process without name, using the process ID.
  892. processInfo.ProcessName = processInfo.ProcessId.ToString(CultureInfo.InvariantCulture);
  893. }
  894. }
  895. else
  896. {
  897. string processName = GetProcessShortName(Marshal.PtrToStringUni(pi.NamePtr, pi.NameLength / sizeof(char)));
  898. processInfo.ProcessName = processName;
  899. }
  900. // get the threads for current process
  901. processInfos[processInfo.ProcessId] = processInfo;
  902. currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(pi));
  903. int i = 0;
  904. while (i < pi.NumberOfThreads)
  905. {
  906. SystemThreadInformation ti = new SystemThreadInformation();
  907. Marshal.PtrToStructure(currentPtr, ti);
  908. ThreadInfo threadInfo = new ThreadInfo();
  909. threadInfo._processId = (int)ti.UniqueProcess;
  910. threadInfo._threadId = (ulong)ti.UniqueThread;
  911. threadInfo._basePriority = ti.BasePriority;
  912. threadInfo._currentPriority = ti.Priority;
  913. threadInfo._startAddress = ti.StartAddress;
  914. threadInfo._threadState = (ThreadState)ti.ThreadState;
  915. threadInfo._threadWaitReason = NtProcessManager.GetThreadWaitReason((int)ti.WaitReason);
  916. processInfo._threadInfoList.Add(threadInfo);
  917. currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(ti));
  918. i++;
  919. }
  920. if (pi.NextEntryOffset == 0)
  921. {
  922. break;
  923. }
  924. totalOffset += pi.NextEntryOffset;
  925. }
  926. ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
  927. processInfos.Values.CopyTo(temp, 0);
  928. return temp;
  929. }
  930. // This function generates the short form of process name.
  931. //
  932. // This is from GetProcessShortName in NT code base.
  933. // Check base\screg\winreg\perfdlls\process\perfsprc.c for details.
  934. internal static string GetProcessShortName(String name)
  935. {
  936. if (String.IsNullOrEmpty(name))
  937. {
  938. #if FEATURE_TRACESWITCH
  939. Debug.WriteLineIf(Process._processTracing.TraceVerbose, "GetProcessInfos() - unexpected blank ProcessName");
  940. #endif
  941. return String.Empty;
  942. }
  943. int slash = -1;
  944. int period = -1;
  945. for (int i = 0; i < name.Length; i++)
  946. {
  947. if (name[i] == '\\')
  948. slash = i;
  949. else if (name[i] == '.')
  950. period = i;
  951. }
  952. if (period == -1)
  953. period = name.Length - 1; // set to end of string
  954. else
  955. {
  956. // if a period was found, then see if the extension is
  957. // .EXE, if so drop it, if not, then use end of string
  958. // (i.e. include extension in name)
  959. String extension = name.Substring(period);
  960. if (String.Equals(".exe", extension, StringComparison.OrdinalIgnoreCase))
  961. period--; // point to character before period
  962. else
  963. period = name.Length - 1; // set to end of string
  964. }
  965. if (slash == -1)
  966. slash = 0; // set to start of string
  967. else
  968. slash++; // point to character next to slash
  969. // copy characters between period (or end of string) and
  970. // slash (or start of string) to make image name
  971. return name.Substring(slash, period - slash + 1);
  972. }
  973. // native struct defined in ntexapi.h
  974. [StructLayout(LayoutKind.Sequential)]
  975. internal class SystemProcessInformation
  976. {
  977. internal uint NextEntryOffset;
  978. internal uint NumberOfThreads;
  979. private long _SpareLi1;
  980. private long _SpareLi2;
  981. private long _SpareLi3;
  982. private long _CreateTime;
  983. private long _UserTime;
  984. private long _KernelTime;
  985. internal ushort NameLength; // UNICODE_STRING
  986. internal ushort MaximumNameLength;
  987. internal IntPtr NamePtr; // This will point into the data block returned by NtQuerySystemInformation
  988. internal int BasePriority;
  989. internal IntPtr UniqueProcessId;
  990. internal IntPtr InheritedFromUniqueProcessId;
  991. internal uint HandleCount;
  992. internal uint SessionId;
  993. internal UIntPtr PageDirectoryBase;
  994. internal UIntPtr PeakVirtualSize; // SIZE_T
  995. internal UIntPtr VirtualSize;
  996. internal uint PageFaultCount;
  997. internal UIntPtr PeakWorkingSetSize;
  998. internal UIntPtr WorkingSetSize;
  999. internal UIntPtr QuotaPeakPagedPoolUsage;
  1000. internal UIntPtr QuotaPagedPoolUsage;
  1001. internal UIntPtr QuotaPeakNonPagedPoolUsage;
  1002. internal UIntPtr QuotaNonPagedPoolUsage;
  1003. internal UIntPtr PagefileUsage;
  1004. internal UIntPtr PeakPagefileUsage;
  1005. internal UIntPtr PrivatePageCount;
  1006. private long _ReadOperationCount;
  1007. private long _WriteOperationCount;
  1008. private long _OtherOperationCount;
  1009. private long _ReadTransferCount;
  1010. private long _WriteTransferCount;
  1011. private long _OtherTransferCount;
  1012. }
  1013. [StructLayout(LayoutKind.Sequential)]
  1014. internal c