diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs index 10171374d39c78..851db8f923bcd9 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs @@ -22,15 +22,7 @@ internal DateTime StartTimeCore } } - /// - /// 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 + public partial TimeSpan TotalProcessorTime { get { @@ -45,14 +37,7 @@ public TimeSpan TotalProcessorTime } } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get { @@ -68,11 +53,7 @@ public TimeSpan UserProcessorTime } } - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get { diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs index 4b22d5bbd8e088..ce1bd2180efe2e 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs @@ -13,11 +13,7 @@ namespace System.Diagnostics { public partial class Process : IDisposable { - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get => IsCurrentProcess ? Environment.CpuUsage.PrivilegedTime : TicksToTimeSpan(GetStat().stime); } @@ -83,15 +79,7 @@ private static DateTime BootTime return null; } - /// - /// 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 + public partial TimeSpan TotalProcessorTime { get { @@ -105,14 +93,7 @@ public TimeSpan TotalProcessorTime } } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get => IsCurrentProcess ? Environment.CpuUsage.UserTime : TicksToTimeSpan(GetStat().utime); } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs index f3f236b3f93e5b..629335c06e4705 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs @@ -14,11 +14,7 @@ public partial class Process private const int MicrosecondsToSecondsFactor = 1_000_000; - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get { @@ -57,15 +53,7 @@ internal static string GetPathToOpenFile() return "/usr/bin/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 + public partial TimeSpan TotalProcessorTime { get { @@ -80,14 +68,7 @@ public TimeSpan TotalProcessorTime } } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get { diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs index f3ccc9cb35b613..40c2953d90b101 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OpenBSD.cs @@ -22,15 +22,7 @@ internal DateTime StartTimeCore } } - /// - /// 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 + public partial TimeSpan TotalProcessorTime { get { @@ -45,14 +37,7 @@ public TimeSpan TotalProcessorTime } } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get { @@ -68,11 +53,7 @@ public TimeSpan UserProcessorTime } } - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get { 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 index fb515db23e5ccb..cf0ba747869f0c 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.SunOS.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.SunOS.cs @@ -41,15 +41,7 @@ internal DateTime StartTimeCore return ProcessUtils.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 + public partial TimeSpan TotalProcessorTime { get { @@ -61,14 +53,7 @@ public TimeSpan TotalProcessorTime } } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get { @@ -79,14 +64,7 @@ public TimeSpan UserProcessorTime } } - /// - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get { diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index 8686f53aeb076d..489459e5c00483 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -192,11 +192,7 @@ private DateTime ExitTimeCore get { return GetProcessTimes().ExitTime; } } - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get => IsCurrentProcess ? Environment.CpuUsage.PrivilegedTime : GetProcessTimes().PrivilegedProcessorTime; } @@ -207,27 +203,12 @@ internal DateTime StartTimeCore get { return GetProcessTimes().StartTime; } } - /// - /// 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 + public partial TimeSpan TotalProcessorTime { get => IsCurrentProcess ? Environment.CpuUsage.TotalTime : GetProcessTimes().TotalProcessorTime; } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get => IsCurrentProcess ? Environment.CpuUsage.UserTime : GetProcessTimes().UserProcessorTime; } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index 7450c980e98069..c088f4226902d8 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -188,6 +188,15 @@ public bool HasExited } /// Gets the time the associated process was started. + /// + /// On Windows, if a handle to the process is available, this property can be read after the process exits. + /// On Unix, this property caches its value on first access. A cached value can be read after the process exits, + /// but accessing an uncached value after the process exits may throw when the value is unavailable. + /// + /// + /// No process is associated with this object; on Windows, there is no process handle available; or on Unix, + /// the value was not cached and is unavailable after the process exited. + /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] [SupportedOSPlatform("maccatalyst")] @@ -203,11 +212,11 @@ public DateTime StartTime } } - /// - /// - /// Gets the time that the associated process exited. - /// - /// + /// Gets the time that the associated process exited. + /// + /// No process is associated with this object, the process has not exited, + /// or there is no process handle available. + /// public DateTime ExitTime { get @@ -222,6 +231,57 @@ public DateTime ExitTime } } + /// Gets the amount of time the process has spent running code inside the operating system core. + /// + /// On Windows, if a handle to the process is available, the value can be retrieved after the process exits. + /// On Unix, the value is unavailable after the process exits. + /// + /// + /// No process is associated with this object. On Windows, there is no process handle available. + /// On Unix, the process has exited. + /// + /// The operating system could not retrieve process timing information. + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] + public partial TimeSpan PrivilegedProcessorTime { get; } + + /// + /// Gets the amount of time the associated process has spent utilizing the CPU. + /// It is the sum of the and . + /// + /// + /// On Windows, if a handle to the process is available, the value can be retrieved after the process exits. + /// On Unix, the value is unavailable after the process exits. + /// + /// + /// No process is associated with this object. On Windows, there is no process handle available. + /// On Unix, the process has exited. + /// + /// The operating system could not retrieve process timing information. + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] + public partial TimeSpan TotalProcessorTime { get; } + + /// + /// Gets the amount of time the associated process has spent running code + /// inside the application portion of the process (not the operating system core). + /// + /// + /// On Windows, if a handle to the process is available, the value can be retrieved after the process exits. + /// On Unix, the value is unavailable after the process exits. + /// + /// + /// No process is associated with this object. On Windows, there is no process handle available. + /// On Unix, the process has exited. + /// + /// The operating system could not retrieve process timing information. + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] + public partial TimeSpan UserProcessorTime { get; } + /// /// /// Gets diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs index 2a5d3ddb785291..e2bd120e8e1cc8 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs @@ -18,11 +18,7 @@ public void Kill(bool entireProcessTree) throw new PlatformNotSupportedException(); } - /// 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 + public partial TimeSpan PrivilegedProcessorTime { get { throw new PlatformNotSupportedException(); } } @@ -35,27 +31,12 @@ internal static DateTime StartTimeCore get { throw new PlatformNotSupportedException(); } } - /// - /// 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 + public partial TimeSpan TotalProcessorTime { get { throw new PlatformNotSupportedException(); } } - /// - /// 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 + public partial TimeSpan UserProcessorTime { get { throw new PlatformNotSupportedException(); } } diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index a8f25b6f282d98..d903ced342bc91 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -751,6 +751,49 @@ public void TestExitTime() Assert.InRange(p.ExitTime.ToUniversalTime(), timeBeforeProcessStart, DateTime.MaxValue); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TimingProperties_AfterProcessExit() + { + using Process process = CreateProcess(static () => RemoteExecutor.SuccessExitCode); + process.Start(); + Assert.True(process.WaitForExit(WaitInMS)); + + Assert.Equal(RemoteExecutor.SuccessExitCode, process.ExitCode); + Assert.NotEqual(default, process.ExitTime); + + if (OperatingSystem.IsWindows()) + { + // Windows process handles retain timing information after the process exits. + Assert.NotEqual(default, process.StartTime); + Assert.InRange(process.PrivilegedProcessorTime, TimeSpan.Zero, TimeSpan.MaxValue); + Assert.InRange(process.TotalProcessorTime, TimeSpan.Zero, TimeSpan.MaxValue); + Assert.InRange(process.UserProcessorTime, TimeSpan.Zero, TimeSpan.MaxValue); + } + else + { + // Unix reaps child processes after exit. Processor times are unavailable, and StartTime was not + // accessed before exit and therefore was not cached. + Assert.Throws(() => process.StartTime); + Assert.Throws(() => process.PrivilegedProcessorTime); + Assert.Throws(() => process.TotalProcessorTime); + Assert.Throws(() => process.UserProcessorTime); + } + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void StartTime_AfterProcessExit_WhenCachedBeforeExit_IsAvailable() + { + using Process process = CreateProcessLong(); + process.Start(); + DateTime startTime = process.StartTime; + + process.Kill(); + Assert.True(process.WaitForExit(WaitInMS)); + + Assert.NotEqual(default, startTime); + Assert.Equal(startTime, process.StartTime); + } + [Fact] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "libproc is not supported on iOS/tvOS")] public void StartTime_GetNotStarted_ThrowsInvalidOperationException()