Skip to content

Commit cfa7b27

Browse files
simonrozsivaldotnet-maestro[bot]am11adamsitnikjeffhandley
authored
[release/6.0] Use CLOCK_BOOTTIME to calculate BootTime on linux (#67589) (#83965)
* Update dependencies from https://github.com/dotnet/emsdk build 20230323.2 (#83857) Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 , Microsoft.NET.Workload.Emscripten.Manifest-6.0.300 , Microsoft.NET.Workload.Emscripten.Manifest-6.0.400 From Version 6.0.15 -> To Version 6.0.16 Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> * Use CLOCK_BOOTTIME to calculate BootTime on linux (#67589) Co-authored-by: Adam Sitnik <[email protected]> Co-authored-by: Jeff Handley <[email protected]> Co-authored-by: Simon Rozsival <[email protected]> * Replace LibraryImport with DllImport in .NET 6.0 * Fix backport * Revert "Update dependencies from https://github.com/dotnet/emsdk build 20230323.2 (#83857)" This reverts commit f2db6d6. * Define TARGET_LINUX --------- Co-authored-by: dotnet-maestro[bot] <42748379+dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: Adeel Mujahid <[email protected]> Co-authored-by: Adam Sitnik <[email protected]> Co-authored-by: Jeff Handley <[email protected]>
1 parent 0254cce commit cfa7b27

File tree

9 files changed

+68
-35
lines changed

9 files changed

+68
-35
lines changed

src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ internal static partial class procfs
1919
private const string FileDescriptorDirectoryName = "/fd/";
2020
private const string TaskDirectoryName = "/task/";
2121

22-
internal const string SelfExeFilePath = RootPath + "self" + ExeFileName;
23-
internal const string SelfCmdLineFilePath = RootPath + "self" + CmdLineFileName;
24-
internal const string ProcStatFilePath = RootPath + "stat";
25-
2622
internal struct ParsedStat
2723
{
2824
// Commented out fields are available in the stat data file but
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices;
5+
6+
internal static partial class Interop
7+
{
8+
internal static partial class Sys
9+
{
10+
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetBootTimeTicks")]
11+
[SuppressGCTransition]
12+
internal static extern long GetBootTimeTicks();
13+
}
14+
}

src/libraries/Native/Unix/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ else ()
220220
endif ()
221221

222222
if (CLR_CMAKE_TARGET_LINUX)
223+
add_definitions(-DTARGET_LINUX)
223224
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")
224225
endif ()
225226

src/libraries/Native/Unix/System.Native/entrypoints.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ static const Entry s_sysNative[] =
235235
DllImportEntry(SystemNative_LowLevelMonitor_Signal_Release)
236236
DllImportEntry(SystemNative_UTimensat)
237237
DllImportEntry(SystemNative_GetTimestamp)
238+
DllImportEntry(SystemNative_GetBootTimeTicks)
238239
DllImportEntry(SystemNative_GetCpuUtilization)
239240
DllImportEntry(SystemNative_GetPwUidR)
240241
DllImportEntry(SystemNative_GetPwNamR)

src/libraries/Native/Unix/System.Native/pal_time.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818

1919
enum
2020
{
21-
SecondsToMicroSeconds = 1000000, // 10^6
22-
SecondsToNanoSeconds = 1000000000, // 10^9
23-
MicroSecondsToNanoSeconds = 1000 // 10^3
21+
MicroSecondsToNanoSeconds = 1000, // 10^3
22+
SecondsToNanoSeconds = 1000000000, // 10^9
23+
SecondsToTicks = 10000000, // 10^7
24+
TicksToNanoSeconds = 100, // 10^2
2425
};
2526

2627
int32_t SystemNative_UTimensat(const char* path, TimeSpec* times)
@@ -62,6 +63,29 @@ uint64_t SystemNative_GetTimestamp()
6263
#endif
6364
}
6465

66+
int64_t SystemNative_GetBootTimeTicks()
67+
{
68+
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
69+
struct timespec ts;
70+
71+
int result = clock_gettime(CLOCK_BOOTTIME, &ts);
72+
assert(result == 0); // only possible errors are if the given clockId isn't supported or &ts is an invalid address
73+
(void)result; // suppress unused parameter warning in release builds
74+
75+
int64_t sinceBootTicks = ((int64_t)ts.tv_sec * SecondsToTicks) + (ts.tv_nsec / TicksToNanoSeconds);
76+
77+
result = clock_gettime(CLOCK_REALTIME_COARSE, &ts);
78+
assert(result == 0);
79+
80+
int64_t sinceEpochTicks = ((int64_t)ts.tv_sec * SecondsToTicks) + (ts.tv_nsec / TicksToNanoSeconds);
81+
const int64_t UnixEpochTicks = 621355968000000000;
82+
83+
return UnixEpochTicks + sinceEpochTicks - sinceBootTicks;
84+
#else
85+
return -1;
86+
#endif
87+
}
88+
6589
int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo)
6690
{
6791
uint64_t kernelTime = 0;
@@ -76,7 +100,7 @@ int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo)
76100
else
77101
{
78102
kernelTime =
79-
((uint64_t)(resUsage.ru_stime.tv_sec) * SecondsToNanoSeconds) +
103+
((uint64_t)(resUsage.ru_stime.tv_sec) * SecondsToNanoSeconds) +
80104
((uint64_t)(resUsage.ru_stime.tv_usec) * MicroSecondsToNanoSeconds);
81105
userTime =
82106
((uint64_t)(resUsage.ru_utime.tv_sec) * SecondsToNanoSeconds) +

src/libraries/Native/Unix/System.Native/pal_time.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ PALEXPORT int32_t SystemNative_UTimensat(const char* path, TimeSpec* times);
3232
*/
3333
PALEXPORT uint64_t SystemNative_GetTimestamp(void);
3434

35+
/**
36+
* Gets system boot time ticks. (Linux only)
37+
*/
38+
PALEXPORT int64_t SystemNative_GetBootTimeTicks(void);
39+
3540
/**
3641
* The main purpose of this function is to compute the overall CPU utilization
3742
* for the CLR thread pool to regulate the number of worker threads.

src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@
306306
Link="Common\Interop\Linux\Interop.ProcFsStat.ParseMapModules.cs" />
307307
<Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs"
308308
Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
309+
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetBootTimeTicks.cs"
310+
Link="Common\Interop\Linux\Interop.GetBootTimeTicks.cs" />
309311
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SchedGetSetAffinity.cs"
310312
Link="Common\Interop\Linux\Interop.SchedGetSetAffinity.cs" />
311313
</ItemGroup>

src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.IO;
1010
using System.Runtime.Versioning;
1111
using System.Text;
12+
using System.Threading;
1213

1314
namespace System.Diagnostics
1415
{
@@ -75,33 +76,25 @@ internal static DateTime BootTimeToDateTime(TimeSpan timespanAfterBoot)
7576
return dt.ToLocalTime();
7677
}
7778

79+
private static long s_bootTimeTicks;
7880
/// <summary>Gets the system boot time.</summary>
7981
private static DateTime BootTime
8082
{
8183
get
8284
{
83-
// '/proc/stat -> btime' gets the boot time.
84-
// btime is the time of system boot in seconds since the Unix epoch.
85-
// It includes suspended time and is updated based on the system time (settimeofday).
86-
const string StatFile = Interop.procfs.ProcStatFilePath;
87-
string text = File.ReadAllText(StatFile);
88-
int btimeLineStart = text.IndexOf("\nbtime ", StringComparison.Ordinal);
89-
if (btimeLineStart >= 0)
90-
{
91-
int btimeStart = btimeLineStart + "\nbtime ".Length;
92-
int btimeEnd = text.IndexOf('\n', btimeStart);
93-
if (btimeEnd > btimeStart)
85+
long bootTimeTicks = Interlocked.Read(ref s_bootTimeTicks);
86+
if (bootTimeTicks == 0)
87+
{
88+
bootTimeTicks = Interop.Sys.GetBootTimeTicks();
89+
long oldValue = Interlocked.CompareExchange(ref s_bootTimeTicks, bootTimeTicks, 0);
90+
if (oldValue != 0) // a different thread has managed to update the ticks first
9491
{
95-
if (long.TryParse(text.AsSpan(btimeStart, btimeEnd - btimeStart), out long bootTimeSeconds))
96-
{
97-
return DateTime.UnixEpoch + TimeSpan.FromSeconds(bootTimeSeconds);
98-
}
92+
bootTimeTicks = oldValue; // consistency
9993
}
100-
}
101-
102-
return DateTime.UtcNow;
103-
}
104-
}
94+
}
95+
return new DateTime(bootTimeTicks);
96+
}
97+
}
10598

10699
/// <summary>Gets the parent process ID</summary>
107100
private int ParentProcessId =>
@@ -248,11 +241,8 @@ private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr
248241
/// <param name="processId">The pid for the target process, or -1 for the current process.</param>
249242
internal static string? GetExePath(int processId = -1)
250243
{
251-
string exeFilePath = processId == -1 ?
252-
Interop.procfs.SelfExeFilePath :
253-
Interop.procfs.GetExeFilePathForProcess(processId);
254-
255-
return Interop.Sys.ReadLink(exeFilePath);
244+
return processId == -1 ? Environment.ProcessPath :
245+
Interop.Sys.ReadLink(Interop.procfs.GetExeFilePathForProcess(processId));
256246
}
257247

258248
/// <summary>Gets the name that was used to start the process, or null if it could not be retrieved.</summary>

src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,13 +1126,13 @@ public void TestGetProcesses()
11261126

11271127
// Get all the processes running on the machine, and check if the current process is one of them.
11281128
var foundCurrentProcess = (from p in Process.GetProcesses()
1129-
where (p.Id == currentProcess.Id) && (p.ProcessName.Equals(currentProcess.ProcessName))
1129+
where (p.Id == currentProcess.Id) && (p.ProcessName.Equals(currentProcess.ProcessName)) && (p.StartTime == currentProcess.StartTime)
11301130
select p).Any();
11311131

11321132
Assert.True(foundCurrentProcess, "TestGetProcesses001 failed");
11331133

11341134
foundCurrentProcess = (from p in Process.GetProcesses(currentProcess.MachineName)
1135-
where (p.Id == currentProcess.Id) && (p.ProcessName.Equals(currentProcess.ProcessName))
1135+
where (p.Id == currentProcess.Id) && (p.ProcessName.Equals(currentProcess.ProcessName)) && (p.StartTime == currentProcess.StartTime)
11361136
select p).Any();
11371137

11381138
Assert.True(foundCurrentProcess, "TestGetProcesses002 failed");

0 commit comments

Comments
 (0)