Get CPU stats from the cgroup
This commit is contained in:
parent
20f66c6889
commit
fa68eb367e
6 changed files with 71 additions and 11 deletions
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -78,6 +78,9 @@ struct BuildResult
|
|||
was repeated). */
|
||||
time_t startTime = 0, stopTime = 0;
|
||||
|
||||
/* User and system CPU time the build took. */
|
||||
std::optional<std::chrono::microseconds> cpuUser, cpuSystem;
|
||||
|
||||
bool success()
|
||||
{
|
||||
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
||||
|
|
|
@ -869,6 +869,14 @@ void DerivationGoal::buildDone()
|
|||
|
||||
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;
|
||||
|
||||
try {
|
||||
|
|
|
@ -137,7 +137,7 @@ void LocalDerivationGoal::killChild()
|
|||
also send a conventional kill to the child. */
|
||||
::kill(-pid, SIGKILL); /* ignore the result */
|
||||
|
||||
killSandbox();
|
||||
killSandbox(true);
|
||||
|
||||
pid.wait();
|
||||
}
|
||||
|
@ -146,10 +146,14 @@ void LocalDerivationGoal::killChild()
|
|||
}
|
||||
|
||||
|
||||
void LocalDerivationGoal::killSandbox()
|
||||
void LocalDerivationGoal::killSandbox(bool getStats)
|
||||
{
|
||||
if (cgroup) {
|
||||
destroyCgroup(*cgroup);
|
||||
auto stats = destroyCgroup(*cgroup);
|
||||
if (getStats) {
|
||||
buildResult.cpuUser = stats.cpuUser;
|
||||
buildResult.cpuSystem = stats.cpuSystem;
|
||||
}
|
||||
}
|
||||
|
||||
else if (buildUser) {
|
||||
|
@ -270,7 +274,7 @@ void LocalDerivationGoal::cleanupPostChildKill()
|
|||
malicious user from leaving behind a process that keeps files
|
||||
open and modifies them after they have been chown'ed to
|
||||
root. */
|
||||
killSandbox();
|
||||
killSandbox(true);
|
||||
|
||||
/* Terminate the recursive Nix daemon. */
|
||||
stopDaemon();
|
||||
|
@ -410,7 +414,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
/* Make sure that no other processes are executing under the
|
||||
sandbox uids. This must be done before any chownToBuilder()
|
||||
calls. */
|
||||
killSandbox();
|
||||
killSandbox(false);
|
||||
|
||||
/* Right platform? */
|
||||
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
|
||||
cgroup of the build. */
|
||||
void killSandbox();
|
||||
void killSandbox(bool getStats);
|
||||
|
||||
/* Create alternative path calculated from but distinct from the
|
||||
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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (entry.type != DT_DIR) continue;
|
||||
destroyCgroup(cgroup + "/" + entry.name);
|
||||
destroyCgroup(cgroup + "/" + entry.name, false);
|
||||
}
|
||||
|
||||
int round = 1;
|
||||
|
@ -79,8 +82,38 @@ void destroyCgroup(const Path & cgroup)
|
|||
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)
|
||||
throw SysError("deleting cgroup '%s'", cgroup);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
CgroupStats destroyCgroup(const Path & cgroup)
|
||||
{
|
||||
return destroyCgroup(cgroup, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,13 +2,25 @@
|
|||
|
||||
#if __linux__
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
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