lix/src/libstore/cgroup.cc

122 lines
3.6 KiB
C++
Raw Normal View History

2020-05-16 19:09:48 +00:00
#if __linux__
#include "cgroup.hh"
#include "util.hh"
#include <chrono>
#include <cmath>
#include <regex>
#include <unordered_set>
#include <thread>
2020-05-16 19:09:48 +00:00
#include <dirent.h>
namespace nix {
// FIXME: obsolete, check for cgroup2
2020-05-16 19:21:41 +00:00
std::map<std::string, std::string> getCgroups(const Path & cgroupFile)
{
std::map<std::string, std::string> cgroups;
for (auto & line : tokenizeString<std::vector<std::string>>(readFile(cgroupFile), "\n")) {
static std::regex regex("([0-9]+):([^:]*):(.*)");
std::smatch match;
if (!std::regex_match(line, match, regex))
throw Error("invalid line '%s' in '%s'", line, cgroupFile);
std::string name = hasPrefix(std::string(match[2]), "name=") ? std::string(match[2], 5) : match[2];
2020-05-16 19:21:41 +00:00
cgroups.insert_or_assign(name, match[3]);
}
return cgroups;
}
2022-11-18 12:40:59 +00:00
static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats)
2020-05-16 19:09:48 +00:00
{
2022-11-18 12:40:59 +00:00
if (!pathExists(cgroup)) return {};
if (!pathExists(cgroup + "/cgroup.procs"))
throw Error("'%s' is not a cgroup", cgroup);
2020-05-16 19:09:48 +00:00
for (auto & entry : readDirectory(cgroup)) {
if (entry.type != DT_DIR) continue;
2022-11-18 12:40:59 +00:00
destroyCgroup(cgroup + "/" + entry.name, false);
2020-05-16 19:09:48 +00:00
}
int round = 1;
std::unordered_set<pid_t> pidsShown;
2020-05-16 19:09:48 +00:00
while (true) {
auto pids = tokenizeString<std::vector<std::string>>(readFile(cgroup + "/cgroup.procs"));
if (pids.empty()) break;
if (round > 20)
throw Error("cannot kill cgroup '%s'", cgroup);
for (auto & pid_s : pids) {
pid_t pid;
if (auto o = string2Int<pid_t>(pid_s))
pid = *o;
else
throw Error("invalid pid '%s'", pid);
if (pidsShown.insert(pid).second) {
try {
auto cmdline = readFile(fmt("/proc/%d/cmdline", pid));
using namespace std::string_literals;
warn("killing stray builder process %d (%s)...",
pid, trim(replaceStrings(cmdline, "\0"s, " ")));
} catch (SysError &) {
}
}
2020-05-16 19:09:48 +00:00
// FIXME: pid wraparound
if (kill(pid, SIGKILL) == -1 && errno != ESRCH)
throw SysError("killing member %d of cgroup '%s'", pid, cgroup);
}
auto sleep = std::chrono::milliseconds((int) std::pow(2.0, std::min(round, 10)));
if (sleep.count() > 100)
printError("waiting for %d ms for cgroup '%s' to become empty", sleep.count(), cgroup);
2020-05-16 19:09:48 +00:00
std::this_thread::sleep_for(sleep);
round++;
}
2022-11-18 12:40:59 +00:00
CgroupStats stats;
if (returnStats) {
auto cpustatPath = cgroup + "/cpu.stat";
if (pathExists(cpustatPath)) {
for (auto & line : tokenizeString<std::vector<std::string>>(readFile(cpustatPath), "\n")) {
std::string_view userPrefix = "user_usec ";
if (hasPrefix(line, userPrefix)) {
auto n = string2Int<uint64_t>(line.substr(userPrefix.size()));
if (n) stats.cpuUser = std::chrono::microseconds(*n);
}
std::string_view systemPrefix = "system_usec ";
if (hasPrefix(line, systemPrefix)) {
auto n = string2Int<uint64_t>(line.substr(systemPrefix.size()));
if (n) stats.cpuSystem = std::chrono::microseconds(*n);
}
}
}
}
2020-05-16 19:09:48 +00:00
if (rmdir(cgroup.c_str()) == -1)
throw SysError("deleting cgroup '%s'", cgroup);
2022-11-18 12:40:59 +00:00
return stats;
}
CgroupStats destroyCgroup(const Path & cgroup)
{
return destroyCgroup(cgroup, true);
2020-05-16 19:09:48 +00:00
}
}
#endif