]> git.ipfire.org Git - thirdparty/ccache.git/blame - src/core/Statistics.cpp
fix: Disable caching for modified source/include files
[thirdparty/ccache.git] / src / core / Statistics.cpp
CommitLineData
d75dbe26 1// Copyright (C) 2021-2024 Joel Rosdahl and other contributors
98db303d
JR
2//
3// See doc/AUTHORS.adoc for a complete list of contributors.
4//
5// This program is free software; you can redistribute it and/or modify it
6// under the terms of the GNU General Public License as published by the Free
7// Software Foundation; either version 3 of the License, or (at your option)
8// any later version.
9//
10// This program is distributed in the hope that it will be useful, but WITHOUT
11// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13// more details.
14//
15// You should have received a copy of the GNU General Public License along with
16// this program; if not, write to the Free Software Foundation, Inc., 51
17// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19#include "Statistics.hpp"
20
a28af38b 21#include <Config.hpp>
a28af38b 22#include <Util.hpp>
8892814e 23#include <util/TextTable.hpp>
ef96a84a 24#include <util/fmtmacros.hpp>
13cb56cd 25#include <util/logging.hpp>
75d2a191 26#include <util/string.hpp>
f657af6a 27#include <util/time.hpp>
98db303d 28
9ebc876d
JR
29#include <algorithm>
30
a28af38b 31namespace core {
ba2e4ad6 32
c24446f5 33using core::Statistic;
ba2e4ad6 34
8892814e
JR
35const unsigned FLAG_NOZERO = 1U << 0; // don't zero with --zero-stats
36const unsigned FLAG_NEVER = 1U << 1; // don't include in --print-stats
37const unsigned FLAG_ERROR = 1U << 2; // include in error count
38const unsigned FLAG_UNCACHEABLE = 1U << 3; // include in uncacheable count
ba2e4ad6
JR
39
40namespace {
41
42struct StatisticsField
43{
a28af38b
JR
44 StatisticsField(const Statistic statistic_,
45 const char* const id_,
8892814e
JR
46 const char* const description_,
47 const unsigned flags_ = 0)
4ef97572
JR
48 : statistic(statistic_),
49 id(id_),
8892814e
JR
50 description(description_),
51 flags(flags_)
ba2e4ad6
JR
52 {
53 }
54
55 const Statistic statistic;
8892814e
JR
56 const char* const id; // for --print-stats
57 const char* const description; // for --show-stats --verbose
58 const unsigned flags; // bitmask of FLAG_* values
ba2e4ad6
JR
59};
60
61} // namespace
62
8892814e 63#define FIELD(id, ...) \
ba2e4ad6
JR
64 { \
65 Statistic::id, #id, __VA_ARGS__ \
66 }
67
ba2e4ad6 68const StatisticsField k_statistics_fields[] = {
8892814e 69 // Field "none" intentionally omitted.
e08efb42
JR
70
71 // Uncacheable compilation or linking by an Autoconf test.
8892814e 72 FIELD(autoconf_test, "Autoconf compile/link", FLAG_UNCACHEABLE),
e08efb42
JR
73
74 // Malformed compiler argument, e.g. missing a value for a compiler option
75 // that requires an argument or failure to read a file specified by a compiler
76 // option argument.
8892814e 77 FIELD(bad_compiler_arguments, "Bad compiler arguments", FLAG_UNCACHEABLE),
e08efb42 78
d75dbe26
JR
79 // An input file could not be read or parsed (see the debug log for details).
80 FIELD(bad_input_file, "Could not read or parse input file", FLAG_ERROR),
81
e08efb42 82 // The output path specified with -o could not be written to.
8892814e 83 FIELD(bad_output_file, "Could not write to output file", FLAG_ERROR),
e08efb42
JR
84
85 // A cacheable call resulted in a miss.
8892814e 86 FIELD(cache_miss, nullptr),
e08efb42
JR
87
88 // Size in KiB of a subdirectory of the cache. This is only set for level 1
89 // subdirectories.
8892814e 90 FIELD(cache_size_kibibyte, nullptr, FLAG_NOZERO),
e08efb42
JR
91
92 // The compiler was called for linking, not compiling. Ccache only supports
93 // compilation of a single file, i.e. calling the compiler with the -c option
94 // to produce a single object file from a single source file.
8892814e 95 FIELD(called_for_link, "Called for linking", FLAG_UNCACHEABLE),
e08efb42
JR
96
97 // The compiler was called for preprocessing, not compiling.
8892814e 98 FIELD(called_for_preprocessing, "Called for preprocessing", FLAG_UNCACHEABLE),
e08efb42
JR
99
100 // How many cleanups were performed, either manually or automatically. Only
101 // cleanup operations that actually removed files are counted.
8892814e 102 FIELD(cleanups_performed, nullptr),
e08efb42
JR
103
104 // The compilation failed. No result stored in the cache.
8892814e 105 FIELD(compile_failed, "Compilation failed", FLAG_UNCACHEABLE),
e08efb42
JR
106
107 // A compiler check program specified by compiler_check/CCACHE_COMPILERCHECK
108 // failed.
8892814e 109 FIELD(compiler_check_failed, "Compiler check failed", FLAG_ERROR),
e08efb42
JR
110
111 // One of the files expected to be produced by the compiler was missing after
112 // compilation.
b53afeb5
JR
113 FIELD(compiler_produced_no_output,
114 "Compiler output file missing",
115 FLAG_UNCACHEABLE),
e08efb42
JR
116
117 // The compiler's output file (typically an object file) was empty after
118 // compilation.
8892814e
JR
119 FIELD(compiler_produced_empty_output,
120 "Compiler produced empty output",
121 FLAG_UNCACHEABLE),
e08efb42
JR
122
123 // Compiler produced output. [This field is obsolete since ccache now supports
124 // caching stdout output as well.]
8892814e 125 FIELD(compiler_produced_stdout, "Compiler produced stdout", FLAG_UNCACHEABLE),
e08efb42
JR
126
127 // The compiler to execute could not be found.
8892814e 128 FIELD(could_not_find_compiler, "Could not find compiler", FLAG_ERROR),
e08efb42
JR
129
130 // Preconditions for using C++ modules were not fulfilled.
8892814e 131 FIELD(could_not_use_modules, "Could not use modules", FLAG_UNCACHEABLE),
e08efb42
JR
132
133 // Preconditions for using precompiled headers were not fulfilled.
8892814e
JR
134 FIELD(could_not_use_precompiled_header,
135 "Could not use precompiled header",
136 FLAG_UNCACHEABLE),
e08efb42
JR
137
138 // A cacheable call resulted in a hit when attempting direct mode lookup.
8892814e 139 FIELD(direct_cache_hit, nullptr),
e08efb42
JR
140
141 // A cacheable call resulted in a miss when attempting direct mode lookup.
8892814e 142 FIELD(direct_cache_miss, nullptr),
e08efb42 143
20438ee6 144 // Ccache was disabled by a comment in the source code file.
b3c3e799 145 FIELD(disabled, "Ccache disabled", FLAG_UNCACHEABLE),
e08efb42
JR
146
147 // Failure reading a file specified by extra_files_to_hash/CCACHE_EXTRAFILES.
8892814e 148 FIELD(error_hashing_extra_file, "Error hashing extra file", FLAG_ERROR),
e08efb42
JR
149
150 // Number of files in a subdirectory of the cache. This is only set for level
151 // 1 subdirectories.
8892814e 152 FIELD(files_in_cache, nullptr, FLAG_NOZERO),
e08efb42
JR
153
154 // Unexpected failure, e.g. due to problems reading/writing the cache.
8892814e 155 FIELD(internal_error, "Internal error", FLAG_ERROR),
e08efb42
JR
156
157 // A cacheable call resulted in a hit when attempting to look up a result from
158 // local storage.
e5d2eb8c 159 FIELD(local_storage_hit, nullptr),
e08efb42
JR
160
161 // A cacheable call resulted in a miss when attempting to look up a result
162 // from local storage.
e5d2eb8c 163 FIELD(local_storage_miss, nullptr),
e08efb42
JR
164
165 // A read from local storage found an entry (manifest or result file).
2ea3416d 166 FIELD(local_storage_read_hit, nullptr),
e08efb42
JR
167
168 // A read from local storage did not find an entry (manifest or result file).
2ea3416d 169 FIELD(local_storage_read_miss, nullptr),
e08efb42
JR
170
171 // An entry (manifest or result file) was written local storage.
2ea3416d 172 FIELD(local_storage_write, nullptr),
e08efb42
JR
173
174 // A file was unexpectedly missing from the cache. This only happens in rare
175 // situations, e.g. if one ccache instance is about to get a file from the
176 // cache while another instance removed the file as part of cache cleanup.
8892814e 177 FIELD(missing_cache_file, "Missing cache file", FLAG_ERROR),
e08efb42 178
d75dbe26
JR
179 // An input file was modified during compilation.
180 FIELD(
181 modified_input_file, "Input file modified during compilation", FLAG_ERROR),
182
e08efb42
JR
183 // The compiler was called to compile multiple source files in one go. This is
184 // not supported by ccache.
8892814e 185 FIELD(multiple_source_files, "Multiple source files", FLAG_UNCACHEABLE),
e08efb42
JR
186
187 // No input file was specified to the compiler.
8892814e 188 FIELD(no_input_file, "No input file", FLAG_UNCACHEABLE),
e08efb42
JR
189
190 // [Obsolete field used before ccache 3.2.]
8892814e 191 FIELD(obsolete_max_files, nullptr, FLAG_NOZERO | FLAG_NEVER),
e08efb42
JR
192
193 // [Obsolete field used before ccache 3.2.]
8892814e 194 FIELD(obsolete_max_size, nullptr, FLAG_NOZERO | FLAG_NEVER),
e08efb42
JR
195
196 // The compiler was instructed to write its output to standard output using
197 // "-o -". This is not supported by ccache.
8892814e 198 FIELD(output_to_stdout, "Output to stdout", FLAG_UNCACHEABLE),
e08efb42
JR
199
200 // A cacheable call resulted in a hit when attempting preprocessed mode
201 // lookup.
8892814e 202 FIELD(preprocessed_cache_hit, nullptr),
e08efb42
JR
203
204 // A cacheable call resulted in a miss when attempting preprocessed mode
205 // lookup.
8892814e 206 FIELD(preprocessed_cache_miss, nullptr),
e08efb42
JR
207
208 // Preprocessing the source code using the compiler's -E option failed.
8892814e 209 FIELD(preprocessor_error, "Preprocessing failed", FLAG_UNCACHEABLE),
e08efb42
JR
210
211 // recache/CCACHE_RECACHE was used to overwrite an existing result.
8892814e 212 FIELD(recache, "Forced recache", FLAG_UNCACHEABLE),
e08efb42
JR
213
214 // Error when connecting to, reading from or writing to remote storage.
0cd6f70b 215 FIELD(remote_storage_error, nullptr),
e08efb42
JR
216
217 // A cacheable call resulted in a hit when attempting to look up a result from
218 // remote storage.
0cd6f70b 219 FIELD(remote_storage_hit, nullptr),
e08efb42
JR
220
221 // A cacheable call resulted in a miss when attempting to look up a result
222 // from remote storage.
0cd6f70b 223 FIELD(remote_storage_miss, nullptr),
e08efb42
JR
224
225 // A read from remote storage found an entry (manifest or result file).
2ea3416d 226 FIELD(remote_storage_read_hit, nullptr),
e08efb42
JR
227
228 // A read from remote storage did not find an entry (manifest or result file).
2ea3416d 229 FIELD(remote_storage_read_miss, nullptr),
e08efb42
JR
230
231 // An entry (manifest or result file) was written remote storage.
2ea3416d 232 FIELD(remote_storage_write, nullptr),
e08efb42
JR
233
234 // Timeout when connecting to, reading from or writing to remote storage.
0cd6f70b 235 FIELD(remote_storage_timeout, nullptr),
e08efb42
JR
236
237 // Last time statistics counters were zeroed.
8892814e 238 FIELD(stats_zeroed_timestamp, nullptr),
e08efb42
JR
239
240 // Code like the assembler .inc bin (without the space) directive was found.
241 // This is not supported by ccache.
8892814e
JR
242 FIELD(
243 unsupported_code_directive, "Unsupported code directive", FLAG_UNCACHEABLE),
e08efb42
JR
244
245 // A compiler option not supported by ccache was found.
8892814e
JR
246 FIELD(unsupported_compiler_option,
247 "Unsupported compiler option",
248 FLAG_UNCACHEABLE),
e08efb42
JR
249
250 // An environment variable not supported by ccache was set.
a2988ace
JR
251 FIELD(unsupported_environment_variable,
252 "Unsupported environment variable",
253 FLAG_UNCACHEABLE),
e08efb42
JR
254
255 // A source language e.g. specified with -x was unsupported by ccache.
8892814e
JR
256 FIELD(unsupported_source_language,
257 "Unsupported source language",
258 FLAG_UNCACHEABLE),
defb83cc
JR
259
260 // subdir_files_base and subdir_size_kibibyte_base are intentionally omitted
261 // since they are not interesting to show.
ba2e4ad6
JR
262};
263
e658c34a 264static_assert(std::size(k_statistics_fields)
defb83cc
JR
265 == static_cast<size_t>(Statistic::END)
266 - (/*none*/ 1 + /*subdir files*/ 16 + /*subdir size*/ 16));
fe22a1de 267
a28af38b 268static std::string
653dde81 269format_timestamp(const util::TimePoint& value)
a28af38b 270{
653dde81 271 if (value.sec() == 0) {
8892814e
JR
272 return "never";
273 } else {
f657af6a 274 const auto tm = util::localtime(value);
a28af38b
JR
275 char buffer[100] = "?";
276 if (tm) {
277 strftime(buffer, sizeof(buffer), "%c", &*tm);
fe22a1de 278 }
8892814e 279 return buffer;
fe22a1de 280 }
fe22a1de
AB
281}
282
8892814e
JR
283static std::string
284percent(const uint64_t nominator, const uint64_t denominator)
98db303d 285{
8892814e
JR
286 if (denominator == 0) {
287 return "";
dfb749a6
JR
288 }
289
930d3b67
JR
290 std::string result = FMT("({:5.2f}%)",
291 (100.0 * static_cast<double>(nominator))
292 / static_cast<double>(denominator));
dfb749a6
JR
293 if (result.length() <= 8) {
294 return result;
8892814e 295 } else {
930d3b67
JR
296 return FMT("({:5.1f}%)",
297 (100.0 * static_cast<double>(nominator))
298 / static_cast<double>(denominator));
8892814e 299 }
0e93faaa
JR
300}
301
a28af38b
JR
302Statistics::Statistics(const StatisticsCounters& counters)
303 : m_counters(counters)
fe22a1de 304{
fe22a1de
AB
305}
306
8892814e
JR
307std::vector<std::string>
308Statistics::get_statistics_ids() const
ba2e4ad6 309{
9b1e89d2 310 std::vector<std::string> result;
ba2e4ad6 311 for (const auto& field : k_statistics_fields) {
e8885a2e
JR
312 if (!(field.flags & FLAG_NOZERO)) {
313 for (size_t i = 0; i < m_counters.get(field.statistic); ++i) {
314 result.emplace_back(field.id);
315 }
ba2e4ad6
JR
316 }
317 }
9b1e89d2
JR
318 std::sort(result.begin(), result.end());
319 return result;
fe22a1de
AB
320}
321
8892814e
JR
322uint64_t
323Statistics::count_stats(const unsigned flags) const
fe22a1de 324{
8892814e
JR
325 uint64_t sum = 0;
326 for (const auto& field : k_statistics_fields) {
327 if (field.flags & flags) {
328 sum += m_counters.get(field.statistic);
329 }
330 }
331 return sum;
fe22a1de
AB
332}
333
8892814e
JR
334std::vector<std::pair<std::string, uint64_t>>
335Statistics::get_stats(unsigned flags, const bool all) const
ba2e4ad6 336{
8892814e
JR
337 std::vector<std::pair<std::string, uint64_t>> result;
338 for (const auto& field : k_statistics_fields) {
339 const auto count = m_counters.get(field.statistic);
340 if ((field.flags & flags) && (all || count > 0)) {
341 result.emplace_back(field.description, count);
342 }
343 }
fe22a1de
AB
344 return result;
345}
346
a69e6f6a
JR
347static void
348add_ratio_row(util::TextTable& table,
349 const std::string& text,
350 const uint64_t nominator,
351 const uint64_t denominator)
352{
353 if (denominator > 0) {
354 table.add_row({
355 text,
356 nominator,
357 "/",
358 denominator,
359 percent(nominator, denominator),
360 });
361 } else {
362 table.add_row({text, nominator});
363 }
364}
365
fe22a1de 366std::string
8892814e 367Statistics::format_human_readable(const Config& config,
653dde81 368 const util::TimePoint& last_updated,
8892814e 369 const uint8_t verbosity,
a28af38b 370 const bool from_log) const
fe22a1de 371{
8892814e
JR
372 util::TextTable table;
373 using C = util::TextTable::Cell;
374
375#define S(x_) m_counters.get(Statistic::x_)
376
377 const uint64_t d_hits = S(direct_cache_hit);
378 const uint64_t d_misses = S(direct_cache_miss);
379 const uint64_t p_hits = S(preprocessed_cache_hit);
380 const uint64_t p_misses = S(preprocessed_cache_miss);
381 const uint64_t hits = d_hits + p_hits;
382 const uint64_t misses = S(cache_miss);
a69e6f6a
JR
383 const uint64_t uncacheable = count_stats(FLAG_UNCACHEABLE);
384 const uint64_t errors = count_stats(FLAG_ERROR);
385 const uint64_t total_calls = hits + misses + errors + uncacheable;
386
387 auto cmp_fn = [](const auto& e1, const auto& e2) {
388 return e1.first.compare(e2.first) < 0;
389 };
8892814e 390
8892814e 391 if (verbosity > 0 && !from_log) {
a69e6f6a 392 table.add_row({"Cache directory:", C(config.cache_dir()).colspan(4)});
a2e7f910 393 table.add_row({"Config file:", C(config.config_path()).colspan(4)});
8892814e 394 table.add_row(
a2e7f910 395 {"System config file:", C(config.system_config_path()).colspan(4)});
8892814e 396 table.add_row(
a69e6f6a 397 {"Stats updated:", C(format_timestamp(last_updated)).colspan(4)});
8892814e 398 if (verbosity > 1) {
653dde81 399 const util::TimePoint last_zeroed(S(stats_zeroed_timestamp));
8892814e 400 table.add_row(
a69e6f6a 401 {"Stats zeroed:", C(format_timestamp(last_zeroed)).colspan(4)});
ba2e4ad6 402 }
8892814e 403 }
a69e6f6a
JR
404
405 if (total_calls > 0 || verbosity > 1) {
406 add_ratio_row(table, "Cacheable calls:", hits + misses, total_calls);
407 add_ratio_row(table, " Hits:", hits, hits + misses);
408 add_ratio_row(table, " Direct:", d_hits, hits);
409 add_ratio_row(table, " Preprocessed:", p_hits, hits);
410 add_ratio_row(table, " Misses:", misses, hits + misses);
8892814e 411 }
a69e6f6a
JR
412
413 if (uncacheable > 0 || verbosity > 1) {
414 add_ratio_row(table, "Uncacheable calls:", uncacheable, total_calls);
415 if (verbosity > 0) {
416 auto uncacheable_stats = get_stats(FLAG_UNCACHEABLE, verbosity > 1);
417 std::sort(uncacheable_stats.begin(), uncacheable_stats.end(), cmp_fn);
4812396d
JR
418 for (const auto& [name, value] : uncacheable_stats) {
419 add_ratio_row(table, FMT(" {}:", name), value, uncacheable);
a69e6f6a
JR
420 }
421 }
422 }
423
424 if (errors > 0 || verbosity > 1) {
425 add_ratio_row(table, "Errors:", errors, total_calls);
426 if (verbosity > 0) {
427 auto error_stats = get_stats(FLAG_ERROR, verbosity > 1);
428 std::sort(error_stats.begin(), error_stats.end(), cmp_fn);
4812396d
JR
429 for (const auto& [name, value] : error_stats) {
430 add_ratio_row(table, FMT(" {}:", name), value, errors);
a69e6f6a
JR
431 }
432 }
433 }
434
435 if (total_calls > 0 && verbosity > 0) {
436 table.add_heading("Successful lookups:");
437 add_ratio_row(table, " Direct:", d_hits, d_hits + d_misses);
438 add_ratio_row(table, " Preprocessed:", p_hits, p_hits + p_misses);
ba2e4ad6
JR
439 }
440
ac98dee7
JR
441 const char* size_unit =
442 config.size_unit_prefix_type() == util::SizeUnitPrefixType::binary ? "GiB"
443 : "GB";
444 const uint64_t size_divider =
445 config.size_unit_prefix_type() == util::SizeUnitPrefixType::binary
446 ? 1024 * 1024 * 1024
447 : 1000 * 1000 * 1000;
0cd6f70b
JR
448 const uint64_t local_hits = S(local_storage_hit);
449 const uint64_t local_misses = S(local_storage_miss);
2ea3416d
JR
450 const uint64_t local_reads =
451 S(local_storage_read_hit) + S(local_storage_read_miss);
452 const uint64_t local_writes = S(local_storage_write);
0cd6f70b 453 const uint64_t local_size = S(cache_size_kibibyte) * 1024;
8892814e 454 const uint64_t cleanups = S(cleanups_performed);
2ea3416d
JR
455 const uint64_t remote_hits = S(remote_storage_hit);
456 const uint64_t remote_misses = S(remote_storage_miss);
457 const uint64_t remote_reads =
458 S(remote_storage_read_hit) + S(remote_storage_read_miss);
459 const uint64_t remote_writes = S(remote_storage_write);
460 const uint64_t remote_errors = S(remote_storage_error);
461 const uint64_t remote_timeouts = S(remote_storage_timeout);
ab4074ed 462
5d00143e
JR
463 if (!from_log || verbosity > 0 || (local_hits + local_misses) > 0) {
464 table.add_heading("Local storage:");
465 }
8892814e 466 if (!from_log) {
930d3b67
JR
467 std::vector<C> size_cells{FMT(" Cache size ({}):", size_unit),
468 C(FMT("{:.1f}",
469 static_cast<double>(local_size)
470 / static_cast<double>(size_divider)))
471 .right_align()};
ab4074ed
JR
472 if (config.max_size() != 0) {
473 size_cells.emplace_back("/");
930d3b67
JR
474 size_cells.emplace_back(C(FMT("{:.1f}",
475 static_cast<double>(config.max_size())
476 / static_cast<double>(size_divider)))
477 .right_align());
0cd6f70b 478 size_cells.emplace_back(percent(local_size, config.max_size()));
ab4074ed
JR
479 }
480 table.add_row(size_cells);
481
defb83cc 482 if (verbosity > 0 || config.max_files() > 0) {
ab4074ed 483 std::vector<C> files_cells{" Files:", S(files_in_cache)};
8892814e 484 if (config.max_files() > 0) {
ab4074ed
JR
485 files_cells.emplace_back("/");
486 files_cells.emplace_back(config.max_files());
487 files_cells.emplace_back(
488 percent(S(files_in_cache), config.max_files()));
8892814e 489 }
ab4074ed 490 table.add_row(files_cells);
ba2e4ad6 491 }
73fd7b43 492 if (cleanups > 0 || verbosity > 1) {
8892814e 493 table.add_row({" Cleanups:", cleanups});
ba2e4ad6 494 }
8892814e 495 }
5d00143e 496 if (verbosity > 0 || (local_hits + local_misses) > 0) {
2ea3416d
JR
497 add_ratio_row(table, " Hits:", local_hits, local_hits + local_misses);
498 add_ratio_row(table, " Misses:", local_misses, local_hits + local_misses);
499 }
500 if (verbosity > 0) {
501 table.add_row({" Reads:", local_reads});
502 table.add_row({" Writes:", local_writes});
503 }
0cd6f70b
JR
504
505 if (verbosity > 1
506 || remote_hits + remote_misses + remote_errors + remote_timeouts > 0) {
507 table.add_heading("Remote storage:");
508 add_ratio_row(table, " Hits:", remote_hits, remote_hits + remote_misses);
509 add_ratio_row(
510 table, " Misses:", remote_misses, remote_hits + remote_misses);
2ea3416d
JR
511 if (verbosity > 0) {
512 table.add_row({" Reads:", remote_reads});
513 table.add_row({" Writes:", remote_writes});
514 }
0cd6f70b
JR
515 if (verbosity > 1 || remote_errors > 0) {
516 table.add_row({" Errors:", remote_errors});
fe22a1de 517 }
0cd6f70b
JR
518 if (verbosity > 1 || remote_timeouts > 0) {
519 table.add_row({" Timeouts:", remote_timeouts});
ba2e4ad6
JR
520 }
521 }
522
8892814e 523 return table.render();
ba2e4ad6
JR
524}
525
526std::string
babf276d
JR
527Statistics::format_machine_readable(const Config& config,
528 const util::TimePoint& last_updated) const
ba2e4ad6 529{
75d2a191 530 std::vector<std::string> lines;
ba2e4ad6 531
653dde81 532 lines.push_back(FMT("stats_updated_timestamp\t{}\n", last_updated.sec()));
ba2e4ad6 533
babf276d
JR
534 auto add_line = [&](auto id, auto value) {
535 lines.push_back(FMT("{}\t{}\n", id, value));
536 };
537
8892814e
JR
538 for (const auto& field : k_statistics_fields) {
539 if (!(field.flags & FLAG_NEVER)) {
babf276d 540 add_line(field.id, m_counters.get(field.statistic));
ba2e4ad6
JR
541 }
542 }
543
babf276d
JR
544 add_line("max_cache_size_kibibyte", config.max_size() / 1024);
545 add_line("max_files_in_cache", config.max_files());
546
75d2a191
JR
547 std::sort(lines.begin(), lines.end());
548 return util::join(lines, "");
ba2e4ad6
JR
549}
550
a28af38b
JR
551std::unordered_map<std::string, Statistic>
552Statistics::get_id_map()
553{
554 std::unordered_map<std::string, Statistic> result;
555 for (const auto& field : k_statistics_fields) {
556 result[field.id] = field.statistic;
557 }
558 return result;
559}
560
561std::vector<Statistic>
562Statistics::get_zeroable_fields()
563{
564 std::vector<Statistic> result;
565 for (const auto& field : k_statistics_fields) {
566 if (!(field.flags & FLAG_NOZERO)) {
567 result.push_back(field.statistic);
568 }
569 }
570 return result;
571}
572
573} // namespace core