Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 122 additions & 90 deletions sources/agent/src/collectors/rapl_collector.cc
Original file line number Diff line number Diff line change
@@ -1,56 +1,137 @@
#include "rapl_collector.h"

#include <fcntl.h>
#include <unistd.h>

#include <algorithm>
#include <chrono>
#include <cmath>
#include <filesystem>
#include <fstream>
#include <limits>
#include <regex>

namespace volta {
namespace agent {
namespace collectors {

RaplCollector::RaplCollector() = default;
namespace {

bool RaplCollector::Init() {
constexpr const char* kPowercapRoot = "/sys/class/powercap";
constexpr double kMicrojoulesPerJoule = 1.0e6;

const std::regex kPackageNameRegex(R"(^package(-[0-9]+)?$)");
const std::regex kPackageIndexRegex(R"(^package-([0-9]+)$)");

} // namespace

bool RaplCollector::IsPackageName(const std::string& name) {
return std::regex_match(name, kPackageNameRegex);
}

std::optional<int> RaplCollector::ParseSocketIndex(const std::string& name) {
if (name == "package") return 0;
std::smatch match;
if (!std::regex_match(name, match, kPackageIndexRegex)) {
return std::nullopt;
}
try {
OpenMSR();
uint64_t readout = ReadMSR(0, MSR_RAPL::POWER_UNIT);
power_units_ = pow(0.5, (double)(readout & 0xf));
energy_units_ = pow(0.5, (double)((readout >> 8) & 0x1f));
time_units_ = pow(0.5, (double)((readout >> 16) & 0xf));
readout = ReadMSR(0, MSR_RAPL::PKG::ENERGY_STATUS);
last_value = energy_units_ * readout;
initialized_ = true;
return true;
} catch (const MSR_Open_Exception&) {
return false;
} catch (const MSR_Read_Exception&) {
return false;
return std::stoi(match[1].str());
} catch (...) {
return std::nullopt;
}
}

bool RaplCollector::IsSupported() {
const std::filesystem::path cpu_base = "/dev/cpu";
std::error_code ec;
if (!std::filesystem::exists(cpu_base, ec) ||
!std::filesystem::is_directory(cpu_base, ec)) {
return false;
uint64_t RaplCollector::DeltaEnergyUj(uint64_t now, uint64_t last,
uint64_t max_range_uj) {
if (now >= last) return now - last;
if (max_range_uj > 0 && last <= max_range_uj) {
return (max_range_uj - last) + now;
}
return (std::numeric_limits<uint64_t>::max() - last) + now + 1;
}

bool RaplCollector::ReadU64File(const std::filesystem::path& path,
uint64_t* out) const {
std::ifstream in(path);
if (!in) return false;
uint64_t v = 0;
in >> v;
if (!in && !in.eof()) return false;
*out = v;
return true;
}

for (const auto& entry : std::filesystem::directory_iterator(cpu_base)) {
bool RaplCollector::ReadNameFile(const std::filesystem::path& path,
std::string* out) const {
std::ifstream in(path);
if (!in) return false;
std::string line;
if (!std::getline(in, line)) return false;
while (!line.empty() &&
(line.back() == '\n' || line.back() == '\r' || line.back() == ' ')) {
line.pop_back();
}
*out = line;
return true;
}

std::vector<RaplCollector::PackageZone> RaplCollector::DiscoverPackages()
const {
std::vector<PackageZone> packages;
std::error_code ec;
const std::filesystem::path root(kPowercapRoot);
if (!std::filesystem::is_directory(root, ec)) return packages;

for (const auto& entry : std::filesystem::directory_iterator(root, ec)) {
if (!entry.is_directory()) continue;

const auto msr_path = entry.path() / "msr";
if (std::filesystem::exists(msr_path, ec) &&
access(msr_path.c_str(), R_OK) == 0) {
return true;
}
const auto zone_dir = entry.path();
const auto energy_path = zone_dir / "energy_uj";
std::ifstream energy_in(energy_path);
if (!energy_in) continue;

std::string name;
if (!ReadNameFile(zone_dir / "name", &name)) continue;
if (!IsPackageName(name)) continue;

PackageZone z;
z.path = zone_dir;
z.name = name;
auto sock = ParseSocketIndex(name);
z.socket_index = sock.value_or(-1);
uint64_t max_range = 0;
(void)ReadU64File(zone_dir / "max_energy_range_uj", &max_range);
z.max_energy_range_uj = max_range;
packages.push_back(std::move(z));
}

std::sort(packages.begin(), packages.end(),
[](const PackageZone& a, const PackageZone& b) {
if (a.socket_index != b.socket_index)
return a.socket_index < b.socket_index;
return a.name < b.name;
});
return packages;
}

bool RaplCollector::IsSupported() { return !DiscoverPackages().empty(); }

bool RaplCollector::Init() {
initialized_ = false;
auto packages = DiscoverPackages();
if (packages.empty()) return false;

auto it =
std::find_if(packages.begin(), packages.end(),
[](const PackageZone& z) { return z.socket_index == 0; });
if (it == packages.end()) {
it = packages.begin();
it->socket_index = 0;
}

return false;
uint64_t energy = 0;
if (!ReadU64File(it->path / "energy_uj", &energy)) return false;

active_ = *it;
last_energy_uj_ = energy;
initialized_ = true;
return true;
}

void RaplCollector::SetRequestedMetrics(
Expand All @@ -66,77 +147,28 @@ std::vector<Metric> RaplCollector::Collect() {
return {};
}

uint64_t readout;
try {
readout = ReadMSR(0, MSR_RAPL::PKG::ENERGY_STATUS);
} catch (const MSR_Read_Exception&) {
return {};
}
uint64_t energy = 0;
if (!ReadU64File(active_.path / "energy_uj", &energy)) return {};

const uint64_t delta_uj =
DeltaEnergyUj(energy, last_energy_uj_, active_.max_energy_range_uj);
last_energy_uj_ = energy;

double value = energy_units_ * readout;
Metric m;
m.type = MetricType::METRIC_TYPE_CPU_POWER_PACKAGE;
CpuID cpu_id;
cpu_id.set_socket_index(0);
cpu_id.set_socket_index(active_.socket_index >= 0 ? active_.socket_index : 0);
Comment thread
patryk-przybysz marked this conversation as resolved.
cpu_id.set_core_index(0);
m.devId = DeviceId{std::move(cpu_id)};
m.value = value - last_value;
m.value = static_cast<double>(delta_uj) / kMicrojoulesPerJoule;
m.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
last_value = value;
return {m};
}

std::vector<MetricType> RaplCollector::Satisfiable() {
return {MetricType::METRIC_TYPE_CPU_POWER_PACKAGE};
}

uint64_t RaplCollector::ReadMSR(uint8_t core, uint32_t offset) {
uint64_t data;
if (core + 1 > MSR_files_.size()) {
throw MSR_Read_Exception();
}
if (pread(MSR_files_[core], &data, sizeof data, offset) != sizeof data) {
throw MSR_Read_Exception();
}
return data;
}

void RaplCollector::OpenMSR() {
const std::filesystem::path cpu_base = "/dev/cpu";
MSR_files_.clear();
std::error_code ec;

if (!std::filesystem::exists(cpu_base, ec)) {
throw MSR_Open_Exception();
}
std::vector<std::pair<int, std::filesystem::path>> cpu_entries;
for (const auto& entry : std::filesystem::directory_iterator(cpu_base)) {
if (!entry.is_directory()) continue;
const auto& dirname = entry.path().filename().string();
if (!std::ranges::all_of(dirname, ::isdigit)) continue;
cpu_entries.emplace_back(std::stoi(dirname), entry.path());
}

std::ranges::sort(cpu_entries);
for (const auto& [id, path] : cpu_entries) {
int fd = open((path / "msr").c_str(), O_RDONLY);
if (fd >= 0) {
MSR_files_.push_back(fd);
}
}
if (MSR_files_.empty()) {
throw MSR_Open_Exception();
}
}

void RaplCollector::CloseMSR(int fd) { close(fd); }

RaplCollector::~RaplCollector() {
for (auto file : MSR_files_) {
CloseMSR(file);
}
}

} // namespace collectors
} // namespace agent
} // namespace volta
74 changes: 23 additions & 51 deletions sources/agent/src/collectors/rapl_collector.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#ifndef VOLTA_AGENT_SRC_COLLECTORS_RAPL_COLLECTOR_H_
#define VOLTA_AGENT_SRC_COLLECTORS_RAPL_COLLECTOR_H_

#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <vector>

#include "collectors/collector.h"

namespace volta {
Expand All @@ -9,67 +15,33 @@ namespace collectors {

class RaplCollector final : public RegisteredCollector<RaplCollector> {
public:
RaplCollector();
RaplCollector(const RaplCollector&) = delete;
RaplCollector& operator=(const RaplCollector&) = delete;
bool Init() override;
std::vector<Metric> Collect() override;
bool IsSupported() override;
std::vector<MetricType> Satisfiable() override;
void SetRequestedMetrics(const std::vector<MetricType>& metrics) override;
~RaplCollector();

private:
uint64_t ReadMSR(uint8_t core, uint32_t offset);
void OpenMSR();
void CloseMSR(int fd);
std::vector<MetricType> requested_metrics_;
bool initialized_ = false;
double power_units_ = 0, energy_units_ = 0, time_units_ = 0;
std::vector<int> MSR_files_;
double last_value = 0;

class MSR_Read_Exception : std::exception {};
class MSR_Open_Exception : std::exception {};

struct MSR_RAPL {
static constexpr uint32_t POWER_UNIT = 0x606;
struct Units {
static constexpr uint32_t POWER_UNIT_OFFSET = 0;
static constexpr uint32_t POWER_UNIT_MASK = 0x0F;
static constexpr uint32_t ENERGY_UNIT_OFFSET = 0x08;
static constexpr uint32_t ENERGY_UNIT_MASK = 0x1F00;
static constexpr uint32_t TIME_UNIT_OFFSET = 0x10;
static constexpr uint32_t TIME_UNIT_MASK = 0xF000;
};

struct PKG {
static constexpr uint32_t POWER_LIMIT = 0x610;
static constexpr uint32_t ENERGY_STATUS = 0x611;
static constexpr uint32_t PERF_STATUS = 0x613;
static constexpr uint32_t POWER_INFO = 0x614;
};
struct PackageZone {
std::filesystem::path path;
std::string name;
int socket_index = 0;
uint64_t max_energy_range_uj = 0;
};

struct PP0 {
static constexpr uint32_t POWER_LIMIT = 0x638;
static constexpr uint32_t ENERGY_STATUS = 0x639;
static constexpr uint32_t POLICY = 0x63A;
static constexpr uint32_t PERF_STATUS = 0x63B;
};
static bool IsPackageName(const std::string& name);
static std::optional<int> ParseSocketIndex(const std::string& name);
static uint64_t DeltaEnergyUj(uint64_t now, uint64_t last,
uint64_t max_range_uj);

struct PP1 {
static constexpr uint32_t POWER_LIMIT = 0x640;
static constexpr uint32_t ENERGY_STATUS = 0x641;
static constexpr uint32_t POLICY = 0x642;
};
std::vector<PackageZone> DiscoverPackages() const;
bool ReadU64File(const std::filesystem::path& path, uint64_t* out) const;
bool ReadNameFile(const std::filesystem::path& path, std::string* out) const;

struct DRAM {
static constexpr uint32_t POWER_LIMIT = 0x618;
static constexpr uint32_t ENERGY_STATUS = 0x619;
static constexpr uint32_t PERF_STATUS = 0x61B;
static constexpr uint32_t POWER_INFO = 0x61C;
};
};
std::vector<MetricType> requested_metrics_;
bool initialized_ = false;
PackageZone active_;
uint64_t last_energy_uj_ = 0;
};

} // namespace collectors
Expand Down
Loading