about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2017-08-16T15·32+0200
committerEelco Dolstra <edolstra@gmail.com>2017-08-16T18·56+0200
commitb4ed97e3a3116909fcaa79f5ce84487ed3838112 (patch)
treed8f1a29dc7a8049cfaa301ee2a04795afbf6c712
parent23b8b7e096d3a2a784387a09f68115706b1e9552 (diff)
nix optimise-store: Show how much space has been freed
-rw-r--r--src/libstore/build.cc2
-rw-r--r--src/libstore/local-store.hh2
-rw-r--r--src/libstore/optimise-store.cc14
-rw-r--r--src/libutil/logging.hh31
-rw-r--r--src/nix/progress-bar.cc107
5 files changed, 101 insertions, 55 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 1a1fc1dee54d..fc039a8e52b5 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -3257,7 +3257,7 @@ void DerivationGoal::flushLine()
         logTail.push_back(currentLogLine);
         if (logTail.size() > settings.logLines) logTail.pop_front();
     }
-    act->progress(currentLogLine);
+    act->result(resBuildLogLine, currentLogLine);
     currentLogLine = "";
     currentLogLinePos = 0;
 }
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 551c6b506fb1..2af1bfbb3892 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -251,7 +251,7 @@ private:
 
     InodeHash loadInodeHash();
     Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash);
-    void optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash);
+    void optimisePath_(Activity * act, OptimiseStats & stats, const Path & path, InodeHash & inodeHash);
 
     // Internal versions that are not wrapped in retry_sqlite.
     bool isValidPath_(State & state, const Path & path);
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 5093305a13e3..f1325ba5a1ce 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -89,7 +89,8 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
 }
 
 
-void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash)
+void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
+    const Path & path, InodeHash & inodeHash)
 {
     checkInterrupt();
 
@@ -114,7 +115,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
     if (S_ISDIR(st.st_mode)) {
         Strings names = readDirectoryIgnoringInodes(path, inodeHash);
         for (auto & i : names)
-            optimisePath_(stats, path + "/" + i, inodeHash);
+            optimisePath_(act, stats, path + "/" + i, inodeHash);
         return;
     }
 
@@ -244,6 +245,9 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
     stats.filesLinked++;
     stats.bytesFreed += st.st_size;
     stats.blocksFreed += st.st_blocks;
+
+    if (act)
+        act->result(resFileLinked, st.st_size, st.st_blocks);
 }
 
 
@@ -263,7 +267,7 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
         if (!isValidPath(i)) continue; /* path was GC'ed, probably */
         {
             Activity act(*logger, actUnknown, fmt("optimising path '%s'", i));
-            optimisePath_(stats, realStoreDir + "/" + baseNameOf(i), inodeHash);
+            optimisePath_(&act, stats, realStoreDir + "/" + baseNameOf(i), inodeHash);
         }
         done++;
         act.progress(done, paths.size());
@@ -281,7 +285,7 @@ void LocalStore::optimiseStore()
 
     optimiseStore(stats);
 
-    printError(
+    printInfo(
         format("%1% freed by hard-linking %2% files")
         % showBytes(stats.bytesFreed)
         % stats.filesLinked);
@@ -292,7 +296,7 @@ void LocalStore::optimisePath(const Path & path)
     OptimiseStats stats;
     InodeHash inodeHash;
 
-    if (settings.autoOptimiseStore) optimisePath_(stats, path, inodeHash);
+    if (settings.autoOptimiseStore) optimisePath_(nullptr, stats, path, inodeHash);
 }
 
 
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index bb89643d0a0b..e0b8c0855eae 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -24,6 +24,11 @@ typedef enum {
     actOptimiseStore = 106,
 } ActivityType;
 
+typedef enum {
+    resFileLinked = 100,
+    resBuildLogLine = 101,
+} ResultType;
+
 typedef uint64_t ActivityId;
 
 class Logger
@@ -49,9 +54,20 @@ public:
 
     virtual void progress(ActivityId act, uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, uint64_t failed = 0) { };
 
-    virtual void progress(ActivityId act, const std::string & s) { };
-
     virtual void setExpected(ActivityId act, ActivityType type, uint64_t expected) { };
+
+    struct Field
+    {
+        // FIXME: use std::variant.
+        enum { tInt, tString } type;
+        uint64_t i = 0;
+        std::string s;
+        Field(const std::string & s) : type(tString), s(s) { }
+        Field(const char * s) : type(tString), s(s) { }
+        Field(const uint64_t & i) : type(tInt), i(i) { }
+    };
+
+    virtual void result(ActivityId act, ResultType type, const std::vector<Field> & fields) { };
 };
 
 struct Activity
@@ -68,12 +84,17 @@ struct Activity
     void progress(uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, uint64_t failed = 0) const
     { logger.progress(id, done, expected, running, failed); }
 
-    void progress(const std::string & s) const
-    { logger.progress(id, s); }
-
     void setExpected(ActivityType type2, uint64_t expected) const
     { logger.setExpected(id, type2, expected); }
 
+    template<typename... Args>
+    void result(ResultType type, const Args & ... args)
+    {
+        std::vector<Logger::Field> fields;
+        nop{(fields.emplace_back(Logger::Field(args)), 1)...};
+        logger.result(id, type, fields);
+    }
+
     friend class Logger;
 };
 
diff --git a/src/nix/progress-bar.cc b/src/nix/progress-bar.cc
index 15ef95370a8b..cc2928472d13 100644
--- a/src/nix/progress-bar.cc
+++ b/src/nix/progress-bar.cc
@@ -12,6 +12,20 @@
 
 namespace nix {
 
+static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
+{
+    assert(n < fields.size());
+    assert(fields[n].type == Logger::Field::tString);
+    return fields[n].s;
+}
+
+static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n)
+{
+    assert(n < fields.size());
+    assert(fields[n].type == Logger::Field::tInt);
+    return fields[n].i;
+}
+
 class ProgressBar : public Logger
 {
 private:
@@ -41,6 +55,8 @@ private:
         std::map<ActivityId, std::list<ActInfo>::iterator> its;
 
         std::map<ActivityType, ActivitiesByType> activitiesByType;
+
+        uint64_t filesLinked = 0, bytesLinked = 0;
     };
 
     Sync<State> state_;
@@ -80,14 +96,33 @@ public:
     void startActivity(ActivityId act, ActivityType type, const std::string & s) override
     {
         auto state(state_.lock());
-        createActivity(*state, act, s, type);
+
+        state->activities.emplace_back(ActInfo{s, "", type});
+        auto i = std::prev(state->activities.end());
+        state->its.emplace(act, i);
+        state->activitiesByType[type].its.emplace(act, i);
+
         update(*state);
     }
 
     void stopActivity(ActivityId act) override
     {
         auto state(state_.lock());
-        deleteActivity(*state, act);
+
+        auto i = state->its.find(act);
+        if (i != state->its.end()) {
+            auto & actByType = state->activitiesByType[i->second->type];
+            actByType.done += i->second->done;
+            actByType.failed += i->second->failed;
+
+            for (auto & j : i->second->expectedByType)
+                state->activitiesByType[j.first].expected -= j.second;
+
+            actByType.its.erase(act);
+            state->activities.erase(i->second);
+            state->its.erase(i);
+        }
+
         update(*state);
     }
 
@@ -106,16 +141,6 @@ public:
         update(*state);
     }
 
-    void progress(ActivityId act, const std::string & s) override
-    {
-        auto state(state_.lock());
-        auto s2 = trim(s);
-        if (!s2.empty()) {
-            updateActivity(*state, act, s2);
-            update(*state);
-        }
-    }
-
     void setExpected(ActivityId act, ActivityType type, uint64_t expected) override
     {
         auto state(state_.lock());
@@ -131,40 +156,29 @@ public:
         update(*state);
     }
 
-    void createActivity(State & state, ActivityId activity, const std::string & s, ActivityType type = actUnknown)
+    void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override
     {
-        state.activities.emplace_back(ActInfo{s, "", type});
-        auto i = std::prev(state.activities.end());
-        state.its.emplace(activity, i);
-        state.activitiesByType[type].its.emplace(activity, i);
-    }
-
-    void deleteActivity(State & state, ActivityId activity)
-    {
-        auto i = state.its.find(activity);
-        if (i != state.its.end()) {
-            auto & act = state.activitiesByType[i->second->type];
-            act.done += i->second->done;
-            act.failed += i->second->failed;
-
-            for (auto & j : i->second->expectedByType)
-                state.activitiesByType[j.first].expected -= j.second;
+        auto state(state_.lock());
 
-            act.its.erase(activity);
-            state.activities.erase(i->second);
-            state.its.erase(i);
+        if (type == resFileLinked) {
+            state->filesLinked++;
+            state->bytesLinked += getI(fields, 0);
+            update(*state);
         }
-    }
 
-    void updateActivity(State & state, ActivityId activity, const std::string & s2)
-    {
-        auto i = state.its.find(activity);
-        assert(i != state.its.end());
-        ActInfo info = *i->second;
-        state.activities.erase(i->second);
-        info.s2 = s2;
-        state.activities.emplace_back(info);
-        i->second = std::prev(state.activities.end());
+        else if (type == resBuildLogLine) {
+            auto s2 = trim(getS(fields, 0));
+            if (!s2.empty()) {
+                auto i = state->its.find(act);
+                assert(i != state->its.end());
+                ActInfo info = *i->second;
+                state->activities.erase(i->second);
+                info.s2 = s2;
+                state->activities.emplace_back(info);
+                i->second = std::prev(state->activities.end());
+                update(*state);
+            }
+        }
     }
 
     void update()
@@ -262,7 +276,14 @@ public:
 
         showActivity(actDownload, "%s MiB DL", "%.1f", MiB);
 
-        showActivity(actOptimiseStore, "%s paths optimised");
+        {
+            auto s = renderActivity(actOptimiseStore, "%s paths optimised");
+            if (s != "") {
+                s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
+                if (!res.empty()) res += ", ";
+                res += s;
+            }
+        }
 
         return res;
     }