/// <summary> /// Gets information about process or thread(s) /// </summary> /// <param name="pid">The PID of the process. If PID is 0, this will return all processes</param> /// <param name="threads">Whether to include thread information.</param> /// <param name="count">The number of kinfo_proc entries returned.</param> public static unsafe kinfo_proc *GetProcInfo(int pid, bool threads, out int count) { Span <int> sysctlName = stackalloc int[4]; if (pid == 0) { // get all processes sysctlName[3] = 0; sysctlName[2] = KERN_PROC_PROC; } else { // get specific process, possibly with threads sysctlName[3] = pid; sysctlName[2] = KERN_PROC_PID | (threads ? KERN_PROC_INC_THREAD : 0); } sysctlName[1] = KERN_PROC; sysctlName[0] = CTL_KERN; byte *pBuffer = null; int bytesLength = 0; Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength); kinfo_proc *kinfo = (kinfo_proc *)pBuffer; Debug.Assert(kinfo->ki_structsize == sizeof(kinfo_proc)); count = (int)bytesLength / sizeof(kinfo_proc); // Buffer ownership transferred to the caller return(kinfo); }
/// <summary> /// Gets the process information for a given process /// </summary> /// <param name="pid">The PID (process ID) of the process</param> /// <returns> /// Returns a valid ProcessInfo struct for valid processes that the caller /// has permission to access; otherwise, returns null /// </returns> static public unsafe ProcessInfo GetProcessInfoById(int pid) { kinfo_proc *kinfo = null; int count; ProcessInfo info; // Negative PIDs are invalid if (pid < 0) { throw new ArgumentOutOfRangeException(nameof(pid)); } try { kinfo = GetProcInfo(pid, true, out count); if (kinfo == null || count < 1) { throw new ArgumentOutOfRangeException(nameof(pid)); } Span <kinfo_proc> process = new Span <kinfo_proc>(kinfo, count); // Get the process information for the specified pid info = new ProcessInfo(); info.ProcessName = Marshal.PtrToStringAnsi((IntPtr)kinfo->ki_comm); info.BasePriority = kinfo->ki_nice; info.VirtualBytes = (long)kinfo->ki_size; info.WorkingSet = kinfo->ki_rssize; info.SessionId = kinfo->ki_sid; for (int i = 0; i < process.Length; i++) { var ti = new ThreadInfo() { _processId = pid, _threadId = (ulong)process[i].ki_tid, _basePriority = process[i].ki_nice, _startAddress = (IntPtr)process[i].ki_tdaddr }; info._threadInfoList.Add(ti); } } finally { Marshal.FreeHGlobal((IntPtr)kinfo); } return(info); }
/// <summary> /// Gets information about process or thread(s) /// </summary> /// <param name="pid">The PID of the process. If PID is 0, this will return all processes</param> public static unsafe kinfo_proc *GetProcInfo(int pid, bool threads, out int count) { Span <int> sysctlName = stackalloc int[4]; int bytesLength = 0; byte * pBuffer = null; kinfo_proc *kinfo = null; int ret; count = -1; if (pid == 0) { // get all processes sysctlName[3] = 0; sysctlName[2] = KERN_PROC_PROC; } else { // get specific process, possibly with threads sysctlName[3] = pid; sysctlName[2] = KERN_PROC_PID | (threads ? KERN_PROC_INC_THREAD : 0); } sysctlName[1] = KERN_PROC; sysctlName[0] = CTL_KERN; try { ret = Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength); if (ret != 0) { throw new ArgumentOutOfRangeException(nameof(pid)); } kinfo = (kinfo_proc *)pBuffer; if (kinfo->ki_structsize != sizeof(kinfo_proc)) { // failed consistency check throw new ArgumentOutOfRangeException(nameof(pid)); } count = (int)bytesLength / sizeof(kinfo_proc); } catch { Marshal.FreeHGlobal((IntPtr)pBuffer); throw; } return(kinfo); }
/// <summary> /// Gets the process information for a given process /// </summary> /// <param name="pid">The PID (process ID) of the process</param> /// <param name="tid">The TID (thread ID) of the process</param> /// <returns> /// Returns basic info about thread. If tis is 0, it will return /// info for process e.g. main thread. /// </returns> public static unsafe proc_stats GetThreadInfo(int pid, int tid) { proc_stats ret = default; kinfo_proc *info = null; int count; try { info = GetProcInfo(pid, (tid != 0), out count); if (info != null && count >= 1) { if (tid == 0) { ret.startTime = (int)info->ki_start.tv_sec; ret.nice = info->ki_nice; ret.userTime = (ulong)info->ki_rusage.ru_utime.tv_sec * SecondsToNanoseconds + (ulong)info->ki_rusage.ru_utime.tv_usec; ret.systemTime = (ulong)info->ki_rusage.ru_stime.tv_sec * SecondsToNanoseconds + (ulong)info->ki_rusage.ru_stime.tv_usec; } else { var list = new ReadOnlySpan <kinfo_proc>(info, count); for (int i = 0; i < list.Length; i++) { if (list[i].ki_tid == tid) { ret.startTime = (int)list[i].ki_start.tv_sec; ret.nice = list[i].ki_nice; ret.userTime = (ulong)list[i].ki_rusage.ru_utime.tv_sec * SecondsToNanoseconds + (ulong)list[i].ki_rusage.ru_utime.tv_usec; ret.systemTime = (ulong)list[i].ki_rusage.ru_stime.tv_sec * SecondsToNanoseconds + (ulong)list[i].ki_rusage.ru_stime.tv_usec; break; } } } } } finally { Marshal.FreeHGlobal((IntPtr)info); } return(ret); }
/// <summary> /// Queries the OS for the list of all running processes and returns the PID for each /// </summary> /// <returns>Returns a list of PIDs corresponding to all running processes</returns> internal static unsafe int[] ListAllPids() { int numProcesses = 0; int[] pids; kinfo_proc *entries = null; int idx; try { entries = GetProcInfo(0, false, out numProcesses); if (entries == null || numProcesses <= 0) { throw new Win32Exception(SR.CantGetAllPids); } Span <kinfo_proc> list = new Span <kinfo_proc>(entries, numProcesses); pids = new int[numProcesses]; idx = 0; // walk through process list and skip kernel threads for (int i = 0; i < list.Length; i++) { if (list[i].ki_ppid == 0) { // skip kernel threads numProcesses -= 1; } else { pids[idx] = list[i].ki_pid; idx += 1; } } // Remove extra elements Array.Resize <int>(ref pids, numProcesses); } finally { Marshal.FreeHGlobal((IntPtr)entries); } return(pids); }
/// <summary> /// Queries the OS for the list of all running processes and returns the PID for each /// </summary> /// <returns>Returns a list of PIDs corresponding to all running processes</returns> internal static unsafe int[] ListAllPids() { kinfo_proc *entries = GetProcInfo(0, false, out int numProcesses); try { if (numProcesses <= 0) { throw new Win32Exception(SR.CantGetAllPids); } var list = new ReadOnlySpan <kinfo_proc>(entries, numProcesses); var pids = new int[numProcesses]; // walk through process list and skip kernel threads int idx = 0; for (int i = 0; i < list.Length; i++) { if (list[i].ki_ppid == 0) { // skip kernel threads numProcesses -= 1; } else { pids[idx] = list[i].ki_pid; idx += 1; } } // Remove extra elements Array.Resize <int>(ref pids, numProcesses); return(pids); } finally { NativeMemory.Free(entries); } }