280 lines
8.9 KiB
C++
280 lines
8.9 KiB
C++
#include "metrics/Collector.h"
|
|
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <sys/statvfs.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
namespace metrics
|
|
{
|
|
|
|
Collector::Collector(const std::vector<std::vector<std::string>> &disks) : disks(disks)
|
|
{
|
|
std::vector<int> 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<std::string> &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::CpuTimes, std::vector<Collector::CpuTimes>> Collector::readCpuTimes()
|
|
{
|
|
std::ifstream f("/proc/stat");
|
|
std::pair<CpuTimes, std::vector<CpuTimes>> 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<CpuTemperature> Collector::readCpuCoreTemps()
|
|
{
|
|
std::vector<CpuTemperature> physical;
|
|
std::vector<CpuTemperature> 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<int>(physical.size()))
|
|
physical.resize(n + 1);
|
|
physical[n].current = value;
|
|
}
|
|
else
|
|
{
|
|
// _max/_crit: если temps[n] ещё нет, создаём объект
|
|
if (n >= static_cast<int>(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<int>(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<std::string> &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<uint64_t>(vfs.f_blocks) * vfs.f_frsize;
|
|
uint64_t available = static_cast<uint64_t>(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<uint64_t>(up);
|
|
}
|
|
|
|
std::string Collector::readHostname()
|
|
{
|
|
char buf[HOST_NAME_MAX + 1] = {};
|
|
if (gethostname(buf, sizeof(buf)) == 0)
|
|
return std::string(buf);
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
} |