cpuinfo

I have a whole load of unfinished hobby projects. Many times I was taken by some idea, implemented the most challenging part and lost my interest.
But some of the projects have been actually finished and may be interesting to someone.

It’s time to let the world see my creations πŸ™‚

The hero of the day is the small cross-platform utility that measures the clock speed of the CPU and shows it alongside its summary information, such as brand name, family, stepping, etc.

It started as a command-line application made in Borland C++ Builder. This IDE provides a straightforward and convenient syntax for embedding the x86 Assembler instructions into your regular C++ code. Which is great, when your application built around one of them: rdtsc.

freq.exe (download archive)

Here’s the code (please take into account, that I was not professional or even amateur programmer back then, so give me some slack):

#include <stdio.h>
#include <conio.h>
#include <windows.h>

__int64 Get_freq() {

	DWORD ts_hi, ts_lo;
	LARGE_INTEGER qpf, count_1, count_2;
	ULARGE_INTEGER ts_before, ts_after;

	QueryPerformanceFrequency(&qpf);

	__int64 count_persec = qpf.QuadPart;


	asm {
        rdtsc
        push eax
        push edx
	}

	QueryPerformanceCounter(&count_1);

	while (count_2.QuadPart - count_1.QuadPart < count_persec) {

		QueryPerformanceCounter(&count_2);

	}

	asm {
        rdtsc
        push eax
        push edx
	}

	asm {
        pop ts_hi
        pop ts_lo
	}

	ts_after.HighPart = ts_hi;
	ts_after.LowPart = ts_lo;

	asm {
        pop ts_hi
        pop ts_lo
	}

	ts_before.HighPart = ts_hi;
	ts_before.LowPart = ts_lo;

	return ts_after.QuadPart - ts_before.QuadPart;

};

void  main()
{
    char ch;

    printf("Press \"q\" for exit or any other for detecting CPU clock frequency again\n\n" );

    ch = getch();

    while (ch != 'q'){

        printf("Frequency = %u Hz\n", Get_freq());
        ch = getch();

    }
}

There was not so much information on this topic available except the MSDN library and Intel’s Instruction Set Reference. But Borland’s documentation was good enough too, as far as I remember.

In a few months, I ported this to the MS Visual C++. As you can see, the ASM instructions were replaced with the MSVC’s intrinsic functions. The clock frequency alone is not so exciting, so I added some basic processor information, that is returned by the cpuid instruction:

cpuid.exe (download archive)
#include <windows.h>
#include <intrin.h>
#include <stdio.h>

int i;
int table[4];

unsigned __int64 nFreq;

void GetFreq() {

	unsigned __int64 ts_before, ts_after;
	LARGE_INTEGER count_persec, count_1, count_2;

	QueryPerformanceFrequency(&count_persec);

    ts_before = __rdtsc();

	QueryPerformanceCounter(&count_1);

	while (count_2.QuadPart - count_1.QuadPart < count_persec.QuadPart)
        QueryPerformanceCounter(&count_2);
	
    ts_after = __rdtsc();

	nFreq = ts_after - ts_before;

};


int main() {

    char c[3][16] = {0};

    for (i = 2; i <= 4; i++) {
        __cpuid(table, 0x80000000 | i);
        memcpy(&c[i - 2], &table, 16);
    }

    __cpuid(table, 1);
    int nStepping = table[0] & 0xF;
    int nModel = (table[0] & 0xF0) >> 4;
    int nFamily = (table[0] & 0xF00) >> 8;

    GetFreq();

    printf("Frequency: %u\n", nFreq);
    printf("Brand: %s\n", c);
    printf("Family: %x\t", nFamily);
    printf("Model: %x\t", nModel);
    printf("Stepping: %x\t", nStepping);
    getchar();

}

But this was just a preparation for the next step.

The final version of 2006 – GUI application made using the Windows 32 API aka WinApi:

oxp_001.exe (download archive)
// Π’ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ» для Windows-ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ
#include <windows.h>
#include <intrin.h>
#include <string>
#include <stdlib.h>

int i;
int table[4];
char buff[16];

// CPU Information
std::string sBrand;
int nFamily;
int nModel;
int nStepping;

unsigned __int64 nFreq;
void GetFreq();
//----------------

void OnCreate(HWND, HINSTANCE);

WNDCLASS CreateClass(WNDPROC pfWProc, HINSTANCE hInst, LPCSTR sIcon, LPCSTR sCursor, LPCSTR sName, HBRUSH hbColor = (HBRUSH)12) {
    WNDCLASS wc = {CS_HREDRAW | CS_VREDRAW, pfWProc, NULL, NULL, hInst, LoadIcon(hInst, sIcon), LoadCursor(NULL, sCursor), hbColor, NULL, sName};
    return wc;
}

// ОбъявлСниС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΎΠΊΠ½Π° (ΠΎΠΊΠΎΠ½Π½ΠΎΠΉ ΠΏΡ€ΠΎΡ†Π΅Π΄ΡƒΡ€Ρ‹)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

HINSTANCE hInst; // Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ прилоТСния
HWND Tab;

// Π’ΠΎΡ‡ΠΊΠ° Π²Ρ…ΠΎΠ΄Π° Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ - функция WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd; // Π£Π½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΎΠΊΠ½Π° (handle)
	MSG msg; // ОбъявлСниС структуры Ρ‚ΠΈΠΏΠ° MSG, для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с сообщСниями

	hInst = hInstance; // БохраняСм ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ прилоТСния

	RegisterClass(&CreateClass(WndProc, hInst, MAKEINTRESOURCE(102), IDC_ARROW, "Form")); // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΈ рСгистрируСм ΠΎΠΊΠΎΠ½Π½Ρ‹ΠΉ класс
    
    RECT WorkArea;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkArea, 0);

    GetFreq();
    
	// Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΎΠΊΠ½ΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹
	hWnd = CreateWindow(
		"Form", // Имя класса окна
		"OverSoft CPU Informer XP 0.001", // Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΎΠΊΠ½Π°
		WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, // Π‘Ρ‚ΠΈΠ»ΡŒ ΠΎΠΊΠ½Π°
		WorkArea.right/2 - 215, WorkArea.bottom/2 - 75, // Π“ΠΎΡ€ΠΈΠ·ΠΎΠ½Ρ‚Π°Π»ΡŒΠ½Π°Ρ ΠΈ Π²Π΅Ρ€Ρ‚ΠΈΠΊΠ°Π»ΡŒΠ½Π°Ρ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΎΠΊΠ½Π°
		430, 150, // Π¨ΠΈΡ€ΠΈΠ½Π° ΠΈ высота ΠΎΠΊΠ½Π°
		NULL, // Π₯Π΅Π½Π΄Π» Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ ΠΎΠΊΠ½Π°
		NULL, // Π₯Π΅Π½Π΄Π» мСню
		hInst, // Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ прилоТСния
		NULL); // Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ ΠΎΠΊΠ½Π°
    
	UpdateWindow(hWnd); // ΠŸΠ΅Ρ€Π΅Ρ€ΠΈΡΠΎΠ²Ρ‹Π²Π°Π΅ΠΌ ΠΎΠΊΠ½ΠΎ

	// Π‘Ρ‚Π°Π½Π΄Π°Ρ€Ρ‚Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ» ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ сообщСний
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}


// Оконная ΠΏΡ€ΠΎΡ†Π΅Π΄ΡƒΡ€Π°
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
    case WM_CREATE:
        OnCreate(hWnd, hInst);
        break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}

}

void GetBrandStr();
void GetId1();


void OnCreate(HWND hWnd, HINSTANCE hInst) {

    GetBrandStr();
    GetId1();
    //GetFreq();
    
    CreateWindow("static", "Brand:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 20, 80, 20, hWnd, NULL, hInst, NULL);
    CreateWindow("static", sBrand.c_str(), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 20, 300, 20, hWnd, NULL, hInst, NULL);

    CreateWindow("static", "Family:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 40, 80, 20, hWnd, NULL, hInst, NULL);
    CreateWindow("static", CharUpper(_itoa(nFamily, buff, 16)), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 40, 350, 20, hWnd, NULL, hInst, NULL);
    
    CreateWindow("static", "Model:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 60, 80, 20, hWnd, NULL, hInst, NULL);
    CreateWindow("static", CharUpper(_itoa(nModel, buff, 16)), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 60, 350, 20, hWnd, NULL, hInst, NULL);
    
    CreateWindow("static", "Stepping:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 80, 80, 20, hWnd, NULL, hInst, NULL);
    CreateWindow("static", CharUpper(_itoa(nStepping, buff, 16)), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 80, 350, 20, hWnd, NULL, hInst, NULL);

    CreateWindow("static", "Frequency:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 100, 80, 20, hWnd, NULL, hInst, NULL);
    CreateWindow("static", _ui64toa(nFreq, buff, 10), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 100, 350, 20, hWnd, NULL, hInst, NULL);
}

void GetBrandStr() {
    char buff[48];

    for (i = 2; i <= 4; i++) {
        __cpuid(table, 0x80000000 | i);
        memcpy(&buff[16*(i - 2)], &table, 16);
    }
    i = 0;
    while (buff[i]==' ') ++i;
    while (buff[i]!=0) {
        sBrand+= buff[i];
        ++i;  
    }
}

void GetId1() {
    __cpuid(table, 1);
    nStepping = table[0] & 0xF;
    nModel = (table[0] & 0xF0) >> 4;
    nFamily = (table[0] & 0xF00) >> 8;
}

void GetFreq() {

	unsigned __int64 ts_before, ts_after;
	LARGE_INTEGER count_persec, count_1, count_2;

	QueryPerformanceFrequency(&count_persec);

    ts_before = __rdtsc();

	QueryPerformanceCounter(&count_1);

	while (count_2.QuadPart - count_1.QuadPart < count_persec.QuadPart)
        QueryPerformanceCounter(&count_2);
	
    ts_after = __rdtsc();

	nFreq = ts_after - ts_before;

};

Why is it called “OverSoft CPU Informer XP”? Well, that’s a story for another day πŸ™‚

A few years later, in 2011, when I was a full-time web developer already and used FreeBSD as a desktop system at work, I ported the CLI version to the GCC just for fun. My colleague, who’s an actual C++ developer helped me with that. Thanks to a MinGW project, the utility may run on Windows (as well as on macOS) too:

And now, I’m sharing the cross-platform code on GitHub under a permissive license. To make it somewhat usable, I added the Makefile and README files. The rest is a genuine 9-years-old cpp code, which is still compilable by some miracle. The binary executable files for 32 and 64 bit Windows and 64 bit Linux are also available there.

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Leave a Reply