Adding the Mac OS implementation
Let's take a look at the Mac implementation of the SysInfo class. Start by creating a new C++ class named SysInfoMacImpl that inherits from the SysInfo class. Override SysInfo virtual functions and you should have a SysInfoMacImpl.h file like this:
#include "SysInfo.h" #include <QtGlobal> #include <QVector> class SysInfoMacImpl : public SysInfo { public: SysInfoMacImpl(); void init() override; double cpuLoadAverage() override; double memoryUsed() override; };
The first implementation we will do will be the memoryUsed() function, in the SysInfoMacImpl.cpp file:
#include <mach/vm_statistics.h> #include <mach/mach_types.h> #include <mach/mach_init.h> #include <mach/mach_host.h> #include <mach/vm_map.h> SysInfoMacImpl::SysInfoMacImpl() : SysInfo() { } double SysInfoMacImpl::memoryUsed() { vm_size_t pageSize; vm_statistics64_data_t vmStats; mach_port_t machPort = mach_host_self(); mach_msg_type_number_t count = sizeof(vmStats) / sizeof(natural_t); host_page_size(machPort, &pageSize); host_statistics64(machPort, HOST_VM_INFO, (host_info64_t)&vmStats, &count); qulonglong freeMemory = (int64_t)vmStats.free_count * (int64_t)pageSize; qulonglong totalMemoryUsed = ((int64_t)vmStats.active_count + (int64_t)vmStats.inactive_count + (int64_t)vmStats.wire_count) * (int64_t)pageSize; qulonglong totalMemory = freeMemory + totalMemoryUsed; double percent = (double)totalMemoryUsed / (double)totalMemory * 100.0; return qBound(0.0, percent, 100.0); }
We start by including the different headers for the Mac OS kernel. Then we initialize machPort with the call to the mach_host_self() function. A machPort is a kind of special connection to the kernel that enables us to request information about the system. We then proceed to prepare other variables so that we can retrieve virtual memory statistics with host_statistics64().
When the vmStats class is filled with the information needed, we extract the relevant data: the freeMemory and the totalMemoryUsed.
Note that Mac OS has a peculiar way of managing its memory: it keeps a lot of memory in cache, ready to be flushed when needed. This implies that our statistics can be misled; we see the memory as used, whereas it was simply kept "just in case".
The percentage calculation is straightforward; we still return a min/max clamped value to avoid any crazy values in our future graph.
Next comes the cpuLoadAverage() implementation. The pattern is always the same; take samples at regular intervals and compute the growth on this interval. Therefore, we have to store intermediate values to be able to calculate the difference with the next sample:
// In SysInfoMacImpl.h #include "SysInfo.h" #include <QtGlobal> #include <QVector> ... private: QVector<qulonglong> cpuRawData(); private: QVector<qulonglong> mCpuLoadLastValues; }; // In SysInfoMacImpl.cpp void SysInfoMacImpl::init() { mCpuLoadLastValues = cpuRawData(); } QVector<qulonglong> SysInfoMacImpl::cpuRawData() { host_cpu_load_info_data_t cpuInfo; mach_msg_type_number_t cpuCount = HOST_CPU_LOAD_INFO_COUNT; QVector<qulonglong> rawData; qulonglong totalUser = 0, totalUserNice = 0, totalSystem = 0, totalIdle = 0; host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuInfo, &cpuCount); for(unsigned int i = 0; i < cpuCount; i++) { unsigned int maxTicks = CPU_STATE_MAX * i; totalUser += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_USER]; totalUserNice += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_SYSTEM]; totalSystem += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_NICE]; totalIdle += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_IDLE]; } rawData.append(totalUser); rawData.append(totalUserNice); rawData.append(totalSystem); rawData.append(totalIdle); return rawData; }
As you can see, the pattern used is strictly equivalent to the Linux implementation. You can even copy-paste the body of the cpuLoadAverage() function from the SysInfoLinuxImpl.cpp file. They do exactly the same thing.
Now, the implementation is different for the cpuRawData() function. We load cpuInfo and cpuCount with host_statistics() and then we loop through each CPU to have the totalUser, totalUserNice, totalSystem, and totalIdle functions filled. Finally, we append all this data to the rawData object before returning it.
The very last part is to compile the SysInfoMacImpl class only on Mac OS. Modify the .pro file to have the following body:
... linux { SOURCES += SysInfoLinuxImpl.cpp HEADERS += SysInfoLinuxImpl.h } macx { SOURCES += SysInfoMacImpl.cpp HEADERS += SysInfoMacImpl.h } FORMS += MainWindow.ui