xref: /openbsd-src/gnu/llvm/lldb/source/Target/ModuleCache.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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