ResultRetriever.cpp
SignalHandler.cpp
Stat.cpp
+ Statistics.cpp
TemporaryFile.cpp
ThreadPool.cpp
Util.cpp
#include "Counters.hpp"
-#include "stats.hpp"
+#include "Statistics.hpp"
#include <algorithm>
#include "File.hpp"
#include "Logging.hpp"
#include "Stat.hpp"
+#include "Statistics.hpp"
#include "Util.hpp"
#include "exceptions.hpp"
-#include "stats.hpp"
#include <algorithm>
--- /dev/null
+// 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
--- /dev/null
+// 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
#include <utility>
#include <vector>
+class Context;
+
namespace Util {
using DataReceiver = std::function<void(const void* data, size_t size)>;
#pragma once
#include "Args.hpp"
-#include "stats.hpp"
+#include "Statistics.hpp"
#include "third_party/nonstd/optional.hpp"
#include "Logging.hpp"
#include "Manifest.hpp"
#include "Result.hpp"
+#include "Statistics.hpp"
#include "StdMakeUnique.hpp"
#include "ThreadPool.hpp"
#include "ZstdCompressor.hpp"
#include "system.hpp"
#include "FormatNonstdStringView.hpp"
-#include "stats.hpp"
+#include "Statistics.hpp"
#include "third_party/fmt/core.h"
#include "third_party/nonstd/optional.hpp"
#include "ccache.hpp"
#include "execute.hpp"
#include "macroskip.hpp"
-#include "stats.hpp"
#ifdef INODE_CACHE_SUPPORTED
# include "InodeCache.hpp"
#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>
}
}
-// 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)
{
}
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,
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));
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.
}
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);
}
}
}
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;
}
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);
}
}
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);
}
}
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,
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);
test_Lockfile.cpp
test_NullCompression.cpp
test_Stat.cpp
+ test_Statistics.cpp
test_Util.cpp
test_ZstdCompression.cpp
test_argprocessing.cpp
// 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"
--- /dev/null
+// 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();
#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"