Remove reflection from Environment.WorkingSet prop (#43154)
authorAdeel Mujahid <3840695+am11@users.noreply.github.com>
Mon, 12 Oct 2020 23:40:13 +0000 (02:40 +0300)
committerGitHub <noreply@github.com>
Mon, 12 Oct 2020 23:40:13 +0000 (16:40 -0700)
14 files changed:
src/libraries/Common/src/Interop/FreeBSD/Interop.Process.GetProcInfo.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/FreeBSD/Interop.Process.cs
src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs
src/libraries/Common/src/Interop/OSX/Interop.libproc.GetProcessInfoById.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/OSX/Interop.libproc.cs
src/libraries/Common/tests/Common.Tests.csproj
src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Environment.FreeBSD.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Environment.OSX.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs

diff --git a/src/libraries/Common/src/Interop/FreeBSD/Interop.Process.GetProcInfo.cs b/src/libraries/Common/src/Interop/FreeBSD/Interop.Process.GetProcInfo.cs
new file mode 100644 (file)
index 0000000..6496fbd
--- /dev/null
@@ -0,0 +1,236 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+#pragma warning disable CA1823 // analyzer incorrectly flags fixed buffer length const (https://github.com/dotnet/roslyn/issues/37593)
+
+internal static partial class Interop
+{
+    internal static partial class Process
+    {
+        // 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/sysctl.h
+        private const int CTL_KERN = 1;
+        private const int KERN_PROC = 14;
+        private const int KERN_PROC_PROC = 8;
+        private const int KERN_PROC_PID  = 1;
+        private const int KERN_PROC_INC_THREAD = 16;
+
+        // 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)]
+        private 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>
+        /// 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];
+            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;
+        }
+    }
+}
index ddaae38..b2b18ea 100644 (file)
@@ -15,42 +15,8 @@ internal static partial class Interop
     {
         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
         {
@@ -60,152 +26,6 @@ internal static partial class Interop
             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)]
-        private 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>
@@ -274,62 +94,6 @@ internal static partial class Interop
         }
 
         /// <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];
-            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>
diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs
new file mode 100644 (file)
index 0000000..0187e77
--- /dev/null
@@ -0,0 +1,169 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+internal static partial class Interop
+{
+    internal static partial class procfs
+    {
+        internal const string RootPath = "/proc/";
+        private const string StatusFileName = "/status";
+
+        internal struct ParsedStatus
+        {
+#if DEBUG
+            internal int Pid;
+#endif
+            internal ulong VmHWM;
+            internal ulong VmRSS;
+            internal ulong VmData;
+            internal ulong VmSwap;
+            internal ulong VmSize;
+            internal ulong VmPeak;
+        }
+
+        internal static string GetStatusFilePathForProcess(int pid)
+        {
+            return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatusFileName;
+        }
+
+        internal static bool TryReadStatusFile(int pid, out ParsedStatus result, ReusableTextReader reusableReader)
+        {
+            bool b = TryParseStatusFile(GetStatusFilePathForProcess(pid), out result, reusableReader);
+#if DEBUG
+            Debug.Assert(!b || result.Pid == pid, "Expected process ID from status file to match supplied pid");
+#endif
+            return b;
+        }
+
+        internal static bool TryParseStatusFile(string statusFilePath, out ParsedStatus result, ReusableTextReader reusableReader)
+        {
+            if (!TryReadFile(statusFilePath, reusableReader, out string? fileContents))
+            {
+                // Between the time that we get an ID and the time that we try to read the associated stat
+                // file(s), the process could be gone.
+                result = default(ParsedStatus);
+                return false;
+            }
+
+            ParsedStatus results = default(ParsedStatus);
+            ReadOnlySpan<char> statusFileContents = fileContents.AsSpan();
+            int unitSliceLength = -1;
+#if DEBUG
+            int nonUnitSliceLength = -1;
+#endif
+            while (!statusFileContents.IsEmpty)
+            {
+                int startIndex = statusFileContents.IndexOf(':');
+                if (startIndex == -1)
+                {
+                    // Reached end of file
+                    break;
+                }
+
+                ReadOnlySpan<char> title = statusFileContents.Slice(0, startIndex);
+                statusFileContents = statusFileContents.Slice(startIndex + 1);
+                int endIndex = statusFileContents.IndexOf('\n');
+                if (endIndex == -1)
+                {
+                    endIndex = statusFileContents.Length - 1;
+                    unitSliceLength = statusFileContents.Length - 3;
+#if DEBUG
+                    nonUnitSliceLength = statusFileContents.Length;
+#endif
+                }
+                else
+                {
+                    unitSliceLength = endIndex - 3;
+#if DEBUG
+                    nonUnitSliceLength = endIndex;
+#endif
+                }
+
+                ReadOnlySpan<char> value = default;
+                bool valueParsed = true;
+#if DEBUG
+                if (title.SequenceEqual("Pid".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, nonUnitSliceLength);
+                    valueParsed = int.TryParse(value, out results.Pid);
+                }
+#endif
+                if (title.SequenceEqual("VmHWM".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out results.VmHWM);
+                }
+                else if (title.SequenceEqual("VmRSS".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out results.VmRSS);
+                }
+                else if (title.SequenceEqual("VmData".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out ulong vmData);
+                    results.VmData += vmData;
+                }
+                else if (title.SequenceEqual("VmSwap".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out results.VmSwap);
+                }
+                else if (title.SequenceEqual("VmSize".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out results.VmSize);
+                }
+                else if (title.SequenceEqual("VmPeak".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out results.VmPeak);
+                }
+                else if (title.SequenceEqual("VmStk".AsSpan()))
+                {
+                    value = statusFileContents.Slice(0, unitSliceLength);
+                    valueParsed = ulong.TryParse(value, out ulong vmStack);
+                    results.VmData += vmStack;
+                }
+
+                Debug.Assert(valueParsed);
+                statusFileContents = statusFileContents.Slice(endIndex + 1);
+            }
+
+            results.VmData *= 1024;
+            results.VmPeak *= 1024;
+            results.VmSize *= 1024;
+            results.VmSwap *= 1024;
+            results.VmRSS *= 1024;
+            results.VmHWM *= 1024;
+            result = results;
+            return true;
+        }
+
+        private static bool TryReadFile(string filePath, ReusableTextReader reusableReader, [NotNullWhen(true)] out string? fileContents)
+        {
+            try
+            {
+                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, useAsync: false))
+                {
+                    fileContents = reusableReader.ReadAllText(fileStream);
+                    return true;
+                }
+            }
+            catch (IOException)
+            {
+                fileContents = null;
+                return false;
+            }
+        }
+    }
+}
index 1b58498..42a6b5c 100644 (file)
@@ -14,12 +14,10 @@ internal static partial class Interop
 {
     internal static partial class procfs
     {
-        internal const string RootPath = "/proc/";
         private const string ExeFileName = "/exe";
         private const string CmdLineFileName = "/cmdline";
         private const string StatFileName = "/stat";
         private const string MapsFileName = "/maps";
-        private const string StatusFileName = "/status";
         private const string FileDescriptorDirectoryName = "/fd/";
         private const string TaskDirectoryName = "/task/";
 
@@ -80,19 +78,6 @@ internal static partial class Interop
             //internal long cguest_time;
         }
 
-        internal struct ParsedStatus
-        {
-#if DEBUG
-            internal int Pid;
-#endif
-            internal ulong VmHWM;
-            internal ulong VmRSS;
-            internal ulong VmData;
-            internal ulong VmSwap;
-            internal ulong VmSize;
-            internal ulong VmPeak;
-        }
-
         internal struct ParsedMapsModule
         {
             internal string FileName;
@@ -114,11 +99,6 @@ internal static partial class Interop
             return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatFileName;
         }
 
-        internal static string GetStatusFilePathForProcess(int pid)
-        {
-            return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatusFileName;
-        }
-
         internal static string GetMapsFilePathForProcess(int pid)
         {
             return RootPath + pid.ToString(CultureInfo.InvariantCulture) + MapsFileName;
@@ -233,15 +213,6 @@ internal static partial class Interop
             return b;
         }
 
-        internal static bool TryReadStatusFile(int pid, out ParsedStatus result, ReusableTextReader reusableReader)
-        {
-            bool b = TryParseStatusFile(GetStatusFilePathForProcess(pid), out result, reusableReader);
-#if DEBUG
-            Debug.Assert(!b || result.Pid == pid, "Expected process ID from status file to match supplied pid");
-#endif
-            return b;
-        }
-
         internal static bool TryParseStatFile(string statFilePath, out ParsedStat result, ReusableTextReader reusableReader)
         {
             if (!TryReadFile(statFilePath, reusableReader, out string? statFileContents))
@@ -310,127 +281,5 @@ internal static partial class Interop
             result = results;
             return true;
         }
-
-        internal static bool TryParseStatusFile(string statusFilePath, out ParsedStatus result, ReusableTextReader reusableReader)
-        {
-            if (!TryReadFile(statusFilePath, reusableReader, out string? fileContents))
-            {
-                // Between the time that we get an ID and the time that we try to read the associated stat
-                // file(s), the process could be gone.
-                result = default(ParsedStatus);
-                return false;
-            }
-
-            ParsedStatus results = default(ParsedStatus);
-            ReadOnlySpan<char> statusFileContents = fileContents.AsSpan();
-            int unitSliceLength = -1;
-#if DEBUG
-            int nonUnitSliceLength = -1;
-#endif
-            while (!statusFileContents.IsEmpty)
-            {
-                int startIndex = statusFileContents.IndexOf(':');
-                if (startIndex == -1)
-                {
-                    // Reached end of file
-                    break;
-                }
-
-                ReadOnlySpan<char> title = statusFileContents.Slice(0, startIndex);
-                statusFileContents = statusFileContents.Slice(startIndex + 1);
-                int endIndex = statusFileContents.IndexOf('\n');
-                if (endIndex == -1)
-                {
-                    endIndex = statusFileContents.Length - 1;
-                    unitSliceLength = statusFileContents.Length - 3;
-#if DEBUG
-                    nonUnitSliceLength = statusFileContents.Length;
-#endif
-                }
-                else
-                {
-                    unitSliceLength = endIndex - 3;
-#if DEBUG
-                    nonUnitSliceLength = endIndex;
-#endif
-                }
-
-                ReadOnlySpan<char> value = default;
-                bool valueParsed = true;
-#if DEBUG
-                if (title.SequenceEqual("Pid".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, nonUnitSliceLength);
-                    valueParsed = int.TryParse(value, out results.Pid);
-                }
-#endif
-                if (title.SequenceEqual("VmHWM".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out results.VmHWM);
-                }
-                else if (title.SequenceEqual("VmRSS".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out results.VmRSS);
-                }
-                else if (title.SequenceEqual("VmData".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out ulong vmData);
-                    results.VmData += vmData;
-                }
-                else if (title.SequenceEqual("VmSwap".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out results.VmSwap);
-                }
-                else if (title.SequenceEqual("VmSize".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out results.VmSize);
-                }
-                else if (title.SequenceEqual("VmPeak".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out results.VmPeak);
-                }
-                else if (title.SequenceEqual("VmStk".AsSpan()))
-                {
-                    value = statusFileContents.Slice(0, unitSliceLength);
-                    valueParsed = ulong.TryParse(value, out ulong vmStack);
-                    results.VmData += vmStack;
-                }
-
-                Debug.Assert(valueParsed);
-                statusFileContents = statusFileContents.Slice(endIndex + 1);
-            }
-
-            results.VmData *= 1024;
-            results.VmPeak *= 1024;
-            results.VmSize *= 1024;
-            results.VmSwap *= 1024;
-            results.VmRSS *= 1024;
-            results.VmHWM *= 1024;
-            result = results;
-            return true;
-        }
-
-        private static bool TryReadFile(string filePath, ReusableTextReader reusableReader, [NotNullWhen(true)] out string? fileContents)
-        {
-            try
-            {
-                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, useAsync: false))
-                {
-                    fileContents = reusableReader.ReadAllText(fileStream);
-                    return true;
-                }
-            }
-            catch (IOException)
-            {
-                fileContents = null;
-                return false;
-            }
-        }
     }
 }
diff --git a/src/libraries/Common/src/Interop/OSX/Interop.libproc.GetProcessInfoById.cs b/src/libraries/Common/src/Interop/OSX/Interop.libproc.GetProcessInfoById.cs
new file mode 100644 (file)
index 0000000..868fce3
--- /dev/null
@@ -0,0 +1,125 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+
+using System;
+using System.Runtime.InteropServices;
+
+#pragma warning disable CA1823 // analyzer incorrectly flags fixed buffer length const (https://github.com/dotnet/roslyn/issues/37593)
+
+internal static partial class Interop
+{
+    internal static partial class libproc
+    {
+        // Constants from sys\param.h
+        private const int MAXCOMLEN = 16;
+
+        // Constants from proc_info.h
+        private const int PROC_PIDTASKALLINFO = 2;
+
+        // From proc_info.h
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct proc_bsdinfo
+        {
+            internal uint       pbi_flags;
+            internal uint       pbi_status;
+            internal uint       pbi_xstatus;
+            internal uint       pbi_pid;
+            internal uint       pbi_ppid;
+            internal uint       pbi_uid;
+            internal uint       pbi_gid;
+            internal uint       pbi_ruid;
+            internal uint       pbi_rgid;
+            internal uint       pbi_svuid;
+            internal uint       pbi_svgid;
+            internal uint       reserved;
+            internal fixed byte pbi_comm[MAXCOMLEN];
+            internal fixed byte pbi_name[MAXCOMLEN * 2];
+            internal uint       pbi_nfiles;
+            internal uint       pbi_pgid;
+            internal uint       pbi_pjobc;
+            internal uint       e_tdev;
+            internal uint       e_tpgid;
+            internal int        pbi_nice;
+            internal ulong      pbi_start_tvsec;
+            internal ulong      pbi_start_tvusec;
+        }
+
+        // From proc_info.h
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct proc_taskinfo
+        {
+            internal ulong   pti_virtual_size;
+            internal ulong   pti_resident_size;
+            internal ulong   pti_total_user;
+            internal ulong   pti_total_system;
+            internal ulong   pti_threads_user;
+            internal ulong   pti_threads_system;
+            internal int     pti_policy;
+            internal int     pti_faults;
+            internal int     pti_pageins;
+            internal int     pti_cow_faults;
+            internal int     pti_messages_sent;
+            internal int     pti_messages_received;
+            internal int     pti_syscalls_mach;
+            internal int     pti_syscalls_unix;
+            internal int     pti_csw;
+            internal int     pti_threadnum;
+            internal int     pti_numrunning;
+            internal int     pti_priority;
+        };
+
+        // From proc_info.h
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+        internal unsafe struct proc_taskallinfo
+        {
+            internal proc_bsdinfo    pbsd;
+            internal proc_taskinfo   ptinfo;
+        }
+
+        /// <summary>
+        /// Gets information about a process given it's PID
+        /// </summary>
+        /// <param name="pid">The PID of the process</param>
+        /// <param name="flavor">Should be PROC_PIDTASKALLINFO</param>
+        /// <param name="arg">Flavor dependent value</param>
+        /// <param name="buffer">A pointer to a block of memory (of size proc_taskallinfo) allocated that will contain the data</param>
+        /// <param name="bufferSize">The size of the allocated block above</param>
+        /// <returns>
+        /// The amount of data actually returned. If this size matches the bufferSize parameter then
+        /// the data is valid. If the sizes do not match then the data is invalid, most likely due
+        /// to not having enough permissions to query for the data of that specific process
+        /// </returns>
+        [DllImport(Interop.Libraries.libproc, SetLastError = true)]
+        private static extern unsafe int proc_pidinfo(
+            int pid,
+            int flavor,
+            ulong arg,
+            proc_taskallinfo* buffer,
+            int bufferSize);
+
+        /// <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 proc_taskallinfo struct for valid processes that the caller
+        /// has permission to access; otherwise, returns null
+        /// </returns>
+        internal static unsafe proc_taskallinfo? GetProcessInfoById(int pid)
+        {
+            // Negative PIDs are invalid
+            if (pid < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(pid));
+            }
+
+            // Get the process information for the specified pid
+            int size = sizeof(proc_taskallinfo);
+            proc_taskallinfo info = default(proc_taskallinfo);
+            int result = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &info, size);
+            return (result == size ? new proc_taskallinfo?(info) : null);
+        }
+    }
+}
index 41403a3..7b4e237 100644 (file)
@@ -16,12 +16,10 @@ internal static partial class Interop
     internal static partial class libproc
     {
         // Constants from sys\param.h
-        private const int MAXCOMLEN = 16;
         private const int MAXPATHLEN = 1024;
 
         // 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 * MAXPATHLEN;
@@ -47,58 +45,6 @@ internal static partial class Interop
             TH_FLAGS_IDLE       = 0x2
         }
 
-        // From proc_info.h
-        [StructLayout(LayoutKind.Sequential)]
-        internal unsafe struct proc_bsdinfo
-        {
-            internal uint       pbi_flags;
-            internal uint       pbi_status;
-            internal uint       pbi_xstatus;
-            internal uint       pbi_pid;
-            internal uint       pbi_ppid;
-            internal uint       pbi_uid;
-            internal uint       pbi_gid;
-            internal uint       pbi_ruid;
-            internal uint       pbi_rgid;
-            internal uint       pbi_svuid;
-            internal uint       pbi_svgid;
-            internal uint       reserved;
-            internal fixed byte pbi_comm[MAXCOMLEN];
-            internal fixed byte pbi_name[MAXCOMLEN * 2];
-            internal uint       pbi_nfiles;
-            internal uint       pbi_pgid;
-            internal uint       pbi_pjobc;
-            internal uint       e_tdev;
-            internal uint       e_tpgid;
-            internal int        pbi_nice;
-            internal ulong      pbi_start_tvsec;
-            internal ulong      pbi_start_tvusec;
-        }
-
-        // From proc_info.h
-        [StructLayout(LayoutKind.Sequential)]
-        internal unsafe struct proc_taskinfo
-        {
-            internal ulong   pti_virtual_size;
-            internal ulong   pti_resident_size;
-            internal ulong   pti_total_user;
-            internal ulong   pti_total_system;
-            internal ulong   pti_threads_user;
-            internal ulong   pti_threads_system;
-            internal int     pti_policy;
-            internal int     pti_faults;
-            internal int     pti_pageins;
-            internal int     pti_cow_faults;
-            internal int     pti_messages_sent;
-            internal int     pti_messages_received;
-            internal int     pti_syscalls_mach;
-            internal int     pti_syscalls_unix;
-            internal int     pti_csw;
-            internal int     pti_threadnum;
-            internal int     pti_numrunning;
-            internal int     pti_priority;
-        };
-
         // from sys\resource.h
         [StructLayout(LayoutKind.Sequential)]
         internal unsafe struct rusage_info_v3
@@ -134,14 +80,6 @@ internal static partial class Interop
         }
 
         // From proc_info.h
-        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-        internal unsafe struct proc_taskallinfo
-        {
-            internal proc_bsdinfo    pbsd;
-            internal proc_taskinfo   ptinfo;
-        }
-
-        // From proc_info.h
         [StructLayout(LayoutKind.Sequential)]
         internal unsafe struct proc_threadinfo
         {
@@ -221,27 +159,6 @@ internal static partial class Interop
         /// Gets information about a process given it's PID
         /// </summary>
         /// <param name="pid">The PID of the process</param>
-        /// <param name="flavor">Should be PROC_PIDTASKALLINFO</param>
-        /// <param name="arg">Flavor dependent value</param>
-        /// <param name="buffer">A pointer to a block of memory (of size proc_taskallinfo) allocated that will contain the data</param>
-        /// <param name="bufferSize">The size of the allocated block above</param>
-        /// <returns>
-        /// The amount of data actually returned. If this size matches the bufferSize parameter then
-        /// the data is valid. If the sizes do not match then the data is invalid, most likely due
-        /// to not having enough permissions to query for the data of that specific process
-        /// </returns>
-        [DllImport(Interop.Libraries.libproc, SetLastError = true)]
-        private static extern unsafe int proc_pidinfo(
-            int pid,
-            int flavor,
-            ulong arg,
-            proc_taskallinfo* buffer,
-            int bufferSize);
-
-        /// <summary>
-        /// Gets information about a process given it's PID
-        /// </summary>
-        /// <param name="pid">The PID of the process</param>
         /// <param name="flavor">Should be PROC_PIDTHREADINFO</param>
         /// <param name="arg">Flavor dependent value</param>
         /// <param name="buffer">A pointer to a block of memory (of size proc_threadinfo) allocated that will contain the data</param>
@@ -302,29 +219,6 @@ internal static partial class Interop
             int bufferSize);
 
         /// <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 proc_taskallinfo struct for valid processes that the caller
-        /// has permission to access; otherwise, returns null
-        /// </returns>
-        internal static unsafe proc_taskallinfo? GetProcessInfoById(int pid)
-        {
-            // Negative PIDs are invalid
-            if (pid < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(pid));
-            }
-
-            // Get the process information for the specified pid
-            int size = sizeof(proc_taskallinfo);
-            proc_taskallinfo info = default(proc_taskallinfo);
-            int result = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &info, size);
-            return (result == size ? new proc_taskallinfo?(info) : null);
-        }
-
-        /// <summary>
         /// Gets the thread information for the given thread
         /// </summary>
         /// <param name="pid">The process id.</param>
index 01d9af7..698a49c 100644 (file)
@@ -14,6 +14,8 @@
              Link="Common\Interop\Linux\cgroups\Interop.cgroups.cs" />
     <Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.cs"
              Link="Common\Interop\Linux\procfs\Interop.ProcFsStat.cs" />
+    <Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs"
+             Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
     <Compile Include="$(CommonPath)System\CharArrayHelpers.cs"
              Link="Common\System\CharArrayHelpers.cs" />
     <Compile Include="$(CommonPath)System\Marvin.cs"
     <Folder Include="System\Net\Sockets\" />
     <Folder Include="System\Net\VirtualNetwork\" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
index 46f663f..fcfeef1 100644 (file)
              Link="Common\Interop\Linux\Interop.cgroups.cs" />
     <Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.cs"
              Link="Common\Interop\Linux\Interop.ProcFsStat.cs" />
+    <Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs"
+             Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SchedGetSetAffinity.cs"
              Link="Common\Interop\Linux\Interop.SchedGetSetAffinity.cs" />
     <Compile Include="$(CommonPath)System\Text\ReusableTextReader.cs"
     <Compile Include="System\Diagnostics\ProcessThread.OSX.cs" />
     <Compile Include="$(CommonPath)Interop\OSX\Interop.libproc.cs"
              Link="Common\Interop\OSX\Interop.libproc.cs" />
+    <Compile Include="$(CommonPath)Interop\OSX\Interop.libproc.GetProcessInfoById.cs"
+             Link="Common\Interop\OSX\Interop.libproc.GetProcessInfoById.cs" />
     <Compile Include="$(CommonPath)Interop\OSX\Interop.Libraries.cs"
              Link="Common\Interop\OSX\Interop.Libraries.cs" />
   </ItemGroup>
              Link="Common\Interop\BSD\System.Native\Interop.Sysctl.cs" />
     <Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Process.cs"
              Link="Common\Interop\FreeBSD\Interop.Process.cs" />
+    <Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Process.GetProcInfo.cs"
+             Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Stat.cs"
              Link="Common\Unix\System.Native\Interop.Stat.cs" />
   </ItemGroup>
index 4cd55c0..0fd5380 100644 (file)
   <data name="InvalidOp_InvalidNewEnumVariant" xml:space="preserve">
     <value>The returned enumerator does not implement IEnumVARIANT.</value>
   </data>
+  <data name="InvalidSysctl" xml:space="preserve">
+    <value>sysctl {0} failed with {1} error.</value>
+  </data>
   <data name="Argument_StructArrayTooLarge" xml:space="preserve">
     <value>Array size exceeds addressing limitations.</value>
   </data>
index ad02631..28ab1fe 100644 (file)
     <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetPid.cs">
       <Link>Common\Interop\Unix\System.Native\Interop.GetPid.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Process.GetProcInfo.cs" Condition="'$(TargetsFreeBSD)' == 'true'"
+             Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
+    <Compile Include="$(CommonPath)Interop\BSD\System.Native\Interop.Sysctl.cs" Condition="'$(TargetsFreeBSD)' == 'true'"
+             Link="Common\Interop\BSD\System.Native\Interop.Sysctl.cs" />
+    <Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs" Condition="'$(TargetsLinux)' == 'true'"
+             Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
+    <Compile Include="$(CommonPath)System\Text\ReusableTextReader.cs" Condition="'$(TargetsLinux)' == 'true'"
+             Link="Common\System\Text\ReusableTextReader.cs" />
+    <Compile Include="$(CommonPath)System\IO\StringParser.cs" Condition="'$(TargetsLinux)' == 'true'"
+             Link="Common\System\IO\StringParser.cs" />
+    <Compile Include="$(CommonPath)Interop\OSX\Interop.libproc.GetProcessInfoById.cs" Condition="'$(IsOSXLike)' == 'true'"
+             Link="Common\Interop\OSX\Interop.libproc.GetProcessInfoById.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.FreeBSD.cs" Condition="'$(TargetsFreeBSD)' == 'true'" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Linux.cs" Condition="'$(TargetsLinux)' == 'true'" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.GetDisplayName.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.FreeBSD.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.FreeBSD.cs
new file mode 100644 (file)
index 0000000..e9e9178
--- /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.
+
+using System.Runtime.InteropServices;
+
+namespace System
+{
+    public static partial class Environment
+    {
+        public static unsafe long WorkingSet => Interop.Process.GetProcInfo(ProcessId, true, out _)->ki_rssize;
+    }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs
new file mode 100644 (file)
index 0000000..ea6eafe
--- /dev/null
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System
+{
+    public static partial class Environment
+    {
+        private static ReusableTextReader reusableReader = new ReusableTextReader();
+        public static long WorkingSet => (long)(Interop.procfs.TryReadStatusFile(ProcessId, out Interop.procfs.ParsedStatus status, reusableReader) ? status.VmRSS : 0);
+    }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OSX.cs
new file mode 100644 (file)
index 0000000..8206f95
--- /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.
+
+using System.Runtime.InteropServices;
+
+namespace System
+{
+    public static partial class Environment
+    {
+        public static long WorkingSet => (long)(Interop.libproc.GetProcessInfoById(ProcessId)?.ptinfo.pti_resident_size ?? 0);
+    }
+}
index 6ed2b34..97295b7 100644 (file)
@@ -3,7 +3,6 @@
 
 using System.Diagnostics;
 using System.IO;
-using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
@@ -25,25 +24,6 @@ namespace System
             }
         }
 
-        public static long WorkingSet
-        {
-            get
-            {
-                Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
-                if (processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) is IDisposable currentProcess)
-                {
-                    using (currentProcess)
-                    {
-                        if (processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null) is long result)
-                            return result;
-                    }
-                }
-
-                // Could not get the current working set.
-                return 0;
-            }
-        }
-
         public static unsafe string UserName
         {
             get