mirror of https://github.com/dotnet/runtime
Merge 3a905a1603
into 02596ba8d9
This commit is contained in:
commit
a49ccbcde9
|
@ -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</_NativeAotSupportedArch>
|
||||
<NativeAotSupported Condition="'$(_NativeAotSupportedOS)' == 'true' and '$(_NativeAotSupportedArch)' == 'true'">true</NativeAotSupported>
|
||||
|
||||
<!-- TODO: workaround for https://github.com/dotnet/runtime/issues/116929 -->
|
||||
<_Crossgen2Supported Condition="'$(TargetOS)' != 'illumos' and '$(TargetOS)' != 'solaris'">true</_Crossgen2Supported>
|
||||
|
||||
<!-- Determine if we support running the .NET SDK on the target platform -->
|
||||
<_SdkToolsSupportedOS Condition="'$(TargetsMobile)' != 'true' and '$(TargetsLinuxBionic)' != 'true'">true</_SdkToolsSupportedOS>
|
||||
<_SdkToolsSupportedArch Condition="'$(TargetArchitecture)' != 'armel'">true</_SdkToolsSupportedArch>
|
||||
|
@ -451,7 +454,7 @@
|
|||
<ProjectToBuild Include="$(CoreClrProjectRoot).nuget\Microsoft.CrossOsDiag.Private.CoreCLR\Microsoft.CrossOsDiag.Private.CoreCLR.proj" Category="clr" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(_subset.Contains('+clr.tools+'))">
|
||||
<ItemGroup Condition="$(_subset.Contains('+clr.tools+')) and '$(_Crossgen2Supported)' == 'true'">
|
||||
<ProjectToBuild Include="$(CoreClrProjectRoot)tools\runincontext\runincontext.csproj;
|
||||
$(CoreClrProjectRoot)tools\tieringtest\tieringtest.csproj;
|
||||
$(CoreClrProjectRoot)tools\r2rdump\R2RDump.csproj;
|
||||
|
@ -683,7 +686,7 @@
|
|||
<SharedFrameworkProjectToBuild Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<SharedFrameworkProjectToBuild Condition="'$(RuntimeFlavor)' != 'Mono' and ('$(TargetsMobile)' != 'true' and '$(TargetsLinuxBionic)' != 'true')" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Crossgen2.sfxproj" />
|
||||
<SharedFrameworkProjectToBuild Condition="'$(RuntimeFlavor)' != 'Mono' and ('$(TargetsMobile)' != 'true' and '$(TargetsLinuxBionic)' != 'true') and '$(_Crossgen2Supported)' == 'true'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Crossgen2.sfxproj" />
|
||||
<SharedFrameworkProjectToBuild Condition="'$(RuntimeFlavor)' == '$(PrimaryRuntimeFlavor)' and '$(TargetsMobile)' != 'true'" Include="$(InstallerProjectRoot)pkg\sfx\installers\dotnet-host.proj" />
|
||||
<SharedFrameworkProjectToBuild Condition="'$(RuntimeFlavor)' == '$(PrimaryRuntimeFlavor)' and '$(TargetsMobile)' != 'true'" Include="$(InstallerProjectRoot)pkg\sfx\installers\dotnet-hostfxr.proj" />
|
||||
<SharedFrameworkProjectToBuild Condition="'$(RuntimeFlavor)' == '$(PrimaryRuntimeFlavor)' and '$(TargetsMobile)' != 'true'" Include="$(InstallerProjectRoot)pkg\sfx\installers\dotnet-runtime-deps\*.proj" />
|
||||
|
@ -698,7 +701,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.
|
||||
-->
|
||||
<ProjectToBuild Condition="'$(RuntimeFlavor)' != 'Mono' and '$(TargetsMobile)' != 'true' and '$(TargetsLinuxBionic)' != 'true' and '$(BuildHostTools)' == 'true'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Crossgen2.Host.sfxproj" Category="packs" />
|
||||
<ProjectToBuild Condition="'$(RuntimeFlavor)' != 'Mono' and '$(TargetsMobile)' != 'true' and '$(TargetsLinuxBionic)' != 'true' and '$(BuildHostTools)' == 'true' and '$(_Crossgen2Supported)' == 'true'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Crossgen2.Host.sfxproj" Category="packs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<SharedFrameworkProjectToBuild Condition="'$(_BuildHostPack)' == 'true'" Include="$(InstallerProjectRoot)pkg\archives\dotnet-nethost.proj" />
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -9,11 +9,6 @@
|
|||
<OfficialBuildRID Include="osx-arm64">
|
||||
<Platform>arm64</Platform>
|
||||
</OfficialBuildRID>
|
||||
<!-- Not currently built by CoreFX. -->
|
||||
<!-- <OfficialBuildRID Include="netbsd-x64" /> -->
|
||||
<!-- <OfficialBuildRID Include="illumos-x64" /> -->
|
||||
<!-- <OfficialBuildRID Include="solaris-x64" /> -->
|
||||
<!-- <OfficialBuildRID Include="haiku-x64" /> -->
|
||||
<OfficialBuildRID Include="win-x86">
|
||||
<Platform>x86</Platform>
|
||||
</OfficialBuildRID>
|
||||
|
@ -51,6 +46,10 @@
|
|||
in our runtime.json to enable others to provide them. -->
|
||||
<UnofficialBuildRID Include="freebsd-x64" />
|
||||
<UnofficialBuildRID Include="freebsd-arm64" />
|
||||
<UnofficialBuildRID Include="illumos-x64" />
|
||||
<UnofficialBuildRID Include="solaris-x64" />
|
||||
<UnofficialBuildRID Include="netbsd-x64" />
|
||||
<UnofficialBuildRID Include="haiku-x64" />
|
||||
<UnofficialBuildRID Include="tizen.4.0.0-armel">
|
||||
<Platform>armel</Platform>
|
||||
</UnofficialBuildRID>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// 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 @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;
|
||||
|
||||
// Output type for TryGetProcessInfoById()
|
||||
// Keep in sync with pal_io.h ProcessStatus
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct ProcessInfo
|
||||
{
|
||||
internal ulong VirtualSize;
|
||||
internal ulong ResidentSetSize;
|
||||
internal long StartTime;
|
||||
internal long StartTimeNsec;
|
||||
internal long CpuTotalTime;
|
||||
internal long CpuTotalTimeNsec;
|
||||
internal int Pid;
|
||||
internal int ParentPid;
|
||||
internal int SessionId;
|
||||
internal int Priority;
|
||||
internal int NiceVal;
|
||||
// add more fields when needed.
|
||||
}
|
||||
|
||||
// Output type for TryGetThreadInfoById()
|
||||
// Keep in sync with pal_io.h ThreadStatus
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct ThreadInfo
|
||||
{
|
||||
internal long StartTime;
|
||||
internal long StartTimeNsec;
|
||||
internal long CpuTotalTime; // user+sys
|
||||
internal long CpuTotalTimeNsec;
|
||||
internal int Tid;
|
||||
internal int Priority;
|
||||
internal int NiceVal;
|
||||
internal char StatusCode;
|
||||
// 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}";
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// 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.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class @procfs
|
||||
{
|
||||
|
||||
// See caller: ProcessManager.SunOS.cs
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadProcessStatusInfo", SetLastError = true)]
|
||||
private static unsafe partial int ReadProcessStatusInfo(int pid, ProcessInfo* processInfo, byte* argBuf, int argBufSize);
|
||||
|
||||
// Handy helpers for Environment.SunOS etc.
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get status info for the specified process ID.
|
||||
/// </summary>
|
||||
/// <param name="pid">PID of the process to read status info for.</param>
|
||||
/// <param name="processInfo">The pointer to ProcessInfo instance.</param>
|
||||
/// <returns>
|
||||
/// true if the process status was read; otherwise, false.
|
||||
/// </returns>
|
||||
internal static unsafe bool TryGetProcessInfoById(int pid, out ProcessInfo processInfo)
|
||||
{
|
||||
ProcessInfo info = default;
|
||||
if (ReadProcessStatusInfo(pid, &info, null, 0) < 0)
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Sys.GetLastErrorInfo();
|
||||
throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
|
||||
}
|
||||
processInfo = info;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Variant that also gets the arg string.
|
||||
internal static unsafe bool TryGetProcessInfoById(int pid, out ProcessInfo processInfo, out string argString)
|
||||
{
|
||||
ProcessInfo info = default;
|
||||
byte* argBuf = stackalloc byte[PRARGSZ];
|
||||
if (ReadProcessStatusInfo(pid, &info, argBuf, PRARGSZ) < 0)
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Sys.GetLastErrorInfo();
|
||||
throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
|
||||
}
|
||||
processInfo = info;
|
||||
argString = Marshal.PtrToStringUTF8((IntPtr)argBuf)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// 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.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class @procfs
|
||||
{
|
||||
|
||||
// See caller: ProcessManager.SunOS.cs
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadProcessLwpInfo", SetLastError = true)]
|
||||
internal static unsafe partial int ReadProcessLwpInfo(int pid, int tid, ThreadInfo* threadInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get status info for the specified thread ID.
|
||||
/// </summary>
|
||||
/// <param name="pid">PID of the process to read status info for.</param>
|
||||
/// <param name="tid">TID of the thread to read status info for.</param>
|
||||
/// <param name="threadInfo">The pointer to ThreadInfo instance.</param>
|
||||
/// <returns>
|
||||
/// true if the process status was read; otherwise, false.
|
||||
/// </returns>
|
||||
internal static unsafe bool TryGetThreadInfoById(int pid, int tid, out ThreadInfo threadInfo)
|
||||
{
|
||||
ThreadInfo info = default;
|
||||
if (ReadProcessLwpInfo(pid, tid, &info) < 0)
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Sys.GetLastErrorInfo();
|
||||
throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
|
||||
}
|
||||
threadInfo = info;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to get status info for the specified process ID.
|
||||
/// </summary>
|
||||
/// <param name="pid">PID of the process to read status info for.</param>
|
||||
/// <param name="processStatus">The pointer to processStatus instance.</param>
|
||||
/// <returns>
|
||||
/// true if the process status was read; otherwise, false.
|
||||
/// </returns>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ namespace System
|
|||
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;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)</TargetFrameworks>
|
||||
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-solaris;$(NetCoreAppCurrent)</TargetFrameworks>
|
||||
<DefineConstants>$(DefineConstants);FEATURE_REGISTRY</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<UseCompilerGeneratedDocXmlFile>false</UseCompilerGeneratedDocXmlFile>
|
||||
|
@ -369,6 +369,19 @@
|
|||
Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'illumos' or '$(TargetPlatformIdentifier)' == 'solaris'">
|
||||
<Compile Include="System\Diagnostics\Process.BSD.cs" />
|
||||
<Compile Include="System\Diagnostics\Process.SunOS.cs" />
|
||||
<Compile Include="System\Diagnostics\ProcessManager.SunOS.cs" />
|
||||
<Compile Include="System\Diagnostics\ProcessThread.SunOS.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\SunOS\procfs\Interop.ProcFs.Definitions.cs"
|
||||
Link="Common\Interop\SunOS\procfs\Interop.ProcFs.Definitions.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\SunOS\procfs\Interop.ProcFs.TryGetProcessInfoById.cs"
|
||||
Link="Common\Interop\SunOS\procfs\Interop.ProcFs.TryGetProcessInfoById.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\SunOS\procfs\Interop.ProcFs.TryGetThreadInfoById.cs"
|
||||
Link="Common\Interop\SunOS\procfs\Interop.ProcFs.TryGetThreadInfoById.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
|
||||
<Compile Include="System\Diagnostics\Process.iOS.cs" />
|
||||
<Compile Include="System\Diagnostics\ProcessManager.iOS.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
|
||||
{
|
||||
|
||||
/// <summary>Gets the time the associated process was started.</summary>
|
||||
internal DateTime StartTimeCore
|
||||
{
|
||||
get
|
||||
{
|
||||
Interop.procfs.ProcessInfo iinfo = GetProcInfo();
|
||||
|
||||
DateTime startTime = DateTime.UnixEpoch +
|
||||
TimeSpan.FromSeconds(iinfo.StartTime) +
|
||||
TimeSpan.FromMicroseconds(iinfo.StartTimeNsec / 1000);
|
||||
|
||||
// The return value is expected to be in the local time zone.
|
||||
return startTime.ToLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the parent process ID</summary>
|
||||
private int ParentProcessId => GetProcInfo().ParentPid;
|
||||
|
||||
/// <summary>Gets execution path</summary>
|
||||
private static string? GetPathToOpenFile()
|
||||
{
|
||||
return FindProgramInPath("xdg-open");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of time the associated process has spent utilizing the CPU.
|
||||
/// It is the sum of the <see cref='System.Diagnostics.Process.UserProcessorTime'/> and
|
||||
/// <see cref='System.Diagnostics.Process.PrivilegedProcessorTime'/>.
|
||||
/// </summary>
|
||||
[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) +
|
||||
TimeSpan.FromMicroseconds(iinfo.CpuTotalTimeNsec / 1000);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of time the associated process has spent running code
|
||||
/// inside the application portion of the process (not the operating system core).
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of time the process has spent running code inside the operating
|
||||
/// system core.
|
||||
/// </summary>
|
||||
[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 ----
|
||||
// ----------------------------------
|
||||
|
||||
/// <summary>Gets the name that was used to start the process, or null if it could not be retrieved.</summary>
|
||||
internal static string? GetUntruncatedProcessName(ref Interop.procfs.ProcessInfo iProcInfo, ref string argString)
|
||||
{
|
||||
// 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 (iProcInfo.Pid != 0 && !string.IsNullOrEmpty(argString))
|
||||
{
|
||||
string[] argv = argString.Split(' ', 2);
|
||||
if (!string.IsNullOrEmpty(argv[0]))
|
||||
{
|
||||
return Path.GetFileName(argv[0]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Reads the information for this process from the procfs file system.</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
// 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
|
||||
{
|
||||
/// <summary>Gets the IDs of all processes on the current machine.</summary>
|
||||
public static int[] GetProcessIds()
|
||||
{
|
||||
IEnumerable<int> pids = EnumerateProcessIds();
|
||||
return new List<int>(pids).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>Gets process infos for each process on the specified machine.</summary>
|
||||
/// <param name="processNameFilter">Optional process name to use as an inclusion filter.</param>
|
||||
/// <param name="machineName">The target machine.</param>
|
||||
/// <returns>An array of process infos, one per found process.</returns>
|
||||
public static ProcessInfo[] GetProcessInfos(string? processNameFilter, string machineName)
|
||||
{
|
||||
ThrowIfRemoteMachine(machineName);
|
||||
|
||||
// Iterate through all process IDs to load information about each process
|
||||
IEnumerable<int> pids = EnumerateProcessIds();
|
||||
ArrayBuilder<ProcessInfo> processes = default;
|
||||
foreach (int pid in pids)
|
||||
{
|
||||
ProcessInfo? pi = CreateProcessInfo(pid, processNameFilter);
|
||||
if (pi != null)
|
||||
{
|
||||
processes.Add(pi);
|
||||
}
|
||||
}
|
||||
|
||||
return processes.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>Gets an array of module infos for the specified process.</summary>
|
||||
/// <param name="processId">The ID of the process whose modules should be enumerated.</param>
|
||||
/// <returns>The array of modules.</returns>
|
||||
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.
|
||||
|
||||
string argString;
|
||||
Interop.procfs.ProcessInfo iProcInfo;
|
||||
if (Interop.procfs.TryGetProcessInfoById(processId, out iProcInfo, out argString))
|
||||
{
|
||||
string? fullName = Process.GetUntruncatedProcessName(ref iProcInfo, ref argString);
|
||||
if (!string.IsNullOrEmpty(fullName))
|
||||
{
|
||||
return new ProcessModuleCollection(1)
|
||||
{
|
||||
new ProcessModule(fullName, Path.GetFileName(fullName))
|
||||
};
|
||||
}
|
||||
}
|
||||
return new ProcessModuleCollection(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ProcessInfo from the specified process ID.
|
||||
/// </summary>
|
||||
internal static ProcessInfo? CreateProcessInfo(int pid, string? processNameFilter = null)
|
||||
{
|
||||
// Negative PIDs aren't valid
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(pid);
|
||||
|
||||
Interop.procfs.ProcessInfo iProcInfo;
|
||||
string argString;
|
||||
if (!Interop.procfs.TryGetProcessInfoById(pid, out iProcInfo, out argString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string? processName = Process.GetUntruncatedProcessName(ref iProcInfo, ref argString);
|
||||
if (!string.IsNullOrEmpty(processNameFilter) &&
|
||||
!string.Equals(processName, processNameFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateProcessInfo(ref iProcInfo, ref argString);
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// ---- Unix PAL layer ends here ----
|
||||
// ----------------------------------
|
||||
|
||||
/// <summary>Enumerates the IDs of all processes on the current machine.</summary>
|
||||
internal static IEnumerable<int> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Enumerates the IDs of all threads in the specified process.</summary>
|
||||
internal static IEnumerable<int> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ProcessInfo from the data read from a /proc/pid/psinfo file and the associated lwp directory.
|
||||
/// </summary>
|
||||
internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ProcessInfo iProcInfo, ref string argString)
|
||||
{
|
||||
int pid = iProcInfo.Pid;
|
||||
|
||||
string? name = Process.GetUntruncatedProcessName(ref iProcInfo, ref argString);
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
// We were able to read the psinfo for this process, but the /proc reader
|
||||
// found no arg string. The process exists, so rather than fail for this,
|
||||
// just continue with a made-up arg string.
|
||||
// Debug.Fail($"Failed to get args for proc {0:D}");
|
||||
name = string.Format("<proc_{0:D}>", pid);
|
||||
}
|
||||
|
||||
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<int> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ThreadInfo from the data read from a /proc/pid/lwp/lwpsinfo file.
|
||||
/// </summary>
|
||||
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.StatusCode),
|
||||
_threadWaitReason = ThreadWaitReason.Unknown
|
||||
};
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
/// <summary>Gets a ThreadState to represent the value returned from the status field of /proc/pid/stat.</summary>
|
||||
/// <param name="c">The status field value.</param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
||||
/// <summary>Gets the time this thread was started.</summary>
|
||||
internal DateTime GetStartTime()
|
||||
{
|
||||
Interop.procfs.ThreadInfo iinfo = GetThreadInfo();
|
||||
|
||||
DateTime startTime = DateTime.UnixEpoch +
|
||||
TimeSpan.FromSeconds(iinfo.StartTime) +
|
||||
TimeSpan.FromMicroseconds(iinfo.StartTimeNsec / 1000);
|
||||
|
||||
// The return value is expected to be in the local time zone.
|
||||
return startTime.ToLocalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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) +
|
||||
TimeSpan.FromMicroseconds(iinfo.CpuTotalTimeNsec / 1000);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of time the associated thread has spent running code
|
||||
/// inside the application (not the operating system core).
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of time the thread has spent running code inside the operating
|
||||
/// system core.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>Reads the information for this thread from the procfs file system.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -652,7 +652,7 @@ namespace System.Diagnostics.Tests
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -700,7 +700,7 @@ namespace System.Diagnostics.Tests
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -2625,7 +2625,8 @@
|
|||
<Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs" Condition="'$(TargetsLinux)' == 'true'" Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
|
||||
<Compile Include="$(CommonPath)System\IO\StringParser.cs" Condition="'$(TargetsLinux)' == 'true'" Link="Common\System\IO\StringParser.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\OSX\Interop.libproc.GetProcessInfoById.cs" Condition="'$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true'" Link="Common\Interop\OSX\Interop.libproc.GetProcessInfoById.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\SunOS\procfs\Interop.ProcFsStat.TryReadProcessStatusInfo.cs" Condition="'$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true'" Link="Common\Interop\SunOS\Interop.ProcFsStat.TryReadProcessStatusInfo.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\SunOS\procfs\Interop.ProcFs.Definitions.cs" Condition="'$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true'" Link="Common\Interop\SunOS\Interop.ProcFs.Definitions.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\SunOS\procfs\Interop.ProcFs.TryGetProcessInfoById.cs" Condition="'$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true'" Link="Common\Interop\SunOS\Interop.ProcFs.GetProcessInfoById.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\Linux\os-release\Interop.OSReleaseFile.cs" Condition="'$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true'" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.FreeBSD.cs" Condition="'$(TargetsFreeBSD)' == 'true'" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ static const Entry s_sysNative[] =
|
|||
DllImportEntry(SystemNative_LChflagsCanSetHiddenFlag)
|
||||
DllImportEntry(SystemNative_FChflags)
|
||||
DllImportEntry(SystemNative_CanGetHiddenFlag)
|
||||
DllImportEntry(SystemNative_ReadProcessLwpInfo)
|
||||
DllImportEntry(SystemNative_ReadProcessStatusInfo)
|
||||
DllImportEntry(SystemNative_Log)
|
||||
DllImportEntry(SystemNative_LogError)
|
||||
|
|
|
@ -1814,9 +1814,57 @@ int32_t SystemNative_CanGetHiddenFlag(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStatus)
|
||||
int32_t SystemNative_ReadProcessLwpInfo(int32_t pid, int32_t tid, ThreadStatus* threadStatus)
|
||||
{
|
||||
#ifdef __sun
|
||||
char statusFilename[64];
|
||||
snprintf(statusFilename, sizeof(statusFilename), "/proc/%d/lwp/%d/lwpsinfo", pid, tid);
|
||||
|
||||
intptr_t fd;
|
||||
while ((fd = open(statusFilename, O_RDONLY)) < 0 && errno == EINTR);
|
||||
if (fd < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
lwpsinfo_t pr;
|
||||
int result = Common_Read(fd, &pr, sizeof(pr));
|
||||
close(fd);
|
||||
if (result < sizeof (pr))
|
||||
{
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
threadStatus->Tid = pr.pr_lwpid;
|
||||
threadStatus->Priority = pr.pr_pri;
|
||||
threadStatus->NiceVal = pr.pr_nice;
|
||||
// Status code, a char: ...
|
||||
threadStatus->StatusCode = (char)pr.pr_sname;
|
||||
// Thread start time and CPU time
|
||||
threadStatus->StartTime = pr.pr_start.tv_sec;
|
||||
threadStatus->StartTimeNsec = pr.pr_start.tv_nsec;
|
||||
threadStatus->CpuTotalTime = pr.pr_time.tv_sec;
|
||||
threadStatus->CpuTotalTimeNsec = pr.pr_time.tv_nsec;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
(void)pid, (void)tid, (void)threadStatus;
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
#endif // __sun
|
||||
}
|
||||
|
||||
// The struct passing is limited, so the args string is handled separately here.
|
||||
int32_t SystemNative_ReadProcessStatusInfo(int32_t pid, ProcessStatus* processStatus, uint8_t *argBuf, int32_t argBufSize)
|
||||
{
|
||||
#ifdef __sun
|
||||
if (argBufSize != 0 && argBufSize < PRARGSZ)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char statusFilename[64];
|
||||
snprintf(statusFilename, sizeof(statusFilename), "/proc/%d/psinfo", pid);
|
||||
|
||||
|
@ -1827,18 +1875,36 @@ int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStat
|
|||
return 0;
|
||||
}
|
||||
|
||||
psinfo_t status;
|
||||
int result = Common_Read(fd, &status, sizeof(psinfo_t));
|
||||
psinfo_t pr;
|
||||
int result = Common_Read(fd, &pr, sizeof(pr));
|
||||
close(fd);
|
||||
if (result >= 0)
|
||||
if (result < sizeof (pr))
|
||||
{
|
||||
processStatus->ResidentSetSize = status.pr_rssize * 1024; // pr_rssize is in Kbytes
|
||||
return 1;
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
processStatus->Pid = pr.pr_pid;
|
||||
processStatus->ParentPid = pr.pr_ppid;
|
||||
processStatus->SessionId = pr.pr_sid;
|
||||
processStatus->Priority = pr.pr_lwp.pr_pri;
|
||||
processStatus->NiceVal = pr.pr_lwp.pr_nice;
|
||||
// pr_size and pr_rsize are in Kbytes.
|
||||
processStatus->VirtualSize = (uint64_t)pr.pr_size * 1024;
|
||||
processStatus->ResidentSetSize = (uint64_t)pr.pr_rssize * 1024;
|
||||
processStatus->StartTime = pr.pr_start.tv_sec;
|
||||
processStatus->StartTimeNsec = pr.pr_start.tv_nsec;
|
||||
processStatus->CpuTotalTime = pr.pr_time.tv_sec;
|
||||
processStatus->CpuTotalTimeNsec = pr.pr_time.tv_nsec;
|
||||
|
||||
if (argBuf != NULL && argBufSize != 0)
|
||||
{
|
||||
SafeStringCopy((char*)argBuf, PRARGSZ, pr.pr_psargs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
(void)pid, (void)processStatus;
|
||||
(void)pid, (void)processStatus, (void)argBuf, (void)argBufSize;
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
#endif // __sun
|
||||
|
|
|
@ -38,10 +38,35 @@ typedef struct
|
|||
|
||||
typedef struct
|
||||
{
|
||||
size_t ResidentSetSize;
|
||||
// note: sorted by size to avoid alignment padding
|
||||
uint64_t VirtualSize;
|
||||
uint64_t ResidentSetSize;
|
||||
int64_t StartTime; // time proc. started
|
||||
int64_t StartTimeNsec;
|
||||
int64_t CpuTotalTime; // Cumulative CPU time (user+sys)
|
||||
int64_t CpuTotalTimeNsec;
|
||||
int32_t Pid;
|
||||
int32_t ParentPid;
|
||||
int32_t SessionId;
|
||||
int32_t Priority;
|
||||
int32_t NiceVal;
|
||||
// add more fields when needed.
|
||||
} ProcessStatus;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// note: sorted by size to avoid alignment padding
|
||||
int64_t StartTime; // time thread started
|
||||
int64_t StartTimeNsec;
|
||||
int64_t CpuTotalTime; // cumulative CPU time (user+sys)
|
||||
int64_t CpuTotalTimeNsec;
|
||||
int32_t Tid;
|
||||
int32_t Priority;
|
||||
int32_t NiceVal;
|
||||
uint8_t StatusCode; // [ORSTWZ] See ProcFsStateToThreadState()
|
||||
// add more fields when needed.
|
||||
} ThreadStatus;
|
||||
|
||||
// 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,12 +829,19 @@ PALEXPORT int32_t SystemNative_LChflagsCanSetHiddenFlag(void);
|
|||
*/
|
||||
PALEXPORT int32_t SystemNative_CanGetHiddenFlag(void);
|
||||
|
||||
/**
|
||||
* Reads the lwpsinfo_t struct and converts into ThreadStatus.
|
||||
*
|
||||
* Returns 0 on success; otherwise, returns -1 and sets errno.
|
||||
*/
|
||||
PALEXPORT int32_t SystemNative_ReadProcessLwpInfo(int32_t pid, int32_t tid, ThreadStatus* threadStatus);
|
||||
|
||||
/**
|
||||
* Reads the psinfo_t struct and converts into ProcessStatus.
|
||||
*
|
||||
* Returns 1 if the process status was read; otherwise, 0.
|
||||
* Returns 0 on success; otherwise, returns -1 and sets errno.
|
||||
*/
|
||||
PALEXPORT int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStatus);
|
||||
PALEXPORT int32_t SystemNative_ReadProcessStatusInfo(int32_t pid, ProcessStatus* processStatus, uint8_t *argBuf, int32_t argBufSize);
|
||||
|
||||
/**
|
||||
* Reads the number of bytes specified into the provided buffer from the specified, opened file descriptor at specified offset.
|
||||
|
|
|
@ -553,6 +553,9 @@ static int32_t ConvertRLimitResourcesPalToPlatform(RLimitResources value)
|
|||
#ifdef RLIMIT_RSS
|
||||
case PAL_RLIMIT_RSS:
|
||||
return RLIMIT_RSS;
|
||||
#elif defined(RLIMIT_VMEM)
|
||||
case PAL_RLIMIT_RSS:
|
||||
return RLIMIT_VMEM;
|
||||
#endif
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
case PAL_RLIMIT_MEMLOCK:
|
||||
|
|
Loading…
Reference in New Issue