--- /dev/null
+// 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;
+ }
+ }
+}
{
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 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>
}
/// <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>
--- /dev/null
+// 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;
+ }
+ }
+ }
+}
{
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/";
//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;
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;
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))
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;
- }
- }
}
}
--- /dev/null
+// 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);
+ }
+ }
+}
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;
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
}
// 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
{
/// 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>
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>
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>
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>
<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>
<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" />
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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);
+ }
+}
using System.Diagnostics;
using System.IO;
-using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
}
}
- 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