1 //===-- ModuleCache.cpp ---------------------------------------------------===//
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 #include "lldb/Target/ModuleCache.h"
11 #include "lldb/Core/Module.h"
12 #include "lldb/Core/ModuleList.h"
13 #include "lldb/Core/ModuleSpec.h"
14 #include "lldb/Host/File.h"
15 #include "lldb/Host/LockFile.h"
16 #include "lldb/Utility/LLDBLog.h"
17 #include "lldb/Utility/Log.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/FileUtilities.h"
26 using namespace lldb_private
;
30 const char *kModulesSubdir
= ".cache";
31 const char *kLockDirName
= ".lock";
32 const char *kTempFileName
= ".temp";
33 const char *kTempSymFileName
= ".symtemp";
34 const char *kSymFileExtension
= ".sym";
35 const char *kFSIllegalChars
= "\\/:*?\"<>|";
37 std::string
GetEscapedHostname(const char *hostname
) {
38 if (hostname
== nullptr)
40 std::string
result(hostname
);
41 size_t size
= result
.size();
42 for (size_t i
= 0; i
< size
; ++i
) {
43 if ((result
[i
] >= 1 && result
[i
] <= 31) ||
44 strchr(kFSIllegalChars
, result
[i
]) != nullptr)
53 std::unique_ptr
<lldb_private::LockFile
> m_lock
;
57 ModuleLock(const FileSpec
&root_dir_spec
, const UUID
&uuid
, Status
&error
);
61 static FileSpec
JoinPath(const FileSpec
&path1
, const char *path2
) {
62 FileSpec
result_spec(path1
);
63 result_spec
.AppendPathComponent(path2
);
67 static Status
MakeDirectory(const FileSpec
&dir_path
) {
68 namespace fs
= llvm::sys::fs
;
70 return fs::create_directories(dir_path
.GetPath(), true, fs::perms::owner_all
);
73 FileSpec
GetModuleDirectory(const FileSpec
&root_dir_spec
, const UUID
&uuid
) {
74 const auto modules_dir_spec
= JoinPath(root_dir_spec
, kModulesSubdir
);
75 return JoinPath(modules_dir_spec
, uuid
.GetAsString().c_str());
78 FileSpec
GetSymbolFileSpec(const FileSpec
&module_file_spec
) {
79 return FileSpec(module_file_spec
.GetPath() + kSymFileExtension
);
82 void DeleteExistingModule(const FileSpec
&root_dir_spec
,
83 const FileSpec
&sysroot_module_path_spec
) {
84 Log
*log
= GetLog(LLDBLog::Modules
);
88 std::make_shared
<Module
>(ModuleSpec(sysroot_module_path_spec
));
89 module_uuid
= module_sp
->GetUUID();
92 if (!module_uuid
.IsValid())
96 ModuleLock
lock(root_dir_spec
, module_uuid
, error
);
98 LLDB_LOGF(log
, "Failed to lock module %s: %s",
99 module_uuid
.GetAsString().c_str(), error
.AsCString());
102 namespace fs
= llvm::sys::fs
;
104 if (status(sysroot_module_path_spec
.GetPath(), st
))
107 if (st
.getLinkCount() > 2) // module is referred by other hosts.
110 const auto module_spec_dir
= GetModuleDirectory(root_dir_spec
, module_uuid
);
111 llvm::sys::fs::remove_directories(module_spec_dir
.GetPath());
115 void DecrementRefExistingModule(const FileSpec
&root_dir_spec
,
116 const FileSpec
&sysroot_module_path_spec
) {
117 // Remove $platform/.cache/$uuid folder if nobody else references it.
118 DeleteExistingModule(root_dir_spec
, sysroot_module_path_spec
);
120 // Remove sysroot link.
121 llvm::sys::fs::remove(sysroot_module_path_spec
.GetPath());
123 FileSpec symfile_spec
= GetSymbolFileSpec(sysroot_module_path_spec
);
124 llvm::sys::fs::remove(symfile_spec
.GetPath());
127 Status
CreateHostSysRootModuleLink(const FileSpec
&root_dir_spec
,
128 const char *hostname
,
129 const FileSpec
&platform_module_spec
,
130 const FileSpec
&local_module_spec
,
131 bool delete_existing
) {
132 const auto sysroot_module_path_spec
=
133 JoinPath(JoinPath(root_dir_spec
, hostname
),
134 platform_module_spec
.GetPath().c_str());
135 if (FileSystem::Instance().Exists(sysroot_module_path_spec
)) {
136 if (!delete_existing
)
139 DecrementRefExistingModule(root_dir_spec
, sysroot_module_path_spec
);
142 Status error
= MakeDirectory(
143 FileSpec(sysroot_module_path_spec
.GetDirectory().AsCString()));
147 return llvm::sys::fs::create_hard_link(local_module_spec
.GetPath(),
148 sysroot_module_path_spec
.GetPath());
153 ModuleLock::ModuleLock(const FileSpec
&root_dir_spec
, const UUID
&uuid
,
155 const auto lock_dir_spec
= JoinPath(root_dir_spec
, kLockDirName
);
156 error
= MakeDirectory(lock_dir_spec
);
160 m_file_spec
= JoinPath(lock_dir_spec
, uuid
.GetAsString().c_str());
162 auto file
= FileSystem::Instance().Open(
163 m_file_spec
, File::eOpenOptionWriteOnly
| File::eOpenOptionCanCreate
|
164 File::eOpenOptionCloseOnExec
);
166 m_file_up
= std::move(file
.get());
169 error
= Status::FromError(file
.takeError());
173 m_lock
= std::make_unique
<lldb_private::LockFile
>(m_file_up
->GetDescriptor());
174 error
= m_lock
->WriteLock(0, 1);
177 Status::FromErrorStringWithFormatv("Failed to lock file: {0}", error
);
180 void ModuleLock::Delete() {
186 llvm::sys::fs::remove(m_file_spec
.GetPath());
189 /////////////////////////////////////////////////////////////////////////
191 Status
ModuleCache::Put(const FileSpec
&root_dir_spec
, const char *hostname
,
192 const ModuleSpec
&module_spec
, const FileSpec
&tmp_file
,
193 const FileSpec
&target_file
) {
194 const auto module_spec_dir
=
195 GetModuleDirectory(root_dir_spec
, module_spec
.GetUUID());
196 const auto module_file_path
=
197 JoinPath(module_spec_dir
, target_file
.GetFilename().AsCString());
199 const auto tmp_file_path
= tmp_file
.GetPath();
200 const auto err_code
=
201 llvm::sys::fs::rename(tmp_file_path
, module_file_path
.GetPath());
203 return Status::FromErrorStringWithFormat(
204 "Failed to rename file %s to %s: %s", tmp_file_path
.c_str(),
205 module_file_path
.GetPath().c_str(), err_code
.message().c_str());
207 const auto error
= CreateHostSysRootModuleLink(
208 root_dir_spec
, hostname
, target_file
, module_file_path
, true);
210 return Status::FromErrorStringWithFormat("Failed to create link to %s: %s",
211 module_file_path
.GetPath().c_str(),
216 Status
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 (!FileSystem::Instance().Exists(module_file_path
))
234 return Status::FromErrorStringWithFormat(
235 "Module %s not found", module_file_path
.GetPath().c_str());
236 if (FileSystem::Instance().GetByteSize(module_file_path
) !=
237 module_spec
.GetObjectSize())
238 return Status::FromErrorStringWithFormat(
239 "Module %s has invalid file size", module_file_path
.GetPath().c_str());
241 // We may have already cached module but downloaded from an another host - in
242 // this case let's create a link to it.
243 auto error
= CreateHostSysRootModuleLink(root_dir_spec
, hostname
,
244 module_spec
.GetFileSpec(),
245 module_file_path
, false);
247 return Status::FromErrorStringWithFormat("Failed to create link to %s: %s",
248 module_file_path
.GetPath().c_str(),
251 auto cached_module_spec(module_spec
);
252 cached_module_spec
.GetUUID().Clear(); // Clear UUID since it may contain md5
253 // content hash instead of real UUID.
254 cached_module_spec
.GetFileSpec() = module_file_path
;
255 cached_module_spec
.GetPlatformFileSpec() = module_spec
.GetFileSpec();
257 error
= ModuleList::GetSharedModule(cached_module_spec
, cached_module_sp
,
258 nullptr, nullptr, did_create_ptr
, false);
262 FileSpec symfile_spec
= GetSymbolFileSpec(cached_module_sp
->GetFileSpec());
263 if (FileSystem::Instance().Exists(symfile_spec
))
264 cached_module_sp
->SetSymbolFileFileSpec(symfile_spec
);
266 m_loaded_modules
.insert(
267 std::make_pair(module_spec
.GetUUID().GetAsString(), cached_module_sp
));
272 Status
ModuleCache::GetAndPut(const FileSpec
&root_dir_spec
,
273 const char *hostname
,
274 const ModuleSpec
&module_spec
,
275 const ModuleDownloader
&module_downloader
,
276 const SymfileDownloader
&symfile_downloader
,
277 lldb::ModuleSP
&cached_module_sp
,
278 bool *did_create_ptr
) {
279 const auto module_spec_dir
=
280 GetModuleDirectory(root_dir_spec
, module_spec
.GetUUID());
281 auto error
= MakeDirectory(module_spec_dir
);
285 ModuleLock
lock(root_dir_spec
, module_spec
.GetUUID(), error
);
287 return Status::FromErrorStringWithFormat(
288 "Failed to lock module %s: %s",
289 module_spec
.GetUUID().GetAsString().c_str(), error
.AsCString());
291 const auto escaped_hostname(GetEscapedHostname(hostname
));
292 // Check local cache for a module.
293 error
= Get(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
294 cached_module_sp
, did_create_ptr
);
298 const auto tmp_download_file_spec
= JoinPath(module_spec_dir
, kTempFileName
);
299 error
= module_downloader(module_spec
, tmp_download_file_spec
);
300 llvm::FileRemover
tmp_file_remover(tmp_download_file_spec
.GetPath());
302 return Status::FromErrorStringWithFormat("Failed to download module: %s",
305 // Put downloaded file into local module cache.
306 error
= Put(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
307 tmp_download_file_spec
, module_spec
.GetFileSpec());
309 return Status::FromErrorStringWithFormat(
310 "Failed to put module into cache: %s", error
.AsCString());
312 tmp_file_remover
.releaseFile();
313 error
= Get(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
314 cached_module_sp
, did_create_ptr
);
318 // Fetching a symbol file for the module
319 const auto tmp_download_sym_file_spec
=
320 JoinPath(module_spec_dir
, kTempSymFileName
);
321 error
= symfile_downloader(cached_module_sp
, tmp_download_sym_file_spec
);
322 llvm::FileRemover
tmp_symfile_remover(tmp_download_sym_file_spec
.GetPath());
324 // Failed to download a symfile but fetching the module was successful. The
325 // module might contain the necessary symbols and the debugging is also
326 // possible without a symfile.
329 error
= Put(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
330 tmp_download_sym_file_spec
,
331 GetSymbolFileSpec(module_spec
.GetFileSpec()));
333 return Status::FromErrorStringWithFormat(
334 "Failed to put symbol file into cache: %s", error
.AsCString());
336 tmp_symfile_remover
.releaseFile();
338 FileSpec symfile_spec
= GetSymbolFileSpec(cached_module_sp
->GetFileSpec());
339 cached_module_sp
->SetSymbolFileFileSpec(symfile_spec
);