From 64c33c493c50a5f805753697513eab4c636d7102 Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Sat, 6 Jun 2026 20:55:36 +0300
Subject: [PATCH 1/3] Implement Environment.WorkingSet on OpenBSD
---
.../src/System.Private.CoreLib.Shared.projitems | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 7e9355d26d52a0..bdc5cc85f3c975 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -2723,7 +2723,8 @@
Common\Interop\Unix\System.Native\Interop.GetSid.cs
-
+
+
@@ -2731,6 +2732,7 @@
+
From 7e5dfa1fb848c110bf0e9f9f4eb682db7482dbff Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Sat, 6 Jun 2026 20:56:46 +0300
Subject: [PATCH 2/3] .
---
.../OpenBSD/Interop.Process.GetProcInfo.cs | 202 ++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644 src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs
diff --git a/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs b/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs
new file mode 100644
index 00000000000000..0a7dae2df64c73
--- /dev/null
+++ b/src/libraries/Common/src/Interop/OpenBSD/Interop.Process.GetProcInfo.cs
@@ -0,0 +1,202 @@
+// 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.CompilerServices;
+using System.Runtime.InteropServices;
+
+#pragma warning disable CA1823 // unused fields are present to match the native struct layout
+
+internal static partial class Interop
+{
+ 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;
+
+ // Constants from sys/sysctl.h that determine the fixed-size members of kinfo_proc
+ private const int KI_NGROUPS = 16;
+ private const int KI_MAXCOMLEN = 24; // _MAXCOMLEN, includes NUL
+ private const int KI_WMESGLEN = 8;
+ private const int KI_MAXLOGNAME = 32;
+ private const int KI_EMULNAMELEN = 8;
+
+ // From sys/sysctl.h: "struct kinfo_proc". OpenBSD guarantees a stable binary
+ // layout for these members (new members are only appended to the end), and the
+ // members use 8-byte alignment, matching the natural alignment used here.
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct @kinfo_proc
+ {
+ private ulong p_forw; /* PTR: linked run/sleep queue. */
+ private ulong p_back;
+ private ulong p_paddr; /* PTR: address of proc */
+ private ulong p_addr; /* PTR: Kernel virtual addr of u-area */
+ private ulong p_fd; /* PTR: Ptr to open files structure. */
+ private ulong p_stats; /* unused, always zero. */
+ private ulong p_limit; /* PTR: Process limits. */
+ private ulong p_vmspace; /* PTR: Address space. */
+ private ulong p_sigacts; /* PTR: Signal actions, state */
+ private ulong p_sess; /* PTR: session pointer */
+ private ulong p_tsess; /* PTR: tty session pointer */
+ private ulong p_ru; /* PTR: Exit information. XXX */
+
+ private int p_eflag; /* LONG: extra kinfo_proc flags */
+ private int p_exitsig; /* unused, always zero. */
+ private int p_flag; /* INT: P_* flags. */
+
+ public int p_pid; /* PID_T: Process identifier. */
+ public int p_ppid; /* PID_T: Parent process id */
+ public int p_sid; /* PID_T: session id */
+ private int p__pgid; /* PID_T: process group id */
+ private int p_tpgid; /* PID_T: tty process group id */
+
+ public uint p_uid; /* UID_T: effective user id */
+ private uint p_ruid; /* UID_T: real user id */
+ public uint p_gid; /* GID_T: effective group id */
+ private uint p_rgid; /* GID_T: real group id */
+
+ private GroupsBuffer p_groups; /* GID_T: groups */
+ private short p_ngroups; /* SHORT: number of groups */
+
+ private short p_jobc; /* SHORT: job control counter */
+ private uint p_tdev; /* DEV_T: controlling tty dev */
+
+ private uint p_estcpu; /* U_INT: Time averaged value of p_cpticks. */
+ private uint p_rtime_sec; /* STRUCT TIMEVAL: Real time. */
+ private uint p_rtime_usec; /* STRUCT TIMEVAL: Real time. */
+ private int p_cpticks; /* INT: Ticks of cpu time. */
+ public uint p_pctcpu; /* FIXPT_T: %cpu for this process */
+ private uint p_swtime; /* unused, always zero */
+ private uint p_slptime; /* U_INT: Time since last blocked. */
+ private int p_schedflags; /* INT: PSCHED_* flags */
+
+ private ulong p_uticks; /* U_QUAD_T: Statclock hits in user mode. */
+ private ulong p_sticks; /* U_QUAD_T: Statclock hits in system mode. */
+ private ulong p_iticks; /* U_QUAD_T: Statclock hits processing intr. */
+
+ private ulong p_tracep; /* PTR: Trace to vnode or file */
+ private int p_traceflag; /* INT: Kernel trace points. */
+
+ private int p_holdcnt; /* INT: If non-zero, don't swap. */
+
+ private int p_siglist; /* INT: Signals arrived but not delivered. */
+ private uint p_sigmask; /* SIGSET_T: Current signal mask. */
+ private uint p_sigignore; /* SIGSET_T: Signals being ignored. */
+ private uint p_sigcatch; /* SIGSET_T: Signals being caught by user. */
+
+ private sbyte p_stat; /* CHAR: S* process status (from LWP). */
+ private byte p_priority; /* U_CHAR: Process priority. */
+ private byte p_usrpri; /* U_CHAR: User-priority. */
+ public byte p_nice; /* U_CHAR: Process "nice" value. */
+
+ private ushort p_xstat; /* U_SHORT: Exit status for wait; also stop signal. */
+ private ushort p_spare; /* U_SHORT: unused */
+
+ public fixed byte p_comm[KI_MAXCOMLEN]; /* command name */
+
+ private WmesgBuffer p_wmesg; /* wchan message */
+ private ulong p_wchan; /* PTR: sleep address. */
+
+ 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) */
+
+ private long p_uvalid; /* CHAR: following p_u* members are valid */
+ public ulong p_ustart_sec; /* STRUCT TIMEVAL: starting time. */
+ public uint p_ustart_usec; /* STRUCT TIMEVAL: starting time. */
+
+ public uint p_uutime_sec; /* STRUCT TIMEVAL: user time. */
+ public uint p_uutime_usec; /* STRUCT TIMEVAL: user time. */
+ 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. */
+ 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 ". */
+ private ulong p_uru_minflt; /* LONG: page reclaims. */
+ private ulong p_uru_majflt; /* LONG: page faults. */
+ private ulong p_uru_nswap; /* LONG: swaps. */
+ private ulong p_uru_inblock; /* LONG: block input operations. */
+ private ulong p_uru_oublock; /* LONG: block output operations. */
+ private ulong p_uru_msgsnd; /* LONG: messages sent. */
+ private ulong p_uru_msgrcv; /* LONG: messages received. */
+ private ulong p_uru_nsignals; /* LONG: signals received. */
+ private ulong p_uru_nvcsw; /* LONG: voluntary context switches. */
+ private ulong p_uru_nivcsw; /* LONG: involuntary ". */
+
+ private uint p_uctime_sec; /* STRUCT TIMEVAL: child u+s time. */
+ private uint p_uctime_usec; /* STRUCT TIMEVAL: child u+s time. */
+ private uint p_psflags; /* UINT: PS_* flags on the process. */
+ private uint p_acflag; /* UINT: Accounting flags. */
+ private uint p_svuid; /* UID_T: saved user id */
+ private uint p_svgid; /* GID_T: saved group id */
+ private EmulNameBuffer p_emul; /* syscall emulation name */
+ private ulong p_rlim_rss_cur; /* RLIM_T: soft limit for rss */
+ private ulong p_cpuid; /* LONG: CPU id */
+ public ulong p_vm_map_size; /* VSIZE_T: virtual size */
+ public int p_tid; /* PID_T: Thread identifier. */
+ private uint p_rtableid; /* U_INT: Routing table identifier. */
+
+ private ulong p_pledge; /* U_INT64_T: Pledge flags. */
+ private NameBuffer p_name; /* thread name */
+
+ [InlineArray(KI_NGROUPS)]
+ private struct GroupsBuffer
+ {
+ private uint _element0;
+ }
+
+ [InlineArray(KI_WMESGLEN)]
+ private struct WmesgBuffer
+ {
+ private byte _element0;
+ }
+
+ [InlineArray(KI_MAXLOGNAME)]
+ private struct LoginBuffer
+ {
+ private byte _element0;
+ }
+
+ [InlineArray(KI_EMULNAMELEN)]
+ private struct EmulNameBuffer
+ {
+ private byte _element0;
+ }
+
+ [InlineArray(KI_MAXCOMLEN)]
+ private struct NameBuffer
+ {
+ private byte _element0;
+ }
+ }
+
+ ///
+ /// Gets information about a single process by its PID.
+ ///
+ /// The PID of the process.
+ /// The number of kinfo_proc entries returned.
+ public static unsafe kinfo_proc* GetProcInfo(int pid, 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];
+
+ byte* pBuffer = null;
+ uint bytesLength = 0;
+ Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength);
+
+ count = (int)(bytesLength / sizeof(kinfo_proc));
+
+ // Buffer ownership transferred to the caller
+ return (kinfo_proc*)pBuffer;
+ }
+ }
+}
From 2d495a5112b2d0a97999b50e80702c2158d2b433 Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Sat, 6 Jun 2026 20:57:28 +0300
Subject: [PATCH 3/3] .
---
.../src/System/Environment.OpenBSD.cs | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs
new file mode 100644
index 00000000000000..73c747bdfde9f6
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.OpenBSD.cs
@@ -0,0 +1,27 @@
+// 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
+ {
+ get
+ {
+ Interop.Process.kinfo_proc* processInfo = Interop.Process.GetProcInfo(ProcessId, out int count);
+ try
+ {
+ // p_vm_rssize is the current resident set size in pages.
+ return count >= 1 ? (long)processInfo->p_vm_rssize * SystemPageSize : 0;
+ }
+ finally
+ {
+ NativeMemory.Free(processInfo);
+ }
+ }
+ }
+ }
+}