File indexing completed on 2024-12-01 04:29:21

0001 /*
0002 SPDX-FileCopyrightText: 2022 Eric Jiang <erjiang@alumni.iu.edu>
0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "sysinfo.hpp"
0007 
0008 #include <QDebug>
0009 
0010 #include <kcoreaddons_version.h>
0011 
0012 /*
0013  * TODO: kmemoryinfo was introduced in KF 5.95 but currently we still support
0014  * some systems with KF 5.92. Once we bump the minimum requirement to KF 5.95+
0015  * we can get rid of this class completely and replace it with KMemoryInfo.
0016  */
0017 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 95, 0)
0018 #include <kmemoryinfo.h>
0019 
0020 #elif defined(Q_OS_WIN)
0021 #include <windows.h>
0022 
0023 #elif defined(Q_OS_MAC)
0024 #include <mach/mach.h>
0025 #include <mach/vm_statistics.h>
0026 #include <sys/sysctl.h>
0027 
0028 #elif defined(Q_OS_LINUX)
0029 #include <QFile>
0030 
0031 #elif defined(Q_OS_FREEBSD)
0032 #include <fcntl.h>
0033 #include <kvm.h>
0034 #include <sys/sysctl.h>
0035 
0036 template <class T> bool sysctlread(const char *name, T &var)
0037 {
0038     auto sz = sizeof(var);
0039     return (sysctlbyname(name, &var, &sz, NULL, 0) == 0);
0040 }
0041 
0042 #endif
0043 
0044 // returns system free memory in MB
0045 SysMemInfo SysMemInfo::getMemoryInfo()
0046 {
0047 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 95, 0)
0048     KMemoryInfo memInfo;
0049     if (!memInfo.isNull()) {
0050         return {true, static_cast<int>(memInfo.availablePhysical() / 1024 / 1024), static_cast<int>(memInfo.totalPhysical() / 1024 / 1024)};
0051     }
0052 
0053 #elif defined(Q_OS_WIN)
0054     MEMORYSTATUSEX status;
0055     ZeroMemory(&status, sizeof(MEMORYSTATUSEX));
0056     status.dwLength = sizeof(MEMORYSTATUSEX);
0057     GlobalMemoryStatusEx(&status);
0058     return {true, status.ullAvailPhys / 1024 / 1024, status.ullTotalPhys / 1024 / 1024};
0059 
0060 #elif defined(Q_OS_MAC)
0061     // get total memory
0062     int mib[] = {CTL_HW, HW_MEMSIZE};
0063     int64_t value = 0;
0064     size_t length = sizeof(value);
0065 
0066     if (-1 == sysctl(mib, 2, &value, &length, NULL, 0)) {
0067         qDebug() << "SysMemInfo: sysctl failed";
0068         return {false, -1, -1};
0069     }
0070     int total = value / 1024 / 1024;
0071 
0072     mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
0073     vm_statistics64_data_t vmstat;
0074     if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info_t)&vmstat, &count) != KERN_SUCCESS) {
0075         qDebug() << "SysMemInfo: Failed to get host_statistics64()";
0076         return meminfo;
0077     }
0078     int available = (vmstat.free_count +
0079                      // purgeable_count is memory that can be reclaimed by the system
0080                      vmstat.purgeable_count +
0081                      // external_page_count includes caches
0082                      vmstat.external_page_count) *
0083                     vm_page_size / 1024 / 1024;
0084 
0085     return {true, available, total};
0086 
0087 #elif defined(Q_OS_LINUX)
0088     // use /proc/meminfo to get available and total memory with Qt
0089     QFile meminfo("/proc/meminfo");
0090     if (!meminfo.open(QIODevice::ReadOnly)) {
0091         qDebug() << "SysMemInfo: Failed to open /proc/meminfo";
0092         return {false, -1, -1};
0093     }
0094     QTextStream stream(&meminfo);
0095     QString line;
0096     int memTotal = -1;
0097     int memAvailable = -1;
0098     while (true) {
0099         line = stream.readLine();
0100         // can't use atEnd() because /proc/meminfo's apparent size is 0 bytes
0101         if (line.isNull()) {
0102             break;
0103         }
0104         if (line.startsWith("MemTotal:")) {
0105             memTotal = line.split(" ", Qt::SkipEmptyParts)[1].toInt();
0106         } else if (line.startsWith("MemAvailable:")) {
0107             // Use MemAvailable because OS caches in memory are evictable
0108             memAvailable = line.split(" ", Qt::SkipEmptyParts)[1].toInt();
0109         }
0110     }
0111     if (memAvailable != -1 && memTotal != -1) {
0112         return {true, memAvailable / 1024, memTotal / 1024};
0113     }
0114     return {false, -1, -1};
0115 
0116 #elif defined(Q_OS_FREEBSD)
0117     // FreeBSD code from KMemoryInfo
0118     quint64 memSize = 0;
0119     quint64 pageSize = 0;
0120 
0121     int mib[4];
0122     size_t sz = 0;
0123 
0124     mib[0] = CTL_HW;
0125     mib[1] = HW_PHYSMEM;
0126     sz = sizeof(memSize);
0127     if (sysctl(mib, 2, &memSize, &sz, NULL, 0) != 0) {
0128         return {false, -1, -1};
0129     }
0130 
0131     mib[0] = CTL_HW;
0132     mib[1] = HW_PAGESIZE;
0133     sz = sizeof(pageSize);
0134     if (sysctl(mib, 2, &pageSize, &sz, NULL, 0) != 0) {
0135         return {false, -1, -1};
0136     }
0137 
0138     quint32 v_pageSize = 0;
0139     if (sysctlread("vm.stats.vm.v_page_size", v_pageSize)) {
0140         pageSize = v_pageSize;
0141     }
0142     quint64 zfs_arcstats_size = 0;
0143     if (!sysctlread("kstat.zfs.misc.arcstats.size", zfs_arcstats_size)) {
0144         zfs_arcstats_size = 0; // no ZFS used
0145     }
0146     quint32 v_cache_count = 0;
0147     if (!sysctlread("vm.stats.vm.v_cache_count", v_cache_count)) {
0148         return {false, -1, -1};
0149     }
0150     quint32 v_inactive_count = 0;
0151     if (!sysctlread("vm.stats.vm.v_inactive_count", v_inactive_count)) {
0152         return {false, -1, -1};
0153     }
0154     quint32 v_free_count = 0;
0155     if (!sysctlread("vm.stats.vm.v_free_count", v_free_count)) {
0156         return {false, -1, -1};
0157     }
0158     quint64 vfs_bufspace = 0;
0159     if (!sysctlread("vfs.bufspace", vfs_bufspace)) {
0160         return {false, -1, -1};
0161     }
0162 
0163     return {true, static_cast<int>((pageSize * (v_cache_count + v_free_count + v_inactive_count) + vfs_bufspace + zfs_arcstats_size) / 1024 / 1024),
0164             static_cast<int>(memSize / 1024 / 1024)};
0165 
0166 #else
0167     qDebug() << "WARNING: SysMemInfo() not implemented for this OS";
0168 
0169 #endif
0170     return {false, -1, -1};
0171 }