diff --git a/src/libraries/Common/src/Interop/Haiku/Interop.Image.cs b/src/libraries/Common/src/Interop/Haiku/Interop.Image.cs
new file mode 100644
index 00000000000000..8394eb2f4c36bf
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Haiku/Interop.Image.cs
@@ -0,0 +1,42 @@
+// 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;
+
+#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 Image
+ {
+ internal const int MAXPATHLEN = 1024;
+
+ internal enum ImageType : int
+ {
+ B_APP_IMAGE = 1,
+ B_LIBRARY_IMAGE,
+ B_ADD_ON_IMAGE,
+ B_SYSTEM_IMAGE,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct ImageInfo
+ {
+ public ImageType type;
+ public fixed byte name[MAXPATHLEN];
+ public void* text;
+ public int text_size;
+ public int data_size;
+ }
+
+ ///
+ /// Gets information about images owned by a team.
+ ///
+ /// The team ID to iterate.
+ /// A cookie to track the iteration.
+ /// The structure to fill in.
+ /// Returns 0 on success. Returns an error code on failure or when there are no more images to iterate.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNextImageInfo")]
+ internal static partial int GetNextImageInfo(int team, ref int cookie, out ImageInfo info);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Haiku/Interop.OS.cs b/src/libraries/Common/src/Interop/Haiku/Interop.OS.cs
index 3246a7c45bd0cd..29dd28652fbfe1 100644
--- a/src/libraries/Common/src/Interop/Haiku/Interop.OS.cs
+++ b/src/libraries/Common/src/Interop/Haiku/Interop.OS.cs
@@ -1,28 +1,166 @@
// 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 OS
{
+ internal const int B_OS_NAME_LENGTH = 32;
+
[StructLayout(LayoutKind.Sequential)]
- internal unsafe struct AreaInfo
+ internal struct AreaInfo
{
public nuint size;
public uint ram_size;
}
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct TeamInfo
+ {
+ public int team;
+ public int session_id;
+ public int parent;
+ public fixed byte name[B_OS_NAME_LENGTH];
+ public long start_time;
+ }
+
+ internal enum BTeamUsage : int
+ {
+ B_TEAM_USAGE_SELF = 0,
+ B_TEAM_USAGE_CHILDREN = -1,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct TeamUsageInfo
+ {
+ public long user_time;
+ public long kernel_time;
+ }
+
+ internal enum ThreadState : int
+ {
+ B_THREAD_RUNNING = 1,
+ B_THREAD_READY,
+ B_THREAD_RECEIVING,
+ B_THREAD_ASLEEP,
+ B_THREAD_SUSPENDED,
+ B_THREAD_WAITING,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ThreadInfo
+ {
+ public int thread;
+ public int team;
+ public ThreadState state;
+ public int priority;
+ public long user_time;
+ public long kernel_time;
+ }
+
+ internal enum BPriority : int
+ {
+ B_IDLE_PRIORITY = 0,
+ B_LOWEST_ACTIVE_PRIORITY = 1,
+ B_LOW_PRIORITY = 5,
+ B_NORMAL_PRIORITY = 10,
+ B_DISPLAY_PRIORITY = 15,
+ B_URGENT_DISPLAY_PRIORITY = 20,
+ B_REAL_TIME_DISPLAY_PRIORITY = 100,
+ B_URGENT_PRIORITY = 110,
+ B_REAL_TIME_PRIORITY = 120,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct SystemInfo
+ {
+ public long boot_time;
+ }
+
///
/// Gets information about areas owned by a team.
///
/// The team ID of the areas to iterate.
/// A cookie to track the iteration.
- /// The structure to fill in.
+ /// The structure to fill in.
/// Returns 0 on success. Returns an error code on failure or when there are no more areas to iterate.
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNextAreaInfo")]
- internal static unsafe partial int GetNextAreaInfo(int team, ref nint cookie, out AreaInfo areaInfo);
+ internal static partial int GetNextAreaInfo(int team, ref nint cookie, out AreaInfo info);
+
+ ///
+ /// Gets information about a team.
+ ///
+ /// The team ID.
+ /// The structure to fill in.
+ /// Returns 0 on success or an error code on failure.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetTeamInfo")]
+ internal static partial int GetTeamInfo(int team, out TeamInfo info);
+
+ ///
+ /// Gets information about teams.
+ ///
+ /// A cookie to track the iteration.
+ /// The structure to fill in.
+ /// Returns 0 on success. Returns an error code on failure or when there are no more teams to iterate.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNextTeamInfo")]
+ internal static partial int GetNextTeamInfo(ref int cookie, out TeamInfo info);
+
+ ///
+ /// Gets team IDs.
+ ///
+ /// A cookie to track the iteration.
+ /// The integer to store the retrieved team ID.
+ /// Returns 0 on success. Returns an error code on failure or when there are no more teams to iterate.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNextTeamId")]
+ internal static partial int GetNextTeamId(ref int cookie, out int team);
+
+ ///
+ /// Gets information about a team's usage.
+ ///
+ /// The team ID.
+ /// Specifies whether to get usage information for the team or its children.
+ /// The structure to fill in.
+ /// Returns 0 on success or an error code on failure.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetTeamUsageInfo")]
+ internal static partial int GetTeamUsageInfo(int team, BTeamUsage who, out TeamUsageInfo info);
+
+ ///
+ /// Sets the priority of a thread.
+ ///
+ /// The thread ID.
+ /// The new priority.
+ /// The previous priority if successful or an error code on failure.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetThreadPriority")]
+ internal static partial int SetThreadPriority(int thread, int newPriority);
+
+ ///
+ /// Gets information about a thread.
+ ///
+ /// The thread ID.
+ /// The structure to fill in.
+ /// Returns 0 on success or an error code on failure.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetThreadInfo")]
+ internal static partial int GetThreadInfo(int thread, out ThreadInfo info);
+
+ ///
+ /// Gets information about threads owned by a team.
+ ///
+ /// The team ID of the threads to iterate.
+ /// A cookie to track the iteration.
+ /// The structure to fill in.
+ /// Returns 0 on success. Returns an error code on failure or when there are no more threads to iterate.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNextThreadInfo")]
+ internal static partial int GetNextThreadInfo(int team, ref int cookie, out ThreadInfo info);
+
+ ///
+ /// Gets information about the system.
+ ///
+ /// The system info to store retrieved information.
+ /// 0 if successful.
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetSystemInfo")]
+ internal static partial int GetSystemInfo(out SystemInfo info);
}
}
diff --git a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs
index 0907c2dc7b69f1..06bd962d02b7a2 100644
--- a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs
+++ b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs
@@ -369,7 +369,7 @@ internal ProcessThread() { }
public int Id { get { throw null; } }
public int IdealProcessor { set { } }
public bool PriorityBoostEnabled { get { throw null; } set { } }
- public System.Diagnostics.ThreadPriorityLevel PriorityLevel { [System.Runtime.Versioning.SupportedOSPlatform("windows")] [System.Runtime.Versioning.SupportedOSPlatform("linux")] [System.Runtime.Versioning.SupportedOSPlatform("freebsd")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } }
+ public System.Diagnostics.ThreadPriorityLevel PriorityLevel { [System.Runtime.Versioning.SupportedOSPlatform("windows")] [System.Runtime.Versioning.SupportedOSPlatform("linux")] [System.Runtime.Versioning.SupportedOSPlatform("freebsd")] [System.Runtime.Versioning.SupportedOSPlatform("haiku")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] [System.Runtime.Versioning.SupportedOSPlatformAttribute("haiku")] set { } }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
[System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] // this needs to come after the ios attribute due to limitations in the platform analyzer
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..51fca4bcbbf052 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)-linux;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)-haiku;$(NetCoreAppCurrent)
$(DefineConstants);FEATURE_REGISTRY
true
false
@@ -403,12 +403,26 @@
Link="Common\Interop\SunOS\procfs\Interop.ProcFs.GetThreadInfoById.cs" />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Haiku.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Haiku.cs
new file mode 100644
index 00000000000000..789c7bb41063e1
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Haiku.cs
@@ -0,0 +1,196 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.ComponentModel;
+using System.Runtime.Versioning;
+using System.Threading;
+
+namespace System.Diagnostics
+{
+ public partial class Process
+ {
+ // bigtime_t represents microseconds while DateTime.Ticks is in 100ns units
+ private const int BigTimeToTicks = 10;
+
+ private static long s_bootTimeTicks;
+ /// Gets the system boot time.
+ private static DateTime BootTime
+ {
+ get
+ {
+ long bootTimeTicks = Interlocked.Read(ref s_bootTimeTicks);
+ if (bootTimeTicks == 0)
+ {
+ Interop.OS.SystemInfo info;
+ int status = Interop.OS.GetSystemInfo(out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ bootTimeTicks = info.boot_time * BigTimeToTicks;
+ long oldValue = Interlocked.CompareExchange(ref s_bootTimeTicks, bootTimeTicks, 0);
+ if (oldValue != 0) // a different thread has managed to update the ticks first
+ {
+ bootTimeTicks = oldValue; // consistency
+ }
+ }
+ return DateTime.UnixEpoch.AddTicks(bootTimeTicks);
+ }
+ }
+
+ /// Gets the time the associated process was started.
+ internal DateTime StartTimeCore
+ {
+ get
+ {
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.OS.TeamInfo info;
+ int status = Interop.OS.GetTeamInfo(_processId, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return BootTime.AddTicks(info.start_time * BigTimeToTicks).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
+ {
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.OS.TeamUsageInfo info;
+ int status = Interop.OS.GetTeamUsageInfo(_processId, Interop.OS.BTeamUsage.B_TEAM_USAGE_SELF, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return TimeSpan.FromMicroseconds(info.user_time + info.kernel_time);
+ }
+ }
+
+ ///
+ /// 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
+ {
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.OS.TeamUsageInfo info;
+ int status = Interop.OS.GetTeamUsageInfo(_processId, Interop.OS.BTeamUsage.B_TEAM_USAGE_SELF, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return TimeSpan.FromMicroseconds(info.user_time);
+ }
+ }
+
+ /// 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
+ {
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.OS.TeamUsageInfo info;
+ int status = Interop.OS.GetTeamUsageInfo(_processId, Interop.OS.BTeamUsage.B_TEAM_USAGE_SELF, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return TimeSpan.FromMicroseconds(info.kernel_time);
+ }
+ }
+
+ /// Gets parent process ID
+ private unsafe int ParentProcessId
+ {
+ get
+ {
+ EnsureState(State.HaveNonExitedId);
+
+ Interop.OS.TeamInfo info;
+ int status = Interop.OS.GetTeamInfo(_processId, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return info.parent;
+ }
+ }
+
+ ///
+ /// Gets or sets which processors the threads in this process can be scheduled to run on.
+ ///
+ private static IntPtr ProcessorAffinityCore
+ {
+ get { throw new PlatformNotSupportedException(); }
+ set { throw new PlatformNotSupportedException(); }
+ }
+
+#pragma warning disable IDE0060
+ ///
+ /// Make sure we have obtained the min and max working set limits.
+ ///
+ private static void GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ /// Sets one or both of the minimum and maximum working set limits.
+ /// The new minimum working set limit, or null not to change it.
+ /// The new maximum working set limit, or null not to change it.
+ /// The resulting minimum working set limit after any changes applied.
+ /// The resulting maximum working set limit after any changes applied.
+ private static void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr resultingMin, out IntPtr resultingMax)
+ {
+ throw new PlatformNotSupportedException();
+ }
+#pragma warning restore IDE0060
+
+ /// Gets execution path
+ internal static string? GetPathToOpenFile()
+ {
+ if (Interop.Sys.Stat("/boot/system/bin/open", out _) == 0)
+ {
+ return "/boot/system/bin/open";
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Haiku.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Haiku.cs
new file mode 100644
index 00000000000000..54fe41b88fce16
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Haiku.cs
@@ -0,0 +1,192 @@
+// 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;
+
+namespace System.Diagnostics
+{
+ internal static partial class ProcessManager
+ {
+ /// Gets process infos for each process on the local machine.
+ /// The builder to add found process infos to.
+ /// Optional process name to use as an inclusion filter.
+ public static void GetProcessInfos(ref ArrayBuilder builder, string? processNameFilter)
+ {
+ int cookie = 0;
+ Interop.OS.TeamInfo info;
+
+ while ((Interop.OS.GetNextTeamInfo(ref cookie, out info)) == 0)
+ {
+ ProcessInfo? pi = GetProcessInfoFromTeamInfo(ref info, processNameFilter);
+ if (pi != null)
+ {
+ builder.Add(pi);
+ }
+ }
+ }
+
+ /// Gets an array of module infos for the specified process.
+ /// The ID of the process whose modules should be enumerated.
+ /// The array of modules.
+ internal static unsafe ProcessModuleCollection GetModules(int processId)
+ {
+ ProcessModuleCollection modules = new ProcessModuleCollection(0);
+ int cookie = 0;
+ Interop.Image.ImageInfo info;
+
+ while ((Interop.Image.GetNextImageInfo(processId, ref cookie, out info)) == 0)
+ {
+ string modulePath = GetString(info.name, Interop.Image.MAXPATHLEN);
+
+ var procModule = new ProcessModule(modulePath, Path.GetFileName(modulePath))
+ {
+ BaseAddress = (nint)info.text,
+ ModuleMemorySize = info.text_size + info.data_size,
+ EntryPointAddress = IntPtr.Zero // unknown
+ };
+
+ if (info.type == Interop.Image.ImageType.B_APP_IMAGE)
+ {
+ modules.Insert(0, procModule);
+ }
+ else
+ {
+ modules.Add(procModule);
+ }
+ }
+
+ return modules;
+ }
+
+ 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);
+
+ Interop.OS.TeamInfo info;
+ int status = Interop.OS.GetTeamInfo(pid, out info);
+
+ if (status != 0)
+ {
+ return null;
+ }
+
+ return GetProcessInfoFromTeamInfo(ref info, processNameFilter);
+ }
+
+ // ----------------------------------
+ // ---- Unix PAL layer ends here ----
+ // ----------------------------------
+
+ private static unsafe ProcessInfo? GetProcessInfoFromTeamInfo(ref Interop.OS.TeamInfo info, string? processNameFilter = null)
+ {
+ string processName;
+
+ fixed (byte* p = info.name)
+ {
+ processName = GetString(p, Interop.OS.B_OS_NAME_LENGTH);
+ }
+
+ if (!string.IsNullOrEmpty(processNameFilter) &&
+ !processNameFilter.Equals(processName, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ var procInfo = new ProcessInfo()
+ {
+ ProcessId = info.team,
+ ProcessName = processName,
+ SessionId = info.session_id
+ };
+
+ {
+ nint cookie = 0;
+ Interop.OS.AreaInfo areaInfo;
+
+ while (Interop.OS.GetNextAreaInfo(info.team, ref cookie, out areaInfo) == 0)
+ {
+ procInfo.VirtualBytes += (long)areaInfo.size;
+ procInfo.WorkingSet += areaInfo.ram_size;
+ }
+ }
+
+ {
+ int cookie = 0;
+ Interop.OS.ThreadInfo threadInfo;
+ bool first = true;
+
+ while (Interop.OS.GetNextThreadInfo(info.team, ref cookie, out threadInfo) == 0)
+ {
+ if (first)
+ {
+ procInfo.BasePriority = threadInfo.priority;
+ first = false;
+ }
+
+ procInfo._threadInfoList.Add(new ThreadInfo()
+ {
+ _processId = threadInfo.team,
+ _threadId = (ulong)threadInfo.thread,
+ _basePriority = procInfo.BasePriority,
+ _currentPriority = threadInfo.priority,
+ _startAddress = null,
+ _threadState = GetThreadStateFromBThreadState(threadInfo.state),
+ _threadWaitReason = GetThreadWaitReasonFromBThreadState(threadInfo.state),
+ });
+ }
+ }
+
+ return procInfo;
+ }
+
+ private static ThreadState GetThreadStateFromBThreadState(Interop.OS.ThreadState state)
+ {
+ switch (state)
+ {
+ case Interop.OS.ThreadState.B_THREAD_RUNNING:
+ return ThreadState.Running;
+ case Interop.OS.ThreadState.B_THREAD_READY:
+ return ThreadState.Ready;
+ case Interop.OS.ThreadState.B_THREAD_RECEIVING:
+ case Interop.OS.ThreadState.B_THREAD_ASLEEP:
+ case Interop.OS.ThreadState.B_THREAD_SUSPENDED:
+ case Interop.OS.ThreadState.B_THREAD_WAITING:
+ return ThreadState.Wait;
+ default:
+ return ThreadState.Unknown;
+ }
+ }
+
+ private static ThreadWaitReason GetThreadWaitReasonFromBThreadState(Interop.OS.ThreadState state)
+ {
+ switch (state)
+ {
+ case Interop.OS.ThreadState.B_THREAD_ASLEEP:
+ return ThreadWaitReason.ExecutionDelay;
+ case Interop.OS.ThreadState.B_THREAD_SUSPENDED:
+ return ThreadWaitReason.Suspended;
+ default:
+ return ThreadWaitReason.Unknown;
+ }
+ }
+
+ private static unsafe string GetString(byte* ptr, int maxLength)
+ {
+ int length = new ReadOnlySpan(ptr, maxLength).IndexOf((byte)0);
+ return new string((sbyte*)ptr, 0, (length < 0) ? maxLength : length);
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Haiku.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Haiku.cs
new file mode 100644
index 00000000000000..1d22ccb7b51507
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Haiku.cs
@@ -0,0 +1,129 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.ComponentModel;
+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.OS.ThreadInfo info;
+ int status = Interop.OS.GetThreadInfo(Id, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ // thread_info.priority are BeOS-style priority values, not POSIX nice values (those returned by getpriority()).
+ return
+ (info.priority >= (int)Interop.OS.BPriority.B_REAL_TIME_DISPLAY_PRIORITY) ? ThreadPriorityLevel.TimeCritical :
+ (info.priority >= (int)Interop.OS.BPriority.B_URGENT_DISPLAY_PRIORITY) ? ThreadPriorityLevel.Highest :
+ (info.priority >= (int)Interop.OS.BPriority.B_DISPLAY_PRIORITY) ? ThreadPriorityLevel.AboveNormal :
+ (info.priority >= (int)Interop.OS.BPriority.B_NORMAL_PRIORITY) ? ThreadPriorityLevel.Normal :
+ (info.priority >= (int)Interop.OS.BPriority.B_LOW_PRIORITY) ? ThreadPriorityLevel.BelowNormal :
+ (info.priority >= (int)Interop.OS.BPriority.B_LOWEST_ACTIVE_PRIORITY) ? ThreadPriorityLevel.Lowest :
+ ThreadPriorityLevel.Idle;
+ }
+ set
+ {
+ int newPriority = (value == ThreadPriorityLevel.TimeCritical) ? (int)Interop.OS.BPriority.B_REAL_TIME_DISPLAY_PRIORITY :
+ (value == ThreadPriorityLevel.Highest) ? (int)Interop.OS.BPriority.B_URGENT_DISPLAY_PRIORITY :
+ (value == ThreadPriorityLevel.AboveNormal) ? (int)Interop.OS.BPriority.B_DISPLAY_PRIORITY :
+ (value == ThreadPriorityLevel.Normal) ? (int)Interop.OS.BPriority.B_NORMAL_PRIORITY :
+ (value == ThreadPriorityLevel.BelowNormal) ? (int)Interop.OS.BPriority.B_LOW_PRIORITY :
+ (value == ThreadPriorityLevel.Lowest) ? (int)Interop.OS.BPriority.B_LOWEST_ACTIVE_PRIORITY :
+ (int)Interop.OS.BPriority.B_IDLE_PRIORITY;
+
+ int oldPriorityOrError = Interop.OS.SetThreadPriority(Id, newPriority);
+
+ if (oldPriorityOrError < 0)
+ {
+ throw new Win32Exception(oldPriorityOrError);
+ }
+ }
+ }
+
+ 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.OS.ThreadInfo info;
+ int status = Interop.OS.GetThreadInfo(Id, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return TimeSpan.FromMicroseconds(info.user_time + info.kernel_time);
+ }
+ }
+
+ ///
+ /// 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.OS.ThreadInfo info;
+ int status = Interop.OS.GetThreadInfo(Id, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return TimeSpan.FromMicroseconds(info.user_time);
+ }
+ }
+
+ ///
+ /// 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.OS.ThreadInfo info;
+ int status = Interop.OS.GetThreadInfo(Id, out info);
+
+ if (status != 0)
+ {
+ throw new Win32Exception(status);
+ }
+
+ return TimeSpan.FromMicroseconds(info.kernel_time);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.cs
index aaf843466b0b41..6abd29c7e4d8ab 100644
--- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.cs
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.cs
@@ -91,6 +91,7 @@ public ThreadPriorityLevel PriorityLevel
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("freebsd")]
+ [SupportedOSPlatform("haiku")]
get
{
if (!_priorityLevel.HasValue)
@@ -100,6 +101,7 @@ public ThreadPriorityLevel PriorityLevel
return _priorityLevel.Value;
}
[SupportedOSPlatform("windows")]
+ [SupportedOSPlatform("haiku")]
set
{
PriorityLevelCore = value;
diff --git a/src/native/libs/Common/pal_config.h.in b/src/native/libs/Common/pal_config.h.in
index 58f883951f0397..f93b8d0816935d 100644
--- a/src/native/libs/Common/pal_config.h.in
+++ b/src/native/libs/Common/pal_config.h.in
@@ -140,6 +140,7 @@
#cmakedefine01 HAVE_NET_IF_H
#cmakedefine01 HAVE_SYS_PROCINFO_H
#cmakedefine01 HAVE_IOSS_H
+#cmakedefine01 HAVE_IMAGE_H
#cmakedefine01 HAVE_OS_H
#cmakedefine01 HAVE_ALIGNED_ALLOC
#cmakedefine01 HAVE_MALLOC_SIZE
diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c
index 34131dee0312e5..9edf40fa1ac9dc 100644
--- a/src/native/libs/System.Native/entrypoints.c
+++ b/src/native/libs/System.Native/entrypoints.c
@@ -307,6 +307,15 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_LowLevelCrossProcessMutex_SetAbandoned)
DllImportEntry(SystemNative_Select)
DllImportEntry(SystemNative_GetNextAreaInfo)
+ DllImportEntry(SystemNative_GetTeamInfo)
+ DllImportEntry(SystemNative_GetNextTeamInfo)
+ DllImportEntry(SystemNative_GetNextTeamId)
+ DllImportEntry(SystemNative_GetTeamUsageInfo)
+ DllImportEntry(SystemNative_SetThreadPriority)
+ DllImportEntry(SystemNative_GetThreadInfo)
+ DllImportEntry(SystemNative_GetNextThreadInfo)
+ DllImportEntry(SystemNative_GetSystemInfo)
+ DllImportEntry(SystemNative_GetNextImageInfo)
};
EXTERN_C const void* SystemResolveDllImport(const char* name);
diff --git a/src/native/libs/System.Native/pal_getosinfo.c b/src/native/libs/System.Native/pal_getosinfo.c
index c499e9b9be68cb..98806b3ddc4691 100644
--- a/src/native/libs/System.Native/pal_getosinfo.c
+++ b/src/native/libs/System.Native/pal_getosinfo.c
@@ -12,6 +12,39 @@
#include
#endif
+#if HAVE_IMAGE_H
+#include
+#endif
+
+#if HAVE_OS_H
+static void CopyTeamInfo(TeamInfo* info, const team_info* nativeInfo)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->team = nativeInfo->team;
+ info->session_id = nativeInfo->session_id;
+ info->parent = nativeInfo->parent;
+ SafeStringCopy((char*)info->name, sizeof(info->name), (const char*)nativeInfo->name);
+ info->start_time = nativeInfo->start_time;
+}
+
+static void CopyThreadInfo(HaikuThreadInfo* info, const thread_info* nativeInfo)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->thread = nativeInfo->thread;
+ info->team = nativeInfo->team;
+ info->state = (int32_t)nativeInfo->state;
+ info->priority = nativeInfo->priority;
+ info->user_time = nativeInfo->user_time;
+ info->kernel_time = nativeInfo->kernel_time;
+}
+#endif
+
+#if HAVE_IMAGE_H
+c_static_assert(SYSTEMNATIVE_MAX_PATH >= MAXPATHLEN);
+#endif
+
int32_t SystemNative_GetNextAreaInfo(int32_t team, intptr_t* cookie, AreaInfo* areaInfo)
{
#if HAVE_OS_H
@@ -40,3 +73,231 @@ int32_t SystemNative_GetNextAreaInfo(int32_t team, intptr_t* cookie, AreaInfo* a
return ENOTSUP;
#endif
}
+
+int32_t SystemNative_GetTeamInfo(int32_t team, TeamInfo* info)
+{
+#if HAVE_OS_H
+ if (info == NULL)
+ {
+ return EINVAL;
+ }
+
+ team_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_team_info((team_id)team, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ CopyTeamInfo(info, &nativeInfo);
+ return (int32_t)status;
+#else
+ (void)team;
+ (void)info;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetNextTeamInfo(int32_t* cookie, TeamInfo* info)
+{
+#if HAVE_OS_H
+ if (cookie == NULL || info == NULL)
+ {
+ return EINVAL;
+ }
+
+ team_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_next_team_info(cookie, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ CopyTeamInfo(info, &nativeInfo);
+ return (int32_t)status;
+#else
+ (void)cookie;
+ (void)info;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetNextTeamId(int32_t* cookie, int32_t* team)
+{
+#if HAVE_OS_H
+ if (cookie == NULL || team == NULL)
+ {
+ return EINVAL;
+ }
+
+ team_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_next_team_info(cookie, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ *team = (int32_t)nativeInfo.team;
+ return (int32_t)status;
+#else
+ (void)cookie;
+ (void)team;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetTeamUsageInfo(int32_t team, int32_t who, TeamUsageInfo* info)
+{
+#if HAVE_OS_H
+ if (info == NULL)
+ {
+ return EINVAL;
+ }
+
+ team_usage_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_team_usage_info((team_id)team, who, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ info->user_time = nativeInfo.user_time;
+ info->kernel_time = nativeInfo.kernel_time;
+ return (int32_t)status;
+#else
+ (void)team;
+ (void)who;
+ (void)info;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_SetThreadPriority(int32_t thread, int32_t newPriority)
+{
+#if HAVE_OS_H
+ status_t status = set_thread_priority((thread_id)thread, newPriority);
+ return (int32_t)status;
+#else
+ (void)thread;
+ (void)newPriority;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetThreadInfo(int32_t thread, HaikuThreadInfo* info)
+{
+#if HAVE_OS_H
+ if (info == NULL)
+ {
+ return EINVAL;
+ }
+
+ thread_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_thread_info((thread_id)thread, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ CopyThreadInfo(info, &nativeInfo);
+ return (int32_t)status;
+#else
+ (void)thread;
+ (void)info;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetNextThreadInfo(int32_t team, int32_t* cookie, HaikuThreadInfo* info)
+{
+#if HAVE_OS_H
+ if (cookie == NULL || info == NULL)
+ {
+ return EINVAL;
+ }
+
+ thread_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_next_thread_info((team_id)team, cookie, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ CopyThreadInfo(info, &nativeInfo);
+ return (int32_t)status;
+#else
+ (void)team;
+ (void)cookie;
+ (void)info;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetSystemInfo(SystemInfo* info)
+{
+#if HAVE_OS_H
+ if (info == NULL)
+ {
+ return EINVAL;
+ }
+
+ system_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_system_info(&nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ info->boot_time = nativeInfo.boot_time;
+ return (int32_t)status;
+#else
+ (void)info;
+ return ENOTSUP;
+#endif
+}
+
+int32_t SystemNative_GetNextImageInfo(int32_t team, int32_t* cookie, ImageInfo* info)
+{
+#if HAVE_OS_H && HAVE_IMAGE_H
+ if (cookie == NULL || info == NULL)
+ {
+ return EINVAL;
+ }
+
+ image_info nativeInfo;
+ memset(&nativeInfo, 0, sizeof(nativeInfo));
+
+ status_t status = get_next_image_info((team_id)team, cookie, &nativeInfo);
+ if (status != B_OK)
+ {
+ return (int32_t)status;
+ }
+
+ memset(info, 0, sizeof(*info));
+ info->type = (int32_t)nativeInfo.type;
+ SafeStringCopy((char*)info->name, sizeof(info->name), (const char*)nativeInfo.name);
+ info->text = (uintptr_t)nativeInfo.text;
+ info->text_size = nativeInfo.text_size;
+ info->data_size = nativeInfo.data_size;
+ return (int32_t)status;
+#else
+ (void)team;
+ (void)cookie;
+ (void)info;
+ return ENOTSUP;
+#endif
+}
diff --git a/src/native/libs/System.Native/pal_getosinfo.h b/src/native/libs/System.Native/pal_getosinfo.h
index 464269e1054347..1ab43944eb1333 100644
--- a/src/native/libs/System.Native/pal_getosinfo.h
+++ b/src/native/libs/System.Native/pal_getosinfo.h
@@ -6,10 +6,61 @@
#include "pal_compiler.h"
#include "pal_types.h"
+#define SYSTEMNATIVE_OS_NAME_LENGTH 32
+#define SYSTEMNATIVE_MAX_PATH 1024
+
typedef struct
{
uintptr_t size;
uint32_t ram_size;
} AreaInfo;
+typedef struct
+{
+ int32_t team;
+ int32_t session_id;
+ int32_t parent;
+ uint8_t name[SYSTEMNATIVE_OS_NAME_LENGTH];
+ int64_t start_time;
+} TeamInfo;
+
+typedef struct
+{
+ int64_t user_time;
+ int64_t kernel_time;
+} TeamUsageInfo;
+
+typedef struct
+{
+ int32_t thread;
+ int32_t team;
+ int32_t state;
+ int32_t priority;
+ int64_t user_time;
+ int64_t kernel_time;
+} HaikuThreadInfo;
+
+typedef struct
+{
+ int64_t boot_time;
+} SystemInfo;
+
+typedef struct
+{
+ int32_t type;
+ uint8_t name[SYSTEMNATIVE_MAX_PATH];
+ uintptr_t text;
+ int32_t text_size;
+ int32_t data_size;
+} ImageInfo;
+
PALEXPORT int32_t SystemNative_GetNextAreaInfo(int32_t team, intptr_t* cookie, AreaInfo* areaInfo);
+PALEXPORT int32_t SystemNative_GetTeamInfo(int32_t team, TeamInfo* info);
+PALEXPORT int32_t SystemNative_GetNextTeamInfo(int32_t* cookie, TeamInfo* info);
+PALEXPORT int32_t SystemNative_GetNextTeamId(int32_t* cookie, int32_t* team);
+PALEXPORT int32_t SystemNative_GetTeamUsageInfo(int32_t team, int32_t who, TeamUsageInfo* info);
+PALEXPORT int32_t SystemNative_SetThreadPriority(int32_t thread, int32_t newPriority);
+PALEXPORT int32_t SystemNative_GetThreadInfo(int32_t thread, HaikuThreadInfo* info);
+PALEXPORT int32_t SystemNative_GetNextThreadInfo(int32_t team, int32_t* cookie, HaikuThreadInfo* info);
+PALEXPORT int32_t SystemNative_GetSystemInfo(SystemInfo* info);
+PALEXPORT int32_t SystemNative_GetNextImageInfo(int32_t team, int32_t* cookie, ImageInfo* info);
diff --git a/src/native/libs/configure.cmake b/src/native/libs/configure.cmake
index ba504df4834918..4d2e3cb81bf505 100644
--- a/src/native/libs/configure.cmake
+++ b/src/native/libs/configure.cmake
@@ -997,6 +997,10 @@ check_include_files(
IOKit/serial/ioss.h
HAVE_IOSS_H)
+check_include_files(
+ image.h
+ HAVE_IMAGE_H)
+
check_include_files(
OS.h
HAVE_OS_H)