1dda28197Spatrick //===-- ModuleCache.cpp ---------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "lldb/Target/ModuleCache.h"
10061da546Spatrick
11061da546Spatrick #include "lldb/Core/Module.h"
12061da546Spatrick #include "lldb/Core/ModuleList.h"
13061da546Spatrick #include "lldb/Core/ModuleSpec.h"
14061da546Spatrick #include "lldb/Host/File.h"
15061da546Spatrick #include "lldb/Host/LockFile.h"
16*f6aab3d8Srobert #include "lldb/Utility/LLDBLog.h"
17061da546Spatrick #include "lldb/Utility/Log.h"
18061da546Spatrick #include "llvm/Support/FileSystem.h"
19061da546Spatrick #include "llvm/Support/FileUtilities.h"
20061da546Spatrick
21be691f3bSpatrick #include <cassert>
22061da546Spatrick
23061da546Spatrick #include <cstdio>
24061da546Spatrick
25061da546Spatrick using namespace lldb;
26061da546Spatrick using namespace lldb_private;
27061da546Spatrick
28061da546Spatrick namespace {
29061da546Spatrick
30061da546Spatrick const char *kModulesSubdir = ".cache";
31061da546Spatrick const char *kLockDirName = ".lock";
32061da546Spatrick const char *kTempFileName = ".temp";
33061da546Spatrick const char *kTempSymFileName = ".symtemp";
34061da546Spatrick const char *kSymFileExtension = ".sym";
35061da546Spatrick const char *kFSIllegalChars = "\\/:*?\"<>|";
36061da546Spatrick
GetEscapedHostname(const char * hostname)37061da546Spatrick std::string GetEscapedHostname(const char *hostname) {
38061da546Spatrick if (hostname == nullptr)
39061da546Spatrick hostname = "unknown";
40061da546Spatrick std::string result(hostname);
41061da546Spatrick size_t size = result.size();
42061da546Spatrick for (size_t i = 0; i < size; ++i) {
43061da546Spatrick if ((result[i] >= 1 && result[i] <= 31) ||
44061da546Spatrick strchr(kFSIllegalChars, result[i]) != nullptr)
45061da546Spatrick result[i] = '_';
46061da546Spatrick }
47061da546Spatrick return result;
48061da546Spatrick }
49061da546Spatrick
50061da546Spatrick class ModuleLock {
51061da546Spatrick private:
52061da546Spatrick FileUP m_file_up;
53061da546Spatrick std::unique_ptr<lldb_private::LockFile> m_lock;
54061da546Spatrick FileSpec m_file_spec;
55061da546Spatrick
56061da546Spatrick public:
57061da546Spatrick ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error);
58061da546Spatrick void Delete();
59061da546Spatrick };
60061da546Spatrick
JoinPath(const FileSpec & path1,const char * path2)61061da546Spatrick static FileSpec JoinPath(const FileSpec &path1, const char *path2) {
62061da546Spatrick FileSpec result_spec(path1);
63061da546Spatrick result_spec.AppendPathComponent(path2);
64061da546Spatrick return result_spec;
65061da546Spatrick }
66061da546Spatrick
MakeDirectory(const FileSpec & dir_path)67061da546Spatrick static Status MakeDirectory(const FileSpec &dir_path) {
68061da546Spatrick namespace fs = llvm::sys::fs;
69061da546Spatrick
70061da546Spatrick return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all);
71061da546Spatrick }
72061da546Spatrick
GetModuleDirectory(const FileSpec & root_dir_spec,const UUID & uuid)73061da546Spatrick FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) {
74061da546Spatrick const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir);
75061da546Spatrick return JoinPath(modules_dir_spec, uuid.GetAsString().c_str());
76061da546Spatrick }
77061da546Spatrick
GetSymbolFileSpec(const FileSpec & module_file_spec)78061da546Spatrick FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {
79061da546Spatrick return FileSpec(module_file_spec.GetPath() + kSymFileExtension);
80061da546Spatrick }
81061da546Spatrick
DeleteExistingModule(const FileSpec & root_dir_spec,const FileSpec & sysroot_module_path_spec)82061da546Spatrick void DeleteExistingModule(const FileSpec &root_dir_spec,
83061da546Spatrick const FileSpec &sysroot_module_path_spec) {
84*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Modules);
85061da546Spatrick UUID module_uuid;
86061da546Spatrick {
87061da546Spatrick auto module_sp =
88061da546Spatrick std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec));
89061da546Spatrick module_uuid = module_sp->GetUUID();
90061da546Spatrick }
91061da546Spatrick
92061da546Spatrick if (!module_uuid.IsValid())
93061da546Spatrick return;
94061da546Spatrick
95061da546Spatrick Status error;
96061da546Spatrick ModuleLock lock(root_dir_spec, module_uuid, error);
97061da546Spatrick if (error.Fail()) {
98061da546Spatrick LLDB_LOGF(log, "Failed to lock module %s: %s",
99061da546Spatrick module_uuid.GetAsString().c_str(), error.AsCString());
100061da546Spatrick }
101061da546Spatrick
102061da546Spatrick namespace fs = llvm::sys::fs;
103061da546Spatrick fs::file_status st;
104061da546Spatrick if (status(sysroot_module_path_spec.GetPath(), st))
105061da546Spatrick return;
106061da546Spatrick
107061da546Spatrick if (st.getLinkCount() > 2) // module is referred by other hosts.
108061da546Spatrick return;
109061da546Spatrick
110061da546Spatrick const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid);
111061da546Spatrick llvm::sys::fs::remove_directories(module_spec_dir.GetPath());
112061da546Spatrick lock.Delete();
113061da546Spatrick }
114061da546Spatrick
DecrementRefExistingModule(const FileSpec & root_dir_spec,const FileSpec & sysroot_module_path_spec)115061da546Spatrick void DecrementRefExistingModule(const FileSpec &root_dir_spec,
116061da546Spatrick const FileSpec &sysroot_module_path_spec) {
117061da546Spatrick // Remove $platform/.cache/$uuid folder if nobody else references it.
118061da546Spatrick DeleteExistingModule(root_dir_spec, sysroot_module_path_spec);
119061da546Spatrick
120061da546Spatrick // Remove sysroot link.
121061da546Spatrick llvm::sys::fs::remove(sysroot_module_path_spec.GetPath());
122061da546Spatrick
123061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec);
124061da546Spatrick llvm::sys::fs::remove(symfile_spec.GetPath());
125061da546Spatrick }
126061da546Spatrick
CreateHostSysRootModuleLink(const FileSpec & root_dir_spec,const char * hostname,const FileSpec & platform_module_spec,const FileSpec & local_module_spec,bool delete_existing)127061da546Spatrick Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec,
128061da546Spatrick const char *hostname,
129061da546Spatrick const FileSpec &platform_module_spec,
130061da546Spatrick const FileSpec &local_module_spec,
131061da546Spatrick bool delete_existing) {
132061da546Spatrick const auto sysroot_module_path_spec =
133061da546Spatrick JoinPath(JoinPath(root_dir_spec, hostname),
134061da546Spatrick platform_module_spec.GetPath().c_str());
135061da546Spatrick if (FileSystem::Instance().Exists(sysroot_module_path_spec)) {
136061da546Spatrick if (!delete_existing)
137061da546Spatrick return Status();
138061da546Spatrick
139061da546Spatrick DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);
140061da546Spatrick }
141061da546Spatrick
142061da546Spatrick const auto error = MakeDirectory(
143061da546Spatrick FileSpec(sysroot_module_path_spec.GetDirectory().AsCString()));
144061da546Spatrick if (error.Fail())
145061da546Spatrick return error;
146061da546Spatrick
147061da546Spatrick return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(),
148061da546Spatrick sysroot_module_path_spec.GetPath());
149061da546Spatrick }
150061da546Spatrick
151061da546Spatrick } // namespace
152061da546Spatrick
ModuleLock(const FileSpec & root_dir_spec,const UUID & uuid,Status & error)153061da546Spatrick ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid,
154061da546Spatrick Status &error) {
155061da546Spatrick const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName);
156061da546Spatrick error = MakeDirectory(lock_dir_spec);
157061da546Spatrick if (error.Fail())
158061da546Spatrick return;
159061da546Spatrick
160061da546Spatrick m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str());
161061da546Spatrick
162061da546Spatrick auto file = FileSystem::Instance().Open(
163*f6aab3d8Srobert m_file_spec, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate |
164061da546Spatrick File::eOpenOptionCloseOnExec);
165061da546Spatrick if (file)
166061da546Spatrick m_file_up = std::move(file.get());
167061da546Spatrick else {
168061da546Spatrick m_file_up.reset();
169061da546Spatrick error = Status(file.takeError());
170061da546Spatrick return;
171061da546Spatrick }
172061da546Spatrick
173dda28197Spatrick m_lock = std::make_unique<lldb_private::LockFile>(m_file_up->GetDescriptor());
174061da546Spatrick error = m_lock->WriteLock(0, 1);
175061da546Spatrick if (error.Fail())
176061da546Spatrick error.SetErrorStringWithFormat("Failed to lock file: %s",
177061da546Spatrick error.AsCString());
178061da546Spatrick }
179061da546Spatrick
Delete()180061da546Spatrick void ModuleLock::Delete() {
181061da546Spatrick if (!m_file_up)
182061da546Spatrick return;
183061da546Spatrick
184061da546Spatrick m_file_up->Close();
185061da546Spatrick m_file_up.reset();
186061da546Spatrick llvm::sys::fs::remove(m_file_spec.GetPath());
187061da546Spatrick }
188061da546Spatrick
189061da546Spatrick /////////////////////////////////////////////////////////////////////////
190061da546Spatrick
Put(const FileSpec & root_dir_spec,const char * hostname,const ModuleSpec & module_spec,const FileSpec & tmp_file,const FileSpec & target_file)191061da546Spatrick Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname,
192061da546Spatrick const ModuleSpec &module_spec, const FileSpec &tmp_file,
193061da546Spatrick const FileSpec &target_file) {
194061da546Spatrick const auto module_spec_dir =
195061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
196061da546Spatrick const auto module_file_path =
197061da546Spatrick JoinPath(module_spec_dir, target_file.GetFilename().AsCString());
198061da546Spatrick
199061da546Spatrick const auto tmp_file_path = tmp_file.GetPath();
200061da546Spatrick const auto err_code =
201061da546Spatrick llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());
202061da546Spatrick if (err_code)
203061da546Spatrick return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(),
204061da546Spatrick module_file_path.GetPath().c_str(),
205061da546Spatrick err_code.message().c_str());
206061da546Spatrick
207061da546Spatrick const auto error = CreateHostSysRootModuleLink(
208061da546Spatrick root_dir_spec, hostname, target_file, module_file_path, true);
209061da546Spatrick if (error.Fail())
210061da546Spatrick return Status("Failed to create link to %s: %s",
211061da546Spatrick module_file_path.GetPath().c_str(), error.AsCString());
212061da546Spatrick return Status();
213061da546Spatrick }
214061da546Spatrick
Get(const FileSpec & root_dir_spec,const char * hostname,const ModuleSpec & module_spec,ModuleSP & cached_module_sp,bool * did_create_ptr)215061da546Spatrick Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname,
216061da546Spatrick const ModuleSpec &module_spec,
217061da546Spatrick ModuleSP &cached_module_sp, bool *did_create_ptr) {
218061da546Spatrick const auto find_it =
219061da546Spatrick m_loaded_modules.find(module_spec.GetUUID().GetAsString());
220061da546Spatrick if (find_it != m_loaded_modules.end()) {
221061da546Spatrick cached_module_sp = (*find_it).second.lock();
222061da546Spatrick if (cached_module_sp)
223061da546Spatrick return Status();
224061da546Spatrick m_loaded_modules.erase(find_it);
225061da546Spatrick }
226061da546Spatrick
227061da546Spatrick const auto module_spec_dir =
228061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
229061da546Spatrick const auto module_file_path = JoinPath(
230061da546Spatrick module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString());
231061da546Spatrick
232061da546Spatrick if (!FileSystem::Instance().Exists(module_file_path))
233061da546Spatrick return Status("Module %s not found", module_file_path.GetPath().c_str());
234061da546Spatrick if (FileSystem::Instance().GetByteSize(module_file_path) !=
235061da546Spatrick module_spec.GetObjectSize())
236061da546Spatrick return Status("Module %s has invalid file size",
237061da546Spatrick module_file_path.GetPath().c_str());
238061da546Spatrick
239061da546Spatrick // We may have already cached module but downloaded from an another host - in
240061da546Spatrick // this case let's create a link to it.
241061da546Spatrick auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname,
242061da546Spatrick module_spec.GetFileSpec(),
243061da546Spatrick module_file_path, false);
244061da546Spatrick if (error.Fail())
245061da546Spatrick return Status("Failed to create link to %s: %s",
246061da546Spatrick module_file_path.GetPath().c_str(), error.AsCString());
247061da546Spatrick
248061da546Spatrick auto cached_module_spec(module_spec);
249061da546Spatrick cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5
250061da546Spatrick // content hash instead of real UUID.
251061da546Spatrick cached_module_spec.GetFileSpec() = module_file_path;
252061da546Spatrick cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();
253061da546Spatrick
254061da546Spatrick error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,
255061da546Spatrick nullptr, nullptr, did_create_ptr, false);
256061da546Spatrick if (error.Fail())
257061da546Spatrick return error;
258061da546Spatrick
259061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
260061da546Spatrick if (FileSystem::Instance().Exists(symfile_spec))
261061da546Spatrick cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
262061da546Spatrick
263061da546Spatrick m_loaded_modules.insert(
264061da546Spatrick std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));
265061da546Spatrick
266061da546Spatrick return Status();
267061da546Spatrick }
268061da546Spatrick
GetAndPut(const FileSpec & root_dir_spec,const char * hostname,const ModuleSpec & module_spec,const ModuleDownloader & module_downloader,const SymfileDownloader & symfile_downloader,lldb::ModuleSP & cached_module_sp,bool * did_create_ptr)269061da546Spatrick Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec,
270061da546Spatrick const char *hostname,
271061da546Spatrick const ModuleSpec &module_spec,
272061da546Spatrick const ModuleDownloader &module_downloader,
273061da546Spatrick const SymfileDownloader &symfile_downloader,
274061da546Spatrick lldb::ModuleSP &cached_module_sp,
275061da546Spatrick bool *did_create_ptr) {
276061da546Spatrick const auto module_spec_dir =
277061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
278061da546Spatrick auto error = MakeDirectory(module_spec_dir);
279061da546Spatrick if (error.Fail())
280061da546Spatrick return error;
281061da546Spatrick
282061da546Spatrick ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error);
283061da546Spatrick if (error.Fail())
284061da546Spatrick return Status("Failed to lock module %s: %s",
285061da546Spatrick module_spec.GetUUID().GetAsString().c_str(),
286061da546Spatrick error.AsCString());
287061da546Spatrick
288061da546Spatrick const auto escaped_hostname(GetEscapedHostname(hostname));
289061da546Spatrick // Check local cache for a module.
290061da546Spatrick error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
291061da546Spatrick cached_module_sp, did_create_ptr);
292061da546Spatrick if (error.Success())
293061da546Spatrick return error;
294061da546Spatrick
295061da546Spatrick const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);
296061da546Spatrick error = module_downloader(module_spec, tmp_download_file_spec);
297061da546Spatrick llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());
298061da546Spatrick if (error.Fail())
299061da546Spatrick return Status("Failed to download module: %s", error.AsCString());
300061da546Spatrick
301061da546Spatrick // Put downloaded file into local module cache.
302061da546Spatrick error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
303061da546Spatrick tmp_download_file_spec, module_spec.GetFileSpec());
304061da546Spatrick if (error.Fail())
305061da546Spatrick return Status("Failed to put module into cache: %s", error.AsCString());
306061da546Spatrick
307061da546Spatrick tmp_file_remover.releaseFile();
308061da546Spatrick error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
309061da546Spatrick cached_module_sp, did_create_ptr);
310061da546Spatrick if (error.Fail())
311061da546Spatrick return error;
312061da546Spatrick
313061da546Spatrick // Fetching a symbol file for the module
314061da546Spatrick const auto tmp_download_sym_file_spec =
315061da546Spatrick JoinPath(module_spec_dir, kTempSymFileName);
316061da546Spatrick error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec);
317061da546Spatrick llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());
318061da546Spatrick if (error.Fail())
319061da546Spatrick // Failed to download a symfile but fetching the module was successful. The
320061da546Spatrick // module might contain the necessary symbols and the debugging is also
321061da546Spatrick // possible without a symfile.
322061da546Spatrick return Status();
323061da546Spatrick
324061da546Spatrick error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
325061da546Spatrick tmp_download_sym_file_spec,
326061da546Spatrick GetSymbolFileSpec(module_spec.GetFileSpec()));
327061da546Spatrick if (error.Fail())
328061da546Spatrick return Status("Failed to put symbol file into cache: %s",
329061da546Spatrick error.AsCString());
330061da546Spatrick
331061da546Spatrick tmp_symfile_remover.releaseFile();
332061da546Spatrick
333061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
334061da546Spatrick cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
335061da546Spatrick return Status();
336061da546Spatrick }
337