clang 23.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
13#include "llvm/Support/Error.h"
14#include "llvm/Support/FileSystem.h"
15#include "llvm/Support/IOSandbox.h"
16#include "llvm/Support/LockFileManager.h"
17#include "llvm/Support/Path.h"
18
19using namespace clang;
20
21/// Write a new timestamp file with the given path.
22static void writeTimestampFile(StringRef TimestampFile) {
23 std::error_code EC;
24 llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::OF_None);
25}
26
27void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
28 time_t PruneAfter) {
29 if (PruneInterval <= 0 || PruneAfter <= 0)
30 return;
31
32 // This is a compiler-internal input/output, let's bypass the sandbox.
33 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
34
35 llvm::SmallString<128> TimestampFile(Path);
36 llvm::sys::path::append(TimestampFile, "modules.timestamp");
37
38 // Try to stat() the timestamp file.
39 llvm::sys::fs::file_status StatBuf;
40 if (std::error_code EC = llvm::sys::fs::status(TimestampFile, StatBuf)) {
41 // If the timestamp file wasn't there, create one now.
42 if (EC == std::errc::no_such_file_or_directory)
43 writeTimestampFile(TimestampFile);
44 return;
45 }
46
47 // Check whether the time stamp is older than our pruning interval.
48 // If not, do nothing.
49 time_t TimestampModTime =
50 llvm::sys::toTimeT(StatBuf.getLastModificationTime());
51 time_t CurrentTime = time(nullptr);
52 if (CurrentTime - TimestampModTime <= PruneInterval)
53 return;
54
55 // Write a new timestamp file so that nobody else attempts to prune.
56 // There is a benign race condition here, if two Clang instances happen to
57 // notice at the same time that the timestamp is out-of-date.
58 writeTimestampFile(TimestampFile);
59
60 // Walk the entire module cache, looking for unused module files and module
61 // indices.
62 std::error_code EC;
63 auto TryPruneFile = [&](StringRef FilePath) {
64 // We only care about module and global module index files.
65 StringRef Filename = llvm::sys::path::filename(FilePath);
66 StringRef Extension = llvm::sys::path::extension(FilePath);
67 if (Extension != ".pcm" && Extension != ".timestamp" &&
68 Filename != "modules.idx")
69 return;
70
71 // Don't prune the pruning timestamp file.
72 if (Filename == "modules.timestamp")
73 return;
74
75 // Look at this file. If we can't stat it, there's nothing interesting
76 // there.
77 if (llvm::sys::fs::status(FilePath, StatBuf))
78 return;
79
80 // If the file has been used recently enough, leave it there.
81 time_t FileAccessTime = llvm::sys::toTimeT(StatBuf.getLastAccessedTime());
82 if (CurrentTime - FileAccessTime <= PruneAfter)
83 return;
84
85 // Remove the file.
86 llvm::sys::fs::remove(FilePath);
87
88 // Remove the timestamp file created by implicit module builds.
89 std::string TimestampFilename = FilePath.str() + ".timestamp";
90 llvm::sys::fs::remove(TimestampFilename);
91 };
92
93 for (llvm::sys::fs::directory_iterator Dir(Path, EC), DirEnd;
94 Dir != DirEnd && !EC; Dir.increment(EC)) {
95 // If we don't have a directory, try to prune it as a file in the root.
96 if (!llvm::sys::fs::is_directory(Dir->path())) {
97 TryPruneFile(Dir->path());
98 continue;
99 }
100
101 // Walk all the files within this directory.
102 for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd;
103 File != FileEnd && !EC; File.increment(EC))
104 TryPruneFile(File->path());
105
106 // If we removed all the files in the directory, remove the directory
107 // itself.
108 if (llvm::sys::fs::directory_iterator(Dir->path(), EC) ==
109 llvm::sys::fs::directory_iterator() &&
110 !EC)
111 llvm::sys::fs::remove(Dir->path());
112 }
113}
114
115std::error_code clang::writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer) {
116 StringRef Extension = llvm::sys::path::extension(Path);
117 SmallString<128> ModelPath = StringRef(Path).drop_back(Extension.size());
118 ModelPath += "-%%%%%%%%";
119 ModelPath += Extension;
120 ModelPath += ".tmp";
121
122 std::error_code EC;
123 int FD;
124 SmallString<128> TmpPath;
125 if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath))) {
126 if (EC != std::errc::no_such_file_or_directory)
127 return EC;
128
129 StringRef Dir = llvm::sys::path::parent_path(Path);
130 if (std::error_code InnerEC = llvm::sys::fs::create_directories(Dir))
131 return InnerEC;
132
133 if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath)))
134 return EC;
135 }
136
137 {
138 llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
139 OS << Buffer.getBuffer();
140 }
141
142 if ((EC = llvm::sys::fs::rename(TmpPath, Path)))
143 return EC;
144
145 return {};
146}
147
149clang::readImpl(StringRef FileName, off_t &Size, time_t &ModTime) {
151 llvm::sys::fs::openNativeFileForRead(FileName);
152 if (!FD)
153 return FD.takeError();
154 llvm::sys::fs::file_status Status;
155 if (std::error_code EC = llvm::sys::fs::status(*FD, Status))
156 return llvm::errorCodeToError(EC);
157 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf =
158 llvm::MemoryBuffer::getOpenFile(*FD, FileName, Status.getSize(),
159 /*RequiresNullTerminator=*/false);
160 if (!Buf)
161 return llvm::errorCodeToError(Buf.getError());
162 Size = Status.getSize();
163 ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
164 return std::move(*Buf);
165}
166
167namespace {
168class CrossProcessModuleCache : public ModuleCache {
169 InMemoryModuleCache InMemory;
170
171public:
172 std::unique_ptr<llvm::AdvisoryLock>
173 getLock(StringRef ModuleFilename) override {
174 return std::make_unique<llvm::LockFileManager>(ModuleFilename);
175 }
176
177 std::time_t getModuleTimestamp(StringRef ModuleFilename) override {
178 // This is a compiler-internal input/output, let's bypass the sandbox.
179 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
180
181 std::string TimestampFilename =
183 llvm::sys::fs::file_status Status;
184 if (llvm::sys::fs::status(TimestampFilename, Status) != std::error_code{})
185 return 0;
186 return llvm::sys::toTimeT(Status.getLastModificationTime());
187 }
188
189 void updateModuleTimestamp(StringRef ModuleFilename) override {
190 // This is a compiler-internal input/output, let's bypass the sandbox.
191 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
192
193 // Overwrite the timestamp file contents so that file's mtime changes.
194 std::error_code EC;
195 llvm::raw_fd_ostream OS(
197 llvm::sys::fs::OF_TextWithCRLF);
198 if (EC)
199 return;
200 OS << "Timestamp file\n";
201 OS.close();
202 OS.clear_error(); // Avoid triggering a fatal error.
203 }
204
205 void maybePrune(StringRef Path, time_t PruneInterval,
206 time_t PruneAfter) override {
207 // This is a compiler-internal input/output, let's bypass the sandbox.
208 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
209
210 maybePruneImpl(Path, PruneInterval, PruneAfter);
211 }
212
213 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
214 const InMemoryModuleCache &getInMemoryModuleCache() const override {
215 return InMemory;
216 }
217
218 std::error_code write(StringRef Path, llvm::MemoryBufferRef Buffer) override {
219 // This is a compiler-internal input/output, let's bypass the sandbox.
220 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
221
222 return writeImpl(Path, Buffer);
223 }
224
225 Expected<std::unique_ptr<llvm::MemoryBuffer>>
226 read(StringRef FileName, off_t &Size, time_t &ModTime) override {
227 // This is a compiler-internal input/output, let's bypass the sandbox.
228 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
229
230 return readImpl(FileName, Size, ModTime);
231 }
232};
233} // namespace
234
235std::shared_ptr<ModuleCache> clang::createCrossProcessModuleCache() {
236 return std::make_shared<CrossProcessModuleCache>();
237}
static void writeTimestampFile(StringRef TimestampFile)
Write a new timestamp file with the given path.
In-memory cache for modules.
The module cache used for compiling modules implicitly.
Definition ModuleCache.h:30
static std::string getTimestampFilename(StringRef FileName)
Definition ModuleFile.h:171
@ 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...
void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter)
Shared implementation of ModuleCache::maybePrune().
std::error_code writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer)
Shared implementation of ModuleCache::write().