initial Freebsd support. (dotnet/corefx#24467)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Fri, 20 Oct 2017 12:57:13 +0000 (05:57 -0700)
committerJan Vorlicek <janvorli@microsoft.com>
Fri, 20 Oct 2017 12:57:13 +0000 (14:57 +0200)
* add FreeBSD

* move pragma clang diagnostic pop few lines down to avoild build error on FreeBSD with clang 3.9

* improve detection of in_pktinfo

* correct detection of in_pktinfo

* set __HostDistroRid properly to avoid warning on FreeBSD

* roll-back freebsd change - rejected in dotnet/corefx#23986

*  add support for OSGroup so this can be done consistently with managed code

* support also -release to be consistent with top level build.sh. simple "release" arg still works

* add -lpthread to get posix threads

* initial implementation of System.Diagnostics.Process. more work to come

* few FreeBSD fixes for run-test.sh

* add VerifyFreeBSDDebugName test

* fix up System.Diagnostics.Process to provide at least functionality needed by tests

* add GetPathToOpenFile()

* adress most feedback from review. refactor sysctl() to system/native. fix extra debug can commented code. handle free better. update calculation for process  start.

* minor clenup to avoid unnecesary diff and one more place to use PlatformDetection.IsFreeBSD

* address review comments. mode and merge with OSX is still to do.

* fix typo

* fix mode for chmod(). It needs to be 644 and 744 in ocral mode.

* address remaining comments from review

* more style cleanup. Use -OS instead of -OSGroup to do cross-platform build

* roll back -release and -debug option. this will be handled as separate change

* remove unsued SystemNative_SysctlByName

* remove one more place where sysctlByName sneaked in

Commit migrated from https://github.com/dotnet/corefx/commit/6c50a9e705800bbc5d166c91aa2c7e2a85b2325b

23 files changed:
src/libraries/Common/src/Interop/BSD/System.Native/Interop.Sysctl.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/FreeBSD/Interop.Process.cs [new file with mode: 0644]
src/libraries/CoreFx.Private.TestUtilities/ref/CoreFx.Private.TestUtilities.cs
src/libraries/CoreFx.Private.TestUtilities/src/System/PlatformDetection.cs
src/libraries/Native/Unix/System.Native/CMakeLists.txt
src/libraries/Native/Unix/System.Native/pal_sysctl.cpp [new file with mode: 0644]
src/libraries/Native/Unix/System.Native/pal_sysctl.h [new file with mode: 0644]
src/libraries/Native/Unix/configure.cmake
src/libraries/Native/build-native.sh
src/libraries/System.Diagnostics.Process/src/Configurations.props
src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs [new file with mode: 0644]
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs [new file with mode: 0644]
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.BSD.cs [new file with mode: 0644]
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs [new file with mode: 0644]
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.FreeBSD.cs [new file with mode: 0644]
src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs
src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs
src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/OSPlatform.cs
src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs
src/libraries/run-test.sh

diff --git a/src/libraries/Common/src/Interop/BSD/System.Native/Interop.Sysctl.cs b/src/libraries/Common/src/Interop/BSD/System.Native/Interop.Sysctl.cs
new file mode 100644 (file)
index 0000000..2b4c51e
--- /dev/null
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+using size_t = System.IntPtr;
+
+// This implements shim for sysctl calls.
+// They are available on BSD systems - FreeBSD, OSX and others.
+// Linux has sysctl() but it is deprecated as well as it is missing sysctlbyname()
+
+internal static partial class Interop
+{
+    internal static partial class Sys
+    {
+
+        [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Sysctl", SetLastError = true)]
+        private extern static unsafe int Sysctl(int* name, int namelen, void* value, size_t* len);
+
+        // This is 'raw' sysctl call, only wrapped to allocate memory if needed
+        // caller always needs to free returned buffer using  Marshal.FreeHGlobal()
+
+        public static unsafe int Sysctl(Span<int> name, ref byte* value, ref int len)
+        {
+            fixed (int * ptr = &name.DangerousGetPinnableReference())
+            {
+                return Sysctl(ptr, name.Length, ref value, ref len);
+            }
+        }
+
+        public static unsafe int Sysctl(int* name, int name_len, ref byte* value, ref int len)
+        {
+            IntPtr bytesLength = (IntPtr)len;
+            byte * pBuffer = value;
+            value = null;
+            int ret=-1;
+
+            if (value == null && len == 0)
+            {
+                // do one try to see how much data we need
+                ret = Sysctl(name,  name_len, pBuffer, &bytesLength);
+                if (ret != 0)
+                {
+                    throw new InvalidOperationException(string.Format("sysctl returned {0}", ret));
+                }
+                pBuffer = (byte*)Marshal.AllocHGlobal((int)bytesLength);
+            }
+            ret = Sysctl(name,  name_len, pBuffer, &bytesLength);
+            if (ret != 0)
+            {
+                if (value == null && len == 0)
+                {
+                    // This is case we allocated memory for caller
+                    Marshal.FreeHGlobal((IntPtr)pBuffer);
+                }
+                throw new InvalidOperationException(string.Format("sysctl returned {0}", ret));
+            }
+
+            value = pBuffer;
+            len = (int)bytesLength;
+
+            return ret;
+        }
+    }
+}
diff --git a/src/libraries/Common/src/Interop/FreeBSD/Interop.Process.cs b/src/libraries/Common/src/Interop/FreeBSD/Interop.Process.cs
new file mode 100644 (file)
index 0000000..586fdf1
--- /dev/null
@@ -0,0 +1,444 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+
+internal static partial class Interop
+{
+    internal static partial class Process
+    {
+        private const ulong SecondsToNanoSeconds = 1000000000;
+
+        // Constants from sys/syslimits.h
+        private const int PATH_MAX  = 1024;
+
+        // Constants from sys/user.h
+        private const int TDNAMLEN = 16;
+        private const int WMESGLEN = 8;
+        private const int LOGNAMELEN = 17;
+        private const int LOCKNAMELEN = 8;
+        private const int COMMLEN = 19;
+        private const int KI_EMULNAMELEN = 16;
+        private const int LOGINCLASSLEN = 17;
+        private const int KI_NGROUPS = 16;
+
+        private const int KI_NSPARE_INT = 4;
+        private const int KI_NSPARE_LONG = 12;
+        private const int KI_NSPARE_PTR = 6;
+
+
+        // Constants from sys/_sigset.h
+        private const int _SIG_WORDS = 4;
+
+        // Constants from sys/sysctl.h
+        private const int CTL_KERN = 1;
+        private const int KERN_PROC = 14;
+        private const int KERN_PROC_PATHNAME = 12;
+        private const int KERN_PROC_PROC = 8;
+        private const int KERN_PROC_ALL = 0; 
+        private const int KERN_PROC_PID  = 1;
+        private const int KERN_PROC_INC_THREAD = 16;
+
+        // Constants from proc_info.h
+        private const int MAXTHREADNAMESIZE = 64;
+        private const int PROC_PIDTASKALLINFO = 2;
+        private const int PROC_PIDTHREADINFO = 5;
+        private const int PROC_PIDLISTTHREADS = 6;
+        private const int PROC_PIDPATHINFO_MAXSIZE = 4 * PATH_MAX;
+
+        internal struct proc_stats
+        {
+            internal long startTime;        /* time_t */
+            internal int nice;
+            internal ulong userTime;        /* in ticks */
+            internal ulong systemTime;      /* in ticks */
+        }
+
+        // From sys/_sigset.h
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct sigset_t
+        {
+            private fixed int bits[4];
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct uid_t
+        {
+            public uint id;
+        }
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct gid_t
+        {
+            public uint id;
+        }
+        [StructLayout(LayoutKind.Sequential)]
+        public struct timeval
+        {
+            public IntPtr tv_sec;
+            public IntPtr tv_usec;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        struct vnode
+        {
+            public long tv_sec;
+            public long tv_usec;
+        }
+
+        // sys/resource.h
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct rusage
+        {
+            public timeval ru_utime;        /* user time used */
+            public timeval ru_stime;        /* system time used */
+            public long ru_maxrss;          /* max resident set size */
+            private long ru_ixrss;          /* integral shared memory size */
+            private long ru_idrss;          /* integral unshared data " */
+            private long ru_isrss;          /* integral unshared stack " */
+            private long ru_minflt;         /* page reclaims */
+            private long ru_majflt;         /* page faults */
+            private long ru_nswap;          /* swaps */
+            private long ru_inblock;        /* block input operations */
+            private long ru_oublock;        /* block output operations */
+            private long ru_msgsnd;         /* messages sent */
+            private long ru_msgrcv;         /* messages received */
+            private long ru_nsignals;       /* signals received */
+            private long ru_nvcsw;          /* voluntary context switches */
+            private long ru_nivcsw;         /* involuntary " */
+        }
+
+        // From  sys/user.h
+        [StructLayout(LayoutKind.Sequential)]
+        public unsafe struct kinfo_proc
+        {
+            public int ki_structsize;                   /* size of this structure */
+            private int ki_layout;                      /* reserved: layout identifier */
+            private void* ki_args;                      /* address of command arguments */
+            private void* ki_paddr;                     /* address of proc */
+            private void* ki_addr;                      /* kernel virtual addr of u-area */
+            private vnode* ki_tracep;                   /* pointer to trace file */
+            private vnode* ki_textvp;                   /* pointer to executable file */
+            private void* ki_fd;                        /* pointer to open file info */
+            private void* ki_vmspace;                   /* pointer to kernel vmspace struct */
+            private void* ki_wchan;                     /* sleep address */
+            public int ki_pid;                          /* Process identifier */
+            public int ki_ppid;                         /* parent process id */
+            private int ki_pgid;                        /* process group id */
+            private int ki_tpgid;                       /* tty process group id */
+            public int ki_sid;                          /* Process session ID */
+            public int ki_tsid;                         /* Terminal session ID */
+            private short ki_jobc;                      /* job control counter */
+            private short ki_spare_short1;              /* unused (just here for alignment) */
+            private int ki_tdev;                        /* controlling tty dev */
+            private sigset_t ki_siglist;                /* Signals arrived but not delivered */
+            private sigset_t ki_sigmask;                /* Current signal mask */
+            private sigset_t ki_sigignore;              /* Signals being ignored */
+            private sigset_t ki_sigcatch;               /* Signals being caught by user */
+            public uid_t ki_uid;                        /* effective user id */
+            private uid_t ki_ruid;                      /* Real user id */
+            private uid_t ki_svuid;                     /* Saved effective user id */
+            private gid_t ki_rgid;                      /* Real group id */
+            private gid_t ki_svgid;                     /* Saved effective group id */
+            private short ki_ngroups;                   /* number of groups */
+            private short ki_spare_short2;              /* unused (just here for alignment) */
+            private fixed uint ki_groups[KI_NGROUPS];   /* groups */
+            public ulong ki_size;                       /* virtual size */
+            public long ki_rssize;                      /* current resident set size in pages */
+            private long ki_swrss;                      /* resident set size before last swap */
+            private long ki_tsize;                      /* text size (pages) XXX */
+            private long ki_dsize;                      /* data size (pages) XXX */
+            private long ki_ssize;                      /* stack size (pages) */
+            private ushort ki_xstat;                    /* Exit status for wait & stop signal */
+            private ushort ki_acflag;                   /* Accounting flags */
+            private uint ki_pctcpu;                     /* %cpu for process during ki_swtime */
+            private uint ki_estcpu;                     /* Time averaged value of ki_cpticks */
+            private uint ki_slptime;                    /* Time since last blocked */
+            private uint ki_swtime;                     /* Time swapped in or out */
+            private uint ki_cow;                        /* number of copy-on-write faults */
+            private ulong ki_runtime;                   /* Real time in microsec */
+            public timeval ki_start;                    /* starting time */
+            private timeval ki_childtime;               /* time used by process children */
+            private long ki_flag;                       /* P_* flags */
+            private long ki_kiflag;                     /* KI_* flags (below) */
+            private int ki_traceflag;                   /* Kernel trace points */
+            private byte ki_stat;                       /* S* process status */
+            public byte ki_nice;                        /* Process "nice" value */
+            private byte ki_lock;                       /* Process lock (prevent swap) count */
+            private byte ki_rqindex;                    /* Run queue index */
+            private byte ki_oncpu_old;                  /* Which cpu we are on (legacy) */
+            private byte ki_lastcpu_old;                /* Last cpu we were on (legacy) */
+            public fixed byte ki_tdname[TDNAMLEN+1];    /* thread name */
+            private fixed byte ki_wmesg[WMESGLEN+1];    /* wchan message */
+            private fixed byte ki_login[LOGNAMELEN+1];  /* setlogin name */
+            private fixed byte ki_lockname[LOCKNAMELEN+1]; /* lock name */
+            public fixed byte ki_comm[COMMLEN+1];       /* command name */
+            private fixed byte ki_emul[KI_EMULNAMELEN+1]; /* emulation name */
+            private fixed byte ki_loginclass[LOGINCLASSLEN+1]; /* login class */
+            private fixed byte ki_sparestrings[50];     /* spare string space */
+            private fixed int ki_spareints[KI_NSPARE_INT]; /* spare room for growth */
+            private int ki_oncpu;                       /* Which cpu we are on */
+            private int ki_lastcpu;                     /* Last cpu we were on */
+            private int ki_tracer;                      /* Pid of tracing process */
+            private int ki_flag2;                       /* P2_* flags */
+            private int ki_fibnum;                      /* Default FIB number */
+            private uint ki_cr_flags;                   /* Credential flags */
+            private int ki_jid;                         /* Process jail ID */
+            public int ki_numthreads;                   /* XXXKSE number of threads in total */
+            public int ki_tid;                          /* XXXKSE thread id */
+            private fixed byte ki_pri[4];               /* process priority */
+            public rusage ki_rusage;                    /* process rusage statistics */
+            /* XXX - most fields in ki_rusage_ch are not (yet) filled in */
+            private rusage ki_rusage_ch;                /* rusage of children processes */
+            private void* ki_pcb;                       /* kernel virtual addr of pcb */
+            private void* ki_kstack;                    /* kernel virtual addr of stack */
+            private void* ki_udata;                     /* User convenience pointer */
+            public void* ki_tdaddr;                     /* address of thread */
+
+            private fixed long ki_spareptrs[KI_NSPARE_PTR];     /* spare room for growth */
+            private fixed long ki_sparelongs[KI_NSPARE_LONG];   /* spare room for growth */
+            private long ki_sflag;                              /* PS_* flags */
+            private long ki_tdflags;                            /* XXXKSE kthread flag */
+        }
+
+        /// <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>
+        /// Gets executable name for process given it's PID
+        /// </summary>
+        /// <param name="pid">The PID of the process</param>
+        public static unsafe string GetProcPath(int pid)
+        {
+            Span<int> sysctlName = stackalloc int[4];
+            byte* pBuffer = null;
+            int bytesLength = 0;
+
+            sysctlName[0] = CTL_KERN;
+            sysctlName[1] = KERN_PROC;
+            sysctlName[2] = KERN_PROC_PATHNAME;
+            sysctlName[3] = pid;
+
+            int ret = Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength);
+            if (ret  != 0 ) {
+                return null;
+            }
+            return System.Text.Encoding.UTF8.GetString(pBuffer,(int)bytesLength-1);
+        }
+
+        /// <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>
+        /// <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 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 unsafe static proc_stats GetThreadInfo(int pid, int tid)
+        {
+            proc_stats ret = new proc_stats();
+            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
+                    {
+                        Span<kinfo_proc> list = new Span<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;
+        }
+    }
+}
index 9f9f654..c91522f 100644 (file)
@@ -50,6 +50,7 @@ namespace System
         public static bool IsInvokingStaticConstructorsSupported { get { throw null; } }
         public static bool IsMacOsHighSierraOrHigher { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
         public static bool IsNetBSD { get { throw null; } }
+        public static bool IsFreeBSD { get { throw null; } }
         public static bool IsNetCore { get { throw null; } }
         public static bool IsNetNative { get { throw null; } }
         public static bool IsNonZeroLowerBoundArraySupported { get { throw null; } }
index db9816c..3f7ab04 100644 (file)
@@ -24,6 +24,7 @@ namespace System
         public static bool IsNetNative => RuntimeInformation.FrameworkDescription.StartsWith(".NET Native", StringComparison.OrdinalIgnoreCase);
         public static bool IsNetCore => RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase);
         public static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+        public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"));
         public static bool IsNetBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD"));
         public static bool IsNotWindows8x => !IsWindows8x;
         public static bool IsNotWindowsNanoServer => !IsWindowsNanoServer;
index 19bb0fe..9cc2ead 100644 (file)
@@ -19,6 +19,7 @@ set(NATIVE_SOURCES
     pal_time.cpp
     pal_uid.cpp
     pal_datetime.cpp
+    pal_sysctl.cpp
 )
 
 if (CMAKE_SYSTEM_NAME STREQUAL Linux)
@@ -48,5 +49,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ANDROID)
     target_link_libraries(System.Native rt)
 endif ()
 
+if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
+    target_link_libraries(System.Native pthread)
+endif ()
+
 install_library_and_symbols (System.Native)
 install (TARGETS System.Native-Static DESTINATION .)
diff --git a/src/libraries/Native/Unix/System.Native/pal_sysctl.cpp b/src/libraries/Native/Unix/System.Native/pal_sysctl.cpp
new file mode 100644 (file)
index 0000000..bc4305b
--- /dev/null
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "pal_config.h"
+
+// These functions are only used for platforms which support
+// using sysctl to gather system information.
+
+#if HAVE_SYS_SYSCTL_H
+
+#include "pal_utilities.h"
+#include "pal_sysctl.h"
+#include "pal_errno.h"
+#include "pal_safecrt.h"
+
+#include <errno.h>
+#include <memory>
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+extern "C" int32_t SystemNative_Sysctl(int* name, unsigned int namelen, void* value, size_t* len)
+{
+    void* newp = nullptr;
+    size_t newlen = 0;
+
+#ifdef __linux__
+    return sysctl(name, static_cast<int>(namelen), value, len, newp, newlen);
+#else
+    return sysctl(name, namelen, value, len, newp, newlen);
+#endif
+}
+
+#endif // HAVE_SYSCTL_H
diff --git a/src/libraries/Native/Unix/System.Native/pal_sysctl.h b/src/libraries/Native/Unix/System.Native/pal_sysctl.h
new file mode 100644 (file)
index 0000000..d173f85
--- /dev/null
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include "pal_types.h"
+#include "pal_errno.h"
+
+
+extern "C" int32_t SystemNative_Sysctl(int* name, unsigned int namelen, void* value, size_t* len);
+
index 1598db7..788b079 100644 (file)
@@ -577,7 +577,7 @@ check_cxx_source_compiles(
 )
 
 check_include_files(
-    sys/sysctl.h
+    "sys/types.h;sys/sysctl.h"
     HAVE_SYS_SYSCTL_H)
 
 check_include_files(
index a3c202c..4397f51 100755 (executable)
@@ -40,8 +40,12 @@ initHostDistroRid()
                __HostDistroRid="rhel.6-$__HostArch"
             fi
         fi
+    elif [ "$__HostOS" == "FreeBSD" ]; then
+      __freebsd_version=`sysctl -n kern.osrelease | cut -f1 -d'.'`
+      __HostDistroRid="freebsd.$__freebsd_version-x64"
     fi
 
+
     if [ "$__HostDistroRid" == "" ]; then
         echo "WARNING: Can not determine runtime id for current distro."
     fi
@@ -252,7 +256,7 @@ while :; do
             __BuildType=Release
             __CMakeArgs=RELEASE
             ;;
-        freebsd|-freebsd)
+        freebsd|FreeBSD|-freebsd|-FreeBSD)
             __BuildOS=FreeBSD
             ;;
         linux|-linux)
index 193ee5c..8e84cf5 100644 (file)
@@ -2,10 +2,11 @@
 <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <BuildConfigurations>
+      netcoreapp-FreeBSD;
       netcoreapp-Linux;
       netcoreapp-OSX;
       netcoreapp-Windows_NT;
       uap-Windows_NT;
     </BuildConfigurations>
   </PropertyGroup>
-</Project>
\ No newline at end of file
+</Project>
index fd49b2d..80e9d78 100644 (file)
     </Compile>
   </ItemGroup>
   <ItemGroup Condition=" '$(TargetsOSX)' == 'true'">
+    <Compile Include="System\Diagnostics\Process.BSD.cs" />
     <Compile Include="System\Diagnostics\Process.OSX.cs" />
+    <Compile Include="System\Diagnostics\ProcessManager.BSD.cs" />
     <Compile Include="System\Diagnostics\ProcessManager.OSX.cs" />
     <Compile Include="System\Diagnostics\ProcessThread.OSX.cs" />
     <Compile Include="$(CommonPath)\Interop\OSX\Interop.libproc.cs">
     <Compile Include="System\Diagnostics\ProcessManager.Uap.cs" />
     <Compile Include="System\Diagnostics\ProcessStartInfo.Uap.cs" />
   </ItemGroup>
-  <ItemGroup Condition=" ('$(TargetsFreeBSD)' == 'true' OR '$(TargetsUnknownUnix)' == 'true')">
+  <ItemGroup Condition=" '$(TargetsFreeBSD)' == 'true'">
+    <Compile Include="System\Diagnostics\Process.BSD.cs" />
+    <Compile Include="System\Diagnostics\Process.FreeBSD.cs" />
+    <Compile Include="System\Diagnostics\ProcessManager.BSD.cs" />
+    <Compile Include="System\Diagnostics\ProcessManager.FreeBSD.cs" />
+    <Compile Include="System\Diagnostics\ProcessThread.FreeBSD.cs" />
+    <Compile Include="$(CommonPath)\Interop\BSD\System.Native\Interop.Sysctl.cs">
+      <Link>Common\Interop\BSD\System.Native\Interop.Sysctl.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\FreeBSD\Interop.Process.cs">
+      <Link>Common\Interop\FreeBSD\Interop.Process.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetTimestamp.cs">
+      <Link>Common\Unix\System.Native\Interop.GetTimestamp.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Stat.cs">
+      <Link>Common\Unix\System.Native\Interop.Stat.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup Condition=" ('$(TargetsUnknownUnix)' == 'true')">
     <Compile Include="System\Diagnostics\Process.UnknownUnix.cs" />
     <Compile Include="System\Diagnostics\ProcessManager.UnknownUnix.cs" />
     <Compile Include="System\Diagnostics\ProcessThread.UnknownUnix.cs" />
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs
new file mode 100644 (file)
index 0000000..4f11321
--- /dev/null
@@ -0,0 +1,122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace System.Diagnostics
+{
+    public partial class Process
+    {
+        /// <summary>
+        /// Creates an array of <see cref="Process"/> components that are associated with process resources on a
+        /// remote computer. These process resources share the specified process name.
+        /// </summary>
+        public static Process[] GetProcessesByName(string processName, string machineName)
+        {
+            if (processName == null)
+            {
+                processName = string.Empty;
+            }
+
+            Process[] procs = GetProcesses(machineName);
+            var list = new List<Process>();
+
+            for (int i = 0; i < procs.Length; i++)
+            {
+                if (string.Equals(processName, procs[i].ProcessName, StringComparison.OrdinalIgnoreCase))
+                {
+                    list.Add(procs[i]);
+                }
+                else
+                {
+                    procs[i].Dispose();
+                }
+            }
+
+            return list.ToArray();
+        }
+
+        /// <summary>
+        /// Gets or sets which processors the threads in this process can be scheduled to run on.
+        /// </summary>
+        private unsafe IntPtr ProcessorAffinityCore
+        {
+            get
+            {
+                throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported);
+            }
+            set
+            {
+                throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported);
+            }
+        }
+
+
+        /// <summary>
+        /// Make sure we have obtained the min and max working set limits.
+        /// </summary>
+        private void GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet)
+        {
+            // We can only do this for the current process on OS X
+            if (_processId != Interop.Sys.GetPid())
+                throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported);
+
+            // Minimum working set (or resident set, as it is called on *nix) doesn't exist so set to 0
+            minWorkingSet = IntPtr.Zero;
+
+            // Get the max working set size
+            Interop.Sys.RLimit limit;
+            if (Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limit) == 0)
+            {
+                maxWorkingSet = limit.CurrentLimit == Interop.Sys.RLIM_INFINITY ?
+                    new IntPtr(Int64.MaxValue) :
+                    new IntPtr(Convert.ToInt64(limit.CurrentLimit));
+            }
+            else
+            {
+                // The contract specifies that this throws Win32Exception when it failes to retrieve the info
+                throw new Win32Exception();
+            }
+        }
+
+        /// <summary>Sets one or both of the minimum and maximum working set limits.</summary>
+        /// <param name="newMin">The new minimum working set limit, or null not to change it.</param>
+        /// <param name="newMax">The new maximum working set limit, or null not to change it.</param>
+        /// <param name="resultingMin">The resulting minimum working set limit after any changes applied.</param>
+        /// <param name="resultingMax">The resulting maximum working set limit after any changes applied.</param>
+        private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr resultingMin, out IntPtr resultingMax)
+        {
+            // We can only do this for the current process on OS X
+            if (_processId != Interop.Sys.GetPid())
+                throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported);
+
+            // There isn't a way to set the minimum working set, so throw an exception here
+            if (newMin.HasValue)
+            {
+                throw new PlatformNotSupportedException(SR.MinimumWorkingSetNotSupported);
+            }
+
+            // The minimum resident set will always be 0, default the resulting max to 0 until we set it (to make the compiler happy)
+            resultingMin = IntPtr.Zero;
+            resultingMax = IntPtr.Zero;
+
+            // The default hard upper limit is absurdly high (over 9000PB) so just change the soft limit...especially since
+            // if you aren't root and move the upper limit down, you need root to move it back up
+            if (newMax.HasValue)
+            {
+                Interop.Sys.RLimit limits = new Interop.Sys.RLimit() { CurrentLimit = (ulong)newMax.Value.ToInt64() };
+                int result = Interop.Sys.SetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, ref limits);
+                if (result != 0)
+                {
+                    throw new System.ComponentModel.Win32Exception(SR.RUsageFailure);
+                }
+
+                // Try to grab the actual value, in case the OS decides to fudge the numbers
+                result = Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limits);
+                if (result == 0) resultingMax = new IntPtr((long)limits.CurrentLimit);
+            }
+        }
+    }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs
new file mode 100644 (file)
index 0000000..e25217e
--- /dev/null
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace System.Diagnostics
+{
+    public partial class Process
+    {
+        /// <summary>Gets the time the associated process was started.</summary>
+        internal DateTime StartTimeCore
+        {
+            get
+            {
+                EnsureState(State.HaveId);
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+
+                return  new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(stat.startTime);
+            }
+        }
+
+        /// <summary>
+        /// Gets the amount of time the associated process has spent utilizing the CPU.
+        /// It is the sum of the <see cref='System.Diagnostics.Process.UserProcessorTime'/> and
+        /// <see cref='System.Diagnostics.Process.PrivilegedProcessorTime'/>.
+        /// </summary>
+        public TimeSpan TotalProcessorTime
+        {
+            get
+            {
+                EnsureState(State.HaveId);
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+                return Process.TicksToTimeSpan(stat.userTime + stat.systemTime);
+            }
+        }
+
+        /// <summary>
+        /// Gets the amount of time the associated process has spent running code
+        /// inside the application portion of the process (not the operating system core).
+        /// </summary>
+        public TimeSpan UserProcessorTime
+        {
+            get
+            {
+                EnsureState(State.HaveId);
+
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+                return Process.TicksToTimeSpan(stat.userTime);
+            }
+        }
+
+        /// <summary>Gets the amount of time the process has spent running code inside the operating system core.</summary>
+        public TimeSpan PrivilegedProcessorTime
+        {
+            get
+            {
+                EnsureState(State.HaveId);
+
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+                return Process.TicksToTimeSpan(stat.systemTime);
+            }
+        }
+
+        // <summary>Gets execution path</summary>
+        private string GetPathToOpenFile()
+        {
+            Interop.Sys.FileStatus stat;
+            if (Interop.Sys.Stat("/usr/local/bin/open", out stat) == 0 )
+            {
+                return "/usr/local/bin/open";
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        // -----------------------------
+        // ---- PAL layer ends here ----
+        // -----------------------------
+        /// <summary>Gets the path to the current executable, or null if it could not be retrieved.</summary>
+        private static string GetExePath()
+        {
+            return Interop.Process.GetProcPath(Interop.Sys.GetPid());
+        }
+
+        // ----------------------------------
+        // ---- Unix PAL layer ends here ----
+        // ----------------------------------
+
+
+    }
+}
index 560d04c..750b8eb 100644 (file)
@@ -9,35 +9,6 @@ namespace System.Diagnostics
 {
     public partial class Process
     {
-        /// <summary>
-        /// Creates an array of <see cref="Process"/> components that are associated with process resources on a
-        /// remote computer. These process resources share the specified process name.
-        /// </summary>
-        public static Process[] GetProcessesByName(string processName, string machineName)
-        {
-            if (processName == null)
-            {
-                processName = string.Empty;
-            }
-
-            Process[] procs = GetProcesses(machineName);
-            var list = new List<Process>();
-
-            for (int i = 0; i < procs.Length; i++)
-            {
-                if (string.Equals(processName, procs[i].ProcessName, StringComparison.OrdinalIgnoreCase))
-                {
-                    list.Add(procs[i]);
-                }
-                else
-                {
-                    procs[i].Dispose();
-                }
-            }
-
-            return list.ToArray();
-        }
-
         /// <summary>Gets the amount of time the process has spent running code inside the operating system core.</summary>
         public TimeSpan PrivilegedProcessorTime
         {
@@ -123,87 +94,6 @@ namespace System.Diagnostics
             }
         }
 
-        /// <summary>
-        /// Gets or sets which processors the threads in this process can be scheduled to run on.
-        /// </summary>
-        private unsafe IntPtr ProcessorAffinityCore
-        {
-            get
-            {
-                throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported);
-            }
-            set
-            {
-                throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported);
-            }
-        }
-
-
-        /// <summary>
-        /// Make sure we have obtained the min and max working set limits.
-        /// </summary>
-        private void GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet)
-        {
-            // We can only do this for the current process on OS X
-            if (_processId != Interop.Sys.GetPid())
-                throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported);
-
-            // Minimum working set (or resident set, as it is called on *nix) doesn't exist so set to 0
-            minWorkingSet = IntPtr.Zero;
-
-            // Get the max working set size
-            Interop.Sys.RLimit limit;
-            if (Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limit) == 0)
-            {
-                maxWorkingSet = limit.CurrentLimit == Interop.Sys.RLIM_INFINITY ?
-                    new IntPtr(Int64.MaxValue) :
-                    new IntPtr(Convert.ToInt64(limit.CurrentLimit));
-            }
-            else
-            {
-                // The contract specifies that this throws Win32Exception when it failes to retrieve the info
-                throw new Win32Exception();
-            }
-        }
-
-        /// <summary>Sets one or both of the minimum and maximum working set limits.</summary>
-        /// <param name="newMin">The new minimum working set limit, or null not to change it.</param>
-        /// <param name="newMax">The new maximum working set limit, or null not to change it.</param>
-        /// <param name="resultingMin">The resulting minimum working set limit after any changes applied.</param>
-        /// <param name="resultingMax">The resulting maximum working set limit after any changes applied.</param>
-        private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr resultingMin, out IntPtr resultingMax)
-        {
-            // We can only do this for the current process on OS X
-            if (_processId != Interop.Sys.GetPid())
-                throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported);
-
-            // There isn't a way to set the minimum working set, so throw an exception here
-            if (newMin.HasValue)
-            {
-                throw new PlatformNotSupportedException(SR.MinimumWorkingSetNotSupported);
-            }
-
-            // The minimum resident set will always be 0, default the resulting max to 0 until we set it (to make the compiler happy)
-            resultingMin = IntPtr.Zero;
-            resultingMax = IntPtr.Zero;
-
-            // The default hard upper limit is absurdly high (over 9000PB) so just change the soft limit...especially since
-            // if you aren't root and move the upper limit down, you need root to move it back up
-            if (newMax.HasValue)
-            {
-                Interop.Sys.RLimit limits = new Interop.Sys.RLimit() { CurrentLimit = (ulong)newMax.Value.ToInt64() };
-                int result = Interop.Sys.SetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, ref limits);
-                if (result != 0)
-                {
-                    throw new System.ComponentModel.Win32Exception(SR.RUsageFailure);
-                }
-
-                // Try to grab the actual value, in case the OS decides to fudge the numbers
-                result = Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limits);
-                if (result == 0) resultingMax = new IntPtr((long)limits.CurrentLimit);
-            }
-        }
-
         // -----------------------------
         // ---- PAL layer ends here ----
         // -----------------------------
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.BSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.BSD.cs
new file mode 100644 (file)
index 0000000..1056dbf
--- /dev/null
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace System.Diagnostics
+{
+    internal static partial class ProcessManager
+    {
+        /// <summary>Gets process infos for each process on the specified machine.</summary>
+        /// <param name="machineName">The target machine.</param>
+        /// <returns>An array of process infos, one per found process.</returns>
+        public static ProcessInfo[] GetProcessInfos(string machineName)
+        {
+            ThrowIfRemoteMachine(machineName);
+            int[] procIds = GetProcessIds(machineName);
+
+            // Iterate through all process IDs to load information about each process
+            var processes = new List<ProcessInfo>(procIds.Length);
+            foreach (int pid in procIds)
+            {
+                ProcessInfo pi = CreateProcessInfo(pid);
+                if (pi != null)
+                {
+                    processes.Add(pi);
+                }
+            }
+
+            return processes.ToArray();
+        }
+
+        /// <summary>Gets an array of module infos for the specified process.</summary>
+        /// <param name="processId">The ID of the process whose modules should be enumerated.</param>
+        /// <returns>The array of modules.</returns>
+        internal static ProcessModuleCollection GetModules(int processId)
+        {
+            // We don't have a good way of getting all of the modules of the particular process,
+            // but we can at least get the path to the executable file for the process, and
+            // other than for debugging tools, that's the main reason consumers of Modules care about it,
+            // and why MainModule exists.
+            try
+            {
+                string exePath = GetProcPath(processId);
+                if (!string.IsNullOrEmpty(exePath))
+                {
+                    return new ProcessModuleCollection(1)
+                    {
+                        new ProcessModule()
+                        {
+                            FileName = exePath,
+                            ModuleName = Path.GetFileName(exePath)
+                        }
+                    };
+                }
+            }
+            catch { } // eat all errors
+
+            return new ProcessModuleCollection(0);
+        }
+    }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs
new file mode 100644 (file)
index 0000000..652d0f5
--- /dev/null
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace System.Diagnostics
+{
+    internal static partial class ProcessManager
+    {
+        /// <summary>Gets the IDs of all processes on the current machine.</summary>
+        public static int[] GetProcessIds()
+        {
+            return Interop.Process.ListAllPids();
+        }
+
+        internal static string GetProcPath(int processId)
+        {
+            return Interop.Process.GetProcPath(processId);
+        }
+
+        private static ProcessInfo CreateProcessInfo(int pid)
+        {
+            // Negative PIDs aren't valid
+            if (pid < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(pid));
+            }
+
+            ProcessInfo procInfo = new ProcessInfo()
+            {
+                ProcessId = pid
+            };
+
+            // Try to get the task info. This can fail if the user permissions don't permit
+            // this user context to query the specified process
+            ProcessInfo iinfo = Interop.Process.GetProcessInfoById(pid);
+
+            procInfo.ProcessName = iinfo.ProcessName;
+            procInfo.BasePriority = iinfo.BasePriority;
+            procInfo.VirtualBytes = iinfo.VirtualBytes;
+            procInfo.WorkingSet = iinfo.WorkingSet;
+            procInfo.SessionId = iinfo.SessionId;
+            foreach (ThreadInfo ti in iinfo._threadInfoList)
+            {
+                procInfo._threadInfoList.Add(ti);
+            }
+
+            return procInfo;
+        }
+
+        // ----------------------------------
+        // ---- Unix PAL layer ends here ----
+        // ----------------------------------
+
+    }
+}
index 570826d..dffc977 100644 (file)
@@ -16,26 +16,9 @@ namespace System.Diagnostics
             return Interop.libproc.proc_listallpids();
         }
 
-        /// <summary>Gets process infos for each process on the specified machine.</summary>
-        /// <param name="machineName">The target machine.</param>
-        /// <returns>An array of process infos, one per found process.</returns>
-        public static ProcessInfo[] GetProcessInfos(string machineName)
+        private static string GetProcPath(int processId)
         {
-            ThrowIfRemoteMachine(machineName);
-            int[] procIds = GetProcessIds(machineName);
-
-            // Iterate through all process IDs to load information about each process
-            var processes = new List<ProcessInfo>(procIds.Length);
-            foreach (int pid in procIds)
-            {
-                ProcessInfo pi = CreateProcessInfo(pid);
-                if (pi != null)
-                {
-                    processes.Add(pi);
-                }
-            }
-
-            return processes.ToArray();
+            return Interop.libproc.proc_pidpath(processId);
         }
 
         // -----------------------------
@@ -99,35 +82,6 @@ namespace System.Diagnostics
             return procInfo;
         }
 
-        /// <summary>Gets an array of module infos for the specified process.</summary>
-        /// <param name="processId">The ID of the process whose modules should be enumerated.</param>
-        /// <returns>The array of modules.</returns>
-        internal static ProcessModuleCollection GetModules(int processId)
-        {
-            // We don't have a good way of getting all of the modules of the particular process,
-            // but we can at least get the path to the executable file for the process, and
-            // other than for debugging tools, that's the main reason consumers of Modules care about it,
-            // and why MainModule exists.
-            try
-            {
-                string exePath = Interop.libproc.proc_pidpath(processId);
-                if (!string.IsNullOrEmpty(exePath))
-                {
-                    return new ProcessModuleCollection(1)
-                    {
-                        new ProcessModule()
-                        {
-                            FileName = exePath,
-                            ModuleName = Path.GetFileName(exePath)
-                        }
-                    };
-                }
-            }
-            catch { } // eat all errors
-
-            return new ProcessModuleCollection(0);
-        }
-
         // ----------------------------------
         // ---- Unix PAL layer ends here ----
         // ----------------------------------
@@ -159,5 +113,5 @@ namespace System.Diagnostics
             else
                 return System.Diagnostics.ThreadWaitReason.Unknown; // There isn't a good mapping for anything else
         }
-    }    
+    }
 }
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.FreeBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.FreeBSD.cs
new file mode 100644 (file)
index 0000000..7d60059
--- /dev/null
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Diagnostics
+{
+    public partial class ProcessThread
+    {
+        /// <summary>
+        /// Returns or sets the priority level of the associated thread.  The priority level is
+        /// not an absolute level, but instead contributes to the actual thread priority by
+        /// considering the priority class of the process.
+        /// </summary>
+        private ThreadPriorityLevel PriorityLevelCore
+        {
+            get
+            {
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+                return Interop.Sys.GetThreadPriorityFromNiceValue((int)stat.nice);
+            }
+            set
+            {
+                throw new PlatformNotSupportedException(); // We can find no API to set this
+            }
+        }
+
+        /// <summary>Returns the time the associated thread was started.</summary>
+        public DateTime StartTime
+        {
+            // kinfo_proc has one  entry per thread but ki_start seems to be same for
+            // all threads e.g. reflects process start. This may be re-visited later.
+            get { throw new PlatformNotSupportedException(); }
+        }
+
+        /// <summary>
+        /// Returns the amount of time the associated thread has spent utilizing the CPU.
+        /// It is the sum of the System.Diagnostics.ProcessThread.UserProcessorTime and
+        /// System.Diagnostics.ProcessThread.PrivilegedProcessorTime.
+        /// </summary>
+        public TimeSpan TotalProcessorTime
+        {
+            get
+            {
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+                return Process.TicksToTimeSpan(stat.userTime + stat.systemTime);
+            }
+        }
+
+        /// <summary>
+        /// Returns the amount of time the associated thread has spent running code
+        /// inside the application (not the operating system core).
+        /// </summary>
+        public TimeSpan UserProcessorTime
+        {
+            get
+            {
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+                return Process.TicksToTimeSpan(stat.userTime);
+            }
+        }
+
+        /// <summary>
+        /// Returns the amount of time the thread has spent running code inside the operating
+        /// system core.
+        /// </summary>
+        public TimeSpan PrivilegedProcessorTime
+        {
+            get
+            {
+                Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+                return Process.TicksToTimeSpan(stat.systemTime);
+            }
+
+        }
+    }
+}
index 0757c83..af40481 100644 (file)
@@ -257,7 +257,9 @@ namespace System.Diagnostics.Tests
         {
             string path = GetTestFilePath();
             File.Create(path).Dispose();
-            Assert.Equal(0, chmod(path, 644)); // no execute permissions
+            int mode = Convert.ToInt32("644", 8);
+
+            Assert.Equal(0, chmod(path, mode));
 
             Win32Exception e = Assert.Throws<Win32Exception>(() => Process.Start(path));
             Assert.NotEqual(0, e.NativeErrorCode);
@@ -268,7 +270,9 @@ namespace System.Diagnostics.Tests
         {
             string path = GetTestFilePath();
             File.Create(path).Dispose();
-            Assert.Equal(0, chmod(path, 744)); // execute permissions
+            int mode = Convert.ToInt32("744", 8);
+
+            Assert.Equal(0, chmod(path, mode)); // execute permissions
 
             using (Process p = Process.Start(path))
             {
index d8696ec..cda6074 100644 (file)
@@ -69,7 +69,7 @@ namespace System.Diagnostics.Tests
         }
 
         [Fact]
-        [PlatformSpecific(TestPlatforms.OSX)] // OSX throws PNSE from StartTime
+        [PlatformSpecific(TestPlatforms.OSX|TestPlatforms.FreeBSD)] // OSX and FreeBSD throw PNSE from StartTime
         public void TestStartTimeProperty_OSX()
         {
             using (Process p = Process.GetCurrentProcess())
@@ -86,7 +86,7 @@ namespace System.Diagnostics.Tests
         }
 
         [Fact]
-        [PlatformSpecific(~TestPlatforms.OSX)] // OSX throws PNSE from StartTime
+        [PlatformSpecific(TestPlatforms.Linux|TestPlatforms.Windows)] // OSX and FreeBSD throw PNSE from StartTime
         [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Retrieving information about local processes is not supported on uap")]
         public async Task TestStartTimeProperty()
         {
@@ -178,6 +178,12 @@ namespace System.Diagnostics.Tests
                 return;
             }
 
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")))
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => thread.PriorityLevel = ThreadPriorityLevel.AboveNormal);
+                return;
+            }
+
             try
             {
                 thread.PriorityLevel = ThreadPriorityLevel.AboveNormal;
index 98642bd..3809868 100644 (file)
@@ -18,7 +18,7 @@ namespace System.Runtime.InteropServices
         {
             if (osPlatform == null) throw new ArgumentNullException(nameof(osPlatform));
             if (osPlatform.Length == 0) throw new ArgumentException(SR.Argument_EmptyValue, nameof(osPlatform));
-            
+
             _osPlatform = osPlatform;
         }
 
index f69335e..4432b68 100644 (file)
@@ -80,6 +80,12 @@ namespace System.Runtime.InteropServices.RuntimeInformationTests
             Assert.Contains("netbsd", RuntimeInformation.OSDescription, StringComparison.OrdinalIgnoreCase);
         }
 
+        [Fact, PlatformSpecific(TestPlatforms.FreeBSD)]  // Checks FreeBSD debug name in RuntimeInformation
+        public void VerifyFreeBSDDebugName()
+        {
+            Assert.Contains("FreeBSD", RuntimeInformation.OSDescription, StringComparison.OrdinalIgnoreCase);
+        }
+
         [Fact, PlatformSpecific(TestPlatforms.OSX)]  // Checks OSX debug name in RuntimeInformation
         public void VerifyOSXDebugName()
         {
index 1fdc2d7..9e9e0ea 100755 (executable)
@@ -119,7 +119,9 @@ case $CPUName in
     aarch64)
         __Arch=arm64
         ;;
-
+    amd64)
+        __Arch=x64
+        ;;
     *)
         echo "Unknown CPU $CPUName detected, configuring as if for x64"
         __Arch=x64
@@ -385,7 +387,7 @@ if [ $RunTestSequential -eq 1 ]
 then
     maxProcesses=1;
 else
-    if [ `uname` = "NetBSD" ]; then
+    if [ `uname` = "NetBSD" ] || [ `uname` = "FreeBSD" ]; then
       maxProcesses=$(($(getconf NPROCESSORS_ONLN)+1))
     else
       maxProcesses=$(($(getconf _NPROCESSORS_ONLN)+1))