1 //===- DependencyScanningFilesystem.h - clang-scan-deps fs ===---*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H
10 #define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H
12 #include "clang/Basic/LLVM.h"
13 #include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h"
14 #include "llvm/ADT/StringMap.h"
15 #include "llvm/ADT/StringSet.h"
16 #include "llvm/Support/Allocator.h"
17 #include "llvm/Support/ErrorOr.h"
18 #include "llvm/Support/VirtualFileSystem.h"
23 namespace dependencies {
25 /// An in-memory representation of a file system entity that is of interest to
26 /// the dependency scanning filesystem.
28 /// It represents one of the following:
29 /// - an opened source file with minimized contents and a stat value.
30 /// - an opened source file with original contents and a stat value.
31 /// - a directory entry with its stat value.
32 /// - an error value to represent a file system error.
33 /// - a placeholder with an invalid stat indicating a not yet initialized entry.
34 class CachedFileSystemEntry {
36 /// Default constructor creates an entry with an invalid stat.
37 CachedFileSystemEntry() : MaybeStat(llvm::vfs::Status()) {}
39 CachedFileSystemEntry(std::error_code Error) : MaybeStat(std::move(Error)) {}
41 /// Create an entry that represents an opened source file with minimized or
42 /// original contents.
44 /// The filesystem opens the file even for `stat` calls open to avoid the
45 /// issues with stat + open of minimized files that might lead to a
46 /// mismatching size of the file. If file is not minimized, the full file is
47 /// read and copied into memory to ensure that it's not memory mapped to avoid
48 /// running out of file descriptors.
49 static CachedFileSystemEntry createFileEntry(StringRef Filename,
50 llvm::vfs::FileSystem &FS,
51 bool Minimize = true);
53 /// Create an entry that represents a directory on the filesystem.
54 static CachedFileSystemEntry createDirectoryEntry(llvm::vfs::Status &&Stat);
56 /// \returns True if the entry is valid.
57 bool isValid() const { return !MaybeStat || MaybeStat->isStatusKnown(); }
59 /// \returns True if the current entry points to a directory.
60 bool isDirectory() const { return MaybeStat && MaybeStat->isDirectory(); }
62 /// \returns The error or the file's contents.
63 llvm::ErrorOr<StringRef> getContents() const {
65 return MaybeStat.getError();
66 assert(!MaybeStat->isDirectory() && "not a file");
67 assert(isValid() && "not initialized");
68 return StringRef(Contents);
71 /// \returns The error or the status of the entry.
72 llvm::ErrorOr<llvm::vfs::Status> getStatus() const {
73 assert(isValid() && "not initialized");
77 /// \returns the name of the file.
78 StringRef getName() const {
79 assert(isValid() && "not initialized");
80 return MaybeStat->getName();
83 /// Return the mapping between location -> distance that is used to speed up
84 /// the block skipping in the preprocessor.
85 const PreprocessorSkippedRangeMapping &getPPSkippedRangeMapping() const {
86 return PPSkippedRangeMapping;
89 CachedFileSystemEntry(CachedFileSystemEntry &&) = default;
90 CachedFileSystemEntry &operator=(CachedFileSystemEntry &&) = default;
92 CachedFileSystemEntry(const CachedFileSystemEntry &) = delete;
93 CachedFileSystemEntry &operator=(const CachedFileSystemEntry &) = delete;
96 llvm::ErrorOr<llvm::vfs::Status> MaybeStat;
97 // Store the contents in a small string to allow a
98 // move from the small string for the minimized contents.
99 // Note: small size of 1 allows us to store an empty string with an implicit
100 // null terminator without any allocations.
101 llvm::SmallString<1> Contents;
102 PreprocessorSkippedRangeMapping PPSkippedRangeMapping;
105 /// This class is a shared cache, that caches the 'stat' and 'open' calls to the
106 /// underlying real file system.
108 /// It is sharded based on the hash of the key to reduce the lock contention for
109 /// the worker threads.
110 class DependencyScanningFilesystemSharedCache {
112 struct SharedFileSystemEntry {
113 std::mutex ValueLock;
114 CachedFileSystemEntry Value;
117 DependencyScanningFilesystemSharedCache();
119 /// Returns a cache entry for the corresponding key.
121 /// A new cache entry is created if the key is not in the cache. This is a
122 /// thread safe call.
123 SharedFileSystemEntry &get(StringRef Key);
127 std::mutex CacheLock;
128 llvm::StringMap<SharedFileSystemEntry, llvm::BumpPtrAllocator> Cache;
130 std::unique_ptr<CacheShard[]> CacheShards;
134 /// A virtual file system optimized for the dependency discovery.
136 /// It is primarily designed to work with source files whose contents was was
137 /// preprocessed to remove any tokens that are unlikely to affect the dependency
140 /// This is not a thread safe VFS. A single instance is meant to be used only in
141 /// one thread. Multiple instances are allowed to service multiple threads
142 /// running in parallel.
143 class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
145 DependencyScanningWorkerFilesystem(
146 DependencyScanningFilesystemSharedCache &SharedCache,
147 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
148 ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings)
149 : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache),
150 PPSkipMappings(PPSkipMappings) {}
152 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
153 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
154 openFileForRead(const Twine &Path) override;
156 /// The set of files that should not be minimized.
157 llvm::StringSet<> IgnoredFiles;
160 void setCachedEntry(StringRef Filename, const CachedFileSystemEntry *Entry) {
161 bool IsInserted = Cache.try_emplace(Filename, Entry).second;
163 assert(IsInserted && "local cache is updated more than once");
166 const CachedFileSystemEntry *getCachedEntry(StringRef Filename) {
167 auto It = Cache.find(Filename);
168 return It == Cache.end() ? nullptr : It->getValue();
171 llvm::ErrorOr<const CachedFileSystemEntry *>
172 getOrCreateFileSystemEntry(const StringRef Filename);
174 DependencyScanningFilesystemSharedCache &SharedCache;
175 /// The local cache is used by the worker thread to cache file system queries
176 /// locally instead of querying the global cache every time.
177 llvm::StringMap<const CachedFileSystemEntry *, llvm::BumpPtrAllocator> Cache;
178 /// The optional mapping structure which records information about the
179 /// excluded conditional directive skip mappings that are used by the
180 /// currently active preprocessor.
181 ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
184 } // end namespace dependencies
185 } // end namespace tooling
186 } // end namespace clang
188 #endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H