Skip to content

Get-Uptime is very slightly but increasingly inaccurate relative to Task Manager's up time #24432

Open

Description

@evelyn-bi

Prerequisites

Steps to reproduce

Open Task Manager to the Performance tab and click on the CPU item on the left, and open a PowerShell command window simultaneously next to it. In the PowerShell window run Get-Uptime, note how the time doesn't match that given by Task Manager (listed at the bottom of the CPU page), it can be off by seconds to minutes or more depending on the uptime of the system. You can see in this StackOverflow answer how various methods of getting uptime have a drift relative to the "real" uptime as given by Task Manager (let's all assume Microsoft got it correct in Task Manager and it's the other methods that are wrong): https://stackoverflow.com/a/34010110/15743074, some methods have no drift while others do.

More info: The C# code behind Get-Uptime uses StopWatch.GetTimeStamp() which uses QueryPerformanceCounter, hidden behind a shim but the fact it uses QPC is also indicated on this Microsoft Learn page about high-resolution timestamps:

The primary API for native code is QueryPerformanceCounter (QPC). For managed code, the System.Diagnostics.Stopwatch class uses QPC as its precise time basis.

Which also states,

QPC is typically the best method to use to time-stamp events and measure small time intervals that occur on the same system or virtual machine.

And the page goes into examples of typical crystal oscillators used in computers and how they will drift relative to the real world by seconds per week. I suspect that when Windows synchronizes its time to a timeserver, any corrections to the time are applied to other time sources which is one of the ones Task Manager uses, so if the clock was fast by a second when it gets updated then some kernel counter has 1 second subtracted which causes Task Manager to show an accurate uptime, but the "number of ticks" does not get adjusted to remove a second--which is all the QPC timers use, an unsynchronized number of ticks.

High-resolution timestamps are accurate to microseconds, but microsecond accuracy doesn't hold over days and weeks, these high-resolution timestamps are intended for short profiling events. Get-Uptime is expected to return long times, sometimes even years if you have a server up forever, therefore it should not be using a high resolution timestamp under the hood because it has known issues with drift against real world clocks when used over days or longer.

Expected behavior

Task Manager "up time" and PowerShell `Get-Uptime` should match each other to a second or better.

Actual behavior

Task Manager "up time" and PowerShell `Get-Uptime` are different from each other by seconds or more depending on the uptime of the system. My system has 13 day uptime and the difference is 8 seconds right now, which is admittedly very small at only 0.0007% off after 13 days. The accumulated error will be system dependent and represents an unsynchronized time.

Error details

No response

Environment data

> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.4.5
PSEdition                      Core
GitCommitId                    7.4.5
OS                             Microsoft Windows 10.0.19044
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Activity

  1. added
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on Oct 14, 2024
  2. similar-issues-ai commented on Oct 14, 2024

    @similar-issues-ai

    We've found some similar issues:

    If any of the above are duplicates, please consider closing this issue out and adding additional context in the original issue.

    Note: You can give me feedback by 👍 or 👎 this comment.

  3. evelyn-bi commented on Oct 14, 2024

    @evelyn-bi
    Author

    We've found some similar issues:

    If any of the above are duplicates, please consider closing this issue out and adding additional context in the original issue.

    Note: You can give me feedback by 👍 or 👎 this comment.

    I disagree that this is a duplicate:

    1. That issue is for Linux whereas this one is for Windows.
    2. That issue was closed due to inactivity, so it was never really solved (the linked issue to fix Linux accounting of sleep time is still open also).
    3. In that issue they suspected it was a bug in Linux only and not shown in Windows because, "We use Stopwatch.GetTimestamp() and if it takes into account suspend time on Windows but not on Unix this looks like a bug. It makes sense to open new issue in .Net Runtime repository", however I have shown that there is a time drift in Windows which is unrelated to sleep time--as they say GetTimestamp() handles sleep correctly, which is also pointed out in the Microsoft Learn page I linked, "QueryPerformanceCounter reads the performance counter and returns the total number of ticks that have occurred since the Windows operating system was started, including the time when the machine was in a sleep state such as standby, hibernate, or connected standby".

    This issue is about high performance counters being unsuitable (inaccurate) for timing long running things, such as the uptime of a system. The linked StackOverflow page gives some ideas for alternatives.

  4. SteveL-MSFT commented on Dec 4, 2024

    @SteveL-MSFT
    Member

    The @PowerShell/wg-powershell-cmdlets discussed this. In our ad hoc testing, we also observed on some systems a drift as compared to using WMI. We would approve of a change to the cmdlet specific to Windows to use WMI to get the LastBootUpTime and subtracting from curren time as (Get-Date).Subtract((get-ciminstance win32_operatingsystem).LastBootUpTime). The existing code would still be needed for Linux and macOS which doesn't have WMI.

  5. added
    WG-ReviewedA Working Group has reviewed this and made a recommendation
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    and removed
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    WG-NeedsReviewNeeds a review by the labeled Working Group
    on Dec 4, 2024
  6. TobiasPSP commented on Dec 4, 2024

    @TobiasPSP
    Collaborator

    I did a quick research on where WMI gets this information (as I assumed it might be just a registry key). However it turns out the boot time information is coming from some un(der)documented APIs. The well documented WMI route seems to be the best approach.

  7. iSazonov commented on Dec 25, 2024

    @iSazonov
    Collaborator

    I think it's worth leaving this cmdlet as it is until OS-s or .Net will not offer a standard API.
    Now there are several uptime sources (counters) with different behavior. Any of them may be suitable in some scenarios and unsuitable in others. https://stackoverflow.com/questions/972105/retrieve-system-uptime-using-c-sharp/34010110#34010110
    For example, WMI requires elevated permissions for remote use.
    In addition, there is no clear definition of uptime. What is the zero point? Are we talking about the start time or the time that has passed since the start of the system? Does this time include downtime or sleep time?

    The best we can do is describe the drift problem in the documentation.


    I found a deviation of GetUptime from WMI of several minutes on one system and no deviation on the other. I do not know what this is due to. But this is not important for support scenarios like "Has the user rebooted the system now?" or "Has the CU (requires a reboot) been installed on the system in the last month?"

    Stack Overflow
    Is there a simple way to get a system's uptime using C#?
  8. rhubarb-geek-nz commented on Jan 30, 2025

    @rhubarb-geek-nz

    I have put together a proof of concept module getting the boot time from Windows, Linux, MacOS and hopefully FreeBSD

    For Windows it uses

    (Get-CimInstance -ClassName 'Win32_OperatingSystem').LastBootupTime
    

    For Linux it uses either

    uptime -s
    

    or

    (Get-Item -LiteralPath '/proc/1').CreationTime
    

    For MacOS and the *BSD it uses

    sysctl kern.boottime
    

    Linux Docker images typically don't have uptime so if it is not present it gets the creation date of process 1.

  9. iSazonov commented on Jan 31, 2025

    @iSazonov
    Collaborator

    I'd prefer binary examples for supported OS-s.

  10. rhubarb-geek-nz commented on Jan 31, 2025

    @rhubarb-geek-nz

    I'd prefer binary examples for supported OS-s.

    I also, but as a proof of concept it is about is it getting the information from the correct place and consistent.

  11. rhubarb-geek-nz commented on Feb 1, 2025

    @rhubarb-geek-nz

    I'd prefer binary examples for supported OS-s.

    Rewritten in C# SinceYouAsk.cs using the example OSX DllImport

    var time = new timeval { tv_sec = 0, tv_usec = 0 };
    var size = Marshal.SizeOf<timeval>();
    sysctlbyname("kern.boottime", ref time, ref size, IntPtr.Zero, 0);
    bootTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(time.tv_sec).AddMilliseconds(time.tv_usec / 1000);
    

    Running 7.4.5 on FreeBSD 13.3

    Name                           Value
    ----                           -----
    PSVersion                      7.4.5
    PSEdition                      Core
    GitCommitId                    7.4.5
    OS                             FreeBSD 13.3-RELEASE FreeBSD 13.3-RELEASE releng/13.3-n257428-80d2b634ddf0 GENERIC
    Platform                       Unix
    PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
    PSRemotingProtocolVersion      2.3
    SerializationVersion           1.1.0.1
    WSManStackVersion              3.0
    
    $ pwsh -c Get-UptimeSince
    
    Sunday, 02 February 2025 07:15:13
    
  12. iSazonov commented on Feb 2, 2025

    @iSazonov
    Collaborator

    rhubarb-geek-nz Thanks! Some notices for your code.

    1. We don't support FreeBSD, we haven't CIs for FreeBSD so we can not add a code for FreeBSD
    2. I like the idea to cache boot time.
    3. You can see GetCIMInstanceCommand.cs how use CIM on more low level. (Or WMI alternative Use WMI in Get-Uptime on Windows #24644)
    4. On Linux I'd expect we read and parse /proc/uptime
  13. rhubarb-geek-nz commented on Feb 2, 2025

    @rhubarb-geek-nz
    1. We don't support FreeBSD,

    True, it is very much work in progress at the moment! ReadLine does not work well at all. But the core SDK does give it an entry in OSPlatform, but not in netstandard2.0 Historically much of OSX/Darwin derives from FreeBSD.

    1. On Linux I'd expect we read and parse /proc/uptime

    Does it take into account hibernate/suspend times? If it counts clock tick interrupts then it will drift from true boot time.
    I found that /proc/1 creation time was a reasonable proxy for boot time, except in the case of devices like Raspberry Pi with no real-time clock so they appear to boot at start of epoch until they read the time from disk or update from NTP.

  14. iSazonov commented on Feb 3, 2025

    @iSazonov
    Collaborator

    Does it take into account hibernate/suspend times?

    No.
    From https://man7.org/linux/man-pages/man5/proc_uptime.5.html

    This file contains two numbers (values in seconds): the
    uptime of the system (including time spent in suspend) and
    the amount of time spent in the idle process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In-PRIndicates that a PR is out for the issueUp-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsWG-Cmdletsgeneral cmdlet issuesWG-ReviewedA Working Group has reviewed this and made a recommendation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions