#include "core/sysinfo.h" #include #include #include #include #include #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX #define PSAPI_VERSION 2 // (K32GetProcessMemoryInfo, in kernel32) #include #include #else #include #include #endif #if defined(__APPLE__) #include #include #include #endif #if defined(_MSC_VER) #include #elif defined(__x86_64__) || defined(__i386__) #include #endif namespace keel::sys { namespace { std::string trim(std::string s) { while (!s.empty() && (s.back() == ' ' || s.back() == '\n' || s.back() == '\r' || s.back() == '\0')) s.pop_back(); size_t i = 0; while (i < s.size() && s[i] == ' ') ++i; return s.substr(i); } #if defined(__APPLE__) std::string sysctlText(const char* name) { char buf[256] = {}; size_t size = sizeof buf - 1; return sysctlbyname(name, buf, &size, nullptr, 0) == 0 ? trim(buf) : std::string(); } #endif // The CPU's own name for itself (x86: cpuid's brand string; elsewhere /proc/cpuinfo). std::string cpuName() { #if defined(_MSC_VER) || defined(__x86_64__) || defined(__i386__) unsigned regs[12] = {}; for (unsigned i = 0; i < 3; ++i) { #if defined(_MSC_VER) int r[4]; __cpuid(r, int(0x80000002u + i)); std::memcpy(regs + i * 4, r, sizeof r); #else __get_cpuid(0x80000002u + i, ®s[i * 4], ®s[i * 4 + 1], ®s[i * 4 + 2], ®s[i * 4 + 3]); #endif } char brand[49] = {}; std::memcpy(brand, regs, 48); if (brand[0]) return trim(brand); #endif #if defined(__APPLE__) if (std::string s = sysctlText("machdep.cpu.brand_string"); !s.empty()) return s; // ("Apple M2 Pro" too) #endif std::ifstream in("/proc/cpuinfo"); for (std::string line; std::getline(in, line);) for (const char* key : {"model name", "Hardware", "Processor"}) if (line.rfind(key, 0) == 0) if (const size_t c = line.find(':'); c != std::string::npos) return trim(line.substr(c + 1)); return "unknown CPU"; } std::string osName() { #if defined(_WIN32) // GetVersionEx lies to programs without a manifest; ntdll's RtlGetVersion doesn't. using RtlGetVersionFn = LONG(WINAPI*)(PRTL_OSVERSIONINFOW); RTL_OSVERSIONINFOW v{sizeof v}; if (auto fn = reinterpret_cast(reinterpret_cast(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion")))) fn(&v); const char* name = v.dwMajorVersion == 10 ? (v.dwBuildNumber >= 22000 ? "Windows 11" : "Windows 10") : "Windows"; return std::string(name) + " (build " + std::to_string(v.dwBuildNumber) + ")"; #elif defined(__APPLE__) utsname u{}; uname(&u); const std::string v = sysctlText("kern.osproductversion"); return "macOS " + (v.empty() ? std::string("?") : v) + " (" + u.machine + ")"; #else utsname u{}; uname(&u); std::string pretty; std::ifstream in("/etc/os-release"); for (std::string line; std::getline(in, line);) if (line.rfind("PRETTY_NAME=", 0) == 0) { pretty = line.substr(12); if (pretty.size() >= 2 && pretty.front() == '"') pretty = pretty.substr(1, pretty.size() - 2); } #if defined(__ANDROID__) if (pretty.empty()) pretty = "Android"; #endif if (pretty.empty()) pretty = u.sysname; return pretty + " (" + u.sysname + " " + u.release + ")"; #endif } } // namespace const Machine& machine() { static const Machine m{osName(), cpuName(), int(std::thread::hardware_concurrency())}; return m; } Memory memory() { Memory m; #if defined(_WIN32) MEMORYSTATUSEX s{sizeof s}; if (GlobalMemoryStatusEx(&s)) m.total = s.ullTotalPhys, m.used = s.ullTotalPhys - s.ullAvailPhys; PROCESS_MEMORY_COUNTERS pmc{sizeof pmc}; if (K32GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof pmc)) m.ours = pmc.WorkingSetSize; #elif defined(__APPLE__) uint64_t total = 0; size_t size = sizeof total; if (sysctlbyname("hw.memsize", &total, &size, nullptr, 0) == 0) m.total = total; vm_statistics64_data_t vm{}; mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; if (host_statistics64(mach_host_self(), HOST_VM_INFO64, reinterpret_cast(&vm), &count) == KERN_SUCCESS) { const uint64_t page = uint64_t(sysconf(_SC_PAGESIZE)); // (used as Activity Monitor counts it: in use by apps, wired, compressed) m.used = (uint64_t(vm.internal_page_count) - vm.purgeable_count + vm.wire_count + vm.compressor_page_count) * page; } mach_task_basic_info_data_t task{}; mach_msg_type_number_t taskCount = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, reinterpret_cast(&task), &taskCount) == KERN_SUCCESS) m.ours = task.resident_size; #else uint64_t available = 0; std::ifstream in("/proc/meminfo"); for (std::string line; std::getline(in, line);) { unsigned long long kb = 0; if (std::sscanf(line.c_str(), "MemTotal: %llu kB", &kb) == 1) m.total = kb * 1024; else if (std::sscanf(line.c_str(), "MemAvailable: %llu kB", &kb) == 1) available = kb * 1024; } m.used = m.total > available ? m.total - available : 0; long pages = 0, resident = 0; if (std::FILE* f = std::fopen("/proc/self/statm", "r")) { if (std::fscanf(f, "%ld %ld", &pages, &resident) != 2) resident = 0; std::fclose(f); } m.ours = uint64_t(resident) * uint64_t(sysconf(_SC_PAGESIZE)); #endif return m; } std::string executablePath() { #if defined(_WIN32) char exe[1024] = {}; GetModuleFileNameA(nullptr, exe, sizeof exe); return exe; #elif defined(__APPLE__) char exe[4096] = {}; uint32_t size = sizeof exe; if (_NSGetExecutablePath(exe, &size) != 0) return {}; std::error_code ec; const auto real = std::filesystem::canonical(exe, ec); return ec ? std::string(exe) : real.string(); #else std::error_code ec; const auto p = std::filesystem::read_symlink("/proc/self/exe", ec); return ec ? std::string() : p.string(); #endif } bool debuggerAttached() { #if defined(_WIN32) return IsDebuggerPresent(); #elif defined(__APPLE__) kinfo_proc info{}; size_t size = sizeof info; int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; return sysctl(mib, 4, &info, &size, nullptr, 0) == 0 && (info.kp_proc.p_flag & P_TRACED) != 0; #else std::ifstream f("/proc/self/status"); for (std::string line; std::getline(f, line);) if (line.rfind("TracerPid:", 0) == 0) return std::atoi(line.c_str() + 10) != 0; return false; #endif } } // namespace keel::sys