]>
Commit | Line | Data |
---|---|---|
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 | 31 | namespace core { |
ba2e4ad6 | 32 | |
c24446f5 | 33 | using core::Statistic; |
ba2e4ad6 | 34 | |
8892814e JR |
35 | const unsigned FLAG_NOZERO = 1U << 0; // don't zero with --zero-stats |
36 | const unsigned FLAG_NEVER = 1U << 1; // don't include in --print-stats | |
37 | const unsigned FLAG_ERROR = 1U << 2; // include in error count | |
38 | const unsigned FLAG_UNCACHEABLE = 1U << 3; // include in uncacheable count | |
ba2e4ad6 JR |
39 | |
40 | namespace { | |
41 | ||
42 | struct 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 | 68 | const 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 | 264 | static_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 | 268 | static std::string |
653dde81 | 269 | format_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 |
283 | static std::string |
284 | percent(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 |
302 | Statistics::Statistics(const StatisticsCounters& counters) |
303 | : m_counters(counters) | |
fe22a1de | 304 | { |
fe22a1de AB |
305 | } |
306 | ||
8892814e JR |
307 | std::vector<std::string> |
308 | Statistics::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 |
322 | uint64_t |
323 | Statistics::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 |
334 | std::vector<std::pair<std::string, uint64_t>> |
335 | Statistics::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 |
347 | static void |
348 | add_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 | 366 | std::string |
8892814e | 367 | Statistics::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 | ||
526 | std::string | |
babf276d JR |
527 | Statistics::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 |
551 | std::unordered_map<std::string, Statistic> |
552 | Statistics::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 | ||
561 | std::vector<Statistic> | |
562 | Statistics::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 |