System Up Time

A while ago I set out to write a little tool that would show the time a system had been running since the last reboot. It seemed like something that should be fairly easy to do, but as it turns out, it isn’t entirely straightforward.

The first thing that came to my mind was the GetTickCount function. It returns the number of milliseconds since the system was started, which fits nicely. There is one problem of course, the value returned is a DWORD, and that limits the time it can handle to about 50 days.

Microsoft realized this as well, and added GetTickCount64, which returns a 64-bit value instead. Unfortunately it only works on Vista+.

Looking more closely at the documentation for GetTickCount we see:

“To obtain the time elapsed since the computer was started, retrieve the System Up Time counter in the performance data in the registry key HKEY_PERFORMANCE_DATA. The value returned is an 8-byte value. For more information, see Performance Counters.”

The performance counters are a nice idea. Basically they provide a homogeneous interface to a multitude of counters that give information about how well the operating system or an application, service, or driver is performing.

The recommended way to access the data is through the PDH interface. You access the counters by specifying a counter path; a string that describes the computer, object, counter, and instance you are interested in.

Looking through the list of counters by objects for the ‘System’ object, we find the ‘System Up Time’ counter, which is exactly what we need.

So I wrote a test application to open a query, add the counter “\System\System Up Time”, collect the data, and display it. It failed. Apparently my computer did not have a ‘System Up Time’ counter.

Reading over the documentation for the PDH interface again did not help, but after some searching I ended up at this help article:

“Performance Data Helper (PDH) APIs use object and counter names that are in the localized language. Therefore, applications that use PDH APIs should always use the localized string for the object or counter name specification.”

Since I was running a Danish version of Windows, that explained the problem!

Following the steps outlined there, I found that the ‘System’ object has index 2, and the ‘System Up Time’ counter has index 674. With these indices in hand, you can then call the PdhLookupPerfNameByIndex function to get the localized names. Using the localized path “\System\Computerperiode uden afbrydelser” gave the desired result.

The choice to make the paths use localized names makes it somewhat more involved to use these functions. Also, this should have been described much more clearly in the PDH documentation, since it is quite possible for a developer using English Windows to read over the documentation like I did, and use a hardcoded path for a counter. This will work nicely while testing, and then fail if someone with a localized Windows uses it.

As an example, let’s take a look at the PsInfo tool. It is written by Mark Russinovich, one of the people behind Sysinternals.com, a site that specializes in advanced system utilities and technical information. He is also a coauthor of the Windows Internals book, describing the inner workings of Windows operation systems.

Running PsInfo on my system I get:

Could it be? let’s have a little peek inside PsInfo.exe:

Indeed, a hardcoded path to the counter using the English names.

Microsoft must have realized it could be a problem as well, because I found the function PdhAddEnglishCounter, added in Vista, which made me smile.

9 thoughts on “System Up Time”

  1. Interesting article

    I am trying to find a way to get monotonic time on Windows XP. GetTickCount64() would work great, but it is not supported on Windows. And GetTickCount() has the wrap issue. Performance counters (\System\System Up Time) give the uptime but this value is adjusted if system time is changed. Do you happen to know a way to get an uptime value from performance counters that is independent of system clock? Thanks

  2. Not that I am aware of sorry.

    RDTSC might have been an option, but as described there, it is no longer reliable.

  3. Thanks for the pointer. It says

    “Under Windows platforms, Microsoft strongly discourages using the TSC for high-resolution timing for exactly these reasons, providing instead the Windows APIs QueryPerformanceCounter and QueryPerformanceFrequency.”

    This looks like a good option for systems where CPU supports these High Performance Counters (which I guess would be generally true for laptop computers running Windows XP and beyond)

  4. You have found the bane of my existence — localized counter names. What a pain! I have a 200 line function to try and convert English paths back to the localized version, while being performant and as bullet-proof as possible. I wish there was a nice reference implementation out there…

  5. The QueryPerformanceFrequency and QueryPerformanceCounter pair of functions work fine for me if you just need some kind of clock that increments constantly w/o adjustments (such as the system clock):

    #include

    LARGE_INTEGER f, c;

    QueryPerformanceFrequency(&f);
    QueryPerformanceCounter(&c);

    std::cout << c.QuadPart / f.QuadPart << std::endl;

Leave a Reply