From 89bf499bfc3ea2f3fce522aa1407267cda00e6c3 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Tue, 6 Oct 2026 17:09:48 +0400 Subject: [PATCH 1/2] Fixed Windows build --- llvm/lib/HTTP/HTTPClient.cpp | 6 ++++-- llvm/lib/Support/Windows/Path.inc | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/llvm/lib/HTTP/HTTPClient.cpp b/llvm/lib/HTTP/HTTPClient.cpp index 57641255649a..c50f39768c75 100644 --- a/llvm/lib/HTTP/HTTPClient.cpp +++ b/llvm/lib/HTTP/HTTPClient.cpp @@ -235,6 +235,7 @@ void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) { Session->TimeoutMs = static_cast(Timeout.count()); } +/* static Error VerifyTLSCertWinHTTP(HINTERNET RequestHandle, const std::string &PinnedFingerprint) { // Decode the expected fingerprint from hex into binary. @@ -269,6 +270,7 @@ static Error VerifyTLSCertWinHTTP(HINTERNET RequestHandle, return Error::success(); } +*/ static Error VerifyTLSCertWinHTTP(HINTERNET RequestHandle, const std::string &PinnedFingerprint) { @@ -364,7 +366,7 @@ Error HTTPClient::perform(const HTTPRequest &Request, return createStringError(errc::io_error, "Failed to set secure protocols"); } - +/* // Disallow redirects in general or HTTPS to HTTP only. DWORD RedirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; if (!Request.FollowRedirects) @@ -372,7 +374,7 @@ Error HTTPClient::perform(const HTTPRequest &Request, if (!WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_REDIRECT_POLICY, &RedirectPolicy, sizeof(RedirectPolicy))) return createStringError(errc::io_error, "Failed to set redirect policy"); - +*/ // Disallow redirects in general or HTTPS to HTTP only. DWORD RedirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; if (!Request.FollowRedirects) diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index d229baefa1da..d9ddbfc58eca 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -1581,7 +1581,7 @@ Expected readNativeFileSlice(file_t FileHandle, std::error_code getRealPathFromHandle(file_t Handle, SmallVectorImpl &RealPath) { - return realPathFromHandle(Handle, RealPath); + return realPathFromHandle(Handle.get(), RealPath); } std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout, From 3379b4a1387044ade4a33800e3a4e1f5e972ae08 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Tue, 6 Oct 2026 17:12:59 +0400 Subject: [PATCH 2/2] Draft llvm-cas --show-stats using Diags --- .../include/clang/Basic/DiagnosticCASKinds.td | 2 + .../include/clang/Frontend/CompilerInstance.h | 17 ++ clang/lib/Frontend/CompilerInstance.cpp | 88 ++++++++++ clang/test/CAS/stats.c | 32 ++++ clang/tools/driver/cc1depscan_main.cpp | 4 + llvm/include/llvm/CAS/Stats.h | 75 +++++++++ llvm/lib/CAS/CMakeLists.txt | 1 + llvm/lib/CAS/Stats.cpp | 153 ++++++++++++++++++ llvm/tools/llvm-cas/Options.td | 2 + llvm/tools/llvm-cas/llvm-cas.cpp | 81 +++++++++- 10 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 clang/test/CAS/stats.c create mode 100644 llvm/include/llvm/CAS/Stats.h create mode 100644 llvm/lib/CAS/Stats.cpp diff --git a/clang/include/clang/Basic/DiagnosticCASKinds.td b/clang/include/clang/Basic/DiagnosticCASKinds.td index d378edbe672a..af5c8b794a22 100644 --- a/clang/include/clang/Basic/DiagnosticCASKinds.td +++ b/clang/include/clang/Basic/DiagnosticCASKinds.td @@ -44,6 +44,8 @@ def err_cas_missing_input_cache_entry : Error< "CAS missing expected input cache key '%0'">, DefaultFatal; def err_cas_cannot_load_api_notes_include_tree : Error< "cannot load APINotes from include-tree-id '%0'">, DefaultFatal; +def err_cas_stats_cannot_be_initialized : Error< + "CAS stats cannot be initialized: %0">, DefaultFatal; def warn_clang_cache_disabled_caching: Warning< "caching disabled because %0">, diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 7e58cae27198..eaef612d3a8f 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -27,6 +27,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/CAS/CASID.h" #include "llvm/CAS/CASOutputBackend.h" +#include "llvm/CAS/Stats.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/PrefixMapper.h" @@ -1085,6 +1086,22 @@ class CompilerInstance : public ModuleLoader { void setCASDatabases(std::shared_ptr CAS, std::shared_ptr Cache); + + static bool setupCacheStatsDiagnostics(DiagnosticsEngine &Diags); +}; + +class CacheStatsDiagnosticConsumer : public DiagnosticConsumer { +public: + CacheStatsDiagnosticConsumer() = default; + CacheStatsDiagnosticConsumer(CacheStatsDiagnosticConsumer &&Other) + : Stats(std::move(Other.Stats)) {}; + CacheStatsDiagnosticConsumer(llvm::cas::Stats &Stats) + : Stats(std::move(Stats)) {}; + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) override; + +private: + llvm::cas::Stats Stats; }; } // end namespace clang diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 69532e2ec4d0..47c3cbbf40ad 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -67,6 +67,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SmallVectorMemoryBuffer.h" #include "llvm/Support/Threading.h" @@ -381,6 +382,27 @@ static void SetupSerializedDiagnostics(DiagnosticOptions &DiagOpts, } } +bool CompilerInstance::setupCacheStatsDiagnostics(DiagnosticsEngine &Diags) { + auto CASPath = llvm::sys::Process::GetEnv("LLVM_CACHE_CAS_PATH"); + if (!CASPath) + return true; + llvm::cas::Stats Stats; + if (auto Err = Stats.open(*CASPath)) { + Diags.Report(diag::err_cas_stats_cannot_be_initialized) + << llvm::toString(std::move(Err)); + return false; + } + auto SC = std::make_unique(Stats); + if (Diags.ownsClient()) { + Diags.setClient( + new ChainedDiagnosticConsumer(Diags.takeClient(), std::move(SC))); + } else { + Diags.setClient( + new ChainedDiagnosticConsumer(Diags.getClient(), std::move(SC))); + } + return true; +} + void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client, bool ShouldOwnClient) { Diagnostics = createDiagnostics(getVirtualFileSystem(), getDiagnosticOpts(), @@ -403,6 +425,8 @@ IntrusiveRefCntPtr CompilerInstance::createDiagnostics( } else Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); + setupCacheStatsDiagnostics(*Diags); + // Chain in -verify checker, if requested. if (Opts.VerifyDiagnostics) Diags->setClient(new VerifyDiagnosticConsumer(*Diags)); @@ -2915,3 +2939,67 @@ bool CompileCacheASTReaderHelper::readIncludeTreeID(StringRef ID, return checkCASID(Complain, ID, diag::err_cas_cannot_parse_include_tree_id, diag::err_cas_missing_include_tree_id); } + +void CacheStatsDiagnosticConsumer::HandleDiagnostic( + DiagnosticsEngine::Level Level, const Diagnostic &Info) { + if (Level == DiagnosticsEngine::Remark) { + // StringRef remark = + // Info.getDiags()->getDiagnosticIDs()->getWarningOptionForDiag(Info.getID()); + // "compile-job-cache-miss" + + //SmallString<100> MsgBuf; + //Info.FormatDiagnostic(MsgBuf); + // std::string Message(MsgBuf); + + double T = 0.0; + if (Info.getNumArgs() > 0 && + Info.getArgKind(0) == DiagnosticsEngine::ak_std_string) { + llvm::StringRef S = StringRef(Info.getArgStdStr(0)).rtrim(" s\r\n\t"); + llvm::to_float(S, T); + } + + switch (Info.getID()) { + case diag::remark_compile_job_cache_hit: + Stats.inc(llvm::cas::Stats::Key::hit); + break; + case diag::remark_compile_job_cache_miss: + Stats.inc(llvm::cas::Stats::Key::miss); + break; + case diag::remark_compile_job_cache_miss_result_not_found: + Stats.inc(llvm::cas::Stats::Key::miss_result_not_found); + break; + case diag::remark_compile_job_cache_backend_output_not_found: + Stats.inc(llvm::cas::Stats::Key::backend_output_not_found); + break; + case diag::remark_compile_job_cache_skipped: + Stats.inc(llvm::cas::Stats::Key::skipped); + break; + + case diag::remark_compile_job_cache_timing_depscan: + Stats.inc(llvm::cas::Stats::Key::depscan_count); + Stats.inc_f(llvm::cas::Stats::Key::depscan_time, T); + break; + case diag::remark_compile_job_cache_timing_backend_key_query: + Stats.inc(llvm::cas::Stats::Key::backend_key_query_count); + Stats.inc_f(llvm::cas::Stats::Key::backend_key_query_time, T); + break; + case diag::remark_compile_job_cache_timing_backend_key_update: + Stats.inc(llvm::cas::Stats::Key::backend_key_update_count); + Stats.inc_f(llvm::cas::Stats::Key::backend_key_update_time, T); + break; + case diag::remark_compile_job_cache_timing_backend_load: + Stats.inc(llvm::cas::Stats::Key::backend_load_count); + Stats.inc_f(llvm::cas::Stats::Key::backend_load_time, T); + break; + case diag::remark_compile_job_cache_timing_backend_store: + Stats.inc(llvm::cas::Stats::Key::backend_store_count); + Stats.inc_f(llvm::cas::Stats::Key::backend_store_time, T); + break; + + default: + break; + } + } + + // DiagnosticConsumer::HandleDiagnostic(Level, Info); +} diff --git a/clang/test/CAS/stats.c b/clang/test/CAS/stats.c new file mode 100644 index 000000000000..a58ca7cecf70 --- /dev/null +++ b/clang/test/CAS/stats.c @@ -0,0 +1,32 @@ +// REQUIRES: ondisk_cas +// REQUIRES: llvm-cas + +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %llvm-cas --show-stats -cas %t/cas | FileCheck %s -check-prefix=NO-CACHE + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -c %s -o %t/test.o -Xclang -Rcompile-job-cache +// RUN: %llvm-cas --show-stats -cas %t/cas | FileCheck %s -check-prefix=CACHE-MISS + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -c %s -o %t/test.o -Xclang -Rcompile-job-cache +// RUN: %llvm-cas --show-stats -cas %t/cas | FileCheck %s -check-prefix=CACHE-HIT + +// RUN: %llvm-cas --zero-stats -cas %t/cas | FileCheck %s -check-prefix=ZERO + +// RUN: %llvm-cas --show-stats -cas %t/cas | FileCheck %s -check-prefix=AFTER-ZERO + +// NO-CACHE: Cache hits: 0 +// NO-CACHE: Cache misses: 0 + +// CACHE-MISS: Cache hits: 0 +// CACHE-MISS: Cache misses: 1 + +// CACHE-HIT: Cache hits: 1 +// CACHE-HIT: Cache misses: 1 + +// ZERO: zero-stats: successfull + +// AFTER-ZERO: Cache hits: 0 +// AFTER-ZERO: Cache misses: 0 + +int main() { return 0; } \ No newline at end of file diff --git a/clang/tools/driver/cc1depscan_main.cpp b/clang/tools/driver/cc1depscan_main.cpp index 4ea9adfba311..e48d118bea46 100644 --- a/clang/tools/driver/cc1depscan_main.cpp +++ b/clang/tools/driver/cc1depscan_main.cpp @@ -736,6 +736,10 @@ int cc1depscan_main(ArrayRef Argv, const char *Argv0, std::make_unique(llvm::errs(), *DiagOpts, false); DiagnosticsEngine Diags(new DiagnosticIDs(), *DiagOpts); Diags.setClient(DiagsConsumer.get(), /*ShouldOwnClient=*/false); + + if (!CompilerInstance::setupCacheStatsDiagnostics(Diags)) + return 1; + auto VFS = [] { auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); return llvm::vfs::getRealFileSystem(); diff --git a/llvm/include/llvm/CAS/Stats.h b/llvm/include/llvm/CAS/Stats.h new file mode 100644 index 000000000000..b409f607915b --- /dev/null +++ b/llvm/include/llvm/CAS/Stats.h @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CAS_STATS_H +#define LLVM_CAS_STATS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" + +namespace llvm::cas { + +class Stats { +public: + static const uint64_t Version = 1; + + enum class Key : uint16_t { + version = 0, + hit = 1, + miss = 2, + miss_result_not_found = 3, + backend_output_not_found = 4, + skipped = 5, + depscan_time = 6, + depscan_count = 7, + backend_key_query_time = 8, + backend_key_query_count = 9, + backend_key_update_time = 10, + backend_key_update_count = 11, + backend_load_time = 12, + backend_load_count = 13, + backend_store_time = 14, + backend_store_count = 15, + last = 16, + }; + + Stats() : FD(-1) {}; + Stats(Stats &&Other) : Map(std::move(Other.Map)), FD(Other.FD) { + Other.FD = -1; + }; + ~Stats() { close(); } + + Error open(StringRef Path); + void close(); + + uint64_t get(Key K); + void set(Key K, uint64_t V); + void inc(Key K, uint64_t V = 1); + + double get_f(Key K); + void set_f(Key K, double V); + void inc_f(Key K, double V); + +private: + bool key_valid(Key K) { return (Map && K < Key::last); } + void *key2addr(Key K) { + return reinterpret_cast(Map.data() + (uint64_t)K * sizeof(uint64_t)); + } + int FD; + sys::fs::mapped_file_region Map; +}; + +} // namespace llvm::cas + +#endif // LLVM_CAS_STATS_H diff --git a/llvm/lib/CAS/CMakeLists.txt b/llvm/lib/CAS/CMakeLists.txt index 67f107678af6..ee76bf06ebfe 100644 --- a/llvm/lib/CAS/CMakeLists.txt +++ b/llvm/lib/CAS/CMakeLists.txt @@ -30,6 +30,7 @@ add_llvm_component_library(LLVMCAS OnDiskTrieRawHashMap.cpp OnDiskKeyValueDB.cpp PluginCAS.cpp + Stats.cpp TreeSchema.cpp UnifiedOnDiskCache.cpp Utils.cpp diff --git a/llvm/lib/CAS/Stats.cpp b/llvm/lib/CAS/Stats.cpp new file mode 100644 index 000000000000..44d909d54619 --- /dev/null +++ b/llvm/lib/CAS/Stats.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// +/// +// +//===----------------------------------------------------------------------===// + +#include "llvm/CAS/Stats.h" +#include "llvm/Support/IOSandbox.h" +#include "llvm/Support/Path.h" + +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#include +#else +#include +// #define close(fd) _close(fd) +#endif + +// std::atomic_ref and std::atomic::fetch_add are available since C++20 +#include + +namespace llvm::cas { + +static constexpr StringLiteral StatsName = "stats"; + +Error Stats::open(StringRef Path) { + SmallString<256> FullPath(Path); + sys::path::append(FullPath, StatsName); + + auto BypassSandbox = sys::sandbox::scopedDisable(); + + if (auto EC = sys::fs::create_directory(Path)) + return createFileError(Path, EC); + + if (auto EC = sys::fs::openFileForReadWrite( + FullPath, FD, sys::fs::CD_OpenAlways, sys::fs::OF_None)) + return createFileError(FullPath, EC); + + uint64_t Size = (uint64_t)(Key::last) * sizeof(uint64_t); + + if (auto EC = sys::fs::resize_file_before_mapping_readwrite(FD, Size)) + return createFileError(FullPath, EC); + + std::error_code EC; + + const char *Name = nullptr; +#ifdef _WIN32 + // Give the file mapping a name to ensure the same mappings are + // shared across processes. + std::replace(FullPath.begin(), FullPath.end(), '\\', '/'); + std::string MapName("Local\\"); + MapName += FullPath.c_str(); + Name = MapName.c_str(); +#endif + + sys::fs::mapped_file_region M(sys::fs::convertFDToNativeFile(FD), + sys::fs::mapped_file_region::readwrite, + Size, 0, EC, Name); + if (EC) + return createFileError(FullPath, EC); + + Map = std::move(M); + + set(Key::version, Version); + + return Error::success(); +} + +void Stats::close() { + if (Map) { + Map.sync(); + Map.unmap(); + } + if (FD != -1) { + ::close(FD); + FD = -1; + } +} + +// Assume the same endianness for reading and writing, as the stats file is only +// used on the same machine. + +uint64_t Stats::get(Stats::Key K) { + if (!key_valid(K)) + return 0; + auto *AP = reinterpret_cast *>(key2addr(K)); + return AP->load(std::memory_order_relaxed); +} + +void Stats::set(Stats::Key K, uint64_t V) { + if (!key_valid(K)) + return; + auto *AP = reinterpret_cast *>(key2addr(K)); + AP->store(V, std::memory_order_relaxed); + Map.sync(); +} + +void Stats::inc(Stats::Key K, uint64_t V) { + if (!key_valid(K)) + return; + auto *AP = reinterpret_cast *>(key2addr(K)); + AP->fetch_add(V, std::memory_order_relaxed); + Map.sync(); +} + +double Stats::get_f(Key K) { + if (!key_valid(K)) + return 0.0; + auto *AP = reinterpret_cast *>(key2addr(K)); + return AP->load(std::memory_order_relaxed); +} + +void Stats::set_f(Key K, double V) { + if (!key_valid(K)) + return; + auto *AP = reinterpret_cast *>(key2addr(K)); + AP->store(V, std::memory_order_relaxed); + Map.sync(); +} + +// FIXME: Use std::atomic::fetch_add for C++20 and later. + +static double atomic_fetch_add_double(double *P, double V) { + auto *AP = reinterpret_cast *>(P); + uint64_t OB; + uint64_t NB; + double OV; + do { + OB = AP->load(std::memory_order_relaxed); + std::memcpy(&OV, &OB, sizeof(double)); + double NV = OV + V; + std::memcpy(&NB, &NV, sizeof(uint64_t)); + } while (!AP->compare_exchange_weak( + OB, NB, std::memory_order_seq_cst, std::memory_order_relaxed)); + return OV; +} + +void Stats::inc_f(Key K, double V) { + if (!key_valid(K)) + return; + double *P = reinterpret_cast(key2addr(K)); + atomic_fetch_add_double(P, V); + Map.sync(); +} + +} // namespace llvm::cas diff --git a/llvm/tools/llvm-cas/Options.td b/llvm/tools/llvm-cas/Options.td index 33a438b5c1fc..e5ce896c8aec 100644 --- a/llvm/tools/llvm-cas/Options.td +++ b/llvm/tools/llvm-cas/Options.td @@ -50,6 +50,8 @@ def validate_if_needed : F<"validate-if-needed">, HelpText<"Validate cas contents if needed">, Group; def prune : F<"prune">, HelpText<"Prune local cas storage">, Group; +def stats_show : F<"show-stats">, HelpText<"show stats">, Group; +def stats_zero : F<"zero-stats">, HelpText<"zero stats">, Group; // Tool options diff --git a/llvm/tools/llvm-cas/llvm-cas.cpp b/llvm/tools/llvm-cas/llvm-cas.cpp index 3190bec3d3da..53d57ad09831 100644 --- a/llvm/tools/llvm-cas/llvm-cas.cpp +++ b/llvm/tools/llvm-cas/llvm-cas.cpp @@ -18,6 +18,7 @@ #include "llvm/CAS/CASFileSystem.h" #include "llvm/CAS/ObjectStore.h" #include "llvm/CAS/TreeSchema.h" +#include "llvm/CAS/Stats.h" #include "llvm/CAS/Utils.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -74,6 +75,8 @@ enum class CommandKind { ValidateObject, ValidateIfNeeded, Prune, + StatsShow, + StatsZero, }; struct CommandOptions { @@ -133,6 +136,10 @@ struct CommandOptions { return CommandKind::ValidateIfNeeded; case OPT_prune: return CommandKind::Prune; + case OPT_stats_show: + return CommandKind::StatsShow; + case OPT_stats_zero: + return CommandKind::StatsZero; } return CommandKind::Invalid; } @@ -142,7 +149,8 @@ struct CommandOptions { return Kind != CommandKind::ValidateIfNeeded && Kind != CommandKind::Validate && Kind != CommandKind::MakeBlob && Kind != CommandKind::MakeNode && Kind != CommandKind::Dump && - Kind != CommandKind::Prune && Kind != CommandKind::MergeTrees; + Kind != CommandKind::Prune && Kind != CommandKind::MergeTrees && + Kind != CommandKind::StatsShow && Kind != CommandKind::StatsZero; } }; } // namespace @@ -175,6 +183,7 @@ static int validate(ObjectStore &CAS, ActionCache &AC, bool CheckHash); static int ingestCasIDFile(cas::ObjectStore &CAS, ArrayRef CASIDs); static int validateIfNeeded(const CommandOptions &Opts, const char *Argv0); static int prune(cas::ObjectStore &CAS); +static int stats(StringRef Path, bool Zero); static Expected parseOptions(int Argc, char **Argv) { BumpPtrAllocator Alloc; @@ -261,6 +270,12 @@ int main(int Argc, char **Argv) { } assert(CAS); + if (Opts.Command == CommandKind::StatsShow) + return stats(Opts.CASPath, false); + + if (Opts.Command == CommandKind::StatsZero) + return stats(Opts.CASPath, true); + if (Opts.Command == CommandKind::Dump) return dump(*CAS); @@ -794,3 +809,67 @@ static int prune(cas::ObjectStore &CAS) { ExitOnErr(CAS.pruneStorageData()); return 0; } + +int stats(StringRef Path, bool Zero) { + llvm::cas::Stats Stats; + ExitOnError ExitOnErr("llvm-cas: stats: "); + ExitOnErr(Stats.open(Path)); + + outs() << "llvm-cas: stats:\n"; + outs() << "Cache hits: " << Stats.get(llvm::cas::Stats::Key::hit) << "\n"; + outs() << "Cache misses: " << Stats.get(llvm::cas::Stats::Key::miss) << "\n"; + outs() << "Cache misses (result_not_found): " << Stats.get(llvm::cas::Stats::Key::miss_result_not_found) << "\n"; + outs() << "Cache backend output not found: " << Stats.get(llvm::cas::Stats::Key::backend_output_not_found) << "\n"; + outs() << "Cache skipped: " << Stats.get(llvm::cas::Stats::Key::skipped) << "\n"; + + uint64_t C = Stats.get(llvm::cas::Stats::Key::depscan_count); + double T = Stats.get_f(llvm::cas::Stats::Key::depscan_time); + T = (C > 0) ? T / C : 0.0; + outs() << "Average depscan: " << llvm::formatv("{0:f6} s ({1})\n", T, C); + + C = Stats.get(llvm::cas::Stats::Key::backend_key_query_count); + T = Stats.get_f(llvm::cas::Stats::Key::backend_key_query_time); + T = (C > 0) ? T / C : 0.0; + outs() << "Average backend key query: " << llvm::formatv("{0:f6} s ({1})\n", T, C); + + C = Stats.get(llvm::cas::Stats::Key::backend_key_update_count); + T = Stats.get_f(llvm::cas::Stats::Key::backend_key_update_time); + T = (C > 0) ? T / C : 0.0; + outs() << "Average backend key update: " << llvm::formatv("{0:f6} s ({1})\n", T, C); + + C = Stats.get(llvm::cas::Stats::Key::backend_load_count); + T = Stats.get_f(llvm::cas::Stats::Key::backend_load_time); + T = (C > 0) ? T / C : 0.0; + outs() << "Average backend load: " << llvm::formatv("{0:f6} s ({1})\n", T, C); + + C = Stats.get(llvm::cas::Stats::Key::backend_store_count); + T = Stats.get_f(llvm::cas::Stats::Key::backend_store_time); + T = (C > 0) ? T / C : 0.0; + outs() << "Average backend store: " << llvm::formatv("{0:f6} s ({1})\n", T, C); + + outs() << "Cache location: " << Path << "\n"; + + if (Zero) { + outs() << "zero-stats: "; + Stats.set(llvm::cas::Stats::Key::hit, 0); + Stats.set(llvm::cas::Stats::Key::miss, 0); + Stats.set(llvm::cas::Stats::Key::miss_result_not_found, 0); + Stats.set(llvm::cas::Stats::Key::backend_output_not_found, 0); + Stats.set(llvm::cas::Stats::Key::skipped, 0); + + Stats.set_f(llvm::cas::Stats::Key::depscan_time, 0.0); + Stats.set(llvm::cas::Stats::Key::depscan_count, 0); + Stats.set_f(llvm::cas::Stats::Key::backend_key_query_time, 0.0); + Stats.set(llvm::cas::Stats::Key::backend_key_query_count, 0); + Stats.set_f(llvm::cas::Stats::Key::backend_key_update_time, 0.0); + Stats.set(llvm::cas::Stats::Key::backend_key_update_count, 0); + Stats.set_f(llvm::cas::Stats::Key::backend_load_time, 0.0); + Stats.set(llvm::cas::Stats::Key::backend_load_count, 0); + Stats.set_f(llvm::cas::Stats::Key::backend_store_time, 0.0); + Stats.set(llvm::cas::Stats::Key::backend_store_count, 0); + + outs() << "successfull\n"; + } + + return 0; +}