Skip to content

Commit

Permalink
[release/7.0] Use CLOCK_BOOTTIME to calculate BootTime on linux (#67589
Browse files Browse the repository at this point in the history
…) (#83966)

* Update dependencies from https://github.com/dotnet/emsdk build 20230323.1 (#83860)

Microsoft.NET.Workload.Emscripten.net6.Manifest-7.0.100 , Microsoft.NET.Workload.Emscripten.net7.Manifest-7.0.100
 From Version 7.0.4 -> To Version 7.0.5

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 <adam.sitnik@gmail.com>
Co-authored-by: Jeff Handley <jeffhandley@users.noreply.github.com>
Co-authored-by: Simon Rozsival <simon@rozsival.com>

* Do no test StartTime in GetProcessesByName test (#75334)

* Revert "Update dependencies from https://github.com/dotnet/emsdk build 20230323.1 (#83860)"

This reverts commit 6a533c1.

---------

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 <3840695+am11@users.noreply.github.com>
Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
Co-authored-by: Jeff Handley <jeffhandley@users.noreply.github.com>
  • Loading branch information
6 people committed Mar 31, 2023
1 parent 641393e commit fa133ae
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ internal static partial class @procfs
private const string FileDescriptorDirectoryName = "/fd/";
private const string TaskDirectoryName = "/task/";

internal const string SelfExeFilePath = RootPath + "self" + ExeFileName;
internal const string SelfCmdLineFilePath = RootPath + "self" + CmdLineFileName;
internal const string ProcStatFilePath = RootPath + "stat";

internal struct ParsedStat
{
// Commented out fields are available in the stat data file but
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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 Sys
{
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetBootTimeTicks")]
[SuppressGCTransition]
internal static partial long GetBootTimeTicks();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@
Link="Common\Interop\Linux\Interop.ProcFsStat.ParseMapModules.cs" />
<Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs"
Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetBootTimeTicks.cs"
Link="Common\Interop\Linux\Interop.GetBootTimeTicks.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SchedGetSetAffinity.cs"
Link="Common\Interop\Linux\Interop.SchedGetSetAffinity.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.IO;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;

namespace System.Diagnostics
{
Expand Down Expand Up @@ -79,33 +80,25 @@ internal static DateTime BootTimeToDateTime(TimeSpan timespanAfterBoot)
return dt.ToLocalTime();
}

private static long s_bootTimeTicks;
/// <summary>Gets the system boot time.</summary>
private static DateTime BootTime
{
get
{
// '/proc/stat -> btime' gets the boot time.
// btime is the time of system boot in seconds since the Unix epoch.
// It includes suspended time and is updated based on the system time (settimeofday).
const string StatFile = Interop.procfs.ProcStatFilePath;
string text = File.ReadAllText(StatFile);
int btimeLineStart = text.IndexOf("\nbtime ", StringComparison.Ordinal);
if (btimeLineStart >= 0)
{
int btimeStart = btimeLineStart + "\nbtime ".Length;
int btimeEnd = text.IndexOf('\n', btimeStart);
if (btimeEnd > btimeStart)
long bootTimeTicks = Interlocked.Read(ref s_bootTimeTicks);
if (bootTimeTicks == 0)
{
bootTimeTicks = Interop.Sys.GetBootTimeTicks();
long oldValue = Interlocked.CompareExchange(ref s_bootTimeTicks, bootTimeTicks, 0);
if (oldValue != 0) // a different thread has managed to update the ticks first
{
if (long.TryParse(text.AsSpan(btimeStart, btimeEnd - btimeStart), out long bootTimeSeconds))
{
return DateTime.UnixEpoch + TimeSpan.FromSeconds(bootTimeSeconds);
}
bootTimeTicks = oldValue; // consistency
}
}

return DateTime.UtcNow;
}
}
}
return new DateTime(bootTimeTicks);
}
}

/// <summary>Gets the parent process ID</summary>
private int ParentProcessId =>
Expand Down Expand Up @@ -258,11 +251,8 @@ private static void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out
/// <param name="processId">The pid for the target process, or -1 for the current process.</param>
internal static string? GetExePath(int processId = -1)
{
string exeFilePath = processId == -1 ?
Interop.procfs.SelfExeFilePath :
Interop.procfs.GetExeFilePathForProcess(processId);

return Interop.Sys.ReadLink(exeFilePath);
return processId == -1 ? Environment.ProcessPath :
Interop.Sys.ReadLink(Interop.procfs.GetExeFilePathForProcess(processId));
}

/// <summary>Gets the name that was used to start the process, or null if it could not be retrieved.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1151,13 +1151,13 @@ public void TestGetProcesses()

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

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

foundCurrentProcess = (from p in Process.GetProcesses(currentProcess.MachineName)
where (p.Id == currentProcess.Id) && (p.ProcessName.Equals(currentProcess.ProcessName))
where (p.Id == currentProcess.Id) && (p.ProcessName.Equals(currentProcess.ProcessName)) && (p.StartTime == currentProcess.StartTime)
select p).Any();

Assert.True(foundCurrentProcess, "TestGetProcesses002 failed");
Expand Down
1 change: 1 addition & 0 deletions src/native/libs/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_UTimensat)
DllImportEntry(SystemNative_FUTimens)
DllImportEntry(SystemNative_GetTimestamp)
DllImportEntry(SystemNative_GetBootTimeTicks)
DllImportEntry(SystemNative_GetCpuUtilization)
DllImportEntry(SystemNative_GetPwUidR)
DllImportEntry(SystemNative_GetPwNamR)
Expand Down
30 changes: 27 additions & 3 deletions src/native/libs/System.Native/pal_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@

enum
{
SecondsToMicroSeconds = 1000000, // 10^6
SecondsToNanoSeconds = 1000000000, // 10^9
MicroSecondsToNanoSeconds = 1000 // 10^3
MicroSecondsToNanoSeconds = 1000, // 10^3
SecondsToNanoSeconds = 1000000000, // 10^9
SecondsToTicks = 10000000, // 10^7
TicksToNanoSeconds = 100, // 10^2
};

int32_t SystemNative_UTimensat(const char* path, TimeSpec* times)
Expand Down Expand Up @@ -95,6 +96,29 @@ uint64_t SystemNative_GetTimestamp()
#endif
}

int64_t SystemNative_GetBootTimeTicks()
{
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
struct timespec ts;

int result = clock_gettime(CLOCK_BOOTTIME, &ts);
assert(result == 0); // only possible errors are if the given clockId isn't supported or &ts is an invalid address
(void)result; // suppress unused parameter warning in release builds

int64_t sinceBootTicks = ((int64_t)ts.tv_sec * SecondsToTicks) + (ts.tv_nsec / TicksToNanoSeconds);

result = clock_gettime(CLOCK_REALTIME_COARSE, &ts);
assert(result == 0);

int64_t sinceEpochTicks = ((int64_t)ts.tv_sec * SecondsToTicks) + (ts.tv_nsec / TicksToNanoSeconds);
const int64_t UnixEpochTicks = 621355968000000000;

return UnixEpochTicks + sinceEpochTicks - sinceBootTicks;
#else
return -1;
#endif
}

double SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo)
{
uint64_t kernelTime = 0;
Expand Down
5 changes: 5 additions & 0 deletions src/native/libs/System.Native/pal_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ PALEXPORT int32_t SystemNative_FUTimens(intptr_t fd, TimeSpec* times);
*/
PALEXPORT uint64_t SystemNative_GetTimestamp(void);

/**
* Gets system boot time ticks. (Linux only)
*/
PALEXPORT int64_t SystemNative_GetBootTimeTicks(void);

/**
* The main purpose of this function is to compute the overall CPU utilization
* for the CLR thread pool to regulate the number of worker threads.
Expand Down

0 comments on commit fa133ae

Please sign in to comment.