diff --git a/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs b/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs
index 0a7dae2df64c73..e62c4e29f8d083 100644
--- a/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs
+++ b/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs
@@ -14,7 +14,11 @@ internal static partial class Process
// Constants from sys/sysctl.h
private const int CTL_KERN = 1;
private const int KERN_PROC = 66;
- private const int KERN_PROC_PID = 1;
+ private const int KERN_PROC_ALL = 0; // everything but kernel threads
+ private const int KERN_PROC_PID = 1; // by process id
+ private const int KERN_PROC_SHOW_THREADS = unchecked((int)0x40000000); // also return threads
+ private const int KERN_PROC_ARGS = 55; // node: proc args and env
+ private const int KERN_PROC_ARGV = 1; // KERN_PROC_ARGS subtype: argv
// Constants from sys/sysctl.h that determine the fixed-size members of kinfo_proc
private const int KI_NGROUPS = 16;
@@ -102,9 +106,9 @@ public unsafe struct @kinfo_proc
private LoginBuffer p_login; /* setlogin() name */
public int p_vm_rssize; /* SEGSZ_T: current resident set size in pages */
- private int p_vm_tsize; /* SEGSZ_T: text size (pages) */
- private int p_vm_dsize; /* SEGSZ_T: data size (pages) */
- private int p_vm_ssize; /* SEGSZ_T: stack size (pages) */
+ public int p_vm_tsize; /* SEGSZ_T: text size (pages) */
+ public int p_vm_dsize; /* SEGSZ_T: data size (pages) */
+ public int p_vm_ssize; /* SEGSZ_T: stack size (pages) */
private long p_uvalid; /* CHAR: following p_u* members are valid */
public ulong p_ustart_sec; /* STRUCT TIMEVAL: starting time. */
@@ -115,7 +119,7 @@ public unsafe struct @kinfo_proc
public uint p_ustime_sec; /* STRUCT TIMEVAL: system time. */
public uint p_ustime_usec; /* STRUCT TIMEVAL: system time. */
- private ulong p_uru_maxrss; /* LONG: max resident set size. */
+ public ulong p_uru_maxrss; /* LONG: max resident set size (kilobytes). */
private ulong p_uru_ixrss; /* LONG: integral shared memory size. */
private ulong p_uru_idrss; /* LONG: integral unshared data ". */
private ulong p_uru_isrss; /* LONG: integral unshared stack ". */
@@ -178,22 +182,29 @@ private struct NameBuffer
}
///
- /// Gets information about a single process by its PID.
+ /// Gets information about processes.
///
- /// The PID of the process.
+ /// The PID of the process to query, or 0 to enumerate all processes.
+ /// When querying a single process, also return its threads.
/// The number of kinfo_proc entries returned.
- public static unsafe kinfo_proc* GetProcInfo(int pid, out int count)
+ public static unsafe kinfo_proc* GetProcInfo(int pid, bool threads, out int count)
{
// OpenBSD's KERN_PROC sysctl mib carries the element size and count inline:
- // { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof(kinfo_proc), elem_count }.
- // A single PID returns at most one entry, so request a count of one.
- ReadOnlySpan sysctlName = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof(kinfo_proc), 1];
+ // { CTL_KERN, KERN_PROC, op, arg, sizeof(kinfo_proc), elem_count }.
+ int op = pid == 0
+ ? KERN_PROC_ALL
+ : KERN_PROC_PID | (threads ? KERN_PROC_SHOW_THREADS : 0);
+ int arg = pid == 0 ? 0 : pid;
+
+ // The kernel bounds the result by the supplied buffer size, so request the
+ // maximum element count and let Sysctl probe, allocate, and grow the buffer.
+ ReadOnlySpan sysctlName = [CTL_KERN, KERN_PROC, op, arg, sizeof(kinfo_proc), int.MaxValue];
byte* pBuffer = null;
uint bytesLength = 0;
Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength);
- count = (int)(bytesLength / sizeof(kinfo_proc));
+ count = (int)(bytesLength / (uint)sizeof(kinfo_proc));
// Buffer ownership transferred to the caller
return (kinfo_proc*)pBuffer;
diff --git a/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.cs b/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.cs
new file mode 100644
index 00000000000000..9d508875c027fd
--- /dev/null
+++ b/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.cs
@@ -0,0 +1,283 @@
+// 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.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+
+#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
+ {
+ private const ulong SecondsToNanoseconds = 1000000000;
+ private const ulong MicroSecondsToNanoSeconds = 1000;
+
+ internal struct proc_stats
+ {
+ internal long startTime;
+ internal int nice;
+ internal ulong userTime; /* in ticks */
+ internal ulong systemTime; /* in ticks */
+ }
+
+ ///
+ /// Queries the OS for the list of all running processes and returns the PID for each
+ ///
+ /// Returns a list of PIDs corresponding to all running processes
+ internal static unsafe int[] ListAllPids()
+ {
+ kinfo_proc* entries = GetProcInfo(0, false, out int numProcesses);
+ try
+ {
+ if (numProcesses <= 0)
+ {
+ throw new Win32Exception(SR.CantGetAllPids);
+ }
+
+ var list = new ReadOnlySpan(entries, numProcesses);
+ var pids = new int[numProcesses];
+
+ // walk through process list and skip kernel threads
+ int idx = 0;
+ for (int i = 0; i < list.Length; i++)
+ {
+ if (list[i].p_ppid == 0)
+ {
+ // skip kernel threads
+ numProcesses -= 1;
+ }
+ else
+ {
+ pids[idx] = list[i].p_pid;
+ idx += 1;
+ }
+ }
+
+ // Remove extra elements
+ Array.Resize(ref pids, numProcesses);
+ return pids;
+ }
+ finally
+ {
+ NativeMemory.Free(entries);
+ }
+ }
+
+ ///
+ /// Gets executable name for process given it's PID
+ ///
+ /// The PID of the process
+ public static unsafe string GetProcPath(int pid)
+ {
+ // OpenBSD has no KERN_PROC_PATHNAME. The closest available information is the
+ // process argv, whose first entry is the path the process was executed with.
+ ReadOnlySpan sysctlName = [CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV];
+
+ byte* pBuffer = null;
+ uint bytesLength = 0;
+ try
+ {
+ Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength);
+
+ if (pBuffer == null || bytesLength < (uint)sizeof(byte*))
+ {
+ return string.Empty;
+ }
+
+ // The kernel relocates the argv pointer array to point within the returned buffer.
+ byte* argv0 = ((byte**)pBuffer)[0];
+ return argv0 is null ? string.Empty : Utf8StringMarshaller.ConvertToManaged(argv0) ?? string.Empty;
+ }
+ finally
+ {
+ NativeMemory.Free(pBuffer);
+ }
+ }
+
+ ///
+ /// Attempts to recover a process name that was truncated in kinfo_proc.p_comm by reading
+ /// the full name from the process argv.
+ ///
+ /// The PID of the process.
+ /// The (possibly truncated) p_comm value used to validate the recovered name.
+ /// The full process name, or null if it could not be recovered.
+ private static unsafe string? GetUntruncatedProcessName(int pid, string prefix)
+ {
+ ReadOnlySpan sysctlName = [CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV];
+
+ byte* pBuffer = null;
+ uint bytesLength = 0;
+ try
+ {
+ Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength);
+
+ if (pBuffer == null || bytesLength < (uint)sizeof(byte*))
+ {
+ return null;
+ }
+
+ // The kernel relocates the argv pointer array to point within the returned buffer.
+ // For native executables the name is argv[0]; for scripts argv[0] is the interpreter
+ // and argv[1] is the script, so check the first two NULL-terminated arguments.
+ byte** argv = (byte**)pBuffer;
+ for (int i = 0; i < 2 && argv[i] is not null; i++)
+ {
+ string arg = Utf8StringMarshaller.ConvertToManaged(argv[i]) ?? string.Empty;
+
+ // Strip directory names.
+ int nameStart = arg.LastIndexOf('/') + 1;
+ string name = nameStart == 0 ? arg : arg.Substring(nameStart);
+
+ if (name.StartsWith(prefix, StringComparison.Ordinal))
+ {
+ return name;
+ }
+ }
+
+ return null;
+ }
+ finally
+ {
+ NativeMemory.Free(pBuffer);
+ }
+ }
+
+ ///
+ /// Gets the process information for a given process
+ ///
+ /// The PID (process ID) of the process
+ ///
+ /// Returns a valid ProcessInfo struct for valid processes that the caller
+ /// has permission to access; otherwise, returns null
+ ///
+ public static unsafe ProcessInfo? GetProcessInfoById(int pid)
+ {
+ // Negative PIDs are invalid
+ ArgumentOutOfRangeException.ThrowIfNegative(pid);
+
+ ProcessInfo info;
+
+ kinfo_proc* kinfo = GetProcInfo(pid, true, out int count);
+ try
+ {
+ // The process may have exited between the time its PID was enumerated and now,
+ // in which case no entries are returned. Report it as not found rather than failing.
+ if (count < 1)
+ {
+ return null;
+ }
+
+ var process = new ReadOnlySpan(kinfo, count);
+
+ // Get the process information for the specified pid
+ info = new ProcessInfo();
+
+ info.ProcessName = Utf8StringMarshaller.ConvertToManaged(kinfo->p_comm)!;
+
+ // p_comm is limited to KI_MAXCOMLEN - 1 characters. When the name is at that
+ // limit it may be truncated, so try to recover the full name from the process argv.
+ if (info.ProcessName.Length >= KI_MAXCOMLEN - 1)
+ {
+ info.ProcessName = GetUntruncatedProcessName(pid, info.ProcessName) ?? info.ProcessName;
+ }
+
+ info.BasePriority = kinfo->p_nice;
+
+ // OpenBSD's KERN_PROC sysctl always reports p_vm_map_size as 0, so derive the
+ // virtual size from the text, data, and stack segment sizes instead.
+ long pageSize = Environment.SystemPageSize;
+ info.VirtualBytes = ((long)kinfo->p_vm_tsize + kinfo->p_vm_dsize + kinfo->p_vm_ssize) * pageSize;
+ // OpenBSD does not track a separate peak virtual size; report the current size.
+ info.VirtualBytesPeak = info.VirtualBytes;
+ info.WorkingSet = kinfo->p_vm_rssize;
+ // p_uru_maxrss is the peak resident set size, reported in kilobytes.
+ info.WorkingSetPeak = (long)kinfo->p_uru_maxrss * 1024;
+ // OpenBSD does not expose a private byte count; approximate it with the
+ // anonymous (data + stack) segment sizes.
+ info.PrivateBytes = ((long)kinfo->p_vm_dsize + kinfo->p_vm_ssize) * pageSize;
+ info.SessionId = kinfo->p_sid;
+
+ for (int i = 0; i < process.Length; i++)
+ {
+ // KERN_PROC_SHOW_THREADS returns a process-summary entry with p_tid == -1
+ // ahead of the real per-thread entries. Skip it so only actual threads are reported.
+ if (process[i].p_tid < 0)
+ {
+ continue;
+ }
+
+ var ti = new ThreadInfo()
+ {
+ _processId = pid,
+ _threadId = (ulong)process[i].p_tid,
+ _basePriority = process[i].p_nice,
+ _startAddress = null // OpenBSD's kinfo_proc does not expose a thread start address.
+ };
+ info._threadInfoList.Add(ti);
+ }
+ }
+ finally
+ {
+ NativeMemory.Free(kinfo);
+ }
+
+ return info;
+ }
+
+ ///
+ /// Gets the process information for a given process
+ ///
+ /// The PID (process ID) of the process
+ /// The TID (thread ID) of the process
+ ///
+ /// Returns basic info about thread. If tid is 0, it will return
+ /// info for process e.g. main thread.
+ ///
+ public static unsafe proc_stats GetThreadInfo(int pid, int tid)
+ {
+ proc_stats ret = default;
+ int count;
+
+ kinfo_proc* info = GetProcInfo(pid, (tid != 0), out count);
+ try
+ {
+ if (info != null && count >= 1)
+ {
+ if (tid == 0)
+ {
+ ret.startTime = (int)info->p_ustart_sec;
+ ret.nice = info->p_nice;
+ ret.userTime = (ulong)info->p_uutime_sec * SecondsToNanoseconds + (ulong)info->p_uutime_usec * MicroSecondsToNanoSeconds;
+ ret.systemTime = (ulong)info->p_ustime_sec * SecondsToNanoseconds + (ulong)info->p_ustime_usec * MicroSecondsToNanoSeconds;
+ }
+ else
+ {
+ var list = new ReadOnlySpan(info, count);
+ for (int i = 0; i < list.Length; i++)
+ {
+ if (list[i].p_tid == tid)
+ {
+ ret.startTime = (int)list[i].p_ustart_sec;
+ ret.nice = list[i].p_nice;
+ ret.userTime = (ulong)list[i].p_uutime_sec * SecondsToNanoseconds + (ulong)list[i].p_uutime_usec * MicroSecondsToNanoSeconds;
+ ret.systemTime = (ulong)list[i].p_ustime_sec * SecondsToNanoseconds + (ulong)list[i].p_ustime_usec * MicroSecondsToNanoSeconds;
+ break;
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ NativeMemory.Free(info);
+ }
+
+ return ret;
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
index a1648c1da6d8b2..7e352915ac39e3 100644
--- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
@@ -43,6 +43,7 @@ public static partial class PlatformDetection
public static bool IsNotCoreClrInterpreter => !IsCoreClrInterpreter;
public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"));
public static bool IsNetBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD"));
+ public static bool IsOpenBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD"));
public static bool IsAndroid => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID"));
public static bool IsNotAndroid => !IsAndroid;
public static bool IsAndroidX86 => IsAndroid && IsX86Process;
@@ -64,7 +65,7 @@ public static partial class PlatformDetection
public static bool IsAppleMobile => IsMacCatalyst || IsiOS || IstvOS;
public static bool IsNotAppleMobile => !IsAppleMobile;
public static bool IsNotNetFramework => !IsNetFramework;
- public static bool IsBsdLike => IsApplePlatform || IsFreeBSD || IsNetBSD;
+ public static bool IsBsdLike => IsApplePlatform || IsFreeBSD || IsNetBSD || IsOpenBSD;
public static bool IsArmProcess => RuntimeInformation.ProcessArchitecture == Architecture.Arm;
public static bool IsNotArmProcess => !IsArmProcess;
diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
index bc1e3b89bb10de..56cfade1d31a1c 100644
--- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
+++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)
+ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-openbsd;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)
$(DefineConstants);FEATURE_REGISTRY
true
false
@@ -392,6 +392,20 @@
Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs
new file mode 100644
index 00000000000000..f3ccc9cb35b613
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs
@@ -0,0 +1,134 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+namespace System.Diagnostics
+{
+ public partial class Process
+ {
+ /// Gets the time the associated process was started.
+ internal DateTime StartTimeCore
+ {
+ get
+ {
+ EnsureState(State.HaveNonExitedId);
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+
+ return new DateTime(DateTime.UnixEpoch.Ticks + (stat.startTime * TimeSpan.TicksPerSecond)).ToLocalTime();
+ }
+ }
+
+ ///
+ /// Gets the amount of time the associated process has spent utilizing the CPU.
+ /// It is the sum of the and
+ /// .
+ ///
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
+ public TimeSpan TotalProcessorTime
+ {
+ get
+ {
+ if (IsCurrentProcess)
+ {
+ return Environment.CpuUsage.TotalTime;
+ }
+
+ EnsureState(State.HaveNonExitedId);
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+ return Process.TicksToTimeSpan(stat.userTime + stat.systemTime);
+ }
+ }
+
+ ///
+ /// Gets the amount of time the associated process has spent running code
+ /// inside the application portion of the process (not the operating system core).
+ ///
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
+ public TimeSpan UserProcessorTime
+ {
+ get
+ {
+ if (IsCurrentProcess)
+ {
+ return Environment.CpuUsage.UserTime;
+ }
+
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+ return Process.TicksToTimeSpan(stat.userTime);
+ }
+ }
+
+ /// Gets the amount of time the process has spent running code inside the operating system core.
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
+ public TimeSpan PrivilegedProcessorTime
+ {
+ get
+ {
+ if (IsCurrentProcess)
+ {
+ return Environment.CpuUsage.PrivilegedTime;
+ }
+
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0);
+ return Process.TicksToTimeSpan(stat.systemTime);
+ }
+ }
+
+ /// Gets parent process ID
+ private unsafe int ParentProcessId
+ {
+ get
+ {
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.Process.kinfo_proc* processInfo = Interop.Process.GetProcInfo(_processId, false, out int count);
+ try
+ {
+ if (count <= 0)
+ {
+ throw new Win32Exception(SR.ProcessInformationUnavailable);
+ }
+
+ return processInfo->p_ppid;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal((IntPtr)processInfo);
+ }
+ }
+ }
+
+ // Gets execution path
+ internal static string? GetPathToOpenFile()
+ {
+ if (Interop.Sys.Stat("/usr/local/bin/open", out _) == 0)
+ {
+ return "/usr/local/bin/open";
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ // ----------------------------------
+ // ---- Unix PAL layer ends here ----
+ // ----------------------------------
+
+
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OpenBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OpenBSD.cs
new file mode 100644
index 00000000000000..19e3933ff2e9f3
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OpenBSD.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace System.Diagnostics
+{
+ internal static partial class ProcessManager
+ {
+ /// Gets the IDs of all processes on the current machine.
+ public static int[] GetProcessIds()
+ {
+ return Interop.Process.ListAllPids();
+ }
+
+ internal static string GetProcPath(int processId)
+ {
+ return Interop.Process.GetProcPath(processId);
+ }
+
+ internal static string? GetProcessName(int processId, string _ /* machineName */, bool __ /* isRemoteMachine */, ref ProcessInfo? processInfo)
+ {
+ if (processInfo is not null)
+ {
+ return processInfo.ProcessName;
+ }
+
+ processInfo = CreateProcessInfo(processId);
+ return processInfo?.ProcessName;
+ }
+
+ internal static ProcessInfo? CreateProcessInfo(int pid, string? processNameFilter = null)
+ {
+ // Negative PIDs aren't valid
+ ArgumentOutOfRangeException.ThrowIfNegative(pid);
+
+ // Try to get the task info. This can fail if the user permissions don't permit
+ // this user context to query the specified process, or if the process has exited.
+ ProcessInfo? iinfo = Interop.Process.GetProcessInfoById(pid);
+ if (iinfo is null)
+ {
+ return null;
+ }
+
+ if (processNameFilter != null && !processNameFilter.Equals(iinfo.ProcessName, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ ProcessInfo procInfo = new ProcessInfo()
+ {
+ ProcessId = pid,
+ ProcessName = iinfo.ProcessName,
+ BasePriority = iinfo.BasePriority,
+ VirtualBytes = iinfo.VirtualBytes,
+ VirtualBytesPeak = iinfo.VirtualBytesPeak,
+ WorkingSet = iinfo.WorkingSet,
+ WorkingSetPeak = iinfo.WorkingSetPeak,
+ PrivateBytes = iinfo.PrivateBytes,
+ SessionId = iinfo.SessionId,
+ };
+
+ foreach (ThreadInfo ti in iinfo._threadInfoList)
+ {
+ procInfo._threadInfoList.Add(ti);
+ }
+
+ return procInfo;
+ }
+
+ // ----------------------------------
+ // ---- Unix PAL layer ends here ----
+ // ----------------------------------
+
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.OpenBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.OpenBSD.cs
new file mode 100644
index 00000000000000..61e63fcc0050f7
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.OpenBSD.cs
@@ -0,0 +1,82 @@
+// 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.Versioning;
+
+namespace System.Diagnostics
+{
+ public partial class ProcessThread
+ {
+ ///
+ /// Returns or sets the priority level of the associated thread. The priority level is
+ /// not an absolute level, but instead contributes to the actual thread priority by
+ /// considering the priority class of the process.
+ ///
+ private ThreadPriorityLevel PriorityLevelCore
+ {
+ get
+ {
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+ return Interop.Sys.GetThreadPriorityFromNiceValue((int)stat.nice);
+ }
+ set
+ {
+ throw new PlatformNotSupportedException(); // OpenBSD does not support thread priority scheduling
+ }
+ }
+
+ // kinfo_proc has one entry per thread but p_ustart_sec is the same for
+ // all threads e.g. reflects process start.
+ private static DateTime GetStartTime() => throw new PlatformNotSupportedException();
+
+ ///
+ /// Returns the amount of time the associated thread has spent utilizing the CPU.
+ /// It is the sum of the System.Diagnostics.ProcessThread.UserProcessorTime and
+ /// System.Diagnostics.ProcessThread.PrivilegedProcessorTime.
+ ///
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
+ public TimeSpan TotalProcessorTime
+ {
+ get
+ {
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+ return Process.TicksToTimeSpan(stat.userTime + stat.systemTime);
+ }
+ }
+
+ ///
+ /// Returns the amount of time the associated thread has spent running code
+ /// inside the application (not the operating system core).
+ ///
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
+ public TimeSpan UserProcessorTime
+ {
+ get
+ {
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+ return Process.TicksToTimeSpan(stat.userTime);
+ }
+ }
+
+ ///
+ /// Returns the amount of time the thread has spent running code inside the operating
+ /// system core.
+ ///
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
+ public TimeSpan PrivilegedProcessorTime
+ {
+ get
+ {
+ Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id);
+ return Process.TicksToTimeSpan(stat.systemTime);
+ }
+
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
index 89d6c78ec7d025..a8f25b6f282d98 100644
--- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
+++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
@@ -887,7 +887,7 @@ public void TestMaxWorkingSet()
Assert.InRange((long)p.MinWorkingSet, 0, long.MaxValue);
}
- if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD() || PlatformDetection.IsSunOS) {
+ if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD() || PlatformDetection.IsOpenBSD || PlatformDetection.IsSunOS) {
return; // doesn't support getting/setting working set for other processes
}
@@ -935,7 +935,7 @@ public void TestMinWorkingSet()
Assert.InRange((long)p.MinWorkingSet, 0, long.MaxValue);
}
- if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD() || PlatformDetection.IsSunOS) {
+ if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD() || PlatformDetection.IsOpenBSD || PlatformDetection.IsSunOS) {
return; // doesn't support getting/setting working set for other processes
}
@@ -1299,7 +1299,7 @@ public void ExitTime_GetNotStarted_ThrowsInvalidOperationException()
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
- [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "getting/setting affinity not supported on OSX and BSD")]
+ [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD | TestPlatforms.OpenBSD, "getting/setting affinity not supported on OSX and BSD")]
public void TestProcessorAffinity()
{
CreateDefaultProcess();
diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs
index 0f9965fb8b9b24..57b24854832bd1 100644
--- a/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs
+++ b/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs
@@ -92,7 +92,7 @@ public void ThreadsAreDisposedWhenProcessIsDisposed()
}
[Fact]
- [PlatformSpecific(TestPlatforms.OSX|TestPlatforms.FreeBSD)] // OSX and FreeBSD throw PNSE from StartTime
+ [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.FreeBSD | TestPlatforms.OpenBSD)] // these platforms throw PNSE from StartTime
public void TestStartTimeProperty_OSX()
{
using (Process p = Process.GetCurrentProcess())
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs
index 73c747bdfde9f6..96a0a494202bfc 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs
@@ -11,7 +11,7 @@ public static unsafe long WorkingSet
{
get
{
- Interop.Process.kinfo_proc* processInfo = Interop.Process.GetProcInfo(ProcessId, out int count);
+ Interop.Process.kinfo_proc* processInfo = Interop.Process.GetProcInfo(ProcessId, false, out int count);
try
{
// p_vm_rssize is the current resident set size in pages.