#include "metrics/Collector.h" #include #include #include #include #include #include #include #include #include namespace metrics { Collector::Collector(const std::vector> &disks) : disks(disks) { std::vector cpuLogicalIds; for (const auto &cpuDir : std::filesystem::directory_iterator("/sys/devices/system/cpu")) { std::string name = cpuDir.path().filename().string(); if (name.find("cpu") != 0) continue; std::string suffix = name.substr(3); if (!std::all_of(suffix.begin(), suffix.end(), ::isdigit)) continue; cpuLogicalIds.push_back(std::stoi(suffix)); } std::sort(cpuLogicalIds.begin(), cpuLogicalIds.end()); // строим логическое → физическое logicalToPhysical.resize(cpuLogicalIds.size()); for (size_t i = 0; i < cpuLogicalIds.size(); ++i) { int cpu = cpuLogicalIds[i]; std::ifstream f("/sys/devices/system/cpu/cpu" + std::to_string(cpu) + "/topology/core_id"); if (!f) throw std::runtime_error("Cannot read core_id for cpu" + std::to_string(cpu)); int coreId; f >> coreId; logicalToPhysical[i] = coreId; } } Host Collector::collect() { Host host; auto curCpu = readCpuTimes(); if (prevCpu.second.empty()) prevCpu = curCpu; host.cpu.coreLoads.resize(curCpu.second.size()); host.cpu.totalLoad = cpuLoad(prevCpu.first, curCpu.first); for (size_t i = 0; i < curCpu.second.size(); ++i) host.cpu.coreLoads[i] = cpuLoad(prevCpu.second[i], curCpu.second[i]); prevCpu = curCpu; host.cpu.coreTemps = readCpuCoreTemps(); host.memory = readMemory(); for (const std::vector &disk : disks) host.disks.push_back(readDisk(disk)); readLoad(host.load1, host.load5, host.load15); host.uptime = readUptime(); host.hostname = readHostname(); return host; } std::pair> Collector::readCpuTimes() { std::ifstream f("/proc/stat"); std::pair> out; std::string line; while (std::getline(f, line)) { if (!line.starts_with("cpu")) break; std::istringstream ss(line); std::string cpu; CpuTimes t; ss >> cpu >> t.user >> t.nice >> t.system >> t.idle >> t.iowait >> t.irq >> t.softirq >> t.steal; if (cpu == "cpu") { out.first = t; } else { out.second.push_back(t); } } return out; } std::vector Collector::readCpuCoreTemps() { std::vector physical; std::vector logical(logicalToPhysical.size()); for (const auto &hwmon : std::filesystem::directory_iterator("/sys/class/hwmon")) { std::string name; std::ifstream nameFile(hwmon.path() / "name"); if (nameFile) std::getline(nameFile, name); // проверяем на CPU if (name != "coretemp" && name != "k10temp") continue; for (const auto &f : std::filesystem::directory_iterator(hwmon)) { std::string fname = f.path().filename().string(); if (fname.find("temp") != 0) continue; // определяем, какой тип файла std::string suffix; if (fname.find("_input") != std::string::npos) suffix = "_input"; else if (fname.find("_max") != std::string::npos) suffix = "_max"; else continue; int millideg = 0; std::ifstream tempFile(f.path()); tempFile >> millideg; float value = millideg / 1000.0f; // находим индекс ядра в temps по tempN std::string numStr = fname.substr(4, fname.find('_') - 4); int n = std::stoi(numStr); // если _input — создаём новый объект if (suffix == "_input") { if (n >= static_cast(physical.size())) physical.resize(n + 1); physical[n].current = value; } else { // _max/_crit: если temps[n] ещё нет, создаём объект if (n >= static_cast(physical.size())) physical.resize(n + 1); if (suffix == "_max") physical[n].max = value; } } // Убираем пропуски: оставшиеся нули → копируем предыдущий или последующий? // Можно фильтровать нулевые ядра, если нужно physical.erase(std::remove_if(physical.begin(), physical.end(), [](const CpuTemperature &t) { return t.current == 0 && t.max == 0; }), physical.end()); for (size_t i = 0; i < logicalToPhysical.size(); ++i) { int phys = logicalToPhysical[i]; if (phys >= static_cast(physical.size())) phys = physical.size() - 1; // safety logical[i] = physical[phys]; } } return logical; } float Collector::cpuLoad(const Collector::CpuTimes &prev, const Collector::CpuTimes &cur) { uint64_t prevIdle = prev.idle + prev.iowait; uint64_t curIdle = cur.idle + cur.iowait; uint64_t prevTotal = prev.user + prev.nice + prev.system + prevIdle + prev.irq + prev.softirq + prev.steal; uint64_t curTotal = cur.user + cur.nice + cur.system + curIdle + cur.irq + cur.softirq + cur.steal; uint64_t totalDiff = curTotal - prevTotal; uint64_t idleDiff = curIdle - prevIdle; if (totalDiff == 0) return 0.f; return 100.f * (totalDiff - idleDiff) / totalDiff; } Memory Collector::readMemory() { std::ifstream f("/proc/meminfo"); std::string key; uint64_t value; std::string unit; uint64_t mem_total = 0, mem_available = 0, swap_total = 0, swap_available = 0; while (f >> key >> value >> unit) { if (key == "MemTotal:") mem_total = value * 1024; else if (key == "MemAvailable:") mem_available = value * 1024; else if (key == "SwapTotal:") swap_total = value * 1024; else if (key == "SwapFree:") swap_available = value * 1024; } Memory memory; memory.memory.total = mem_total; memory.memory.available = mem_available; memory.memory.used = mem_total - mem_available; memory.swap.total = swap_total; memory.swap.available = swap_available; memory.swap.used = swap_total - swap_available; return memory; } Disk Collector::readDisk(const std::vector &disk) { struct statvfs vfs; if (statvfs(disk.at(1).c_str(), &vfs) != 0) { throw std::runtime_error( std::string("statvfs failed for ") + disk.at(1).c_str() + ": " + std::strerror(errno)); } uint64_t total = static_cast(vfs.f_blocks) * vfs.f_frsize; uint64_t available = static_cast(vfs.f_bavail) * vfs.f_frsize; uint64_t used = total - available; Disk d; d.name = disk.at(0); d.path = disk.at(1); d.metrics.total = total; d.metrics.available = available; d.metrics.used = used; return d; } void Collector::readLoad(float &l1, float &l5, float &l15) { std::ifstream f("/proc/loadavg"); f >> l1 >> l5 >> l15; } uint64_t Collector::readUptime() { std::ifstream f("/proc/uptime"); double up; f >> up; return static_cast(up); } std::string Collector::readHostname() { char buf[HOST_NAME_MAX + 1] = {}; if (gethostname(buf, sizeof(buf)) == 0) return std::string(buf); return "Unknown"; } }