state commit

Change-Id: I43a2bb6f771c67fe819cf38c272bc338fa4cb048
This commit is contained in:
Qyriad 2024-06-21 20:25:03 -06:00
parent e30b15ca93
commit cc005058a9
3 changed files with 132 additions and 59 deletions

View file

@ -11,6 +11,8 @@
#include <iostream> #include <iostream>
#include <chrono> #include <chrono>
#include <boost/container/static_vector.hpp>
namespace nix { namespace nix {
using namespace std::literals::chrono_literals; using namespace std::literals::chrono_literals;
@ -124,31 +126,32 @@ void ProgressBar::startActivity(
.parent = parent, .parent = parent,
.startTime = std::chrono::steady_clock::now() .startTime = std::chrono::steady_clock::now()
}); });
auto i = std::prev(state->activities.end());
state->its.emplace(act, i); auto mostRecentAct = std::prev(state->activities.end());
state->activitiesByType[type].its.emplace(act, i); state->its.emplace(act, mostRecentAct);
state->activitiesByType[type].its.emplace(act, mostRecentAct);
if (type == actBuild) { if (type == actBuild) {
std::string name(storePathToName(getS(fields, 0))); std::string name(storePathToName(getS(fields, 0)));
if (name.ends_with(".drv")) if (name.ends_with(".drv"))
name = name.substr(0, name.size() - 4); name = name.substr(0, name.size() - 4);
i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name); mostRecentAct->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
auto machineName = getS(fields, 1); auto machineName = getS(fields, 1);
if (machineName != "") if (machineName != "")
i->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName); mostRecentAct->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName);
// Used to be curRound and nrRounds, but the // Used to be curRound and nrRounds, but the
// implementation was broken for a long time. // implementation was broken for a long time.
if (getI(fields, 2) != 1 || getI(fields, 3) != 1) { if (getI(fields, 2) != 1 || getI(fields, 3) != 1) {
throw Error("log message indicated repeating builds, but this is not currently implemented"); throw Error("log message indicated repeating builds, but this is not currently implemented");
} }
i->name = DrvName(name).name; mostRecentAct->name = DrvName(name).name;
} }
if (type == actSubstitute) { if (type == actSubstitute) {
auto name = storePathToName(getS(fields, 0)); auto name = storePathToName(getS(fields, 0));
auto sub = getS(fields, 1); auto sub = getS(fields, 1);
i->s = fmt( mostRecentAct->s = fmt(
sub.starts_with("local") sub.starts_with("local")
? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s" ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
: "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
@ -159,17 +162,17 @@ void ProgressBar::startActivity(
auto name = storePathToName(getS(fields, 0)); auto name = storePathToName(getS(fields, 0));
if (name.ends_with(".drv")) if (name.ends_with(".drv"))
name = name.substr(0, name.size() - 4); name = name.substr(0, name.size() - 4);
i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name); mostRecentAct->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name);
i->name = DrvName(name).name; mostRecentAct->name = DrvName(name).name;
} }
if (type == actQueryPathInfo) { if (type == actQueryPathInfo) {
auto name = storePathToName(getS(fields, 0)); auto name = storePathToName(getS(fields, 0));
i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1)); mostRecentAct->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1));
} }
if (ancestorTakesPrecedence(*state, type, parent)) { if (ancestorTakesPrecedence(*state, type, parent)) {
i->visible = false; mostRecentAct->visible = false;
} }
update(*state); update(*state);
@ -199,19 +202,22 @@ void ProgressBar::stopActivity(ActivityId act)
{ {
auto state(state_.lock()); auto state(state_.lock());
auto i = state->its.find(act); auto const currentAct = state->its.find(act);
if (i != state->its.end()) { if (currentAct != state->its.end()) {
auto & actByType = state->activitiesByType[i->second->type]; std::list<ActInfo>::iterator const & actInfo = currentAct->second;
actByType.done += i->second->done;
actByType.failed += i->second->failed;
for (auto & j : i->second->expectedByType) auto & actByType = state->activitiesByType[actInfo->type];
state->activitiesByType[j.first].expected -= j.second; actByType.done += actInfo->done;
actByType.failed += actInfo->failed;
for (auto const & [type, expected] : actInfo->expectedByType) {
state->activitiesByType[type].expected -= expected;
}
actByType.its.erase(act); actByType.its.erase(act);
state->activities.erase(i->second); state->activities.erase(actInfo);
state->its.erase(i); state->its.erase(currentAct);
} }
update(*state); update(*state);
@ -230,9 +236,9 @@ void ProgressBar::result(ActivityId act, ResultType type, const std::vector<Fiel
else if (type == resBuildLogLine || type == resPostBuildLogLine) { else if (type == resBuildLogLine || type == resPostBuildLogLine) {
auto lastLine = chomp(getS(fields, 0)); auto lastLine = chomp(getS(fields, 0));
if (!lastLine.empty()) { if (!lastLine.empty()) {
auto i = state->its.find(act); auto found = state->its.find(act);
assert(i != state->its.end()); assert(found != state->its.end());
ActInfo info = *i->second; ActInfo info = *found->second;
if (printBuildLogs) { if (printBuildLogs) {
auto suffix = "> "; auto suffix = "> ";
if (type == resPostBuildLogLine) { if (type == resPostBuildLogLine) {
@ -240,10 +246,10 @@ void ProgressBar::result(ActivityId act, ResultType type, const std::vector<Fiel
} }
log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine); log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
} else { } else {
state->activities.erase(i->second); state->activities.erase(found->second);
info.lastLine = lastLine; info.lastLine = lastLine;
state->activities.emplace_back(info); state->activities.emplace_back(info);
i->second = std::prev(state->activities.end()); found->second = std::prev(state->activities.end());
update(*state); update(*state);
} }
} }
@ -281,11 +287,11 @@ void ProgressBar::result(ActivityId act, ResultType type, const std::vector<Fiel
auto i = state->its.find(act); auto i = state->its.find(act);
assert(i != state->its.end()); assert(i != state->its.end());
ActInfo & actInfo = *i->second; ActInfo & actInfo = *i->second;
auto type = (ActivityType) getI(fields, 0); auto const type = static_cast<ActivityType>(getI(fields, 0));
auto & j = actInfo.expectedByType[type]; uint64_t & thisExpected = actInfo.expectedByType[type];
state->activitiesByType[type].expected -= j; state->activitiesByType[type].expected -= thisExpected;
j = getI(fields, 1); thisExpected = getI(fields, 1);
state->activitiesByType[type].expected += j; state->activitiesByType[type].expected += thisExpected;
update(*state); update(*state);
} }
} }
@ -354,16 +360,24 @@ std::chrono::milliseconds ProgressBar::draw(State & state)
return nextWakeup; return nextWakeup;
} }
std::string ProgressBar::getStatus(State & state) std::string ProgressBar::getStatus(State & state) const
{ {
constexpr auto MiB = 1024.0 * 1024.0; constexpr static auto MiB = 1024.0 * 1024.0;
std::string res; // Each time the `showActivity` lambda is called:
// actBuild, actFileTransfer, actVerifyPaths,
// + 3 permutations for actCopyPaths, actCopyPath,
// + corrupted and unstrusted paths.
constexpr static auto MAX_PARTS = 8;
// Stack-allocated vector.
// No need to heap allocate here when we have such a low maxiumum.
boost::container::static_vector<std::string, MAX_PARTS> parts;
auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
auto & act = state.activitiesByType[type]; auto const & act = state.activitiesByType[type];
uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed; uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed;
for (auto & [actId, infoIt] : act.its) { for (auto const & [actId, infoIt] : act.its) {
done += infoIt->done; done += infoIt->done;
expected += infoIt->expected; expected += infoIt->expected;
running += infoIt->running; running += infoIt->running;
@ -428,31 +442,38 @@ std::string ProgressBar::getStatus(State & state)
}; };
auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
auto s = renderActivity(type, itemFmt, numberFmt, unit); auto rendered = renderActivity(type, itemFmt, numberFmt, unit);
if (s.empty()) return; if (!rendered.empty()) {
if (!res.empty()) res += ", "; parts.emplace_back(std::move(rendered));
res += s; }
}; };
showActivity(actBuilds, "%s built"); showActivity(actBuilds, "%s built");
auto s1 = renderActivity(actCopyPaths, "%s copied"); {
auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB); auto const renderedMultiCopy = renderActivity(actCopyPaths, "%s copied");
auto const renderedSingleCopy = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);
if (!s1.empty() || !s2.empty()) { if (!renderedMultiCopy.empty() || !renderedSingleCopy.empty()) {
if (!res.empty()) res += ", "; std::string part = concatStrings(
if (s1.empty()) res += "0 copied"; else res += s1; renderedMultiCopy.empty() ? "0 copied" : renderedMultiCopy,
if (!s2.empty()) { res += " ("; res += s2; res += ')'; } !renderedSingleCopy.empty() ? fmt(" (%s)", renderedSingleCopy) : ""
);
parts.emplace_back(std::move(part));
}
} }
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB); showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
{ {
auto s = renderActivity(actOptimiseStore, "%s paths optimised"); auto renderedOptimise = renderActivity(actOptimiseStore, "%s paths optimised");
if (s != "") { if (!renderedOptimise.empty()) {
s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked); parts.emplace_back(std::move(renderedOptimise));
if (!res.empty()) res += ", "; parts.emplace_back(fmt(
res += s; "%.1f MiB / %d inodes freed",
state.bytesLinked / MiB,
state.filesLinked
));
} }
} }
@ -460,16 +481,14 @@ std::string ProgressBar::getStatus(State & state)
showActivity(actVerifyPaths, "%s paths verified"); showActivity(actVerifyPaths, "%s paths verified");
if (state.corruptedPaths) { if (state.corruptedPaths) {
if (!res.empty()) res += ", "; parts.emplace_back(fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths));
res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths);
} }
if (state.untrustedPaths) { if (state.untrustedPaths) {
if (!res.empty()) res += ", "; parts.emplace_back(fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths));
res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths);
} }
return res; return concatStringsSep(", ", parts);
} }
void ProgressBar::writeToStdout(std::string_view s) void ProgressBar::writeToStdout(std::string_view s)

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
///@file ///@file
#include "escape-string.hh"
#include "types.hh" #include "types.hh"
#include "error.hh" #include "error.hh"
#include "config.hh" #include "config.hh"
@ -111,6 +112,18 @@ public:
Field(const std::string & s) : type(tString), s(s) { } Field(const std::string & s) : type(tString), s(s) { }
Field(const char * s) : type(tString), s(s) { } Field(const char * s) : type(tString), s(s) { }
Field(const uint64_t & i) : type(tInt), i(i) { } Field(const uint64_t & i) : type(tInt), i(i) { }
inline std::string to_string() const
{
switch (this->type) {
case tString:
return fmt("Field { s = \"%s\" }", escapeString(this->s, EscapeStringOptions{
.escapeNonPrinting = true,
}));
case tInt:
return fmt("Field { i = %d }", this->i);
}
}
}; };
typedef std::vector<Field> Fields; typedef std::vector<Field> Fields;

View file

@ -19,13 +19,18 @@ static_assert(EXPECTED == EXPECTED_RAW, "Hey, hey, the ANSI escape code definiti
namespace nix namespace nix
{ {
TEST(ProgressBar, basicStatusRender) { ProgressBar & init()
{
initNix(); initNix();
initGC(); initGC();
startProgressBar(); startProgressBar();
ASSERT_NE(dynamic_cast<ProgressBar *>(logger), nullptr);
ProgressBar & progressBar = dynamic_cast<ProgressBar &>(*logger); assert(dynamic_cast<ProgressBar *>(logger) != nullptr);
return dynamic_cast<ProgressBar &>(*logger);
}
TEST(ProgressBar, basicStatusRender) {
ProgressBar & progressBar = init();
Activity act( Activity act(
progressBar, progressBar,
@ -40,4 +45,40 @@ namespace nix
ASSERT_EQ(renderedStatus, EXPECTED); ASSERT_EQ(renderedStatus, EXPECTED);
} }
//TEST(ProgressBar, resultPermute) {
// ProgressBar & progressBar = init();
//
// Activity act(
// progressBar,
// lvlDebug,
// actBuild,
// "building '/nix/store/zdp9na4ci9s3g68xi9hnn5gbllf6ak03-lix-2.91.0-dev-lixpre20240616-0ba37dc.drv'",
// Logger::Fields{
// "/nix/store/zdp9na4ci9s3g68xi9hnn5gbllf6ak03-lix-2.91.0-dev-lixpre20240616-0ba37dc.drv",
// "",
// 1,
// 1,
// }
// );
//
// act.progress(
// /* done */ 13,
// /* expected */ 156,
// /* running */ 2,
// /* failed */ 0
// );
//
// act.progress(
// /* done */ 13,
// /* expected */ 156,
// /* running */ 2,
// /* failed */ 0
// );
//
// auto state = progressBar.state_.lock();
// std::string const autoStatus = progressBar.getStatus(*state);
//
// ASSERT_EQ(autoStatus, "a");
//}
} }