- The Counters class has been moved to core::StatisticsCounters.
- Functionality in the Statistics namespace has been split into:
- core::StatsLog: Knows about “stats log” format.
- core::Statistics: Knows about properties of statistics fields and
how to present them.
- storage::primary::PrimaryStorage: Knows how to store and update
statistics in the primary storage backend.
- storage::primary::StatsFile: Knows about the “stats file” format.
CacheEntryWriter.cpp
Config.cpp
Context.cpp
- Counters.cpp
Depfile.cpp
Fd.cpp
Hash.cpp
ResultRetriever.cpp
SignalHandler.cpp
Stat.cpp
- Statistics.cpp
TemporaryFile.cpp
ThreadPool.cpp
Util.cpp
#include "Context.hpp"
-#include "Counters.hpp"
#include "Logging.hpp"
#include "SignalHandler.hpp"
#include "Util.hpp"
#include "Args.hpp"
#include "ArgsInfo.hpp"
#include "Config.hpp"
-#include "Counters.hpp"
#include "Digest.hpp"
#include "File.hpp"
#include "MiniTrace.hpp"
+++ /dev/null
-// Copyright (C) 2020-2021 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 "Counters.hpp"
-
-#include "third_party/nonstd/optional.hpp"
-
-#include <ctime>
-#include <functional>
-#include <string>
-
-class Config;
-
-namespace Statistics {
-
-// Read counters from `path`. No lock is acquired.
-Counters read(const std::string& path);
-
-// Read counters from lines of text in `path`.
-Counters read_log(const std::string& path);
-
-// Acquire a lock, read counters from `path`, call `function` with the counters,
-// write the counters to `path` and release the lock. Returns the resulting
-// counters or nullopt on error (e.g. if the lock could not be acquired).
-nonstd::optional<Counters> update(const std::string& path,
- std::function<void(Counters& counters)>);
-
-// Write input and result to the file in `path`.
-void log_result(const std::string& path,
- const std::string& input,
- const std::string& result);
-
-// Return a human-readable string representing the final ccache result, or
-// nullopt if there was no result.
-nonstd::optional<std::string> get_result_message(const Counters& counters);
-
-// Return a machine-readable string representing the final ccache result, or
-// nullopt if there was no result.
-nonstd::optional<std::string> get_result_id(const Counters& counters);
-
-// Zero all statistics counters except those tracking cache size and number of
-// files in the cache.
-void zero_all_counters(const Config& config);
-
-// Collect cache statistics from all statistics counters.
-std::pair<Counters, time_t> collect_counters(const Config& config);
-
-// Format stats log in human-readable format.
-std::string format_stats_log(const Config& config);
-
-// Format config header in human-readable format.
-std::string format_config_header(const Config& config);
-
-// Format cache statistics in human-readable format.
-std::string format_human_readable(const Counters& counters,
- time_t last_updated,
- bool from_log);
-
-// Format config footer in human-readable format.
-std::string format_config_footer(const Config& config);
-
-// Format cache statistics in machine-readable format.
-std::string format_machine_readable(const Counters& counters,
- time_t last_updated);
-
-} // namespace Statistics
#include "ResultExtractor.hpp"
#include "ResultRetriever.hpp"
#include "SignalHandler.hpp"
-#include "Statistics.hpp"
#include "TemporaryFile.hpp"
#include "UmaskScope.hpp"
#include "Util.hpp"
#include "language.hpp"
#include <compression/types.hpp>
+#include <core/Statistics.hpp>
+#include <core/StatsLog.hpp>
#include <core/exceptions.hpp>
#include <core/types.hpp>
#include <core/wincompat.hpp>
static int cache_compilation(int argc, const char* const* argv);
static Statistic do_cache_compilation(Context& ctx, const char* const* argv);
+static void
+log_result_to_debug_log(Context& ctx)
+{
+ if (ctx.config.log_file().empty() && !ctx.config.debug()) {
+ return;
+ }
+
+ core::Statistics statistics(ctx.storage.primary().get_statistics_updates());
+ const auto result_message = statistics.get_result_message();
+ if (result_message) {
+ LOG("Result: {}", *result_message);
+ }
+}
+
+static void
+log_result_to_stats_log(Context& ctx)
+{
+ if (ctx.config.stats_log().empty()) {
+ return;
+ }
+
+ core::Statistics statistics(ctx.storage.primary().get_statistics_updates());
+ const auto result_id = statistics.get_result_id();
+ if (!result_id) {
+ return;
+ }
+
+ core::StatsLog(ctx.config.stats_log())
+ .log_result(ctx.args_info.input_file, *result_id);
+}
+
static void
finalize_at_exit(Context& ctx)
{
return;
}
- if (!ctx.config.log_file().empty() || ctx.config.debug()) {
- const auto result = ctx.storage.primary().get_result_message();
- if (result) {
- LOG("Result: {}", *result);
- }
- }
-
- if (!ctx.config.stats_log().empty()) {
- const auto result_id = ctx.storage.primary().get_result_id();
- if (result_id) {
- Statistics::log_result(
- ctx.config.stats_log(), ctx.args_info.input_file, *result_id);
- }
- }
+ log_result_to_debug_log(ctx);
+ log_result_to_stats_log(ctx);
ctx.storage.finalize();
} catch (const core::ErrorBase& e) {
}
case PRINT_STATS: {
- Counters counters;
+ core::StatisticsCounters counters;
time_t last_updated;
std::tie(counters, last_updated) =
- Statistics::collect_counters(ctx.config);
- PRINT_RAW(stdout,
- Statistics::format_machine_readable(counters, last_updated));
+ ctx.storage.primary().get_all_statistics();
+ core::Statistics statistics(counters);
+ PRINT_RAW(stdout, statistics.format_machine_readable(last_updated));
break;
}
if (ctx.config.stats_log().empty()) {
throw core::Fatal("No stats log has been configured");
}
- PRINT_RAW(stdout, Statistics::format_stats_log(ctx.config));
- Counters counters = Statistics::read_log(ctx.config.stats_log());
- auto st = Stat::stat(ctx.config.stats_log(), Stat::OnError::log);
- PRINT_RAW(stdout,
- Statistics::format_human_readable(counters, st.mtime(), true));
+ PRINT(stdout, "{:36}{}\n", "stats log", ctx.config.stats_log());
+ core::Statistics statistics(
+ core::StatsLog(ctx.config.stats_log()).read());
+ const auto timestamp =
+ Stat::stat(ctx.config.stats_log(), Stat::OnError::log).mtime();
+ PRINT_RAW(stdout, statistics.format_human_readable(timestamp, true));
break;
}
case 's': { // --show-stats
- PRINT_RAW(stdout, Statistics::format_config_header(ctx.config));
- Counters counters;
+ core::StatisticsCounters counters;
time_t last_updated;
std::tie(counters, last_updated) =
- Statistics::collect_counters(ctx.config);
- PRINT_RAW(
- stdout,
- Statistics::format_human_readable(counters, last_updated, false));
- PRINT_RAW(stdout, Statistics::format_config_footer(ctx.config));
+ ctx.storage.primary().get_all_statistics();
+ core::Statistics statistics(counters);
+ PRINT_RAW(stdout, statistics.format_config_header(ctx.config));
+ PRINT_RAW(stdout, statistics.format_human_readable(last_updated, false));
+ PRINT_RAW(stdout, statistics.format_config_footer(ctx.config));
break;
}
}
case 'z': // --zero-stats
- Statistics::zero_all_counters(ctx.config);
+ ctx.storage.primary().zero_all_statistics();
PRINT_RAW(stdout, "Statistics zeroed\n");
break;
-# set(
-# sources
-# ${CMAKE_CURRENT_SOURCE_DIR}/file.cpp
-# )
+set(
+ sources
+ ${CMAKE_CURRENT_SOURCE_DIR}/Statistics.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/StatisticsCounters.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/StatsLog.cpp
+)
-# target_sources(ccache_lib PRIVATE ${sources})
+target_sources(ccache_lib PRIVATE ${sources})
-// Copyright (C) 2020-2021 Joel Rosdahl and other contributors
+// Copyright (C) 2021 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
#include "Statistics.hpp"
-#include "AtomicFile.hpp"
-#include "Config.hpp"
-#include "File.hpp"
-#include "Lockfile.hpp"
-#include "Logging.hpp"
-#include "Util.hpp"
-#include "assertions.hpp"
-#include "fmtmacros.hpp"
+#include <Config.hpp>
+#include <Logging.hpp>
+#include <Util.hpp>
+#include <fmtmacros.hpp>
-#include <core/exceptions.hpp>
-
-#include <fstream>
-#include <unordered_map>
-
-const unsigned FLAG_NOZERO = 1; // don't zero with the -z option
-const unsigned FLAG_ALWAYS = 2; // always show, even if zero
-const unsigned FLAG_NEVER = 4; // never show
-const unsigned FLAG_NOSTATSLOG = 8; // don't show for statslog
+namespace core {
using core::Statistic;
-using nonstd::nullopt;
-using nonstd::optional;
// Returns a formatted version of a statistics value, or the empty string if the
// statistics line shouldn't be printed.
using FormatFunction = std::string (*)(uint64_t value);
-static std::string
-format_size(uint64_t size)
-{
- return FMT("{:>11}", Util::format_human_readable_size(size));
-}
-
-static std::string
-format_size_times_1024(uint64_t size)
-{
- return format_size(size * 1024);
-}
-
-static std::string
-format_timestamp(uint64_t timestamp)
-{
- if (timestamp > 0) {
- const auto tm = Util::localtime(timestamp);
- char buffer[100] = "?";
- if (tm) {
- strftime(buffer, sizeof(buffer), "%c", &*tm);
- }
- return std::string(" ") + buffer;
- } else {
- return {};
- }
-}
-
-static double
-hit_rate(const Counters& counters)
-{
- const uint64_t direct = counters.get(Statistic::direct_cache_hit);
- const uint64_t preprocessed = counters.get(Statistic::preprocessed_cache_hit);
- const uint64_t hit = direct + preprocessed;
- const uint64_t miss = counters.get(Statistic::cache_miss);
- const uint64_t total = hit + miss;
- return total > 0 ? (100.0 * hit) / total : 0.0;
-}
-
-static void
-for_each_level_1_and_2_stats_file(
- const std::string& cache_dir,
- const std::function<void(const std::string& path)> function)
-{
- for (size_t level_1 = 0; level_1 <= 0xF; ++level_1) {
- function(FMT("{}/{:x}/stats", cache_dir, level_1));
- for (size_t level_2 = 0; level_2 <= 0xF; ++level_2) {
- function(FMT("{}/{:x}/{:x}/stats", cache_dir, level_1, level_2));
- }
- }
-}
+static std::string format_size_times_1024(uint64_t value);
+static std::string format_timestamp(uint64_t value);
-std::pair<Counters, time_t>
-Statistics::collect_counters(const Config& config)
-{
- Counters counters;
- uint64_t zero_timestamp = 0;
- time_t last_updated = 0;
-
- // Add up the stats in each directory.
- for_each_level_1_and_2_stats_file(config.cache_dir(), [&](const auto& path) {
- counters.set(Statistic::stats_zeroed_timestamp, 0); // Don't add
- counters.increment(Statistics::read(path));
- zero_timestamp =
- std::max(counters.get(Statistic::stats_zeroed_timestamp), zero_timestamp);
- last_updated = std::max(last_updated, Stat::stat(path).mtime());
- });
-
- counters.set(Statistic::stats_zeroed_timestamp, zero_timestamp);
- return std::make_pair(counters, last_updated);
-}
+const unsigned FLAG_NOZERO = 1; // don't zero with the -z option
+const unsigned FLAG_ALWAYS = 2; // always show, even if zero
+const unsigned FLAG_NEVER = 4; // never show
+const unsigned FLAG_NOSTATSLOG = 8; // don't show for statslog
namespace {
struct StatisticsField
{
- StatisticsField(Statistic statistic_,
- const char* id_,
- const char* message_,
- unsigned flags_ = 0,
- FormatFunction format_ = nullptr)
+ StatisticsField(const Statistic statistic_,
+ const char* const id_,
+ const char* const message_,
+ const unsigned flags_ = 0,
+ const FormatFunction format_ = nullptr)
: statistic(statistic_),
id(id_),
message(message_),
STATISTICS_FIELD(none, nullptr),
};
-namespace Statistics {
-
-Counters
-read(const std::string& path)
+static std::string
+format_size(const uint64_t value)
{
- Counters counters;
-
- std::string data;
- try {
- data = Util::read_file(path);
- } catch (const core::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;
+ return FMT("{:>11}", Util::format_human_readable_size(value));
}
-Counters
-read_log(const std::string& path)
+static std::string
+format_size_times_1024(const uint64_t value)
{
- Counters counters;
-
- std::unordered_map<std::string, Statistic> m;
- for (const auto& field : k_statistics_fields) {
- m[field.id] = field.statistic;
- }
+ return format_size(value * 1024);
+}
- std::ifstream in(path);
- std::string line;
- while (std::getline(in, line, '\n')) {
- if (line[0] == '#') {
- continue;
- }
- auto search = m.find(line);
- if (search != m.end()) {
- Statistic statistic = search->second;
- counters.increment(statistic, 1);
- } else {
- LOG("Unknown statistic: {}", line);
+static std::string
+format_timestamp(const uint64_t value)
+{
+ if (value > 0) {
+ const auto tm = Util::localtime(value);
+ char buffer[100] = "?";
+ if (tm) {
+ strftime(buffer, sizeof(buffer), "%c", &*tm);
}
+ return std::string(" ") + buffer;
+ } else {
+ return {};
}
-
- return counters;
}
-optional<Counters>
-update(const std::string& path,
- std::function<void(Counters& counters)> function)
+static double
+hit_rate(const core::StatisticsCounters& counters)
{
- Lockfile lock(path);
- if (!lock.acquired()) {
- LOG("Failed to acquire lock for {}", path);
- return nullopt;
- }
-
- auto counters = Statistics::read(path);
- function(counters);
-
- AtomicFile file(path, AtomicFile::Mode::text);
- for (size_t i = 0; i < counters.size(); ++i) {
- file.write(FMT("{}\n", counters.get_raw(i)));
- }
- try {
- file.commit();
- } catch (const core::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());
- }
-
- return counters;
+ const uint64_t direct = counters.get(Statistic::direct_cache_hit);
+ const uint64_t preprocessed = counters.get(Statistic::preprocessed_cache_hit);
+ const uint64_t hit = direct + preprocessed;
+ const uint64_t miss = counters.get(Statistic::cache_miss);
+ const uint64_t total = hit + miss;
+ return total > 0 ? (100.0 * hit) / total : 0.0;
}
-void
-log_result(const std::string& path,
- const std::string& input,
- const std::string& result)
+Statistics::Statistics(const StatisticsCounters& counters)
+ : m_counters(counters)
{
- File file(path, "ab");
- if (file) {
- PRINT(*file, "# {}\n", input);
- PRINT(*file, "{}\n", result);
- } else {
- LOG("Failed to open {}: {}", path, strerror(errno));
- }
}
static const StatisticsField*
-get_result(const Counters& counters)
+get_result(const core::StatisticsCounters& counters)
{
for (const auto& field : k_statistics_fields) {
if (counters.get(field.statistic) != 0 && !(field.flags & FLAG_NOZERO)) {
return nullptr;
}
-optional<std::string>
-get_result_id(const Counters& counters)
+nonstd::optional<std::string>
+Statistics::get_result_id() const
{
- const auto result = get_result(counters);
+ const auto result = get_result(m_counters);
if (result) {
return result->id;
}
- return nullopt;
+ return nonstd::nullopt;
}
-optional<std::string>
-get_result_message(const Counters& counters)
+nonstd::optional<std::string>
+Statistics::get_result_message() const
{
- const auto result = get_result(counters);
+ const auto result = get_result(m_counters);
if (result) {
return result->message;
}
- return nullopt;
-}
-
-void
-zero_all_counters(const Config& config)
-{
- const time_t timestamp = time(nullptr);
-
- for_each_level_1_and_2_stats_file(
- config.cache_dir(), [=](const std::string& path) {
- Statistics::update(path, [=](Counters& cs) {
- for (size_t i = 0; k_statistics_fields[i].message; ++i) {
- if (!(k_statistics_fields[i].flags & FLAG_NOZERO)) {
- cs.set(k_statistics_fields[i].statistic, 0);
- }
- }
- cs.set(Statistic::stats_zeroed_timestamp, timestamp);
- });
- });
-}
-
-std::string
-format_stats_log(const Config& config)
-{
- return FMT("{:36}{}\n", "stats log", config.stats_log());
+ return nonstd::nullopt;
}
std::string
-format_config_header(const Config& config)
+Statistics::format_config_header(const Config& config)
{
std::string result;
}
std::string
-format_human_readable(const Counters& counters,
- time_t last_updated,
- bool from_log)
+Statistics::format_human_readable(const time_t last_updated,
+ const bool from_log) const
{
std::string result;
if (k_statistics_fields[i].flags & FLAG_NEVER) {
continue;
}
- if (counters.get(statistic) == 0
+ if (m_counters.get(statistic) == 0
&& !(k_statistics_fields[i].flags & FLAG_ALWAYS)) {
continue;
}
const std::string value =
k_statistics_fields[i].format
- ? k_statistics_fields[i].format(counters.get(statistic))
- : FMT("{:8}", counters.get(statistic));
+ ? k_statistics_fields[i].format(m_counters.get(statistic))
+ : FMT("{:8}", m_counters.get(statistic));
if (!value.empty()) {
result += FMT("{:32}{}\n", k_statistics_fields[i].message, value);
}
if (statistic == Statistic::cache_miss) {
- double percent = hit_rate(counters);
+ double percent = hit_rate(m_counters);
result += FMT("{:34}{:6.2f} %\n", "cache hit rate", percent);
}
}
}
std::string
-format_config_footer(const Config& config)
+Statistics::format_config_footer(const Config& config)
{
std::string result;
}
std::string
-format_machine_readable(const Counters& counters, time_t last_updated)
+Statistics::format_machine_readable(const time_t last_updated) const
{
std::string result;
if (!(k_statistics_fields[i].flags & FLAG_NEVER)) {
result += FMT("{}\t{}\n",
k_statistics_fields[i].id,
- counters.get(k_statistics_fields[i].statistic));
+ m_counters.get(k_statistics_fields[i].statistic));
}
}
return result;
}
-} // namespace Statistics
+std::unordered_map<std::string, Statistic>
+Statistics::get_id_map()
+{
+ std::unordered_map<std::string, Statistic> result;
+ for (const auto& field : k_statistics_fields) {
+ result[field.id] = field.statistic;
+ }
+ return result;
+}
+
+std::vector<Statistic>
+Statistics::get_zeroable_fields()
+{
+ std::vector<Statistic> result;
+ for (const auto& field : k_statistics_fields) {
+ if (!(field.flags & FLAG_NOZERO)) {
+ result.push_back(field.statistic);
+ }
+ }
+ return result;
+}
+
+} // namespace core
--- /dev/null
+// Copyright (C) 2020-2021 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 <core/StatisticsCounters.hpp>
+
+#include <third_party/nonstd/optional.hpp>
+
+#include <string>
+#include <unordered_map>
+
+class Config;
+
+namespace core {
+
+class Statistics
+{
+public:
+ Statistics(const StatisticsCounters& counters);
+
+ // Return a machine-readable string representing the final ccache result, or
+ // nullopt if there was no result.
+ nonstd::optional<std::string> get_result_id() const;
+
+ // Return a human-readable string representing the final ccache result, or
+ // nullopt if there was no result.
+ nonstd::optional<std::string> get_result_message() const;
+
+ // Format config header in human-readable format.
+ static std::string format_config_header(const Config& config);
+
+ // Format cache statistics in human-readable format.
+ std::string format_human_readable(time_t last_updated, bool from_log) const;
+
+ // Format config footer in human-readable format.
+ static std::string format_config_footer(const Config& config);
+
+ // Format cache statistics in machine-readable format.
+ std::string format_machine_readable(time_t last_updated) const;
+
+ const StatisticsCounters& counters() const;
+
+ static std::unordered_map<std::string, Statistic> get_id_map();
+
+ static std::vector<Statistic> get_zeroable_fields();
+
+private:
+ const StatisticsCounters m_counters;
+};
+
+// --- Inline implementations ---
+
+inline const StatisticsCounters&
+Statistics::counters() const
+{
+ return m_counters;
+}
+
+} // namespace core
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#include "Counters.hpp"
+#include "StatisticsCounters.hpp"
-#include "assertions.hpp"
-
-#include <core/Statistic.hpp>
+#include <assertions.hpp>
#include <algorithm>
-Counters::Counters() : m_counters(static_cast<size_t>(core::Statistic::END))
+namespace core {
+
+StatisticsCounters::StatisticsCounters()
+ : m_counters(static_cast<size_t>(Statistic::END))
{
}
uint64_t
-Counters::get(core::Statistic statistic) const
+StatisticsCounters::get(Statistic statistic) const
{
const auto index = static_cast<size_t>(statistic);
- ASSERT(index < static_cast<size_t>(core::Statistic::END));
+ ASSERT(index < static_cast<size_t>(Statistic::END));
return index < m_counters.size() ? m_counters[index] : 0;
}
void
-Counters::set(core::Statistic statistic, uint64_t value)
+StatisticsCounters::set(Statistic statistic, uint64_t value)
{
const auto index = static_cast<size_t>(statistic);
- ASSERT(index < static_cast<size_t>(core::Statistic::END));
+ ASSERT(index < static_cast<size_t>(Statistic::END));
m_counters[index] = value;
}
uint64_t
-Counters::get_raw(size_t index) const
+StatisticsCounters::get_raw(size_t index) const
{
ASSERT(index < size());
return m_counters[index];
}
void
-Counters::set_raw(size_t index, uint64_t value)
+StatisticsCounters::set_raw(size_t index, uint64_t value)
{
if (index >= m_counters.size()) {
m_counters.resize(index + 1);
}
void
-Counters::increment(core::Statistic statistic, int64_t value)
+StatisticsCounters::increment(Statistic statistic, int64_t value)
{
const auto i = static_cast<size_t>(statistic);
if (i >= m_counters.size()) {
}
void
-Counters::increment(const Counters& other)
+StatisticsCounters::increment(const StatisticsCounters& other)
{
m_counters.resize(std::max(size(), other.size()));
for (size_t i = 0; i < other.size(); ++i) {
}
size_t
-Counters::size() const
+StatisticsCounters::size() const
{
return m_counters.size();
}
bool
-Counters::all_zero() const
+StatisticsCounters::all_zero() const
{
return !std::any_of(
m_counters.begin(), m_counters.end(), [](unsigned v) { return v != 0; });
}
+
+} // namespace core
#pragma once
-#include <core/Statistic.hpp>
+#include "Statistic.hpp"
#include <cstddef>
#include <cstdint>
#include <vector>
+namespace core {
+
// A simple wrapper around a vector of integers used for the statistics
// counters.
-class Counters
+class StatisticsCounters
{
public:
- Counters();
+ StatisticsCounters();
- uint64_t get(core::Statistic statistic) const;
- void set(core::Statistic statistic, uint64_t value);
+ uint64_t get(Statistic statistic) const;
+ void set(Statistic statistic, uint64_t value);
uint64_t get_raw(size_t index) const;
void set_raw(size_t index, uint64_t value);
- void increment(core::Statistic statistic, int64_t value = 1);
- void increment(const Counters& other);
+ void increment(Statistic statistic, int64_t value = 1);
+ void increment(const StatisticsCounters& other);
size_t size() const;
private:
std::vector<uint64_t> m_counters;
};
+
+} // namespace core
--- /dev/null
+// Copyright (C) 2021 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 "StatsLog.hpp"
+
+#include <File.hpp>
+#include <Logging.hpp>
+#include <core/Statistics.hpp>
+#include <fmtmacros.hpp>
+
+#include <fstream>
+
+namespace core {
+
+StatisticsCounters
+StatsLog::read() const
+{
+ core::StatisticsCounters counters;
+
+ const auto id_map = Statistics::get_id_map();
+
+ std::ifstream in(m_path);
+ std::string line;
+ while (std::getline(in, line, '\n')) {
+ if (line[0] == '#') {
+ continue;
+ }
+ const auto entry = id_map.find(line);
+ if (entry != id_map.end()) {
+ Statistic statistic = entry->second;
+ counters.increment(statistic, 1);
+ } else {
+ LOG("Unknown statistic: {}", line);
+ }
+ }
+
+ return counters;
+}
+
+void
+StatsLog::log_result(const std::string& input_file,
+ const std::string& result_id)
+{
+ File file(m_path, "ab");
+ if (file) {
+ PRINT(*file, "# {}\n", input_file);
+ PRINT(*file, "{}\n", result_id);
+ } else {
+ LOG("Failed to open {}: {}", m_path, strerror(errno));
+ }
+}
+
+} // namespace core
--- /dev/null
+// Copyright (C) 2021 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 "StatisticsCounters.hpp"
+
+#include <string>
+
+namespace core {
+
+class StatsLog
+{
+public:
+ StatsLog(const std::string& path);
+
+ StatisticsCounters read() const;
+ void log_result(const std::string& input_file, const std::string& result_id);
+
+private:
+ const std::string m_path;
+};
+
+// --- Inline implementations ---
+
+inline StatsLog::StatsLog(const std::string& path) : m_path(path)
+{
+}
+
+} // namespace core
${CMAKE_CURRENT_SOURCE_DIR}/PrimaryStorage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/PrimaryStorage_cleanup.cpp
${CMAKE_CURRENT_SOURCE_DIR}/PrimaryStorage_compress.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/PrimaryStorage_statistics.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/StatsFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/util.cpp
)
#include "PrimaryStorage.hpp"
#include <Config.hpp>
-#include <Counters.hpp>
#include <Logging.hpp>
#include <MiniTrace.hpp>
-#include <Statistics.hpp>
#include <Util.hpp>
#include <assertions.hpp>
-#include <core/Statistic.hpp>
#include <core/exceptions.hpp>
#include <core/wincompat.hpp>
#include <fmtmacros.hpp>
+#include <storage/primary/StatsFile.hpp>
#include <util/file.hpp>
#ifdef HAVE_UNISTD_H
const auto bucket = getpid() % 256;
const auto stats_file =
FMT("{}/{:x}/{:x}/stats", m_config.cache_dir(), bucket / 16, bucket % 16);
- Statistics::update(
- stats_file, [&](auto& cs) { cs.increment(m_result_counter_updates); });
+ StatsFile(stats_file).update([&](auto& cs) {
+ cs.increment(m_result_counter_updates);
+ });
return;
}
m_result_counter_updates.increment(statistic, value);
}
-// Return a machine-readable string representing the final ccache result, or
-// nullopt if there was no result.
-nonstd::optional<std::string>
-PrimaryStorage::get_result_id() const
-{
- return Statistics::get_result_id(m_result_counter_updates);
-}
-
-// Return a human-readable string representing the final ccache result, or
-// nullopt if there was no result.
-nonstd::optional<std::string>
-PrimaryStorage::get_result_message() const
-{
- return Statistics::get_result_message(m_result_counter_updates);
-}
-
// Private methods
PrimaryStorage::LookUpCacheFileResult
});
}
-nonstd::optional<Counters>
+nonstd::optional<core::StatisticsCounters>
PrimaryStorage::update_stats_and_maybe_move_cache_file(
const Digest& key,
const std::string& current_path,
- const Counters& counter_updates,
+ const core::StatisticsCounters& counter_updates,
const core::CacheEntryType type)
{
if (counter_updates.all_zero()) {
if (!use_stats_on_level_1) {
level_string += FMT("/{:x}", key.bytes()[0] & 0xF);
}
+
const auto stats_file =
FMT("{}/{}/stats", m_config.cache_dir(), level_string);
-
const auto counters =
- Statistics::update(stats_file, [&counter_updates](auto& cs) {
+ StatsFile(stats_file).update([&counter_updates](auto& cs) {
cs.increment(counter_updates);
});
if (!counters) {
#pragma once
-#include "util.hpp"
-
-#include <Counters.hpp>
#include <Digest.hpp>
+#include <core/StatisticsCounters.hpp>
#include <core/types.hpp>
+#include <storage/primary/util.hpp>
#include <storage/types.hpp>
#include <third_party/nonstd/optional.hpp>
class Config;
-class Counters;
namespace storage {
namespace primary {
void increment_statistic(core::Statistic statistic, int64_t value = 1);
- // Return a machine-readable string representing the final ccache result, or
- // nullopt if there was no result.
- nonstd::optional<std::string> get_result_id() const;
+ const core::StatisticsCounters& get_statistics_updates() const;
+
+ // Zero all statistics counters except those tracking cache size and number of
+ // files in the cache.
+ void zero_all_statistics();
- // Return a human-readable string representing the final ccache result, or
- // nullopt if there was no result.
- nonstd::optional<std::string> get_result_message() const;
+ // Get statistics and last time of update for the whole primary storage cache.
+ std::pair<core::StatisticsCounters, time_t> get_all_statistics() const;
// --- Cleanup ---
void clean_old(const ProgressReceiver& progress_receiver, uint64_t max_age);
- void clean_dir(const std::string& subdir,
- uint64_t max_size,
- uint64_t max_files,
- uint64_t max_age,
- const ProgressReceiver& progress_receiver);
-
void clean_all(const ProgressReceiver& progress_receiver);
void wipe_all(const ProgressReceiver& progress_receiver);
// Main statistics updates (result statistics and size/count change for result
// file) which get written into the statistics file belonging to the result
// file.
- Counters m_result_counter_updates;
+ core::StatisticsCounters m_result_counter_updates;
// Statistics updates (only for manifest size/count change) which get written
// into the statistics file belonging to the manifest.
- Counters m_manifest_counter_updates;
+ core::StatisticsCounters m_manifest_counter_updates;
// The manifest and result keys and paths are stored by put() so that
// finalize() can use them to move the files in place.
void clean_internal_tempdir();
- nonstd::optional<Counters>
- update_stats_and_maybe_move_cache_file(const Digest& key,
- const std::string& current_path,
- const Counters& counter_updates,
- core::CacheEntryType type);
+ nonstd::optional<core::StatisticsCounters>
+ update_stats_and_maybe_move_cache_file(
+ const Digest& key,
+ const std::string& current_path,
+ const core::StatisticsCounters& counter_updates,
+ core::CacheEntryType type);
// Join the cache directory, a '/' and `name` into a single path and return
// it. Additionally, `level` single-character, '/'-separated subpaths are
// split from the beginning of `name` before joining them all.
std::string get_path_in_cache(uint8_t level, nonstd::string_view name) const;
+
+ static void clean_dir(const std::string& subdir,
+ uint64_t max_size,
+ uint64_t max_files,
+ uint64_t max_age,
+ const ProgressReceiver& progress_receiver);
};
+// --- Inline implementations ---
+
+inline const core::StatisticsCounters&
+PrimaryStorage::get_statistics_updates() const
+{
+ return m_result_counter_updates;
+}
+
} // namespace primary
} // namespace storage
#include <Config.hpp>
#include <Context.hpp>
#include <Logging.hpp>
-#include <Statistics.hpp>
#include <Util.hpp>
#include <storage/primary/CacheFile.hpp>
+#include <storage/primary/StatsFile.hpp>
#include <storage/primary/util.hpp>
#include <util/string.hpp>
const bool cleanup_performed)
{
const std::string stats_file = dir + "/stats";
- Statistics::update(stats_file, [=](auto& cs) {
+ StatsFile(stats_file).update([=](auto& cs) {
if (cleanup_performed) {
cs.increment(Statistic::cleanups_performed);
}
#include <Logging.hpp>
#include <Manifest.hpp>
#include <Result.hpp>
-#include <Statistics.hpp>
#include <ThreadPool.hpp>
#include <assertions.hpp>
#include <compression/ZstdCompressor.hpp>
#include <core/exceptions.hpp>
#include <core/wincompat.hpp>
#include <fmtmacros.hpp>
+#include <storage/primary/StatsFile.hpp>
#include <util/string.hpp>
#include <third_party/fmt/core.h>
atomic_new_file.commit();
const auto new_stat = Stat::stat(cache_file.path(), Stat::OnError::log);
- Statistics::update(stats_file, [=](auto& cs) {
+ StatsFile(stats_file).update([=](auto& cs) {
cs.increment(core::Statistic::cache_size_kibibyte,
Util::size_change_kibibyte(old_stat, new_stat));
});
--- /dev/null
+// Copyright (C) 2021 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 "PrimaryStorage.hpp"
+
+#include <Config.hpp>
+#include <core/Statistics.hpp>
+#include <fmtmacros.hpp>
+#include <storage/primary/StatsFile.hpp>
+
+#include <algorithm>
+
+namespace storage {
+namespace primary {
+
+static void
+for_each_level_1_and_2_stats_file(
+ const std::string& cache_dir,
+ const std::function<void(const std::string& path)> function)
+{
+ for (size_t level_1 = 0; level_1 <= 0xF; ++level_1) {
+ function(FMT("{}/{:x}/stats", cache_dir, level_1));
+ for (size_t level_2 = 0; level_2 <= 0xF; ++level_2) {
+ function(FMT("{}/{:x}/{:x}/stats", cache_dir, level_1, level_2));
+ }
+ }
+}
+
+// Zero all statistics counters except those tracking cache size and number of
+// files in the cache.
+void
+PrimaryStorage::zero_all_statistics()
+{
+ const time_t timestamp = time(nullptr);
+ const auto zeroable_fields = core::Statistics::get_zeroable_fields();
+
+ for_each_level_1_and_2_stats_file(
+ m_config.cache_dir(), [=](const std::string& path) {
+ StatsFile(path).update([=](core::StatisticsCounters& cs) {
+ for (const auto statistic : zeroable_fields) {
+ cs.set(statistic, 0);
+ }
+ cs.set(core::Statistic::stats_zeroed_timestamp, timestamp);
+ });
+ });
+}
+
+// Get statistics and last time of update for the whole primary storage cache.
+std::pair<core::StatisticsCounters, time_t>
+PrimaryStorage::get_all_statistics() const
+{
+ core::StatisticsCounters counters;
+ uint64_t zero_timestamp = 0;
+ time_t last_updated = 0;
+
+ // Add up the stats in each directory.
+ for_each_level_1_and_2_stats_file(
+ m_config.cache_dir(), [&](const auto& path) {
+ counters.set(core::Statistic::stats_zeroed_timestamp, 0); // Don't add
+ counters.increment(StatsFile(path).read());
+ zero_timestamp = std::max(
+ counters.get(core::Statistic::stats_zeroed_timestamp), zero_timestamp);
+ last_updated = std::max(last_updated, Stat::stat(path).mtime());
+ });
+
+ counters.set(core::Statistic::stats_zeroed_timestamp, zero_timestamp);
+ return std::make_pair(counters, last_updated);
+}
+
+} // namespace primary
+} // namespace storage
--- /dev/null
+// Copyright (C) 2021 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 "StatsFile.hpp"
+
+#include <AtomicFile.hpp>
+#include <Lockfile.hpp>
+#include <Logging.hpp>
+#include <Util.hpp>
+#include <core/exceptions.hpp>
+#include <fmtmacros.hpp>
+
+namespace storage {
+namespace primary {
+
+StatsFile::StatsFile(const std::string& path) : m_path(path)
+{
+}
+
+core::StatisticsCounters
+StatsFile::read() const
+{
+ core::StatisticsCounters counters;
+
+ std::string data;
+ try {
+ data = Util::read_file(m_path);
+ } catch (const core::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;
+}
+
+nonstd::optional<core::StatisticsCounters>
+StatsFile::update(
+ std::function<void(core::StatisticsCounters& counters)> function) const
+{
+ Lockfile lock(m_path);
+ if (!lock.acquired()) {
+ LOG("Failed to acquire lock for {}", m_path);
+ return nonstd::nullopt;
+ }
+
+ auto counters = read();
+ function(counters);
+
+ AtomicFile file(m_path, AtomicFile::Mode::text);
+ for (size_t i = 0; i < counters.size(); ++i) {
+ file.write(FMT("{}\n", counters.get_raw(i)));
+ }
+ try {
+ file.commit();
+ } catch (const core::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());
+ }
+
+ return counters;
+}
+
+} // namespace primary
+} // namespace storage
--- /dev/null
+// Copyright (C) 2021 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 <core/StatisticsCounters.hpp>
+
+#include <third_party/nonstd/optional.hpp>
+
+#include <functional>
+#include <string>
+
+namespace storage {
+namespace primary {
+
+class StatsFile
+{
+public:
+ StatsFile(const std::string& path);
+
+ // Read counters. No lock is acquired. If the file doesn't exist all returned
+ // counters will be zero.
+ core::StatisticsCounters read() const;
+
+ // Acquire a lock, read counters, call `function` with the counters, write the
+ // counters and release the lock. Returns the resulting counters or nullopt on
+ // error (e.g. if the lock could not be acquired).
+ nonstd::optional<core::StatisticsCounters>
+ update(std::function<void(core::StatisticsCounters& counters)>) const;
+
+private:
+ const std::string m_path;
+};
+
+} // namespace primary
+} // namespace storage
test_AtomicFile.cpp
test_Checksum.cpp
test_Config.cpp
- test_Counters.cpp
test_Depfile.cpp
test_FormatNonstdStringView.cpp
test_Hash.cpp
test_Lockfile.cpp
test_NullCompression.cpp
test_Stat.cpp
- test_Statistics.cpp
test_Util.cpp
test_ZstdCompression.cpp
test_argprocessing.cpp
test_ccache.cpp
test_compopt.cpp
test_compression_types.cpp
+ test_core_Statistics.cpp
+ test_core_StatisticsCounters.cpp
+ test_core_StatsLog.cpp
test_hashutil.cpp
+ test_storage_primary_StatsFile.cpp
test_storage_primary_util.cpp
test_util_Tokenizer.cpp
test_util_expected.cpp
--- /dev/null
+// Copyright (C) 2011-2021 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 "TestUtil.hpp"
+
+#include <core/Statistic.hpp>
+#include <core/Statistics.hpp>
+
+#include <third_party/doctest.h>
+
+#include <iostream> // macOS bug: https://github.com/onqtam/doctest/issues/126
+
+using core::Statistic;
+using core::Statistics;
+using core::StatisticsCounters;
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("core::Statistics");
+
+TEST_CASE("get_result_id")
+{
+ TestContext test_context;
+
+ StatisticsCounters counters;
+ counters.increment(Statistic::cache_miss);
+
+ CHECK(*Statistics(counters).get_result_id() == "cache_miss");
+}
+
+TEST_CASE("get_result_message")
+{
+ TestContext test_context;
+
+ StatisticsCounters counters;
+ counters.increment(Statistic::cache_miss);
+
+ CHECK(*Statistics(counters).get_result_message() == "cache miss");
+}
+
+TEST_SUITE_END();
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#include "../src/Counters.hpp"
#include "TestUtil.hpp"
#include <core/Statistic.hpp>
+#include <core/StatisticsCounters.hpp>
#include "third_party/doctest.h"
using core::Statistic;
+using core::StatisticsCounters;
using TestUtil::TestContext;
-TEST_SUITE_BEGIN("Counters");
+TEST_SUITE_BEGIN("core::StatisticsCounters");
-TEST_CASE("Counters")
+TEST_CASE("StatisticsCounters")
{
TestContext test_context;
- Counters counters;
+ StatisticsCounters counters;
CHECK(counters.size() == static_cast<size_t>(Statistic::END));
SUBCASE("Get and set statistic")
counters.set(Statistic::files_in_cache, 10);
counters.set(Statistic::cache_size_kibibyte, 1);
- Counters updates;
+ StatisticsCounters updates;
updates.set(Statistic::direct_cache_hit, 6);
updates.set(Statistic::cache_miss, 5);
updates.set(Statistic::files_in_cache, -1);
--- /dev/null
+// Copyright (C) 2021 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 "TestUtil.hpp"
+
+#include <Util.hpp>
+#include <core/StatsLog.hpp>
+
+#include <third_party/doctest.h>
+
+using core::Statistic;
+using core::StatsLog;
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("core::StatsFile");
+
+TEST_CASE("read")
+{
+ TestContext test_context;
+
+ Util::write_file("stats.log", "# comment\ndirect_cache_hit\n");
+ const auto counters = StatsLog("stats.log").read();
+
+ CHECK(counters.get(Statistic::direct_cache_hit) == 1);
+ CHECK(counters.get(Statistic::cache_miss) == 0);
+}
+
+TEST_CASE("log_result")
+{
+ TestContext test_context;
+
+ StatsLog stats_log("stats.log");
+ stats_log.log_result("foo.c", "cache_miss");
+ stats_log.log_result("bar.c", "preprocessed_cache_hit");
+
+ CHECK(Util::read_file("stats.log")
+ == "# foo.c\ncache_miss\n# bar.c\npreprocessed_cache_hit\n");
+}
+
+TEST_SUITE_END();
// 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 "../src/fmtmacros.hpp"
#include "TestUtil.hpp"
+#include <Util.hpp>
#include <core/Statistic.hpp>
+#include <fmtmacros.hpp>
+#include <storage/primary/StatsFile.hpp>
-#include "third_party/doctest.h"
+#include <third_party/doctest.h>
using core::Statistic;
+using storage::primary::StatsFile;
using TestUtil::TestContext;
-TEST_SUITE_BEGIN("Statistics");
+TEST_SUITE_BEGIN("storage::primary::StatsFile");
TEST_CASE("Read nonexistent")
{
TestContext test_context;
- Counters counters = Statistics::read("test");
+ const auto counters = StatsFile("test").read();
REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
CHECK(counters.get(Statistic::cache_miss) == 0);
TestContext test_context;
Util::write_file("test", "bad 1 2 3 4 5\n");
- Counters counters = Statistics::read("test");
+ const auto counters = StatsFile("test").read();
REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
CHECK(counters.get(Statistic::cache_miss) == 0);
TestContext test_context;
Util::write_file("test", "0 1 2 3 27 5\n");
- Counters counters = Statistics::read("test");
+ const auto counters = StatsFile("test").read();
REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
CHECK(counters.get(Statistic::cache_miss) == 27);
}
Util::write_file("test", content);
- Counters counters = Statistics::read("test");
+ const auto counters = StatsFile("test").read();
REQUIRE(counters.size() == count);
for (size_t i = 0; i < count; ++i) {
}
}
-TEST_CASE("Read log")
-{
- TestContext test_context;
-
- Util::write_file("stats.log", "# comment\ndirect_cache_hit\n");
- Counters counters = Statistics::read_log("stats.log");
-
- CHECK(counters.get(Statistic::direct_cache_hit) == 1);
- CHECK(counters.get(Statistic::cache_miss) == 0);
-}
-
TEST_CASE("Update")
{
TestContext test_context;
Util::write_file("test", "0 1 2 3 27 5\n");
- auto counters = Statistics::update("test", [](auto& cs) {
+ auto counters = StatsFile("test").update([](auto& cs) {
cs.increment(Statistic::internal_error, 1);
cs.increment(Statistic::cache_miss, 6);
});
CHECK(counters->get(Statistic::internal_error) == 4);
CHECK(counters->get(Statistic::cache_miss) == 33);
- counters = Statistics::read("test");
+ counters = StatsFile("test").read();
CHECK(counters->get(Statistic::internal_error) == 4);
CHECK(counters->get(Statistic::cache_miss) == 33);
}
-TEST_CASE("Get result")
-{
- TestContext test_context;
-
- auto counters = Statistics::update(
- "test", [](auto& cs) { cs.increment(Statistic::cache_miss, 1); });
- REQUIRE(counters);
-
- auto result = Statistics::get_result_message(*counters);
- REQUIRE(result);
-}
-
-TEST_CASE("Log result")
-{
- TestContext test_context;
-
- auto counters = Statistics::update(
- "test", [](auto& cs) { cs.increment(Statistic::cache_miss, 1); });
- REQUIRE(counters);
-
- auto result_id = Statistics::get_result_id(*counters);
- REQUIRE(result_id);
- Statistics::log_result("stats.log", "test.c", *result_id);
-
- auto statslog = Util::read_file("stats.log");
- REQUIRE(statslog.find(*result_id + "\n") != std::string::npos);
-}
-
TEST_SUITE_END();