| // Copyright 2014 The Goma Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| |
| #ifndef DEVTOOLS_GOMA_CLIENT_DEPS_CACHE_H_ |
| #define DEVTOOLS_GOMA_CLIENT_DEPS_CACHE_H_ |
| |
| #include <atomic> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| |
| #include "absl/container/node_hash_map.h" |
| #include "absl/time/time.h" |
| #include "absl/types/optional.h" |
| #include "autolock_timer.h" |
| #include "cache_file.h" |
| #include "file_stat_cache.h" |
| #include "filename_id_table.h" |
| #include "goma_hash.h" |
| |
| namespace devtools_goma { |
| |
| class CompilerFlags; |
| class CompilerInfo; |
| class DepsCacheStats; |
| |
| // DepsCache is a cache for dependent files. |
| // We make an 'identifier' which identifies compile command, |
| // and a map from 'identifier' to dependent files (and extra information). |
| // |
| // When we run the same command which has the same identifier, |
| // we check whether we can reuse the dependet files list. |
| // This is done by the following algorithm: |
| // For all dependent files: |
| // 1. Check FileStat. If it's the same, we think a file is not changed. |
| // 2. Check directive_hash, which is a hash value created from file's |
| // directive lines. If it's the same, dependant files won't be changed. |
| class DepsCache { |
| public: |
| using Identifier = absl::optional<SHA256HashValue>; |
| |
| static DepsCache* instance() { return instance_; } |
| static bool IsEnabled() { return instance_ != nullptr; } |
| |
| // Initializes the DepsCache. |
| // When |cache_filename| is empty, this won't be enabled. |
| // When |cache_filename| file exists, we load it. |
| static void Init(const std::string& cache_filename, |
| absl::optional<absl::Duration> identifier_alive_duration, |
| size_t deps_table_size_threshold, |
| int max_proto_size_in_mega_bytes); |
| |
| // Saves .goma_deps file is DepsCache is initialized. |
| static void Quit(); |
| |
| // Creates identifier to set/get dependencies. |
| static Identifier MakeDepsIdentifier( |
| const CompilerInfo& compiler_info, |
| const CompilerFlags& compiler_flags); |
| |
| // Records a dependency; a compile command can be identified with |
| // |identifier|, and the command uses |input_file| as |
| // an input file (e.g. *.cc), also, the command requires |
| // |dependencies| files (e.g. *.h), can be relative. |
| // |cwd| should be absolute. |
| // |identifier| should not be empty. |
| // |input_file| can be relative. |
| bool SetDependencies(const Identifier& identifier, |
| const std::string& cwd, |
| const std::string& input_file, |
| const std::set<std::string>& dependencies, |
| FileStatCache* file_stat_cache); |
| |
| // Gets dependent files using |identifer|. |
| // We check the dependecies are not changed. If changed, false will be |
| // returned and |dependecies| won't be changed. |
| // |input_file| is removed from the result of |dependencies|. |
| // |input_file| can be relative. |
| // |cwd| should be absolute. |
| // path in |dependencies| can be relative. |
| bool GetDependencies(const Identifier& identifier, |
| const std::string& cwd, |
| const std::string& input_file, |
| std::set<std::string>* dependencies, |
| FileStatCache* file_stat_cache); |
| |
| void RemoveDependency(const Identifier& identifier); |
| |
| // Dump internal stats. |
| void DumpStatsToProto(DepsCacheStats* stats) const; |
| |
| private: |
| friend class DepsCacheTest; |
| |
| // DepsHashId is used to check whether an include file is updated. |
| // |directive_hash| is a hash value of the file's directive lines. |
| struct DepsHashId { |
| DepsHashId() {} |
| DepsHashId(FilenameIdTable::Id id, |
| const FileStat& file_stat, |
| const SHA256HashValue& directive_hash) |
| : id(id), file_stat(file_stat), directive_hash(directive_hash) {} |
| |
| bool IsValid() const { |
| return id != FilenameIdTable::kInvalidId && file_stat.IsValid(); |
| } |
| |
| FilenameIdTable::Id id; |
| FileStat file_stat; |
| SHA256HashValue directive_hash; |
| }; |
| |
| struct DepsTableData { |
| DepsTableData() : last_used_time(0) { |
| } |
| |
| // We need to keep this as a time_t to preserve the std::atomic behavior. |
| std::atomic<time_t> last_used_time; |
| std::vector<DepsHashId> deps_hash_ids; |
| }; |
| |
| typedef SHA256HashValue Key; |
| typedef absl::node_hash_map<Key, DepsTableData> DepsTable; |
| |
| DepsCache(const string& cache_filename, |
| absl::optional<absl::Duration> identifier_alive_duration, |
| size_t deps_table_size_threshold, |
| int max_proto_size_in_mega_bytes); |
| ~DepsCache(); |
| |
| void Clear(); |
| |
| bool SaveGomaDeps(); |
| bool LoadGomaDeps(); |
| |
| void IncrMissedCount(); |
| void IncrMissedByUpdatedCount(); |
| void IncrHitCount(); |
| |
| static bool IsDirectiveModified(const string& filename, |
| const FileStat& old_file_stat, |
| const SHA256HashValue& old_directive_hash, |
| FileStatCache* file_stat_cache); |
| |
| // Used for test. |
| bool UpdateLastUsedTime(const Identifier& identifier, |
| absl::optional<absl::Time> last_used_time); |
| size_t deps_table_size() const { |
| AUTO_SHARED_LOCK(lock, &mu_); |
| return deps_table_.size(); |
| } |
| bool GetDepsHashId(const Identifier& identifier, |
| const FilenameIdTable::Id& id, |
| DepsHashId* deps_hash_id) const; |
| |
| static DepsCache* instance_; |
| |
| const CacheFile cache_file_; |
| // When an identifier is older than this value (in second), it won't be |
| // removed in save/load. If unset, we don't dispose of old cache. |
| const absl::optional<absl::Duration> identifier_alive_duration_; |
| // When lots of DepsTable exist, we'd like to remove older DepsTable entry |
| // when saving. |
| const size_t deps_table_size_threshold_; |
| // If the proto for cache exceeds this size, loading will fail. |
| // In that case, cache is just ignored. |
| const int max_proto_size_in_mega_bytes_; |
| |
| mutable ReadWriteLock mu_; |
| DepsTable deps_table_ GUARDED_BY(mu_); |
| |
| // Instead of using a filename, we alternatively use an id for |
| // performance and memory space. So, we manage this table to convert |
| // between filename and id. |
| FilenameIdTable filename_id_table_; |
| |
| mutable Lock count_mu_; |
| unsigned int hit_count_ GUARDED_BY(count_mu_); |
| unsigned int missed_count_ GUARDED_BY(count_mu_); |
| unsigned int missed_by_updated_count_ GUARDED_BY(count_mu_); |
| |
| DISALLOW_COPY_AND_ASSIGN(DepsCache); |
| }; |
| |
| } // namespace devtools_goma |
| |
| #endif // DEVTOOLS_GOMA_CLIENT_DEPS_CACHE_H_ |