0

I'm currently working on a kind of surveillance tool. It's basically like the taskmanager, and I'm just doing this because I want to get in touch with C++ and learn new stuff.

The core of the CPU-usage part is GetSystemTimes(). This function returns the pointers to 3 values, the time the CPU has been idle, the time the CPU has been in kernel mode, and the time the CPU has been in user mode. I call the function twice with 250ms sleep in between, and calculate the percentage with the differences of the values.

I have two problems, though. The function returns pointers to FILETIME structures, but I need the actual value as an integer, float, double, or similar, because I need to calculate (int would be enough for me, but I don't know how large the values are). I know that a pointer tells me where the data is saved, but I don't know how I can actually get that data. And how can I get from FILETIME to something else, once I've got it.

#include <iostream>
#define _WIN32_WINNT 0x0602
#include <windows.h>
#include <stdlib.h>

class Processor{};

class Usage: public Processor
{
    public:

    int now()
    {
        FILETIME a0, a1, a2, b0, b1, b2;

        GetSystemTimes(&a0, &a1, &a2);
        SleepEx(250, false);
        GetSystemTimes(&b0, &b1, &b2);

        // attempt to get the actual value instead of the pointer and convert it to float/double/int
        float idle0 = a0;
        float idle1 = b0;
        float kernel0 = a1;
        float kernel1 = b1;
        float user0 = a2;
        float user1 = b2;

        float idl = idle1 - idle0;
        float ker = kernel0 - kernel1;
        float usr = user0 - user1;

        float cpu = (ker - idl + usr) * 100 / (ker + usr);

        return cpu;
    }
};

int main()
{
    using namespace std;

    Usage Usage;

    for(int i = 0; i < 10; i++)
    {
        cout << "CPU:\t" << Usage.now() << endl;
    }

    cout << "\nFinished!\nPress any key to exit!\n";
    cin.clear();
    cin.get();
    return 0;
}

Thanks for the help! :)

IInspectable
  • 46,945
  • 8
  • 85
  • 181
Caty
  • 11
  • 1
  • 2
  • 4
    Did you look at the documentation of [FILETIME](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284%28v=vs.85%29.aspx)? It's literally the first line in `Remarks` section. – Algirdas Preidžius Jan 04 '16 at 14:08
  • @AlgirdasPreidžius: The [FILETIME](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx) structure is already the appropriate type for performing calculations. Converting it to a [SYSTEMTIME](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950.aspx) structure (as you propose) may be more human-readable, but can no longer be (directly) used in integer or floatingpoint arithmetic. – IInspectable Jan 04 '16 at 15:10
  • It's the second paragraph that matters – David Heffernan Jan 04 '16 at 19:39

2 Answers2

4

Your question can be composed into two easy problems:

  1. Convert a FILETIME object to an integer value.
  2. Calculate the percentage using the integer values previously deduced.

A FILETIME structure...

[c]ontains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).

A suitable integer type to store a FILETIME's timestamp is a uint64_t. The following code performs the conversion:

#include <cstdint>

uint64_t FromFileTime( const FILETIME& ft ) {
    ULARGE_INTEGER uli = { 0 };
    uli.LowPart = ft.dwLowDateTime;
    uli.HighPart = ft.dwHighDateTime;
    return uli.QuadPart;
}

With this utility function you can change the remaining code to produce the percentage you are looking for:

int now() {
    FILETIME a0, a1, a2, b0, b1, b2;

    GetSystemTimes(&a0, &a1, &a2);
    SleepEx(250, false);
    GetSystemTimes(&b0, &b1, &b2);

    uint64_t idle0 = FromFileTime( a0 );
    uint64_t idle1 = FromFileTime( b0 );
    uint64_t kernel0 = FromFileTime( a1 );
    uint64_t kernel1 = FromFileTime( b1 );
    uint64_t user0 = FromFileTime( a2 );
    uint64_t user1 = FromFileTime( b2 );

    uint64_t idl = idle1 - idle0;
    uint64_t ker = kernel1 - kernel0;
    uint64_t usr = user1 - user0;

    uint64_t cpu = (ker + usr) * 100 / (ker + usr + idl);

    return static_cast<int>( cpu );
}

In theory, the multiplication and addition (ker + usr or ker + usr + idl) can overflow. Technically, those errors should be handled. In reality, however, the values should be very small compared to the maximum value the integer types can store.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
0

Two points, first you say "The function returns pointers to FILETIME structures" - the function returns a BOOL which you have not checked. Second, It takes pointers to FILETIME structs as parameters. You have given it the address of local variables - which is fine.
If you have pointers e.g. FILETIME *pa0 you can deference them to find the members variables etc using -> e.g. pa0->dwHighDateTime
For your variables a0, a1 etc a simple . will suffice. Notice the type of the two members of a FILETIMEare actually DWORDs so you can use DWORD instead of float to get what you want:

        DWORD idle0 = a0.dwHighDateTime;
        DWORD idle1 = b0.dwHighDateTime;
        DWORD kernel0 = a1.dwHighDateTime;
        DWORD kernel1 = b1.dwHighDateTime;
        DWORD user0 = a2.dwHighDateTime;
        DWORD user1 = b2.dwLowDateTime;

The API you have linked to has a suggestion of how to use a FILETIME time to get milliseconds:

((ulong)time.dwHighDateTime << 32) + (uint)time.dwLowDateTime) 

In essence, the two parts give you the whole number you are after. As people said in the comments, the FILETIME docs suggest various ways of doing the sums you want.
BUT to spell it out in full, you need to be careful with a large integer. (See comment below). The simplest thing to do, is to use a ULARGE_INTEGER (a 64-bit value) which has a LowPart you can use for the dwLowDateTime and HighPart you can us for the dwHighDateTime then use the QuadPart.
For example see manipulating LARGE_INTEGERS

Community
  • 1
  • 1
doctorlove
  • 18,872
  • 2
  • 46
  • 62
  • That final line of code is wrong (and it's not part of the official documentation either). It assumes C#, where `ulong` is a 64-bit unsigned integer value. On Windows, an `unsigned long` however is 32 bits only. I'd recommend using a [ULARGE_INTEGER](https://msdn.microsoft.com/en-us/library/windows/desktop/aa383742.aspx) to convert between the `DWORD` pairs and a sufficiently large integer type. – IInspectable Jan 04 '16 at 14:21
  • Fair point - I left off where I was going when people started adding comments – doctorlove Jan 04 '16 at 14:28
  • The [`FILETIME`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx) documentation says: "*It is not recommended that you add and subtract values from the FILETIME structure to obtain relative times. Instead, you should copy the low- and high-order parts of the file time to a ULARGE_INTEGER structure, perform 64-bit arithmetic on the QuadPart member, and copy the LowPart and HighPart members into the FILETIME structure. Do not cast a pointer to a FILETIME structure to either a ULARGE_INTEGER* or __int64* value because it can cause alignment faults on 64-bit Windows.*" – Remy Lebeau Jan 04 '16 at 18:53
  • It does. Perhaps it would be better if I removed the addition and bit-shift line of code. I put it there to point out there were clues in the docs the OP linked. I will spell it out in full. – doctorlove Jan 05 '16 at 11:33