forked from lix-project/lix
Get CPU stats from the cgroup
This commit is contained in:
parent
20f66c6889
commit
fa68eb367e
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -78,6 +78,9 @@ struct BuildResult
|
||||||
was repeated). */
|
was repeated). */
|
||||||
time_t startTime = 0, stopTime = 0;
|
time_t startTime = 0, stopTime = 0;
|
||||||
|
|
||||||
|
/* User and system CPU time the build took. */
|
||||||
|
std::optional<std::chrono::microseconds> cpuUser, cpuSystem;
|
||||||
|
|
||||||
bool success()
|
bool success()
|
||||||
{
|
{
|
||||||
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
||||||
|
|
|
@ -869,6 +869,14 @@ void DerivationGoal::buildDone()
|
||||||
|
|
||||||
cleanupPostChildKill();
|
cleanupPostChildKill();
|
||||||
|
|
||||||
|
if (buildResult.cpuUser && buildResult.cpuSystem) {
|
||||||
|
debug("builder for '%s' terminated with status %d, user CPU %.3fs, system CPU %.3fs",
|
||||||
|
worker.store.printStorePath(drvPath),
|
||||||
|
status,
|
||||||
|
((double) buildResult.cpuUser->count()) / 1000000,
|
||||||
|
((double) buildResult.cpuSystem->count()) / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
bool diskFull = false;
|
bool diskFull = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -137,7 +137,7 @@ void LocalDerivationGoal::killChild()
|
||||||
also send a conventional kill to the child. */
|
also send a conventional kill to the child. */
|
||||||
::kill(-pid, SIGKILL); /* ignore the result */
|
::kill(-pid, SIGKILL); /* ignore the result */
|
||||||
|
|
||||||
killSandbox();
|
killSandbox(true);
|
||||||
|
|
||||||
pid.wait();
|
pid.wait();
|
||||||
}
|
}
|
||||||
|
@ -146,10 +146,14 @@ void LocalDerivationGoal::killChild()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalDerivationGoal::killSandbox()
|
void LocalDerivationGoal::killSandbox(bool getStats)
|
||||||
{
|
{
|
||||||
if (cgroup) {
|
if (cgroup) {
|
||||||
destroyCgroup(*cgroup);
|
auto stats = destroyCgroup(*cgroup);
|
||||||
|
if (getStats) {
|
||||||
|
buildResult.cpuUser = stats.cpuUser;
|
||||||
|
buildResult.cpuSystem = stats.cpuSystem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (buildUser) {
|
else if (buildUser) {
|
||||||
|
@ -270,7 +274,7 @@ void LocalDerivationGoal::cleanupPostChildKill()
|
||||||
malicious user from leaving behind a process that keeps files
|
malicious user from leaving behind a process that keeps files
|
||||||
open and modifies them after they have been chown'ed to
|
open and modifies them after they have been chown'ed to
|
||||||
root. */
|
root. */
|
||||||
killSandbox();
|
killSandbox(true);
|
||||||
|
|
||||||
/* Terminate the recursive Nix daemon. */
|
/* Terminate the recursive Nix daemon. */
|
||||||
stopDaemon();
|
stopDaemon();
|
||||||
|
@ -410,7 +414,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
/* Make sure that no other processes are executing under the
|
/* Make sure that no other processes are executing under the
|
||||||
sandbox uids. This must be done before any chownToBuilder()
|
sandbox uids. This must be done before any chownToBuilder()
|
||||||
calls. */
|
calls. */
|
||||||
killSandbox();
|
killSandbox(false);
|
||||||
|
|
||||||
/* Right platform? */
|
/* Right platform? */
|
||||||
if (!parsedDrv->canBuildLocally(worker.store))
|
if (!parsedDrv->canBuildLocally(worker.store))
|
||||||
|
|
|
@ -202,7 +202,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
|
|
||||||
/* Kill any processes running under the build user UID or in the
|
/* Kill any processes running under the build user UID or in the
|
||||||
cgroup of the build. */
|
cgroup of the build. */
|
||||||
void killSandbox();
|
void killSandbox(bool getStats);
|
||||||
|
|
||||||
/* Create alternative path calculated from but distinct from the
|
/* Create alternative path calculated from but distinct from the
|
||||||
input, so we can avoid overwriting outputs (or other store paths)
|
input, so we can avoid overwriting outputs (or other store paths)
|
||||||
|
|
|
@ -31,13 +31,16 @@ std::map<std::string, std::string> getCgroups(const Path & cgroupFile)
|
||||||
return cgroups;
|
return cgroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyCgroup(const Path & cgroup)
|
static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats)
|
||||||
{
|
{
|
||||||
if (!pathExists(cgroup)) return;
|
if (!pathExists(cgroup)) return {};
|
||||||
|
|
||||||
|
if (!pathExists(cgroup + "/cgroup.procs"))
|
||||||
|
throw Error("'%s' is not a cgroup", cgroup);
|
||||||
|
|
||||||
for (auto & entry : readDirectory(cgroup)) {
|
for (auto & entry : readDirectory(cgroup)) {
|
||||||
if (entry.type != DT_DIR) continue;
|
if (entry.type != DT_DIR) continue;
|
||||||
destroyCgroup(cgroup + "/" + entry.name);
|
destroyCgroup(cgroup + "/" + entry.name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int round = 1;
|
int round = 1;
|
||||||
|
@ -79,8 +82,38 @@ void destroyCgroup(const Path & cgroup)
|
||||||
round++;
|
round++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (rmdir(cgroup.c_str()) == -1)
|
if (rmdir(cgroup.c_str()) == -1)
|
||||||
throw SysError("deleting cgroup '%s'", cgroup);
|
throw SysError("deleting cgroup '%s'", cgroup);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
CgroupStats destroyCgroup(const Path & cgroup)
|
||||||
|
{
|
||||||
|
return destroyCgroup(cgroup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,25 @@
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::map<std::string, std::string> getCgroups(const Path & cgroupFile);
|
std::map<std::string, std::string> getCgroups(const Path & cgroupFile);
|
||||||
|
|
||||||
void destroyCgroup(const Path & cgroup);
|
struct CgroupStats
|
||||||
|
{
|
||||||
|
std::optional<std::chrono::microseconds> cpuUser, cpuSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Destroy the cgroup denoted by 'path'. The postcondition is that
|
||||||
|
'path' does not exist, and thus any processes in the cgroup have
|
||||||
|
been killed. Also return statistics from the cgroup just before
|
||||||
|
destruction. */
|
||||||
|
CgroupStats destroyCgroup(const Path & cgroup);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue