diff --git a/eng/Subsets.props b/eng/Subsets.props
index 6e3fb61e30de03..be206d4d03a04b 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -51,6 +51,9 @@
<_NativeAotSupportedArch Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'loongarch64' or '$(TargetArchitecture)' == 'riscv64' or ('$(TargetOS)' == 'windows' and '$(TargetArchitecture)' == 'x86')">true
true
+
+ <_Crossgen2Supported Condition="'$(TargetOS)' != 'illumos' and '$(TargetOS)' != 'solaris'">true
+
<_SdkToolsSupportedOS Condition="'$(TargetsMobile)' != 'true' and '$(TargetsLinuxBionic)' != 'true'">true
<_SdkToolsSupportedArch Condition="'$(TargetArchitecture)' != 'armel'">true
@@ -440,7 +443,7 @@
-
+
-
+
@@ -691,7 +694,7 @@
When we're building in the VMR, we need to provide a crossgen2 that runs on the host machine for downstream repos to use to R2R their code.
In non-VMR builds, downstream repos can use the crossgen2 built for the target host SDK from another build leg, but in the VMR we need to provide one to use.
-->
-
+
diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets
index 8bc3936eda5c70..f59423f35afed3 100644
--- a/eng/targetingpacks.targets
+++ b/eng/targetingpacks.targets
@@ -44,7 +44,7 @@
LatestRuntimeFrameworkVersion="$(ProductVersion)"
RuntimeFrameworkName="$(LocalFrameworkOverrideName)"
RuntimePackNamePatterns="$(LocalFrameworkOverrideName).Runtime.**RID**"
- RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;maccatalyst-x64;maccatalyst-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;freebsd-x64;freebsd-arm64;linux-ppc64le;linux-riscv64;linux-musl-riscv64;linux-loongarch64;linux-musl-loongarch64"
+ RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;maccatalyst-x64;maccatalyst-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;freebsd-x64;freebsd-arm64;illumos-x64;solaris-x64;linux-ppc64le;linux-riscv64;linux-musl-riscv64;linux-loongarch64;linux-musl-loongarch64"
TargetFramework="$(NetCoreAppCurrent)"
TargetingPackName="$(LocalFrameworkOverrideName).Ref"
TargetingPackVersion="$(ProductVersion)"
diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h
index 30d158eea04e8d..9fe904c5120374 100644
--- a/src/coreclr/debug/runtimeinfo/datadescriptor.h
+++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h
@@ -868,7 +868,7 @@ CDAC_GLOBAL_STRING(RID, RID_STRING)
CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION)
CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain)
-CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomain)
+CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomainPtr)
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread)
CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread)
diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp
index fe48f04dfe639b..31ccdabef28d55 100644
--- a/src/coreclr/pal/src/thread/thread.cpp
+++ b/src/coreclr/pal/src/thread/thread.cpp
@@ -1330,10 +1330,9 @@ CorUnix::GetThreadTimesInternal(
CPalThread *pThread;
CPalThread *pTargetThread;
IPalObject *pobjThread = NULL;
+ clockid_t cid;
#ifdef __sun
int fd;
-#else // __sun
- clockid_t cid;
#endif // __sun
pThread = InternalGetCurrentThread();
diff --git a/src/coreclr/runtime/amd64/AllocFast.S b/src/coreclr/runtime/amd64/AllocFast.S
index d5b366b876dad6..f23fda7459372d 100644
--- a/src/coreclr/runtime/amd64/AllocFast.S
+++ b/src/coreclr/runtime/amd64/AllocFast.S
@@ -234,7 +234,7 @@ LEAF_ENTRY RhpNewPtrArrayFast, _TEXT
// Delegate overflow handling to the generic helper conservatively
- cmp rsi, (0x40000000 / 8) // sizeof(void*)
+ cmp rsi, 0x40000000 / 8 // 0x40000000 / sizeof(void*)
jae C_FUNC(RhpNewArrayFast)
// In this case we know the element size is sizeof(void *), or 8 for x64
diff --git a/src/coreclr/tools/superpmi/CMakeLists.txt b/src/coreclr/tools/superpmi/CMakeLists.txt
index d4308d2e03cabd..c48127c6a70a9d 100644
--- a/src/coreclr/tools/superpmi/CMakeLists.txt
+++ b/src/coreclr/tools/superpmi/CMakeLists.txt
@@ -1,3 +1,6 @@
+include(configure.cmake)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
add_subdirectory(superpmi)
add_subdirectory(mcs)
add_subdirectory(superpmi-shim-collector)
diff --git a/src/coreclr/tools/superpmi/config.h.in b/src/coreclr/tools/superpmi/config.h.in
new file mode 100644
index 00000000000000..0a065fc23c2a2d
--- /dev/null
+++ b/src/coreclr/tools/superpmi/config.h.in
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#cmakedefine01 HAVE_DIRENT_D_TYPE
+
+#endif // __CONFIG_H__
diff --git a/src/coreclr/tools/superpmi/configure.cmake b/src/coreclr/tools/superpmi/configure.cmake
new file mode 100644
index 00000000000000..1b090f956443e3
--- /dev/null
+++ b/src/coreclr/tools/superpmi/configure.cmake
@@ -0,0 +1,7 @@
+include(CheckStructHasMember)
+
+check_struct_has_member ("struct dirent" d_type dirent.h HAVE_DIRENT_D_TYPE)
+
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/src/coreclr/tools/superpmi/mcs/verbmerge.cpp b/src/coreclr/tools/superpmi/mcs/verbmerge.cpp
index e85997864188d1..ccede2d090d7ab 100644
--- a/src/coreclr/tools/superpmi/mcs/verbmerge.cpp
+++ b/src/coreclr/tools/superpmi/mcs/verbmerge.cpp
@@ -6,12 +6,19 @@
#include "simpletimer.h"
#include "logging.h"
#include "spmiutil.h"
+#include "config.h"
#include
#ifdef TARGET_UNIX
#include
#include
#include
-#endif
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN 0
+#define DT_DIR 4
+#define DT_REG 8
+#define DT_LNK 10
+#endif // !DT_UNKNOWN
+#endif // TARGET_UNIX
#include
@@ -187,9 +194,9 @@ bool verbMerge::DirectoryFilterDirectories(FilterArgType* findData)
#else // TARGET_WINDOWS
if (findData->d_type == DT_DIR)
{
- if (strcmp(findData->d_name, ".") == 0)
+ if (u16_strcmp(findData->cFileName, W(".")) == 0)
return false;
- if (strcmp(findData->d_name, "..") == 0)
+ if (u16_strcmp(findData->cFileName, W("..")) == 0)
return false;
return true;
@@ -281,12 +288,47 @@ int verbMerge::FilterDirectory(LPCWSTR dir,
dirent *pEntry = readdir(pDir);
while (pEntry != nullptr)
{
- if ((fnmatch(searchPatternUtf8.c_str(), pEntry->d_name, 0) == 0) && filter(pEntry))
+ int dirEntryType;
+
+#if HAVE_DIRENT_D_TYPE
+ dirEntryType = pEntry->d_type;
+#else
+ struct stat sb;
+
+ if (fstatat(dirfd(pDir), pEntry->d_name, &sb, 0) == -1)
+ {
+ continue;
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ dirEntryType = DT_DIR;
+ } else if (S_ISREG(sb.st_mode)) {
+ dirEntryType = DT_REG;
+ } else if (S_ISLNK(sb.st_mode)) {
+ dirEntryType = DT_LNK;
+ } else {
+ dirEntryType = DT_UNKNOWN;
+ }
+#endif
+ if (dirEntryType == DT_UNKNOWN)
{
- FindData findData(pEntry->d_type, ConvertMultiByteToWideChar(pEntry->d_name));
- first = new findDataList(&findData, first);
- ++elemCount;
+ continue;
}
+
+ if (fnmatch(searchPatternUtf8.c_str(), pEntry->d_name, 0) != 0)
+ {
+ continue;
+ }
+
+ FindData findData(dirEntryType, ConvertMultiByteToWideChar(pEntry->d_name));
+ if (!filter(&findData))
+ {
+ continue;
+ }
+
+ first = new findDataList(&findData, first);
+ ++elemCount;
+
errno = 0;
pEntry = readdir(pDir);
}
diff --git a/src/coreclr/tools/superpmi/mcs/verbmerge.h b/src/coreclr/tools/superpmi/mcs/verbmerge.h
index 16a326bd372916..ce1eee96b7c522 100644
--- a/src/coreclr/tools/superpmi/mcs/verbmerge.h
+++ b/src/coreclr/tools/superpmi/mcs/verbmerge.h
@@ -65,7 +65,7 @@ class verbMerge
#ifdef TARGET_WINDOWS
typedef _WIN32_FIND_DATAW FilterArgType;
#else
- typedef struct dirent FilterArgType;
+ typedef struct FindData FilterArgType;
#endif
typedef bool (*DirectoryFilterFunction_t)(FilterArgType*);
diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp
index 116611f8254114..70dde772c57771 100644
--- a/src/coreclr/vm/appdomain.hpp
+++ b/src/coreclr/vm/appdomain.hpp
@@ -1931,7 +1931,7 @@ inline static BOOL IsUnderDomainLock() { LIMITED_METHOD_CONTRACT; return m_Syste
template<>
struct cdac_data
{
- static constexpr PTR_SystemDomain* SystemDomain = &SystemDomain::m_pSystemDomain;
+ static constexpr PTR_SystemDomain* SystemDomainPtr = &SystemDomain::m_pSystemDomain;
};
#endif // DACCESS_COMPILE
diff --git a/src/installer/pkg/projects/netcoreappRIDs.props b/src/installer/pkg/projects/netcoreappRIDs.props
index 6c84841697b420..0675190914905e 100644
--- a/src/installer/pkg/projects/netcoreappRIDs.props
+++ b/src/installer/pkg/projects/netcoreappRIDs.props
@@ -9,11 +9,6 @@
arm64
-
-
-
-
-
x86
@@ -51,6 +46,10 @@
in our runtime.json to enable others to provide them. -->
+
+
+
+ armel
diff --git a/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.Definitions.cs b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.Definitions.cs
new file mode 100644
index 00000000000000..7e7a98bda7c64b
--- /dev/null
+++ b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.Definitions.cs
@@ -0,0 +1,155 @@
+// 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;
+
+// C# equivalents for structures. See: struct lwpsinfo, struct psinfo.
+// We read directly onto these from procfs, so the layouts and sizes of these structures
+// must _exactly_ match those in
+
+// analyzer incorrectly flags fixed buffer length const
+// (https://github.com/dotnet/roslyn/issues/37593)
+#pragma warning disable CA1823
+
+internal static partial class Interop
+{
+ internal static partial class @procfs
+ {
+ internal const string RootPath = "/proc/";
+ private const string psinfoFileName = "/psinfo";
+ private const string lwpDirName = "/lwp";
+ private const string lwpsinfoFileName = "/lwpsinfo";
+
+ // Constants from sys/procfs.h
+ private const int PRARGSZ = 80;
+ private const int PRCLSZ = 8;
+ private const int PRFNSZ = 16;
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct @timestruc_t
+ {
+ public long tv_sec;
+ public long tv_nsec;
+ }
+
+ // lwp ps(1) information file. /proc//lwp//lwpsinfo
+ // Equivalent to sys/procfs.h struct lwpsinfo
+ // "unsafe" because it has fixed sized arrays.
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct @lwpsinfo
+ {
+ private int pr_flag; /* lwp flags (DEPRECATED; do not use) */
+ public uint pr_lwpid; /* lwp id */
+ private long pr_addr; /* internal address of lwp */
+ private long pr_wchan; /* wait addr for sleeping lwp */
+ public byte pr_stype; /* synchronization event type */
+ public byte pr_state; /* numeric lwp state */
+ public byte pr_sname; /* printable character for pr_state */
+ public byte pr_nice; /* nice for cpu usage */
+ private short pr_syscall; /* system call number (if in syscall) */
+ private byte pr_oldpri; /* pre-SVR4, low value is high priority */
+ private byte pr_cpu; /* pre-SVR4, cpu usage for scheduling */
+ public int pr_pri; /* priority, high value is high priority */
+ private ushort pr_pctcpu; /* fixed pt. % of recent cpu time */
+ private ushort pr_pad;
+ public timestruc_t pr_start; /* lwp start time, from the epoch */
+ public timestruc_t pr_time; /* usr+sys cpu time for this lwp */
+ private fixed byte pr_clname[PRCLSZ]; /* scheduling class name */
+ private fixed byte pr_name[PRFNSZ]; /* name of system lwp */
+ private int pr_onpro; /* processor which last ran this lwp */
+ private int pr_bindpro; /* processor to which lwp is bound */
+ private int pr_bindpset; /* processor set to which lwp is bound */
+ private int pr_lgrp; /* lwp home lgroup */
+ private fixed int pr_filler[4]; /* reserved for future use */
+ }
+ private const int PR_LWPSINFO_SIZE = 128; // for debug assertions
+
+ // process ps(1) information file. /proc//psinfo
+ // Equivalent to sys/procfs.h struct psinfo
+ // "unsafe" because it has fixed sized arrays.
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct @psinfo
+ {
+ private int pr_flag; /* process flags (DEPRECATED; do not use) */
+ public int pr_nlwp; /* number of active lwps in the process */
+ public int pr_pid; /* unique process id */
+ public int pr_ppid; /* process id of parent */
+ public int pr_pgid; /* pid of process group leader */
+ public int pr_sid; /* session id */
+ public uint pr_uid; /* real user id */
+ public uint pr_euid; /* effective user id */
+ public uint pr_gid; /* real group id */
+ public uint pr_egid; /* effective group id */
+ private long pr_addr; /* address of process */
+ public ulong pr_size; /* size of process image in Kbytes */
+ public ulong pr_rssize; /* resident set size in Kbytes */
+ private ulong pr_pad1;
+ private ulong pr_ttydev; /* controlling tty device (or PRNODEV) */
+ private ushort pr_pctcpu; /* % of recent cpu time used by all lwps */
+ private ushort pr_pctmem; /* % of system memory used by process */
+ public timestruc_t pr_start; /* process start time, from the epoch */
+ public timestruc_t pr_time; /* usr+sys cpu time for this process */
+ public timestruc_t pr_ctime; /* usr+sys cpu time for reaped children */
+ public fixed byte pr_fname[PRFNSZ]; /* name of execed file */
+ public fixed byte pr_psargs[PRARGSZ]; /* initial characters of arg list */
+ public int pr_wstat; /* if zombie, the wait() status */
+ public int pr_argc; /* initial argument count */
+ private long pr_argv; /* address of initial argument vector */
+ private long pr_envp; /* address of initial environment vector */
+ private byte pr_dmodel; /* data model of the process */
+ private fixed byte pr_pad2[3];
+ public int pr_taskid; /* task id */
+ public int pr_projid; /* project id */
+ public int pr_nzomb; /* number of zombie lwps in the process */
+ public int pr_poolid; /* pool id */
+ public int pr_zoneid; /* zone id */
+ public int pr_contract; /* process contract */
+ private fixed int pr_filler[1]; /* reserved for future use */
+ public lwpsinfo pr_lwp; /* information for representative lwp */
+ // C# magic: Accessor method to get a Span for pr_psargs[]
+ // Does not affect the size or layout of this struct.
+ internal ReadOnlySpan PsArgsSpan =>
+ MemoryMarshal.CreateReadOnlySpan(ref pr_psargs[0], PRARGSZ);
+ }
+ private const int PR_PSINFO_SIZE = 416; // for debug assertions
+
+ // Ouput type for TryGetThreadInfoById()
+ internal struct ThreadInfo
+ {
+ internal uint Tid;
+ internal int Priority;
+ internal int NiceVal;
+ internal char Status;
+ internal Interop.Sys.TimeSpec StartTime;
+ internal Interop.Sys.TimeSpec CpuTotalTime; // user+sys
+ // add more fields when needed.
+ }
+
+ // Ouput type for TryGetProcessInfoById()
+ internal struct ProcessInfo
+ {
+ internal int Pid;
+ internal int ParentPid;
+ internal int SessionId;
+ internal int Priority;
+ internal int NiceVal;
+ internal nuint VirtualSize;
+ internal nuint ResidentSetSize;
+ internal Interop.Sys.TimeSpec StartTime;
+ internal Interop.Sys.TimeSpec CpuTotalTime; // user+sys
+ internal string? Args;
+ // add more fields when needed.
+ }
+
+ internal static string GetInfoFilePathForProcess(int pid) =>
+ $"{RootPath}{(uint)pid}{psinfoFileName}";
+
+ internal static string GetLwpDirForProcess(int pid) =>
+ $"{RootPath}{(uint)pid}{lwpDirName}";
+
+ internal static string GetInfoFilePathForThread(int pid, int tid) =>
+ $"{RootPath}{(uint)pid}{lwpDirName}/{(uint)tid}{lwpsinfoFileName}";
+
+ }
+}
diff --git a/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.TryGetProcessInfoById.cs b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.TryGetProcessInfoById.cs
new file mode 100644
index 00000000000000..d9989c03ae5cf8
--- /dev/null
+++ b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.TryGetProcessInfoById.cs
@@ -0,0 +1,73 @@
+// 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.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.Text;
+
+internal static partial class Interop
+{
+ internal static partial class @procfs
+ {
+
+ ///
+ /// Attempts to get status info for the specified process ID.
+ ///
+ /// PID of the process to read status info for.
+ /// The pointer to ProcessInfo instance.
+ ///
+ /// true if the process info was read; otherwise, false.
+ ///
+
+ // ProcessManager.SunOS.cs calls this
+ internal static bool TryGetProcessInfoById(int pid, out ProcessInfo result)
+ {
+ result = default;
+
+ try
+ {
+ string fileName = GetInfoFilePathForProcess(pid);
+ using FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
+ psinfo pr;
+ Unsafe.SkipInit(out pr);
+ Span prspan = MemoryMarshal.AsBytes(new Span(ref pr));
+ Debug.Assert(prspan.Length == PR_PSINFO_SIZE,
+ $"psinfo struct size {prspan.Length} bytes not {PR_PSINFO_SIZE}.");
+ fs.ReadExactly(prspan);
+
+ result.Pid = pr.pr_pid;
+ result.ParentPid = pr.pr_ppid;
+ result.SessionId = pr.pr_sid;
+ result.VirtualSize = (nuint)pr.pr_size * 1024; // pr_size is in Kbytes
+ result.ResidentSetSize = (nuint)pr.pr_rssize * 1024; // pr_rssize is in Kbytes
+ result.StartTime.TvSec = pr.pr_start.tv_sec;
+ result.StartTime.TvNsec = pr.pr_start.tv_nsec;
+ result.CpuTotalTime.TvSec = pr.pr_time.tv_sec;
+ result.CpuTotalTime.TvNsec = pr.pr_time.tv_nsec;
+
+ // Get Args as a managed string, using accessor for pr_psargs[]
+ ReadOnlySpan argspan = pr.PsArgsSpan;
+ int argslen = argspan.IndexOf((byte)0);
+ argslen = (argslen >= 0) ? argslen : argspan.Length;
+ result.Args = Encoding.UTF8.GetString(argspan.Slice(0, argslen));
+
+ // A couple things from pr_lwp
+ result.Priority = pr.pr_lwp.pr_pri;
+ result.NiceVal = (int)pr.pr_lwp.pr_nice;
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Fail($"Failed to read process info for PID {pid}: {e}");
+ }
+
+ return false;
+ }
+
+ }
+}
diff --git a/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.TryGetThreadInfoById.cs b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.TryGetThreadInfoById.cs
new file mode 100644
index 00000000000000..a3ab094fcef5e6
--- /dev/null
+++ b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFs.TryGetThreadInfoById.cs
@@ -0,0 +1,62 @@
+// 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.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.IO;
+
+internal static partial class Interop
+{
+ internal static partial class @procfs
+ {
+
+ ///
+ /// Attempts to get status info for the specified thread ID.
+ ///
+ /// PID of the process to read status info for.
+ /// TID of the thread to read status info for.
+ /// The pointer to ThreadInfo instance.
+ ///
+ /// true if the thread info was read; otherwise, false.
+ ///
+
+ // ProcessManager.SunOS.cs calls this
+ internal static bool TryGetThreadInfoById(int pid, int tid, out ThreadInfo result)
+ {
+ result = default;
+
+ try
+ {
+ string fileName = GetInfoFilePathForThread(pid, tid);
+ using FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
+ lwpsinfo pr;
+ Unsafe.SkipInit(out pr);
+ Span prspan = MemoryMarshal.AsBytes(new Span(ref pr));
+ Debug.Assert(prspan.Length == PR_LWPSINFO_SIZE,
+ $"psinfo struct size {prspan.Length} bytes not {PR_LWPSINFO_SIZE}.");
+ fs.ReadExactly(prspan);
+
+ result.Tid = pr.pr_lwpid;
+ result.Priority = pr.pr_pri;
+ result.NiceVal = (int)pr.pr_nice;
+ result.Status = (char)pr.pr_sname;
+ result.StartTime.TvSec = pr.pr_start.tv_sec;
+ result.StartTime.TvNsec = pr.pr_start.tv_nsec;
+ result.CpuTotalTime.TvSec = pr.pr_time.tv_sec;
+ result.CpuTotalTime.TvNsec = pr.pr_time.tv_nsec;
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Fail($"Failed to read thread info for PID {pid} TID {tid}: {e}");
+ }
+
+ return false;
+ }
+
+ }
+}
diff --git a/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFsStat.TryReadProcessStatusInfo.cs b/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFsStat.TryReadProcessStatusInfo.cs
deleted file mode 100644
index 5d835f241cda1b..00000000000000
--- a/src/libraries/Common/src/Interop/SunOS/procfs/Interop.ProcFsStat.TryReadProcessStatusInfo.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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;
-
-internal static partial class Interop
-{
- internal static partial class @procfs
- {
- ///
- /// Attempts to get status info for the specified process ID.
- ///
- /// PID of the process to read status info for.
- /// The pointer to processStatus instance.
- ///
- /// true if the process status was read; otherwise, false.
- ///
- [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadProcessStatusInfo", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static unsafe partial bool TryReadProcessStatusInfo(int pid, ProcessStatusInfo* processStatus);
-
- internal struct ProcessStatusInfo
- {
- internal nuint ResidentSetSize;
- // add more fields when needed.
- }
-
- internal static unsafe bool TryReadProcessStatusInfo(int pid, out ProcessStatusInfo statusInfo)
- {
- statusInfo = default;
- fixed (ProcessStatusInfo* pStatusInfo = &statusInfo)
- {
- return TryReadProcessStatusInfo(pid, pStatusInfo);
- }
- }
- }
-}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.TimeSpec.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.TimeSpec.cs
new file mode 100644
index 00000000000000..54a7299dcd4915
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.TimeSpec.cs
@@ -0,0 +1,17 @@
+// 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;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ internal struct TimeSpec
+ {
+ internal long TvSec;
+ internal long TvNsec;
+ }
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UTimensat.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UTimensat.cs
index 261a5ae8562df3..13c5d2fe10bce0 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UTimensat.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UTimensat.cs
@@ -8,12 +8,6 @@ internal static partial class Interop
{
internal static partial class Sys
{
- internal struct TimeSpec
- {
- internal long TvSec;
- internal long TvNsec;
- }
-
///
/// Sets the last access and last modified time of a file
///
diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
index 9a7ef0734ad082..8550b2cef6596e 100644
--- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
@@ -47,6 +47,7 @@ public static partial class PlatformDetection
public static bool IsNotMacCatalyst => !IsMacCatalyst;
public static bool Isillumos => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ILLUMOS"));
public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS"));
+ public static bool IsSunOS => Isillumos || IsSolaris;
public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
public static bool IsWasi => RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI"));
public static bool IsNotBrowser => !IsBrowser;
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 ba226f7bc0da27..62decff146c5fb 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)
+ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)$(DefineConstants);FEATURE_REGISTRYtruefalse
@@ -369,6 +369,21 @@
Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.SunOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.SunOS.cs
new file mode 100644
index 00000000000000..e36029c501bea1
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.SunOS.cs
@@ -0,0 +1,137 @@
+// 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.Buffers;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Text;
+using System.Threading;
+
+namespace System.Diagnostics
+{
+ public partial class Process : IDisposable
+ {
+
+ /// Gets the time the associated process was started.
+ internal DateTime StartTimeCore
+ {
+ get
+ {
+ Interop.procfs.ProcessInfo iinfo = GetProcInfo();
+
+ DateTime startTime = DateTime.UnixEpoch +
+ TimeSpan.FromSeconds(iinfo.StartTime.TvSec) +
+ TimeSpan.FromMicroseconds(iinfo.StartTime.TvNsec / 1000);
+
+ // The return value is expected to be in the local time zone.
+ return startTime.ToLocalTime();
+ }
+ }
+
+ /// Gets the parent process ID
+ private int ParentProcessId => GetProcInfo().ParentPid;
+
+ /// Gets execution path
+ private static string? GetPathToOpenFile()
+ {
+ return FindProgramInPath("xdg-open");
+ }
+
+ ///
+ /// 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
+ {
+ // a.k.a. "user" + "system" time
+ Interop.procfs.ProcessInfo iinfo = GetProcInfo();
+ TimeSpan ts = TimeSpan.FromSeconds(iinfo.CpuTotalTime.TvSec) +
+ TimeSpan.FromMicroseconds(iinfo.CpuTotalTime.TvNsec / 1000);
+ return ts;
+ }
+ }
+
+ ///
+ /// 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
+ {
+ // a.k.a. "user" time
+ // Could get this from /proc/$pid/status
+ // Just say it's all user time for now
+ return TotalProcessorTime;
+ }
+ }
+
+ ///
+ /// 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
+ {
+ // a.k.a. "system" time
+ // Could get this from /proc/$pid/status
+ // Just say it's all user time for now
+ EnsureState(State.HaveNonExitedId);
+ return TimeSpan.Zero;
+ }
+ }
+
+ // ----------------------------------
+ // ---- Unix PAL layer ends here ----
+ // ----------------------------------
+
+ /// Gets the name that was used to start the process, or null if it could not be retrieved.
+ internal static string GetUntruncatedProcessName(ref Interop.procfs.ProcessInfo iinfo)
+ {
+ // This assumes the process name is the first part of the Args string
+ // ending at the first space. That seems to work well enough for now.
+ // If someday this need to support a process name containing spaces,
+ // this could call a new Interop function that reads /proc/$pid/auxv
+ // (sys/auxv.h) and gets the AT_SUN_EXECNAME string from that file.
+ if (!string.IsNullOrEmpty(iinfo.Args))
+ {
+ string[] argv = iinfo.Args.Split(' ', 2);
+ if (!string.IsNullOrEmpty(argv[0]))
+ {
+ return Path.GetFileName(argv[0]);
+ }
+ }
+ return "?";
+ }
+
+ /// Reads the information for this process from the procfs file system.
+ private Interop.procfs.ProcessInfo GetProcInfo()
+ {
+ EnsureState(State.HaveNonExitedId);
+ Interop.procfs.ProcessInfo iinfo;
+ if (!Interop.procfs.TryGetProcessInfoById(_processId, out iinfo))
+ {
+ throw new Win32Exception(SR.ProcessInformationUnavailable);
+ }
+ return iinfo;
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs
new file mode 100644
index 00000000000000..4d5b8025c92181
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs
@@ -0,0 +1,244 @@
+// 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.Globalization;
+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()
+ {
+ IEnumerable pids = EnumerateProcessIds();
+ return new List(pids).ToArray();
+ }
+
+ /// Gets process infos for each process on the specified machine.
+ /// Optional process name to use as an inclusion filter.
+ /// The target machine.
+ /// An array of process infos, one per found process.
+ public static ProcessInfo[] GetProcessInfos(string? processNameFilter, string machineName)
+ {
+ ThrowIfRemoteMachine(machineName);
+
+ // Iterate through all process IDs to load information about each process
+ IEnumerable pids = EnumerateProcessIds();
+ ArrayBuilder processes = default;
+ foreach (int pid in pids)
+ {
+ ProcessInfo? pi = CreateProcessInfo(pid, processNameFilter);
+ if (pi != null)
+ {
+ processes.Add(pi);
+ }
+ }
+
+ return processes.ToArray();
+ }
+
+ /// 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 ProcessModuleCollection GetModules(int processId)
+ {
+
+ // Negative PIDs aren't valid
+ ArgumentOutOfRangeException.ThrowIfNegative(processId);
+
+ // GetModules(x)[0].FileName is often used to find the path to the executable,
+ // so at least get that. That appears to be sufficient, at least for now.
+ // If needed, the full list of loaded modules could be obtained using another
+ // Interop function to read /proc/$pid/auxv similar to how the "pargs" and "pldd"
+ // commands do their work.
+
+ Interop.procfs.ProcessInfo iProcInfo;
+ if (Interop.procfs.TryGetProcessInfoById(processId, out iProcInfo))
+ {
+ string fullName = Process.GetUntruncatedProcessName(ref iProcInfo);
+ if (!string.IsNullOrEmpty(fullName))
+ {
+ return new ProcessModuleCollection(1)
+ {
+ new ProcessModule(fullName, Path.GetFileName(fullName))
+ };
+ }
+ }
+ return new ProcessModuleCollection(0);
+ }
+
+ ///
+ /// Creates a ProcessInfo from the specified process ID.
+ ///
+ internal static ProcessInfo? CreateProcessInfo(int pid, string? processNameFilter = null)
+ {
+ // Negative PIDs aren't valid
+ ArgumentOutOfRangeException.ThrowIfNegative(pid);
+
+ Interop.procfs.ProcessInfo iProcInfo;
+ if (!Interop.procfs.TryGetProcessInfoById(pid, out iProcInfo))
+ {
+ return null;
+ }
+
+ string processName = Process.GetUntruncatedProcessName(ref iProcInfo);
+ if (!string.IsNullOrEmpty(processNameFilter) &&
+ !string.Equals(processName, processNameFilter, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ return CreateProcessInfo(ref iProcInfo);
+ }
+
+ // ----------------------------------
+ // ---- Unix PAL layer ends here ----
+ // ----------------------------------
+
+ /// Enumerates the IDs of all processes on the current machine.
+ internal static IEnumerable EnumerateProcessIds()
+ {
+ // Parse /proc for any directory that's named with a number. Each such
+ // directory represents a process.
+ foreach (string procDir in Directory.EnumerateDirectories(Interop.procfs.RootPath))
+ {
+ string dirName = Path.GetFileName(procDir);
+ int pid;
+ if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out pid))
+ {
+ Debug.Assert(pid >= 0);
+ yield return pid;
+ }
+ }
+ }
+
+ /// Enumerates the IDs of all threads in the specified process.
+ internal static IEnumerable EnumerateThreadIds(int pid)
+ {
+ // Parse /proc/$pid/lwp for any directory that's named with a number.
+ // Each such directory represents a thread.
+ string dir = Interop.procfs.GetLwpDirForProcess(pid);
+ foreach (string lwpDir in Directory.EnumerateDirectories(dir))
+ {
+ string dirName = Path.GetFileName(lwpDir);
+ int tid;
+ if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid))
+ {
+ Debug.Assert(tid >= 0);
+ yield return tid;
+ }
+ }
+ }
+
+ ///
+ /// Creates a ProcessInfo from the data read from a /proc/pid/psinfo file and the associated lwp directory.
+ ///
+ internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ProcessInfo iProcInfo)
+ {
+ int pid = iProcInfo.Pid;
+
+ string name = Process.GetUntruncatedProcessName(ref iProcInfo);
+ var pi = new ProcessInfo()
+ {
+ ProcessId = pid,
+ ProcessName = name,
+ BasePriority = iProcInfo.Priority,
+ SessionId = iProcInfo.SessionId,
+ VirtualBytes = (long)iProcInfo.VirtualSize,
+ WorkingSet = (long)iProcInfo.ResidentSetSize,
+ // StartTime: See Process.StartTimeCore()
+ };
+
+ // Then read through /proc/pid/lwp/ to find each thread in the process...
+ // Can we use a "get" method to avoid loading this for every process until it's asked for?
+ try
+ {
+
+ // Iterate through all thread IDs to load information about each thread
+ IEnumerable tids = EnumerateThreadIds(pid);
+
+ foreach (int tid in tids)
+ {
+ Interop.procfs.ThreadInfo iThrInfo;
+ ThreadInfo? ti;
+
+ if (!Interop.procfs.TryGetThreadInfoById(pid, tid, out iThrInfo))
+ {
+ continue;
+ }
+
+ ti = CreateThreadInfo(ref iProcInfo, ref iThrInfo);
+ if (ti != null)
+ {
+ pi._threadInfoList.Add(ti);
+ }
+ }
+ }
+ catch (IOException)
+ {
+ // Between the time that we get an ID and the time that we try to read the associated
+ // directories and files in procfs, the process could be gone.
+ }
+
+ // Finally return what we've built up
+ return pi;
+ }
+
+ ///
+ /// Creates a ThreadInfo from the data read from a /proc/pid/lwp/lwpsinfo file.
+ ///
+ internal static ThreadInfo CreateThreadInfo(ref Interop.procfs.ProcessInfo iProcInfo,
+ ref Interop.procfs.ThreadInfo iThrInfo)
+ {
+
+ var ti = new ThreadInfo()
+ {
+ _processId = iProcInfo.Pid,
+ _threadId = (ulong)iThrInfo.Tid,
+ _basePriority = iThrInfo.Priority,
+ _currentPriority = iThrInfo.Priority,
+ _startAddress = null,
+ _threadState = ProcFsStateToThreadState(iThrInfo.Status),
+ _threadWaitReason = ThreadWaitReason.Unknown
+ };
+
+ return ti;
+ }
+
+ /// Gets a ThreadState to represent the value returned from the status field of /proc/pid/stat.
+ /// The status field value.
+ ///
+ private static ThreadState ProcFsStateToThreadState(char c)
+ {
+ // Information on these in fs/proc/array.c
+ // `man proc` does not document them all
+ switch (c)
+ {
+ case 'O': // On-CPU
+ case 'R': // Runnable
+ return ThreadState.Running;
+
+ case 'S': // Sleeping in a wait
+ case 'T': // Stopped on a signal
+ return ThreadState.Wait;
+
+ case 'Z': // Zombie
+ return ThreadState.Terminated;
+
+ case 'W': // Waiting for CPU
+ return ThreadState.Transition;
+
+ case '\0': // new, not started yet
+ return ThreadState.Initialized;
+
+ default:
+ Debug.Fail($"Unexpected status character: {(int)c}");
+ return ThreadState.Unknown;
+ }
+ }
+
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.SunOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.SunOS.cs
new file mode 100644
index 00000000000000..ad428290f32710
--- /dev/null
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.SunOS.cs
@@ -0,0 +1,133 @@
+// 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
+ {
+
+ /// Gets the time this thread was started.
+ internal DateTime GetStartTime()
+ {
+ Interop.procfs.ThreadInfo iinfo = GetThreadInfo();
+
+ DateTime startTime = DateTime.UnixEpoch +
+ TimeSpan.FromSeconds(iinfo.StartTime.TvSec) +
+ TimeSpan.FromMicroseconds(iinfo.StartTime.TvNsec / 1000);
+
+ // The return value is expected to be in the local time zone.
+ return startTime.ToLocalTime();
+ }
+
+ ///
+ /// 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.procfs.ThreadInfo iinfo = GetThreadInfo();
+ return GetThreadPriorityFromSysPri(iinfo.Priority);
+ }
+ set
+ {
+ // Raising priority is a privileged operation.
+ // Might be able to adjust our "nice" value. Maybe later...
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ ///
+ /// Gets 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
+ {
+ // a.k.a. "user" + "system" time
+ Interop.procfs.ThreadInfo iinfo = GetThreadInfo();
+ TimeSpan ts = TimeSpan.FromSeconds(iinfo.CpuTotalTime.TvSec) +
+ TimeSpan.FromMicroseconds(iinfo.CpuTotalTime.TvNsec / 1000);
+ return ts;
+ }
+ }
+
+ ///
+ /// Gets 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
+ {
+ // a.k.a. "user" time
+ // Could get this from /proc/$pid/lwp/$lwpid/lwpstatus
+ // Just say it's all user time for now
+ return TotalProcessorTime;
+ }
+ }
+
+ ///
+ /// Gets 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
+ {
+ // a.k.a. "system" time
+ // Could get this from /proc/$pid/lwp/$lwpid/lwpstatus
+ // Just say it's all user time for now
+ return TimeSpan.Zero;
+ }
+
+ }
+
+ // ----------------------------------
+ // ---- exported stuff ends here ----
+ // ----------------------------------
+
+ // System priorities go from 1 to 100, where 60 and above are for "system" things
+ // These mappingsare relatively arbitrary. Normal user processes run at priority 59.
+ // and the other values above and below are simply distributed somewhat evenly.
+ private static System.Diagnostics.ThreadPriorityLevel GetThreadPriorityFromSysPri(int pri)
+ {
+ Debug.Assert((pri >= 0) && (pri <= 100));
+ return
+ (pri >= 90) ? ThreadPriorityLevel.TimeCritical :
+ (pri >= 80) ? ThreadPriorityLevel.Highest :
+ (pri >= 60) ? ThreadPriorityLevel.AboveNormal :
+ (pri == 59) ? ThreadPriorityLevel.Normal :
+ (pri >= 40) ? ThreadPriorityLevel.BelowNormal :
+ (pri >= 20) ? ThreadPriorityLevel.Lowest :
+ ThreadPriorityLevel.Idle;
+ }
+
+ /// Reads the information for this thread from the procfs file system.
+ private Interop.procfs.ThreadInfo GetThreadInfo()
+ {
+ Interop.procfs.ThreadInfo iinfo;
+ if (!Interop.procfs.TryGetThreadInfoById(_processId, tid: Id, out iinfo))
+ {
+ throw new InvalidOperationException(SR.Format(SR.ThreadExited, Id));
+ }
+ return iinfo;
+ }
+
+ }
+}
diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
index a3b2a7a97f0508..ba701a29a78589 100644
--- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
+++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
@@ -575,7 +575,7 @@ public void TestMaxWorkingSet()
Assert.InRange((long)p.MinWorkingSet, 0, long.MaxValue);
}
- if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD()) {
+ if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD() || PlatformDetection.IsSunOS) {
return; // doesn't support getting/setting working set for other processes
}
@@ -623,7 +623,7 @@ public void TestMinWorkingSet()
Assert.InRange((long)p.MinWorkingSet, 0, long.MaxValue);
}
- if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD()) {
+ if (OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD() || PlatformDetection.IsSunOS) {
return; // doesn't support getting/setting working set for other processes
}
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 b8795da22f9062..b319881c3bc3b1 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
@@ -2518,6 +2518,9 @@
Common\Interop\Unix\System.Native\Interop.SysLog.cs
+
+ Common\Interop\Unix\System.Native\Interop.TimeSpec.cs
+
Common\Interop\Unix\System.Native\Interop.Threading.cs
@@ -2617,7 +2620,8 @@
-
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs
index fb7ff75cb2e6c6..d072f52f0c48c5 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs
@@ -7,7 +7,6 @@ namespace System
{
public static partial class Environment
{
- public static long WorkingSet =>
- (long)(Interop.procfs.TryReadProcessStatusInfo(ProcessId, out Interop.procfs.ProcessStatusInfo status) ? status.ResidentSetSize : 0);
+ public static long WorkingSet => (long)(Interop.procfs.TryGetProcessInfoById(ProcessId, out Interop.procfs.ProcessInfo iProcInfo) ? iProcInfo.ResidentSetSize : 0);
}
}
diff --git a/src/native/libs/Common/pal_config.h.in b/src/native/libs/Common/pal_config.h.in
index f64132cd449968..580cd68a574335 100644
--- a/src/native/libs/Common/pal_config.h.in
+++ b/src/native/libs/Common/pal_config.h.in
@@ -31,6 +31,7 @@
#cmakedefine01 HAVE_MNTINFO
#cmakedefine01 HAVE_STATFS_FSTYPENAME
#cmakedefine01 HAVE_STATVFS_FSTYPENAME
+#cmakedefine01 HAVE_STATVFS_BASETYPE
#cmakedefine01 HAVE_NON_LEGACY_STATFS
#cmakedefine01 HAVE_STRCPY_S
#cmakedefine01 HAVE_STRLCPY
diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c
index 6d63c0b6888988..1e03b1654d1ed8 100644
--- a/src/native/libs/System.Native/entrypoints.c
+++ b/src/native/libs/System.Native/entrypoints.c
@@ -120,7 +120,6 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_LChflagsCanSetHiddenFlag)
DllImportEntry(SystemNative_FChflags)
DllImportEntry(SystemNative_CanGetHiddenFlag)
- DllImportEntry(SystemNative_ReadProcessStatusInfo)
DllImportEntry(SystemNative_Log)
DllImportEntry(SystemNative_LogError)
DllImportEntry(SystemNative_AlignedAlloc)
diff --git a/src/native/libs/System.Native/pal_io.c b/src/native/libs/System.Native/pal_io.c
index 74bead7dc0ada9..85c87b577060c6 100644
--- a/src/native/libs/System.Native/pal_io.c
+++ b/src/native/libs/System.Native/pal_io.c
@@ -1812,36 +1812,6 @@ int32_t SystemNative_CanGetHiddenFlag(void)
#endif
}
-int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStatus)
-{
-#ifdef __sun
- char statusFilename[64];
- snprintf(statusFilename, sizeof(statusFilename), "/proc/%d/psinfo", pid);
-
- intptr_t fd;
- while ((fd = open(statusFilename, O_RDONLY)) < 0 && errno == EINTR);
- if (fd < 0)
- {
- return 0;
- }
-
- psinfo_t status;
- int result = Common_Read(fd, &status, sizeof(psinfo_t));
- close(fd);
- if (result >= 0)
- {
- processStatus->ResidentSetSize = status.pr_rssize * 1024; // pr_rssize is in Kbytes
- return 1;
- }
-
- return 0;
-#else
- (void)pid, (void)processStatus;
- errno = ENOTSUP;
- return -1;
-#endif // __sun
-}
-
int32_t SystemNative_PRead(intptr_t fd, void* buffer, int32_t bufferSize, int64_t fileOffset)
{
assert(buffer != NULL);
diff --git a/src/native/libs/System.Native/pal_io.h b/src/native/libs/System.Native/pal_io.h
index 330fa2997b9f20..c4b07f8c0199ce 100644
--- a/src/native/libs/System.Native/pal_io.h
+++ b/src/native/libs/System.Native/pal_io.h
@@ -36,12 +36,6 @@ typedef struct
uint32_t UserFlags; // user defined flags
} FileStatus;
-typedef struct
-{
- size_t ResidentSetSize;
- // add more fields when needed.
-} ProcessStatus;
-
// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.
typedef struct
@@ -804,13 +798,6 @@ PALEXPORT int32_t SystemNative_LChflagsCanSetHiddenFlag(void);
*/
PALEXPORT int32_t SystemNative_CanGetHiddenFlag(void);
-/**
- * Reads the psinfo_t struct and converts into ProcessStatus.
- *
- * Returns 1 if the process status was read; otherwise, 0.
- */
-PALEXPORT int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStatus);
-
/**
* Reads the number of bytes specified into the provided buffer from the specified, opened file descriptor at specified offset.
*
diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c
index 758575e6954f33..386eba5ede1162 100644
--- a/src/native/libs/System.Native/pal_mount.c
+++ b/src/native/libs/System.Native/pal_mount.c
@@ -159,6 +159,14 @@ SystemNative_GetFileSystemTypeNameForMountPoint(const char* name, char* formatNa
}
SafeStringCopy(formatNameBuffer, Int32ToSizeT(bufferLength), stats.f_fstypename);
*formatType = -1;
+#elif HAVE_STATVFS_BASETYPE
+ if (bufferLength < _FSTYPSZ) // SunOS
+ {
+ errno = ERANGE;
+ result = -1;
+ }
+ SafeStringCopy(formatNameBuffer, Int32ToSizeT(bufferLength), stats.f_basetype);
+ *formatType = -1;
#else
SafeStringCopy(formatNameBuffer, Int32ToSizeT(bufferLength), "");
*formatType = (int64_t)(stats.f_type);
diff --git a/src/native/libs/configure.cmake b/src/native/libs/configure.cmake
index 561ab9229986bd..5348adb6b2e0b5 100644
--- a/src/native/libs/configure.cmake
+++ b/src/native/libs/configure.cmake
@@ -324,6 +324,12 @@ check_struct_has_member(
"sys/mount.h"
HAVE_STATVFS_FSTYPENAME)
+check_struct_has_member(
+ "struct statvfs"
+ f_basetype
+ "sys/statvfs.h"
+ HAVE_STATVFS_BASETYPE)
+
set(CMAKE_EXTRA_INCLUDE_FILES dirent.h)
# statfs: Find whether this struct exists
diff --git a/src/native/minipal/debugger.c b/src/native/minipal/debugger.c
index 4fdef053a225a3..763f12079caabd 100644
--- a/src/native/minipal/debugger.c
+++ b/src/native/minipal/debugger.c
@@ -32,6 +32,7 @@
#include
#define MINIPAL_DEBUGGER_PRESENT_CHECK
#elif defined(__sun)
+#include
#include
#include
#include
diff --git a/src/native/minipal/thread.h b/src/native/minipal/thread.h
index 7655f4bf2c7f88..48405dfb3195ae 100644
--- a/src/native/minipal/thread.h
+++ b/src/native/minipal/thread.h
@@ -69,6 +69,8 @@ static inline size_t minipal_get_current_thread_id(void)
tid = (size_t)_lwp_self();
#elif defined(__HAIKU__)
tid = (size_t)find_thread(NULL);
+#elif defined(__sun)
+ tid = (size_t)pthread_self();
#else
tid = (size_t)(void*)pthread_self();
#endif