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 }