1 //===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "ModuleCache.h"
12 #include "lldb/Core/Log.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/ModuleList.h"
15 #include "lldb/Core/ModuleSpec.h"
16 #include "lldb/Host/File.h"
17 #include "lldb/Host/FileSystem.h"
18 #include "lldb/Host/LockFile.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/FileUtilities.h"
27 using namespace lldb_private;
31 const char *kModulesSubdir = ".cache";
32 const char *kLockDirName = ".lock";
33 const char *kTempFileName = ".temp";
34 const char *kTempSymFileName = ".symtemp";
35 const char *kSymFileExtension = ".sym";
36 const char *kFSIllegalChars = "\\/:*?\"<>|";
38 std::string GetEscapedHostname(const char *hostname) {
39 if (hostname == nullptr)
41 std::string result(hostname);
42 size_t size = result.size();
43 for (size_t i = 0; i < size; ++i) {
44 if ((result[i] >= 1 && result[i] <= 31) ||
45 strchr(kFSIllegalChars, result[i]) != nullptr)
54 std::unique_ptr<lldb_private::LockFile> m_lock;
58 ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Error &error);
62 FileSpec JoinPath(const FileSpec &path1, const char *path2) {
63 FileSpec result_spec(path1);
64 result_spec.AppendPathComponent(path2);
68 Error MakeDirectory(const FileSpec &dir_path) {
69 if (dir_path.Exists()) {
70 if (!dir_path.IsDirectory())
71 return Error("Invalid existing path");
76 return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault);
79 FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) {
80 const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir);
81 return JoinPath(modules_dir_spec, uuid.GetAsString().c_str());
84 FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {
85 return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false);
88 void DeleteExistingModule(const FileSpec &root_dir_spec,
89 const FileSpec &sysroot_module_path_spec) {
90 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
94 std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec));
95 module_uuid = module_sp->GetUUID();
98 if (!module_uuid.IsValid())
102 ModuleLock lock(root_dir_spec, module_uuid, error);
105 log->Printf("Failed to lock module %s: %s",
106 module_uuid.GetAsString().c_str(), error.AsCString());
109 auto link_count = FileSystem::GetHardlinkCount(sysroot_module_path_spec);
110 if (link_count == -1)
113 if (link_count > 2) // module is referred by other hosts.
116 const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid);
117 FileSystem::DeleteDirectory(module_spec_dir, true);
121 void DecrementRefExistingModule(const FileSpec &root_dir_spec,
122 const FileSpec &sysroot_module_path_spec) {
123 // Remove $platform/.cache/$uuid folder if nobody else references it.
124 DeleteExistingModule(root_dir_spec, sysroot_module_path_spec);
126 // Remove sysroot link.
127 FileSystem::Unlink(sysroot_module_path_spec);
129 FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec);
130 if (symfile_spec.Exists()) // delete module's symbol file if exists.
131 FileSystem::Unlink(symfile_spec);
134 Error CreateHostSysRootModuleLink(const FileSpec &root_dir_spec,
135 const char *hostname,
136 const FileSpec &platform_module_spec,
137 const FileSpec &local_module_spec,
138 bool delete_existing) {
139 const auto sysroot_module_path_spec =
140 JoinPath(JoinPath(root_dir_spec, hostname),
141 platform_module_spec.GetPath().c_str());
142 if (sysroot_module_path_spec.Exists()) {
143 if (!delete_existing)
146 DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);
149 const auto error = MakeDirectory(
150 FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false));
154 return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec);
159 ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid,
161 const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName);
162 error = MakeDirectory(lock_dir_spec);
166 m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str());
167 m_file.Open(m_file_spec.GetCString(), File::eOpenOptionWrite |
168 File::eOpenOptionCanCreate |
169 File::eOpenOptionCloseOnExec);
171 error.SetErrorToErrno();
175 m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor()));
176 error = m_lock->WriteLock(0, 1);
178 error.SetErrorStringWithFormat("Failed to lock file: %s",
182 void ModuleLock::Delete() {
187 FileSystem::Unlink(m_file_spec);
190 /////////////////////////////////////////////////////////////////////////
192 Error ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname,
193 const ModuleSpec &module_spec, const FileSpec &tmp_file,
194 const FileSpec &target_file) {
195 const auto module_spec_dir =
196 GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
197 const auto module_file_path =
198 JoinPath(module_spec_dir, target_file.GetFilename().AsCString());
200 const auto tmp_file_path = tmp_file.GetPath();
201 const auto err_code =
202 llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());
204 return Error("Failed to rename file %s to %s: %s", tmp_file_path.c_str(),
205 module_file_path.GetPath().c_str(),
206 err_code.message().c_str());
208 const auto error = CreateHostSysRootModuleLink(
209 root_dir_spec, hostname, target_file, module_file_path, true);
211 return Error("Failed to create link to %s: %s",
212 module_file_path.GetPath().c_str(), error.AsCString());
216 Error ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname,
217 const ModuleSpec &module_spec,
218 ModuleSP &cached_module_sp, bool *did_create_ptr) {
220 m_loaded_modules.find(module_spec.GetUUID().GetAsString());
221 if (find_it != m_loaded_modules.end()) {
222 cached_module_sp = (*find_it).second.lock();
223 if (cached_module_sp)
225 m_loaded_modules.erase(find_it);
228 const auto module_spec_dir =
229 GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
230 const auto module_file_path = JoinPath(
231 module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString());
233 if (!module_file_path.Exists())
234 return Error("Module %s not found", module_file_path.GetPath().c_str());
235 if (module_file_path.GetByteSize() != module_spec.GetObjectSize())
236 return Error("Module %s has invalid file size",
237 module_file_path.GetPath().c_str());
239 // We may have already cached module but downloaded from an another host - in
240 // this case let's create a link to it.
241 auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname,
242 module_spec.GetFileSpec(),
243 module_file_path, false);
245 return Error("Failed to create link to %s: %s",
246 module_file_path.GetPath().c_str(), error.AsCString());
248 auto cached_module_spec(module_spec);
249 cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5
250 // content hash instead of real UUID.
251 cached_module_spec.GetFileSpec() = module_file_path;
252 cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();
254 error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,
255 nullptr, nullptr, did_create_ptr, false);
259 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
260 if (symfile_spec.Exists())
261 cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
263 m_loaded_modules.insert(
264 std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));
269 Error ModuleCache::GetAndPut(const FileSpec &root_dir_spec,
270 const char *hostname,
271 const ModuleSpec &module_spec,
272 const ModuleDownloader &module_downloader,
273 const SymfileDownloader &symfile_downloader,
274 lldb::ModuleSP &cached_module_sp,
275 bool *did_create_ptr) {
276 const auto module_spec_dir =
277 GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
278 auto error = MakeDirectory(module_spec_dir);
282 ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error);
284 return Error("Failed to lock module %s: %s",
285 module_spec.GetUUID().GetAsString().c_str(),
288 const auto escaped_hostname(GetEscapedHostname(hostname));
289 // Check local cache for a module.
290 error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
291 cached_module_sp, did_create_ptr);
295 const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);
296 error = module_downloader(module_spec, tmp_download_file_spec);
297 llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());
299 return Error("Failed to download module: %s", error.AsCString());
301 // Put downloaded file into local module cache.
302 error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
303 tmp_download_file_spec, module_spec.GetFileSpec());
305 return Error("Failed to put module into cache: %s", error.AsCString());
307 tmp_file_remover.releaseFile();
308 error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
309 cached_module_sp, did_create_ptr);
313 // Fetching a symbol file for the module
314 const auto tmp_download_sym_file_spec =
315 JoinPath(module_spec_dir, kTempSymFileName);
316 error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec);
317 llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());
319 // Failed to download a symfile but fetching the module was successful. The
321 // contain the necessary symbols and the debugging is also possible without
325 error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
326 tmp_download_sym_file_spec,
327 GetSymbolFileSpec(module_spec.GetFileSpec()));
329 return Error("Failed to put symbol file into cache: %s", error.AsCString());
331 tmp_symfile_remover.releaseFile();
333 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
334 cached_module_sp->SetSymbolFileFileSpec(symfile_spec);