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 
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Runtime.InteropServices;
8 
9 namespace System.Diagnostics
10 {
11     internal static partial class ProcessManager
12     {
13         /// <summary>Gets the IDs of all processes on the current machine.</summary>
GetProcessIds()14         public static int[] GetProcessIds()
15         {
16             return Interop.libproc.proc_listallpids();
17         }
18 
GetProcPath(int processId)19         private static string GetProcPath(int processId)
20         {
21             return Interop.libproc.proc_pidpath(processId);
22         }
23 
24         // -----------------------------
25         // ---- PAL layer ends here ----
26         // -----------------------------
27 
CreateProcessInfo(int pid)28         private static ProcessInfo CreateProcessInfo(int pid)
29         {
30             // Negative PIDs aren't valid
31             if (pid < 0)
32             {
33                 throw new ArgumentOutOfRangeException(nameof(pid));
34             }
35 
36             ProcessInfo procInfo = new ProcessInfo()
37             {
38                 ProcessId = pid
39             };
40 
41             // Try to get the task info. This can fail if the user permissions don't permit
42             // this user context to query the specified process
43             Interop.libproc.proc_taskallinfo? info = Interop.libproc.GetProcessInfoById(pid);
44             if (info.HasValue)
45             {
46                 // Set the values we have; all the other values don't have meaning or don't exist on OSX
47                 Interop.libproc.proc_taskallinfo temp = info.Value;
48                 unsafe { procInfo.ProcessName = Marshal.PtrToStringAnsi(new IntPtr(temp.pbsd.pbi_comm)); }
49                 procInfo.BasePriority = temp.pbsd.pbi_nice;
50                 procInfo.VirtualBytes = (long)temp.ptinfo.pti_virtual_size;
51                 procInfo.WorkingSet = (long)temp.ptinfo.pti_resident_size;
52             }
53 
54             // Get the sessionId for the given pid, getsid returns -1 on error
55             int sessionId = Interop.Sys.GetSid(pid);
56             if (sessionId != -1)
57                 procInfo.SessionId = sessionId;
58 
59             // Create a threadinfo for each thread in the process
60             List<KeyValuePair<ulong, Interop.libproc.proc_threadinfo?>> lstThreads = Interop.libproc.GetAllThreadsInProcess(pid);
61             foreach (KeyValuePair<ulong, Interop.libproc.proc_threadinfo?> t in lstThreads)
62             {
63                 var ti = new ThreadInfo()
64                 {
65                     _processId = pid,
66                     _threadId = t.Key,
67                     _basePriority = procInfo.BasePriority,
68                     _startAddress = IntPtr.Zero
69                 };
70 
71                 // Fill in additional info if we were able to retrieve such data about the thread
72                 if (t.Value.HasValue)
73                 {
74                     ti._currentPriority = t.Value.Value.pth_curpri;
75                     ti._threadState = ConvertOsxThreadRunStateToThreadState((Interop.libproc.ThreadRunState)t.Value.Value.pth_run_state);
76                     ti._threadWaitReason = ConvertOsxThreadFlagsToWaitReason((Interop.libproc.ThreadFlags)t.Value.Value.pth_flags);
77                 }
78 
79                 procInfo._threadInfoList.Add(ti);
80             }
81 
82             return procInfo;
83         }
84 
85         // ----------------------------------
86         // ---- Unix PAL layer ends here ----
87         // ----------------------------------
88 
ConvertOsxThreadRunStateToThreadState(Interop.libproc.ThreadRunState state)89         private static System.Diagnostics.ThreadState ConvertOsxThreadRunStateToThreadState(Interop.libproc.ThreadRunState state)
90         {
91             switch (state)
92             {
93                 case Interop.libproc.ThreadRunState.TH_STATE_RUNNING:
94                     return System.Diagnostics.ThreadState.Running;
95                 case Interop.libproc.ThreadRunState.TH_STATE_STOPPED:
96                     return System.Diagnostics.ThreadState.Terminated;
97                 case Interop.libproc.ThreadRunState.TH_STATE_HALTED:
98                     return System.Diagnostics.ThreadState.Wait;
99                 case Interop.libproc.ThreadRunState.TH_STATE_UNINTERRUPTIBLE:
100                     return System.Diagnostics.ThreadState.Running;
101                 case Interop.libproc.ThreadRunState.TH_STATE_WAITING:
102                     return System.Diagnostics.ThreadState.Standby;
103                 default:
104                     throw new ArgumentOutOfRangeException(nameof(state));
105             }
106         }
107 
ConvertOsxThreadFlagsToWaitReason(Interop.libproc.ThreadFlags flags)108         private static System.Diagnostics.ThreadWaitReason ConvertOsxThreadFlagsToWaitReason(Interop.libproc.ThreadFlags flags)
109         {
110             // Since ThreadWaitReason isn't a flag, we have to do a mapping and will lose some information.
111             if ((flags & Interop.libproc.ThreadFlags.TH_FLAGS_SWAPPED) == Interop.libproc.ThreadFlags.TH_FLAGS_SWAPPED)
112                 return System.Diagnostics.ThreadWaitReason.PageOut;
113             else
114                 return System.Diagnostics.ThreadWaitReason.Unknown; // There isn't a good mapping for anything else
115         }
116     }
117 }
118