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/Log.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/FileUtilities.h"
25 using namespace lldb_private
;
29 const char *kModulesSubdir
= ".cache";
30 const char *kLockDirName
= ".lock";
31 const char *kTempFileName
= ".temp";
32 const char *kTempSymFileName
= ".symtemp";
33 const char *kSymFileExtension
= ".sym";
34 const char *kFSIllegalChars
= "\\/:*?\"<>|";
36 std::string
GetEscapedHostname(const char *hostname
) {
37 if (hostname
== nullptr)
39 std::string
result(hostname
);
40 size_t size
= result
.size();
41 for (size_t i
= 0; i
< size
; ++i
) {
42 if ((result
[i
] >= 1 && result
[i
] <= 31) ||
43 strchr(kFSIllegalChars
, result
[i
]) != nullptr)
52 std::unique_ptr
<lldb_private::LockFile
> m_lock
;
56 ModuleLock(const FileSpec
&root_dir_spec
, const UUID
&uuid
, Status
&error
);
60 static FileSpec
JoinPath(const FileSpec
&path1
, const char *path2
) {
61 FileSpec
result_spec(path1
);
62 result_spec
.AppendPathComponent(path2
);
66 static Status
MakeDirectory(const FileSpec
&dir_path
) {
67 namespace fs
= llvm::sys::fs
;
69 return fs::create_directories(dir_path
.GetPath(), true, fs::perms::owner_all
);
72 FileSpec
GetModuleDirectory(const FileSpec
&root_dir_spec
, const UUID
&uuid
) {
73 const auto modules_dir_spec
= JoinPath(root_dir_spec
, kModulesSubdir
);
74 return JoinPath(modules_dir_spec
, uuid
.GetAsString().c_str());
77 FileSpec
GetSymbolFileSpec(const FileSpec
&module_file_spec
) {
78 return FileSpec(module_file_spec
.GetPath() + kSymFileExtension
);
81 void DeleteExistingModule(const FileSpec
&root_dir_spec
,
82 const FileSpec
&sysroot_module_path_spec
) {
83 Log
*log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES
));
87 std::make_shared
<Module
>(ModuleSpec(sysroot_module_path_spec
));
88 module_uuid
= module_sp
->GetUUID();
91 if (!module_uuid
.IsValid())
95 ModuleLock
lock(root_dir_spec
, module_uuid
, error
);
97 LLDB_LOGF(log
, "Failed to lock module %s: %s",
98 module_uuid
.GetAsString().c_str(), error
.AsCString());
101 namespace fs
= llvm::sys::fs
;
103 if (status(sysroot_module_path_spec
.GetPath(), st
))
106 if (st
.getLinkCount() > 2) // module is referred by other hosts.
109 const auto module_spec_dir
= GetModuleDirectory(root_dir_spec
, module_uuid
);
110 llvm::sys::fs::remove_directories(module_spec_dir
.GetPath());
114 void DecrementRefExistingModule(const FileSpec
&root_dir_spec
,
115 const FileSpec
&sysroot_module_path_spec
) {
116 // Remove $platform/.cache/$uuid folder if nobody else references it.
117 DeleteExistingModule(root_dir_spec
, sysroot_module_path_spec
);
119 // Remove sysroot link.
120 llvm::sys::fs::remove(sysroot_module_path_spec
.GetPath());
122 FileSpec symfile_spec
= GetSymbolFileSpec(sysroot_module_path_spec
);
123 llvm::sys::fs::remove(symfile_spec
.GetPath());
126 Status
CreateHostSysRootModuleLink(const FileSpec
&root_dir_spec
,
127 const char *hostname
,
128 const FileSpec
&platform_module_spec
,
129 const FileSpec
&local_module_spec
,
130 bool delete_existing
) {
131 const auto sysroot_module_path_spec
=
132 JoinPath(JoinPath(root_dir_spec
, hostname
),
133 platform_module_spec
.GetPath().c_str());
134 if (FileSystem::Instance().Exists(sysroot_module_path_spec
)) {
135 if (!delete_existing
)
138 DecrementRefExistingModule(root_dir_spec
, sysroot_module_path_spec
);
141 const auto error
= MakeDirectory(
142 FileSpec(sysroot_module_path_spec
.GetDirectory().AsCString()));
146 return llvm::sys::fs::create_hard_link(local_module_spec
.GetPath(),
147 sysroot_module_path_spec
.GetPath());
152 ModuleLock::ModuleLock(const FileSpec
&root_dir_spec
, const UUID
&uuid
,
154 const auto lock_dir_spec
= JoinPath(root_dir_spec
, kLockDirName
);
155 error
= MakeDirectory(lock_dir_spec
);
159 m_file_spec
= JoinPath(lock_dir_spec
, uuid
.GetAsString().c_str());
161 auto file
= FileSystem::Instance().Open(
162 m_file_spec
, File::eOpenOptionWriteOnly
| File::eOpenOptionCanCreate
|
163 File::eOpenOptionCloseOnExec
);
165 m_file_up
= std::move(file
.get());
168 error
= Status(file
.takeError());
172 m_lock
= std::make_unique
<lldb_private::LockFile
>(m_file_up
->GetDescriptor());
173 error
= m_lock
->WriteLock(0, 1);
175 error
.SetErrorStringWithFormat("Failed to lock file: %s",
179 void ModuleLock::Delete() {
185 llvm::sys::fs::remove(m_file_spec
.GetPath());
188 /////////////////////////////////////////////////////////////////////////
190 Status
ModuleCache::Put(const FileSpec
&root_dir_spec
, const char *hostname
,
191 const ModuleSpec
&module_spec
, const FileSpec
&tmp_file
,
192 const FileSpec
&target_file
) {
193 const auto module_spec_dir
=
194 GetModuleDirectory(root_dir_spec
, module_spec
.GetUUID());
195 const auto module_file_path
=
196 JoinPath(module_spec_dir
, target_file
.GetFilename().AsCString());
198 const auto tmp_file_path
= tmp_file
.GetPath();
199 const auto err_code
=
200 llvm::sys::fs::rename(tmp_file_path
, module_file_path
.GetPath());
202 return Status("Failed to rename file %s to %s: %s", tmp_file_path
.c_str(),
203 module_file_path
.GetPath().c_str(),
204 err_code
.message().c_str());
206 const auto error
= CreateHostSysRootModuleLink(
207 root_dir_spec
, hostname
, target_file
, module_file_path
, true);
209 return Status("Failed to create link to %s: %s",
210 module_file_path
.GetPath().c_str(), error
.AsCString());
214 Status
ModuleCache::Get(const FileSpec
&root_dir_spec
, const char *hostname
,
215 const ModuleSpec
&module_spec
,
216 ModuleSP
&cached_module_sp
, bool *did_create_ptr
) {
218 m_loaded_modules
.find(module_spec
.GetUUID().GetAsString());
219 if (find_it
!= m_loaded_modules
.end()) {
220 cached_module_sp
= (*find_it
).second
.lock();
221 if (cached_module_sp
)
223 m_loaded_modules
.erase(find_it
);
226 const auto module_spec_dir
=
227 GetModuleDirectory(root_dir_spec
, module_spec
.GetUUID());
228 const auto module_file_path
= JoinPath(
229 module_spec_dir
, module_spec
.GetFileSpec().GetFilename().AsCString());
231 if (!FileSystem::Instance().Exists(module_file_path
))
232 return Status("Module %s not found", module_file_path
.GetPath().c_str());
233 if (FileSystem::Instance().GetByteSize(module_file_path
) !=
234 module_spec
.GetObjectSize())
235 return Status("Module %s has invalid file size",
236 module_file_path
.GetPath().c_str());
238 // We may have already cached module but downloaded from an another host - in
239 // this case let's create a link to it.
240 auto error
= CreateHostSysRootModuleLink(root_dir_spec
, hostname
,
241 module_spec
.GetFileSpec(),
242 module_file_path
, false);
244 return Status("Failed to create link to %s: %s",
245 module_file_path
.GetPath().c_str(), error
.AsCString());
247 auto cached_module_spec(module_spec
);
248 cached_module_spec
.GetUUID().Clear(); // Clear UUID since it may contain md5
249 // content hash instead of real UUID.
250 cached_module_spec
.GetFileSpec() = module_file_path
;
251 cached_module_spec
.GetPlatformFileSpec() = module_spec
.GetFileSpec();
253 error
= ModuleList::GetSharedModule(cached_module_spec
, cached_module_sp
,
254 nullptr, nullptr, did_create_ptr
, false);
258 FileSpec symfile_spec
= GetSymbolFileSpec(cached_module_sp
->GetFileSpec());
259 if (FileSystem::Instance().Exists(symfile_spec
))
260 cached_module_sp
->SetSymbolFileFileSpec(symfile_spec
);
262 m_loaded_modules
.insert(
263 std::make_pair(module_spec
.GetUUID().GetAsString(), cached_module_sp
));
268 Status
ModuleCache::GetAndPut(const FileSpec
&root_dir_spec
,
269 const char *hostname
,
270 const ModuleSpec
&module_spec
,
271 const ModuleDownloader
&module_downloader
,
272 const SymfileDownloader
&symfile_downloader
,
273 lldb::ModuleSP
&cached_module_sp
,
274 bool *did_create_ptr
) {
275 const auto module_spec_dir
=
276 GetModuleDirectory(root_dir_spec
, module_spec
.GetUUID());
277 auto error
= MakeDirectory(module_spec_dir
);
281 ModuleLock
lock(root_dir_spec
, module_spec
.GetUUID(), error
);
283 return Status("Failed to lock module %s: %s",
284 module_spec
.GetUUID().GetAsString().c_str(),
287 const auto escaped_hostname(GetEscapedHostname(hostname
));
288 // Check local cache for a module.
289 error
= Get(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
290 cached_module_sp
, did_create_ptr
);
294 const auto tmp_download_file_spec
= JoinPath(module_spec_dir
, kTempFileName
);
295 error
= module_downloader(module_spec
, tmp_download_file_spec
);
296 llvm::FileRemover
tmp_file_remover(tmp_download_file_spec
.GetPath());
298 return Status("Failed to download module: %s", error
.AsCString());
300 // Put downloaded file into local module cache.
301 error
= Put(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
302 tmp_download_file_spec
, module_spec
.GetFileSpec());
304 return Status("Failed to put module into cache: %s", error
.AsCString());
306 tmp_file_remover
.releaseFile();
307 error
= Get(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
308 cached_module_sp
, did_create_ptr
);
312 // Fetching a symbol file for the module
313 const auto tmp_download_sym_file_spec
=
314 JoinPath(module_spec_dir
, kTempSymFileName
);
315 error
= symfile_downloader(cached_module_sp
, tmp_download_sym_file_spec
);
316 llvm::FileRemover
tmp_symfile_remover(tmp_download_sym_file_spec
.GetPath());
318 // Failed to download a symfile but fetching the module was successful. The
319 // module might contain the necessary symbols and the debugging is also
320 // possible without a symfile.
323 error
= Put(root_dir_spec
, escaped_hostname
.c_str(), module_spec
,
324 tmp_download_sym_file_spec
,
325 GetSymbolFileSpec(module_spec
.GetFileSpec()));
327 return Status("Failed to put symbol file into cache: %s",
330 tmp_symfile_remover
.releaseFile();
332 FileSpec symfile_spec
= GetSymbolFileSpec(cached_module_sp
->GetFileSpec());
333 cached_module_sp
->SetSymbolFileFileSpec(symfile_spec
);