aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2022-11-18 13:40:59 +0100
committerEelco Dolstra <edolstra@gmail.com>2022-11-18 13:40:59 +0100
commitfa68eb367e79297bb1c0451cd92ad18a06edce96 (patch)
treefb466b3f1fe55c0db748075232022aa02e6a51c6 /src
parent20f66c6889aa9d907feee4946702d655b6bd796f (diff)
Get CPU stats from the cgroup
Diffstat (limited to 'src')
-rw-r--r--src/libstore/build-result.hh5
-rw-r--r--src/libstore/build/derivation-goal.cc8
-rw-r--r--src/libstore/build/local-derivation-goal.cc14
-rw-r--r--src/libstore/build/local-derivation-goal.hh2
-rw-r--r--src/libstore/cgroup.cc39
-rw-r--r--src/libstore/cgroup.hh14
6 files changed, 71 insertions, 11 deletions
diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh
index 24fb1f763..a5749cf33 100644
--- a/src/libstore/build-result.hh
+++ b/src/libstore/build-result.hh
@@ -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;
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 41d2e2a1c..9bc3dc742 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -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 {
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 2d1e093ca..f273ebe8a 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -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))
diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh
index 1ec6b3649..34c4e9187 100644
--- a/src/libstore/build/local-derivation-goal.hh
+++ b/src/libstore/build/local-derivation-goal.hh
@@ -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)
diff --git a/src/libstore/cgroup.cc b/src/libstore/cgroup.cc
index 56e980be3..2a485f0f9 100644
--- a/src/libstore/cgroup.cc
+++ b/src/libstore/cgroup.cc
@@ -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);
}
}
diff --git a/src/libstore/cgroup.hh b/src/libstore/cgroup.hh
index dc6758957..3ead4735f 100644
--- a/src/libstore/cgroup.hh
+++ b/src/libstore/cgroup.hh
@@ -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);
}