]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Extract stats_{read,write} and Statistic enum to Statistics namespace
authorJoel Rosdahl <joel@rosdahl.net>
Fri, 11 Sep 2020 11:09:36 +0000 (13:09 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 14 Sep 2020 17:40:06 +0000 (19:40 +0200)
16 files changed:
src/CMakeLists.txt
src/Counters.cpp
src/Result.cpp
src/Statistics.cpp [new file with mode: 0644]
src/Statistics.hpp [new file with mode: 0644]
src/Util.hpp
src/argprocessing.hpp
src/compress.cpp
src/exceptions.hpp
src/hashutil.cpp
src/stats.cpp
src/stats.hpp
unittest/CMakeLists.txt
unittest/test_Counters.cpp
unittest/test_Statistics.cpp [new file with mode: 0644]
unittest/test_argprocessing.cpp

index 92fcbc67bb692b5d102fc9e19e70348826af6f0d..88736d8cb1e9fbe3391f327adba1b927dd5951ce 100644 (file)
@@ -25,6 +25,7 @@ set(
   ResultRetriever.cpp
   SignalHandler.cpp
   Stat.cpp
+  Statistics.cpp
   TemporaryFile.cpp
   ThreadPool.cpp
   Util.cpp
index 56603a164e6ffa2a22bee100af0667ab542711b5..986aacfabd76f6ece0f01ad88e62c3a3adec1946 100644 (file)
@@ -18,7 +18,7 @@
 
 #include "Counters.hpp"
 
-#include "stats.hpp"
+#include "Statistics.hpp"
 
 #include <algorithm>
 
index 96174f3edd85d7ace8df21dc8d60d82640a331d1..cbad746e1fa6828efea36074fd996c52a0a89829 100644 (file)
@@ -27,9 +27,9 @@
 #include "File.hpp"
 #include "Logging.hpp"
 #include "Stat.hpp"
+#include "Statistics.hpp"
 #include "Util.hpp"
 #include "exceptions.hpp"
-#include "stats.hpp"
 
 #include <algorithm>
 
diff --git a/src/Statistics.cpp b/src/Statistics.cpp
new file mode 100644 (file)
index 0000000..25de0ec
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "Statistics.hpp"
+
+#include "AtomicFile.hpp"
+#include "Logging.hpp"
+#include "Util.hpp"
+#include "exceptions.hpp"
+
+using Logging::log;
+using nonstd::nullopt;
+using nonstd::optional;
+
+namespace Statistics {
+
+Counters
+read(const std::string& path)
+{
+  Counters counters;
+
+  std::string data;
+  try {
+    data = Util::read_file(path);
+  } catch (const Error&) {
+    // Ignore.
+    return counters;
+  }
+
+  size_t i = 0;
+  const char* str = data.c_str();
+  while (true) {
+    char* end;
+    const uint64_t value = std::strtoull(str, &end, 10);
+    if (end == str) {
+      break;
+    }
+    counters.set_raw(i, value);
+    ++i;
+    str = end;
+  }
+
+  return counters;
+}
+
+void
+write(const std::string& path, const Counters& counters)
+{
+  AtomicFile file(path, AtomicFile::Mode::text);
+  for (size_t i = 0; i < counters.size(); ++i) {
+    file.write(fmt::format("{}\n", counters.get_raw(i)));
+  }
+  try {
+    file.commit();
+  } catch (const Error& e) {
+    // Make failure to write a stats file a soft error since it's not important
+    // enough to fail whole the process and also because it is called in the
+    // Context destructor.
+    log("Error: {}", e.what());
+  }
+}
+
+} // namespace Statistics
diff --git a/src/Statistics.hpp b/src/Statistics.hpp
new file mode 100644 (file)
index 0000000..b517db1
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include "system.hpp"
+
+#include "Counters.hpp"
+
+#include <string>
+
+// Statistics fields in storage order.
+enum class Statistic {
+  none = 0,
+  compiler_produced_stdout = 1,
+  compile_failed = 2,
+  internal_error = 3,
+  cache_miss = 4,
+  preprocessor_error = 5,
+  could_not_find_compiler = 6,
+  missing_cache_file = 7,
+  preprocessed_cache_hit = 8,
+  bad_compiler_arguments = 9,
+  called_for_link = 10,
+  files_in_cache = 11,
+  cache_size_kibibyte = 12,
+  obsolete_max_files = 13,
+  obsolete_max_size = 14,
+  unsupported_source_language = 15,
+  bad_output_file = 16,
+  no_input_file = 17,
+  multiple_source_files = 18,
+  autoconf_test = 19,
+  unsupported_compiler_option = 20,
+  output_to_stdout = 21,
+  direct_cache_hit = 22,
+  compiler_produced_no_output = 23,
+  compiler_produced_empty_output = 24,
+  error_hashing_extra_file = 25,
+  compiler_check_failed = 26,
+  could_not_use_precompiled_header = 27,
+  called_for_preprocessing = 28,
+  cleanups_performed = 29,
+  unsupported_code_directive = 30,
+  stats_zeroed_timestamp = 31,
+  could_not_use_modules = 32,
+
+  END
+};
+
+namespace Statistics {
+
+// Read counters from `path`. No lock is acquired.
+Counters read(const std::string& path);
+
+// Write `counters` to `path`. No lock is acquired.
+void write(const std::string& path, const Counters& counters);
+
+} // namespace Statistics
index 834f9f74e54be4a97477dacbca474eb719ae0931..1c156b8e9c82f6e1dfa211062566d9ea0dd91be4 100644 (file)
@@ -32,6 +32,8 @@
 #include <utility>
 #include <vector>
 
+class Context;
+
 namespace Util {
 
 using DataReceiver = std::function<void(const void* data, size_t size)>;
index cd36c7d0bc9236f5a6dc33cb882602c88f46c5e5..b8336fb08242caada0d9a8c63f3e09e32351fb3f 100644 (file)
@@ -19,7 +19,7 @@
 #pragma once
 
 #include "Args.hpp"
-#include "stats.hpp"
+#include "Statistics.hpp"
 
 #include "third_party/nonstd/optional.hpp"
 
index 961faab765577d225d3451cd33dc8e92bef87a35..2d583631eb1bb8e129640f853b7b777c1a71fabb 100644 (file)
@@ -26,6 +26,7 @@
 #include "Logging.hpp"
 #include "Manifest.hpp"
 #include "Result.hpp"
+#include "Statistics.hpp"
 #include "StdMakeUnique.hpp"
 #include "ThreadPool.hpp"
 #include "ZstdCompressor.hpp"
index a6a6e670b1234f5e38cfcd4f34884d62f740be20..1c731414bc97dce4831202a46efc6d69e9e8f795 100644 (file)
@@ -21,7 +21,7 @@
 #include "system.hpp"
 
 #include "FormatNonstdStringView.hpp"
-#include "stats.hpp"
+#include "Statistics.hpp"
 
 #include "third_party/fmt/core.h"
 #include "third_party/nonstd/optional.hpp"
index 67f6a5b2fda9a153f13d8a7c0b65104b60901686..1ecc3ab4027ce116205e49245897f901e5c1e3bb 100644 (file)
@@ -27,7 +27,6 @@
 #include "ccache.hpp"
 #include "execute.hpp"
 #include "macroskip.hpp"
-#include "stats.hpp"
 
 #ifdef INODE_CACHE_SUPPORTED
 #  include "InodeCache.hpp"
index 5e39f10bb89c71b130f07e7f9841b30e4daa25cc..29513dad55582c5631f6b7ebbb50b46a788d7660 100644 (file)
 #include "Counters.hpp"
 #include "Lockfile.hpp"
 #include "Logging.hpp"
+#include "Statistics.hpp"
 #include "cleanup.hpp"
 #include "hashutil.hpp"
 
 #include "third_party/fmt/core.h"
+#include "third_party/nonstd/optional.hpp"
 
 #include <cmath>
 
@@ -149,42 +151,6 @@ format_timestamp(uint64_t timestamp)
   }
 }
 
-// Parse a stats file from a buffer, adding to the counters.
-static void
-parse_stats(Counters& counters, const std::string& buf)
-{
-  size_t i = 0;
-  const char* p = buf.c_str();
-  while (true) {
-    char* p2;
-    unsigned long long val = std::strtoull(p, &p2, 10);
-    if (p2 == p) {
-      break;
-    }
-    counters.increment(static_cast<Statistic>(i), val);
-    i++;
-    p = p2;
-  }
-}
-
-// Write out a stats file.
-void
-stats_write(const std::string& path, const Counters& counters)
-{
-  AtomicFile file(path, AtomicFile::Mode::text);
-  for (size_t i = 0; i < counters.size(); ++i) {
-    file.write(fmt::format("{}\n", counters.get_raw(i)));
-  }
-  try {
-    file.commit();
-  } catch (const Error& e) {
-    // Make failure to write a stats file a soft error since it's not important
-    // enough to fail whole the process AND because it is called in the Context
-    // destructor.
-    log("Error: {}", e.what());
-  }
-}
-
 static double
 stats_hit_rate(const Counters& counters)
 {
@@ -214,30 +180,15 @@ stats_collect(const Config& config, Counters& counters, time_t* last_updated)
     }
 
     counters.set(Statistic::stats_zeroed_timestamp, 0); // Don't add
-    stats_read(fname, counters);
+    counters.increment(Statistics::read(fname));
     zero_timestamp =
       std::max(counters.get(Statistic::stats_zeroed_timestamp), zero_timestamp);
-    auto st = Stat::stat(fname);
-    if (st && st.mtime() > *last_updated) {
-      *last_updated = st.mtime();
-    }
+    *last_updated = std::max(*last_updated, Stat::stat(fname).mtime());
   }
 
   counters.set(Statistic::stats_zeroed_timestamp, zero_timestamp);
 }
 
-// Read in the stats from one directory and add to the counters.
-void
-stats_read(const std::string& path, Counters& counters)
-{
-  try {
-    std::string data = Util::read_file(path);
-    parse_stats(counters, data);
-  } catch (Error&) {
-    // Ignore.
-  }
-}
-
 // Write counter updates in updates to sfile.
 void
 stats_flush_to_file(const Config& config,
@@ -274,9 +225,9 @@ stats_flush_to_file(const Config& config,
       return;
     }
 
-    stats_read(sfile, counters);
+    counters = Statistics::read(sfile);
     counters.increment(updates);
-    stats_write(sfile, counters);
+    Statistics::write(sfile, counters);
   }
 
   std::string subdir(Util::dir_name(sfile));
@@ -407,7 +358,6 @@ stats_zero(const Context& ctx)
   time_t timestamp = time(nullptr);
 
   for (int dir = 0; dir <= 0xF; dir++) {
-    Counters counters;
     auto fname = fmt::format("{}/{:x}/stats", ctx.config.cache_dir(), dir);
     if (!Stat::stat(fname)) {
       // No point in trying to reset the stats file if it doesn't exist.
@@ -415,14 +365,14 @@ stats_zero(const Context& ctx)
     }
     Lockfile lock(fname);
     if (lock.acquired()) {
-      stats_read(fname, counters);
+      Counters counters = Statistics::read(fname);
       for (size_t i = 0; k_statistics_fields[i].message; i++) {
         if (!(k_statistics_fields[i].flags & FLAG_NOZERO)) {
           counters.set(k_statistics_fields[i].statistic, 0);
         }
       }
       counters.set(Statistic::stats_zeroed_timestamp, timestamp);
-      stats_write(fname, counters);
+      Statistics::write(fname, counters);
     }
   }
 }
@@ -436,9 +386,8 @@ stats_get_obsolete_limits(const std::string& dir,
   assert(maxfiles);
   assert(maxsize);
 
-  Counters counters;
   std::string sname = dir + "/stats";
-  stats_read(sname, counters);
+  Counters counters = Statistics::read(sname);
   *maxfiles = counters.get(Statistic::obsolete_max_files);
   *maxsize = counters.get(Statistic::obsolete_max_size) * 1024;
 }
@@ -447,14 +396,13 @@ stats_get_obsolete_limits(const std::string& dir,
 void
 stats_set_sizes(const std::string& dir, uint64_t num_files, uint64_t total_size)
 {
-  Counters counters;
   std::string statsfile = dir + "/stats";
   Lockfile lock(statsfile);
   if (lock.acquired()) {
-    stats_read(statsfile, counters);
+    Counters counters = Statistics::read(statsfile);
     counters.set(Statistic::files_in_cache, num_files);
     counters.set(Statistic::cache_size_kibibyte, total_size / 1024);
-    stats_write(statsfile, counters);
+    Statistics::write(statsfile, counters);
   }
 }
 
@@ -462,12 +410,11 @@ stats_set_sizes(const std::string& dir, uint64_t num_files, uint64_t total_size)
 void
 stats_add_cleanup(const std::string& dir, uint64_t count)
 {
-  Counters counters;
   std::string statsfile = dir + "/stats";
   Lockfile lock(statsfile);
   if (lock.acquired()) {
-    stats_read(statsfile, counters);
+    Counters counters = Statistics::read(statsfile);
     counters.increment(Statistic::cleanups_performed, count);
-    stats_write(statsfile, counters);
+    Statistics::write(statsfile, counters);
   }
 }
index 67948db52c5766e0c76eaf33c296cbf1469d589f..d8c1b813dab768541d316ddc8de553dbd7855e5d 100644 (file)
 class Config;
 class Context;
 
-// Statistics fields in storage order.
-enum class Statistic {
-  none = 0,
-  compiler_produced_stdout = 1,
-  compile_failed = 2,
-  internal_error = 3,
-  cache_miss = 4,
-  preprocessor_error = 5,
-  could_not_find_compiler = 6,
-  missing_cache_file = 7,
-  preprocessed_cache_hit = 8,
-  bad_compiler_arguments = 9,
-  called_for_link = 10,
-  files_in_cache = 11,
-  cache_size_kibibyte = 12,
-  obsolete_max_files = 13,
-  obsolete_max_size = 14,
-  unsupported_source_language = 15,
-  bad_output_file = 16,
-  no_input_file = 17,
-  multiple_source_files = 18,
-  autoconf_test = 19,
-  unsupported_compiler_option = 20,
-  output_to_stdout = 21,
-  direct_cache_hit = 22,
-  compiler_produced_no_output = 23,
-  compiler_produced_empty_output = 24,
-  error_hashing_extra_file = 25,
-  compiler_check_failed = 26,
-  could_not_use_precompiled_header = 27,
-  called_for_preprocessing = 28,
-  cleanups_performed = 29,
-  unsupported_code_directive = 30,
-  stats_zeroed_timestamp = 31,
-  could_not_use_modules = 32,
-
-  END
-};
-
 void stats_flush(Context& ctx);
 void stats_flush_to_file(const Config& config,
                          const std::string& sfile,
@@ -81,5 +42,3 @@ void stats_set_sizes(const std::string& dir,
                      uint64_t num_files,
                      uint64_t total_size);
 void stats_add_cleanup(const std::string& dir, uint64_t count);
-void stats_read(const std::string& path, Counters& counters);
-void stats_write(const std::string& path, const Counters& counters);
index 6ae54ffa21f16564ff5d58d5886b09e66ff3942e..aa1fa8f527f94ce67daf8941eec592b70542c320 100644 (file)
@@ -13,6 +13,7 @@ set(
   test_Lockfile.cpp
   test_NullCompression.cpp
   test_Stat.cpp
+  test_Statistics.cpp
   test_Util.cpp
   test_ZstdCompression.cpp
   test_argprocessing.cpp
index 951856ad26f2ddd8da695e5131805bf1290b0a98..d2382af3d1a1d294bc06cdb9fb760ebf16e41be0 100644 (file)
@@ -17,7 +17,7 @@
 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "../src/Counters.hpp"
-#include "../src/stats.hpp"
+#include "../src/Statistics.hpp"
 #include "TestUtil.hpp"
 
 #include "third_party/doctest.h"
diff --git a/unittest/test_Statistics.cpp b/unittest/test_Statistics.cpp
new file mode 100644 (file)
index 0000000..a680ee2
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright (C) 2011-2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "../src/Statistics.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("Statistics");
+
+TEST_CASE("Read nonexistent")
+{
+  TestContext test_context;
+
+  Counters counters = Statistics::read("test");
+
+  REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
+  CHECK(counters.get(Statistic::cache_miss) == 0);
+}
+
+TEST_CASE("Read bad")
+{
+  TestContext test_context;
+
+  Util::write_file("test", "bad 1 2 3 4 5\n");
+  Counters counters = Statistics::read("test");
+
+  REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
+  CHECK(counters.get(Statistic::cache_miss) == 0);
+}
+
+TEST_CASE("Read existing")
+{
+  TestContext test_context;
+
+  Util::write_file("test", "0 1 2 3 27 5\n");
+  Counters counters = Statistics::read("test");
+
+  REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
+  CHECK(counters.get(Statistic::cache_miss) == 27);
+  CHECK(counters.get(Statistic::could_not_use_modules) == 0);
+}
+
+TEST_CASE("Read future counters")
+{
+  TestContext test_context;
+
+  std::string content;
+  size_t count = static_cast<size_t>(Statistic::END) + 1;
+  for (size_t i = 0; i < count; ++i) {
+    content += fmt::format("{}\n", i);
+  }
+
+  Util::write_file("test", content);
+  Counters counters = Statistics::read("test");
+
+  REQUIRE(counters.size() == count);
+  for (size_t i = 0; i < count; ++i) {
+    CHECK(counters.get_raw(i) == i);
+  }
+}
+
+TEST_CASE("Write")
+{
+  TestContext test_context;
+
+  Counters counters;
+  size_t count = static_cast<size_t>(Statistic::END) + 1;
+  for (size_t i = 0; i < count; ++i) {
+    counters.set_raw(i, i);
+  }
+
+  Statistics::write("test", counters);
+  counters = Statistics::read("test");
+
+  REQUIRE(counters.size() == count);
+  for (size_t i = 0; i < count; ++i) {
+    CHECK(counters.get_raw(i) == i);
+  }
+}
+
+TEST_SUITE_END();
index 54767146396496fcd3108bcb678972dcb4ad8346..30ef057eda2d4e030cc1396eae97d5975312477d 100644 (file)
@@ -19,8 +19,8 @@
 #include "../src/Args.hpp"
 #include "../src/Config.hpp"
 #include "../src/Context.hpp"
+#include "../src/Statistics.hpp"
 #include "../src/Util.hpp"
-#include "../src/stats.hpp"
 #include "TestUtil.hpp"
 #include "argprocessing.hpp"