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