clang 24.0.0git
ModuleCache.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
10
14#include "llvm/ADT/ScopeExit.h"
15#include "llvm/Support/Error.h"
16#include "llvm/Support/FileSystem.h"
17#include "llvm/Support/IOSandbox.h"
18#include "llvm/Support/LockFileManager.h"
19#include "llvm/Support/Path.h"
20
21using namespace clang;
22
24 auto [ByNameIt, ByNameInserted] = ByPath.insert({Path, nullptr});
25 if (!ByNameIt->second) {
26 // This is a compiler-internal input/output, let's bypass the sandbox.
27 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
28
29 // If we cannot get status of the module cache directory, try if trying to
30 // create it helps.
31 llvm::sys::fs::file_status Status;
32 if (std::error_code EC = llvm::sys::fs::status(Path, Status)) {
33 // Unless the status failed because the directory does not exist yet.
34 if (EC != std::errc::no_such_file_or_directory)
35 return nullptr;
36 // If we're unable to create the directory.
37 if (llvm::sys::fs::create_directories(Path))
38 return nullptr;
39 // If we're unable to stat the newly created directory.
40 if (llvm::sys::fs::status(Path, Status))
41 return nullptr;
42 }
43
44 llvm::sys::fs::UniqueID UID = Status.getUniqueID();
45 auto [ByUIDIt, ByUIDInserted] = ByUID.insert({UID, nullptr});
46 if (!ByUIDIt->second)
47 ByUIDIt->second = std::make_unique<ModuleCacheDirectory>();
48 ByNameIt->second = ByUIDIt->second.get();
49 }
50 return ByNameIt->second;
51}
52
53/// Write a new timestamp file with the given path.
54static void writeTimestampFile(StringRef TimestampFile) {
55 std::error_code EC;
56 llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::OF_None);
57}
58
59void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
60 time_t PruneAfter, bool PruneTopLevel,
61 llvm::function_ref<void(StringRef)> OnPrune) {
62 if (PruneInterval <= 0 || PruneAfter <= 0)
63 return;
64
65 // This is a compiler-internal input/output, let's bypass the sandbox.
66 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
67
68 llvm::SmallString<256> RootPath(Path);
69 (void)llvm::sys::fs::make_absolute(RootPath);
70
71 llvm::SmallString<128> TimestampFile(RootPath);
72 llvm::sys::path::append(TimestampFile, "modules.timestamp");
73
74 // Try to stat() the timestamp file.
75 llvm::sys::fs::file_status StatBuf;
76 if (std::error_code EC = llvm::sys::fs::status(TimestampFile, StatBuf)) {
77 // If the timestamp file wasn't there, create one now.
78 if (EC == std::errc::no_such_file_or_directory)
79 writeTimestampFile(TimestampFile);
80 return;
81 }
82
83 // Check whether the time stamp is older than our pruning interval.
84 // If not, do nothing.
85 time_t TimestampModTime =
86 llvm::sys::toTimeT(StatBuf.getLastModificationTime());
87 time_t CurrentTime = time(nullptr);
88 if (CurrentTime - TimestampModTime <= PruneInterval)
89 return;
90
91 // Write a new timestamp file so that nobody else attempts to prune.
92 // There is a benign race condition here, if two Clang instances happen to
93 // notice at the same time that the timestamp is out-of-date.
94 writeTimestampFile(TimestampFile);
95
96 auto NotifyPruned = [&](StringRef RemovedPath) {
97 if (OnPrune)
98 OnPrune(RemovedPath);
99 };
100
101 // Walk the entire module cache, looking for unused module files and module
102 // indices.
103 std::error_code EC;
104 auto TryPruneFile = [&](StringRef FilePath) {
105 // We only care about module and global module index files.
106 StringRef Filename = llvm::sys::path::filename(FilePath);
107 StringRef Extension = llvm::sys::path::extension(FilePath);
108 if (Extension != ".pcm" && Extension != ".timestamp" &&
109 Filename != "modules.idx")
110 return;
111
112 // Don't prune the pruning timestamp file.
113 if (Filename == "modules.timestamp")
114 return;
115
116 // Look at this file. If we can't stat it, there's nothing interesting
117 // there.
118 if (llvm::sys::fs::status(FilePath, StatBuf))
119 return;
120
121 // If the file has been used recently enough, leave it there.
122 time_t FileAccessTime = llvm::sys::toTimeT(StatBuf.getLastAccessedTime());
123 if (CurrentTime - FileAccessTime <= PruneAfter)
124 return;
125
126 // Remove the file.
127 if (!llvm::sys::fs::remove(FilePath))
128 NotifyPruned(FilePath);
129
130 // Remove the timestamp file created by implicit module builds.
131 std::string TimestampFilename = FilePath.str() + ".timestamp";
132 if (!llvm::sys::fs::remove(TimestampFilename))
133 NotifyPruned(TimestampFilename);
134 };
135
136 for (llvm::sys::fs::directory_iterator Dir(RootPath, EC), DirEnd;
137 Dir != DirEnd && !EC; Dir.increment(EC)) {
138 // If we don't have a directory, try to prune it as a file in the root.
139 if (!llvm::sys::fs::is_directory(Dir->path())) {
140 if (PruneTopLevel)
141 TryPruneFile(Dir->path());
142 continue;
143 }
144
145 // Walk all the files within this directory.
146 for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd;
147 File != FileEnd && !EC; File.increment(EC))
148 TryPruneFile(File->path());
149
150 // If we removed all the files in the directory, remove the directory
151 // itself.
152 if (llvm::sys::fs::directory_iterator(Dir->path(), EC) ==
153 llvm::sys::fs::directory_iterator() &&
154 !EC) {
155 if (!llvm::sys::fs::remove(Dir->path()))
156 NotifyPruned(Dir->path());
157 }
158 }
159}
160
161std::error_code clang::writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer,
162 off_t &Size, time_t &ModTime) {
163 StringRef Extension = llvm::sys::path::extension(Path);
164 SmallString<128> ModelPath = StringRef(Path).drop_back(Extension.size());
165 ModelPath += "-%%%%%%%%";
166 ModelPath += Extension;
167 ModelPath += ".tmp";
168
169 std::error_code EC;
170 int FD;
171 SmallString<128> TmpPath;
172 if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath))) {
173 if (EC != std::errc::no_such_file_or_directory)
174 return EC;
175
176 StringRef Dir = llvm::sys::path::parent_path(Path);
177 if (std::error_code InnerEC = llvm::sys::fs::create_directories(Dir))
178 return InnerEC;
179
180 if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath)))
181 return EC;
182 }
183
184 llvm::sys::fs::file_status Status;
185 {
186 llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
187 OS << Buffer.getBuffer();
188 // Using the status from an open file descriptor ensures this is not racy.
189 if ((EC = llvm::sys::fs::status(FD, Status)))
190 return EC;
191 }
192
193 Size = Status.getSize();
194 ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
195
196 // This preserves both size and modification time.
197 if ((EC = llvm::sys::fs::rename(TmpPath, Path)))
198 return EC;
199
200 return {};
201}
202
204clang::readImpl(StringRef FileName, off_t &Size, time_t &ModTime) {
206 llvm::sys::fs::openNativeFileForRead(FileName);
207 if (!FD)
208 return FD.takeError();
209 llvm::scope_exit CloseFD([&FD]() { llvm::sys::fs::closeFile(*FD); });
210 llvm::sys::fs::file_status Status;
211 if (std::error_code EC = llvm::sys::fs::status(*FD, Status))
212 return llvm::errorCodeToError(EC);
213 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf =
214 llvm::MemoryBuffer::getOpenFile(*FD, FileName, Status.getSize(),
215 /*RequiresNullTerminator=*/false);
216 if (!Buf)
217 return llvm::errorCodeToError(Buf.getError());
218 Size = Status.getSize();
219 ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
220 return std::move(*Buf);
221}
222
223namespace {
224
225static AtomicLineLogger NoOpLogger;
226class CrossProcessModuleCache : public ModuleCache {
227 InMemoryModuleCache InMemory;
228
229public:
230 explicit CrossProcessModuleCache()
231 : ModuleCache(NoOpLogger), InMemory(NoOpLogger) {}
232
233 std::unique_ptr<llvm::AdvisoryLock>
234 getLock(StringRef ModuleFilename) override {
235 return std::make_unique<llvm::LockFileManager>(ModuleFilename);
236 }
237
238 std::time_t getModuleTimestamp(StringRef ModuleFilename) override {
239 // This is a compiler-internal input/output, let's bypass the sandbox.
240 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
241
242 std::string TimestampFilename =
244 llvm::sys::fs::file_status Status;
245 if (llvm::sys::fs::status(TimestampFilename, Status) != std::error_code{})
246 return 0;
247 return llvm::sys::toTimeT(Status.getLastModificationTime());
248 }
249
250 void updateModuleTimestamp(StringRef ModuleFilename) override {
251 // This is a compiler-internal input/output, let's bypass the sandbox.
252 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
253
254 // Overwrite the timestamp file contents so that file's mtime changes.
255 std::error_code EC;
256 llvm::raw_fd_ostream OS(
258 llvm::sys::fs::OF_TextWithCRLF);
259 if (EC)
260 return;
261 OS << "Timestamp file\n";
262 OS.close();
263 OS.clear_error(); // Avoid triggering a fatal error.
264 }
265
266 void maybePrune(StringRef Path, time_t PruneInterval,
267 time_t PruneAfter) override {
268 // This is a compiler-internal input/output, let's bypass the sandbox.
269 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
270
271 maybePruneImpl(Path, PruneInterval, PruneAfter);
272 }
273
274 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
275 const InMemoryModuleCache &getInMemoryModuleCache() const override {
276 return InMemory;
277 }
278
279 std::error_code write(StringRef Path, llvm::MemoryBufferRef Buffer,
280 off_t &Size, time_t &ModTime) override {
281 // This is a compiler-internal input/output, let's bypass the sandbox.
282 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
283
284 return writeImpl(Path, Buffer, Size, ModTime);
285 }
286
287 Expected<std::unique_ptr<llvm::MemoryBuffer>>
288 read(StringRef FileName, off_t &Size, time_t &ModTime) override {
289 // This is a compiler-internal input/output, let's bypass the sandbox.
290 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
291
292 return readImpl(FileName, Size, ModTime);
293 }
294};
295} // namespace
296
297std::shared_ptr<ModuleCache> clang::createCrossProcessModuleCache() {
298 return std::make_shared<CrossProcessModuleCache>();
299}
Defines a logger where each line is written atomically to the file.
static void writeTimestampFile(StringRef TimestampFile)
Write a new timestamp file with the given path.
In-memory cache for modules.
The address of an instance of this class represents the identity of a module cache directory.
Definition ModuleCache.h:35
The module cache used for compiling modules implicitly.
Definition ModuleCache.h:39
virtual const ModuleCacheDirectory * getDirectoryPtr(StringRef Path)
Returns an opaque pointer representing the module cache directory.
static std::string getTimestampFilename(StringRef FileName)
Definition ModuleFile.h:188
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
The JSON file list parser is used to communicate input to InstallAPI.
Expected< std::unique_ptr< llvm::MemoryBuffer > > readImpl(StringRef FileName, off_t &Size, time_t &ModTime)
Shared implementation of ModuleCache::read().
std::shared_ptr< ModuleCache > createCrossProcessModuleCache()
Creates new ModuleCache backed by a file system directory that may be operated on by multiple process...
std::error_code writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer, off_t &Size, time_t &ModTime)
Shared implementation of ModuleCache::write().
void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter, bool PruneTopLevel=false, llvm::function_ref< void(StringRef)> OnPrune={})
Shared implementation of ModuleCache::maybePrune().