xref: /openbsd-src/gnu/llvm/lldb/source/Target/ModuleCache.cpp (revision 061da546b983eb767bad15e67af1174fb0bcf31c)
1*061da546Spatrick //===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===//
2*061da546Spatrick //
3*061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*061da546Spatrick //
7*061da546Spatrick //===----------------------------------------------------------------------===//
8*061da546Spatrick 
9*061da546Spatrick #include "lldb/Target/ModuleCache.h"
10*061da546Spatrick 
11*061da546Spatrick #include "lldb/Core/Module.h"
12*061da546Spatrick #include "lldb/Core/ModuleList.h"
13*061da546Spatrick #include "lldb/Core/ModuleSpec.h"
14*061da546Spatrick #include "lldb/Host/File.h"
15*061da546Spatrick #include "lldb/Host/LockFile.h"
16*061da546Spatrick #include "lldb/Utility/Log.h"
17*061da546Spatrick #include "llvm/Support/FileSystem.h"
18*061da546Spatrick #include "llvm/Support/FileUtilities.h"
19*061da546Spatrick 
20*061da546Spatrick #include <assert.h>
21*061da546Spatrick 
22*061da546Spatrick #include <cstdio>
23*061da546Spatrick 
24*061da546Spatrick using namespace lldb;
25*061da546Spatrick using namespace lldb_private;
26*061da546Spatrick 
27*061da546Spatrick namespace {
28*061da546Spatrick 
29*061da546Spatrick const char *kModulesSubdir = ".cache";
30*061da546Spatrick const char *kLockDirName = ".lock";
31*061da546Spatrick const char *kTempFileName = ".temp";
32*061da546Spatrick const char *kTempSymFileName = ".symtemp";
33*061da546Spatrick const char *kSymFileExtension = ".sym";
34*061da546Spatrick const char *kFSIllegalChars = "\\/:*?\"<>|";
35*061da546Spatrick 
36*061da546Spatrick std::string GetEscapedHostname(const char *hostname) {
37*061da546Spatrick   if (hostname == nullptr)
38*061da546Spatrick     hostname = "unknown";
39*061da546Spatrick   std::string result(hostname);
40*061da546Spatrick   size_t size = result.size();
41*061da546Spatrick   for (size_t i = 0; i < size; ++i) {
42*061da546Spatrick     if ((result[i] >= 1 && result[i] <= 31) ||
43*061da546Spatrick         strchr(kFSIllegalChars, result[i]) != nullptr)
44*061da546Spatrick       result[i] = '_';
45*061da546Spatrick   }
46*061da546Spatrick   return result;
47*061da546Spatrick }
48*061da546Spatrick 
49*061da546Spatrick class ModuleLock {
50*061da546Spatrick private:
51*061da546Spatrick   FileUP m_file_up;
52*061da546Spatrick   std::unique_ptr<lldb_private::LockFile> m_lock;
53*061da546Spatrick   FileSpec m_file_spec;
54*061da546Spatrick 
55*061da546Spatrick public:
56*061da546Spatrick   ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error);
57*061da546Spatrick   void Delete();
58*061da546Spatrick };
59*061da546Spatrick 
60*061da546Spatrick static FileSpec JoinPath(const FileSpec &path1, const char *path2) {
61*061da546Spatrick   FileSpec result_spec(path1);
62*061da546Spatrick   result_spec.AppendPathComponent(path2);
63*061da546Spatrick   return result_spec;
64*061da546Spatrick }
65*061da546Spatrick 
66*061da546Spatrick static Status MakeDirectory(const FileSpec &dir_path) {
67*061da546Spatrick   namespace fs = llvm::sys::fs;
68*061da546Spatrick 
69*061da546Spatrick   return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all);
70*061da546Spatrick }
71*061da546Spatrick 
72*061da546Spatrick FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) {
73*061da546Spatrick   const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir);
74*061da546Spatrick   return JoinPath(modules_dir_spec, uuid.GetAsString().c_str());
75*061da546Spatrick }
76*061da546Spatrick 
77*061da546Spatrick FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {
78*061da546Spatrick   return FileSpec(module_file_spec.GetPath() + kSymFileExtension);
79*061da546Spatrick }
80*061da546Spatrick 
81*061da546Spatrick void DeleteExistingModule(const FileSpec &root_dir_spec,
82*061da546Spatrick                           const FileSpec &sysroot_module_path_spec) {
83*061da546Spatrick   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
84*061da546Spatrick   UUID module_uuid;
85*061da546Spatrick   {
86*061da546Spatrick     auto module_sp =
87*061da546Spatrick         std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec));
88*061da546Spatrick     module_uuid = module_sp->GetUUID();
89*061da546Spatrick   }
90*061da546Spatrick 
91*061da546Spatrick   if (!module_uuid.IsValid())
92*061da546Spatrick     return;
93*061da546Spatrick 
94*061da546Spatrick   Status error;
95*061da546Spatrick   ModuleLock lock(root_dir_spec, module_uuid, error);
96*061da546Spatrick   if (error.Fail()) {
97*061da546Spatrick     LLDB_LOGF(log, "Failed to lock module %s: %s",
98*061da546Spatrick               module_uuid.GetAsString().c_str(), error.AsCString());
99*061da546Spatrick   }
100*061da546Spatrick 
101*061da546Spatrick   namespace fs = llvm::sys::fs;
102*061da546Spatrick   fs::file_status st;
103*061da546Spatrick   if (status(sysroot_module_path_spec.GetPath(), st))
104*061da546Spatrick     return;
105*061da546Spatrick 
106*061da546Spatrick   if (st.getLinkCount() > 2) // module is referred by other hosts.
107*061da546Spatrick     return;
108*061da546Spatrick 
109*061da546Spatrick   const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid);
110*061da546Spatrick   llvm::sys::fs::remove_directories(module_spec_dir.GetPath());
111*061da546Spatrick   lock.Delete();
112*061da546Spatrick }
113*061da546Spatrick 
114*061da546Spatrick void DecrementRefExistingModule(const FileSpec &root_dir_spec,
115*061da546Spatrick                                 const FileSpec &sysroot_module_path_spec) {
116*061da546Spatrick   // Remove $platform/.cache/$uuid folder if nobody else references it.
117*061da546Spatrick   DeleteExistingModule(root_dir_spec, sysroot_module_path_spec);
118*061da546Spatrick 
119*061da546Spatrick   // Remove sysroot link.
120*061da546Spatrick   llvm::sys::fs::remove(sysroot_module_path_spec.GetPath());
121*061da546Spatrick 
122*061da546Spatrick   FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec);
123*061da546Spatrick   llvm::sys::fs::remove(symfile_spec.GetPath());
124*061da546Spatrick }
125*061da546Spatrick 
126*061da546Spatrick Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec,
127*061da546Spatrick                                    const char *hostname,
128*061da546Spatrick                                    const FileSpec &platform_module_spec,
129*061da546Spatrick                                    const FileSpec &local_module_spec,
130*061da546Spatrick                                    bool delete_existing) {
131*061da546Spatrick   const auto sysroot_module_path_spec =
132*061da546Spatrick       JoinPath(JoinPath(root_dir_spec, hostname),
133*061da546Spatrick                platform_module_spec.GetPath().c_str());
134*061da546Spatrick   if (FileSystem::Instance().Exists(sysroot_module_path_spec)) {
135*061da546Spatrick     if (!delete_existing)
136*061da546Spatrick       return Status();
137*061da546Spatrick 
138*061da546Spatrick     DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);
139*061da546Spatrick   }
140*061da546Spatrick 
141*061da546Spatrick   const auto error = MakeDirectory(
142*061da546Spatrick       FileSpec(sysroot_module_path_spec.GetDirectory().AsCString()));
143*061da546Spatrick   if (error.Fail())
144*061da546Spatrick     return error;
145*061da546Spatrick 
146*061da546Spatrick   return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(),
147*061da546Spatrick                                          sysroot_module_path_spec.GetPath());
148*061da546Spatrick }
149*061da546Spatrick 
150*061da546Spatrick } // namespace
151*061da546Spatrick 
152*061da546Spatrick ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid,
153*061da546Spatrick                        Status &error) {
154*061da546Spatrick   const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName);
155*061da546Spatrick   error = MakeDirectory(lock_dir_spec);
156*061da546Spatrick   if (error.Fail())
157*061da546Spatrick     return;
158*061da546Spatrick 
159*061da546Spatrick   m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str());
160*061da546Spatrick 
161*061da546Spatrick   auto file = FileSystem::Instance().Open(
162*061da546Spatrick       m_file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate |
163*061da546Spatrick                        File::eOpenOptionCloseOnExec);
164*061da546Spatrick   if (file)
165*061da546Spatrick     m_file_up = std::move(file.get());
166*061da546Spatrick   else {
167*061da546Spatrick     m_file_up.reset();
168*061da546Spatrick     error = Status(file.takeError());
169*061da546Spatrick     return;
170*061da546Spatrick   }
171*061da546Spatrick 
172*061da546Spatrick   m_lock.reset(new lldb_private::LockFile(m_file_up->GetDescriptor()));
173*061da546Spatrick   error = m_lock->WriteLock(0, 1);
174*061da546Spatrick   if (error.Fail())
175*061da546Spatrick     error.SetErrorStringWithFormat("Failed to lock file: %s",
176*061da546Spatrick                                    error.AsCString());
177*061da546Spatrick }
178*061da546Spatrick 
179*061da546Spatrick void ModuleLock::Delete() {
180*061da546Spatrick   if (!m_file_up)
181*061da546Spatrick     return;
182*061da546Spatrick 
183*061da546Spatrick   m_file_up->Close();
184*061da546Spatrick   m_file_up.reset();
185*061da546Spatrick   llvm::sys::fs::remove(m_file_spec.GetPath());
186*061da546Spatrick }
187*061da546Spatrick 
188*061da546Spatrick /////////////////////////////////////////////////////////////////////////
189*061da546Spatrick 
190*061da546Spatrick Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname,
191*061da546Spatrick                         const ModuleSpec &module_spec, const FileSpec &tmp_file,
192*061da546Spatrick                         const FileSpec &target_file) {
193*061da546Spatrick   const auto module_spec_dir =
194*061da546Spatrick       GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
195*061da546Spatrick   const auto module_file_path =
196*061da546Spatrick       JoinPath(module_spec_dir, target_file.GetFilename().AsCString());
197*061da546Spatrick 
198*061da546Spatrick   const auto tmp_file_path = tmp_file.GetPath();
199*061da546Spatrick   const auto err_code =
200*061da546Spatrick       llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());
201*061da546Spatrick   if (err_code)
202*061da546Spatrick     return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(),
203*061da546Spatrick                   module_file_path.GetPath().c_str(),
204*061da546Spatrick                   err_code.message().c_str());
205*061da546Spatrick 
206*061da546Spatrick   const auto error = CreateHostSysRootModuleLink(
207*061da546Spatrick       root_dir_spec, hostname, target_file, module_file_path, true);
208*061da546Spatrick   if (error.Fail())
209*061da546Spatrick     return Status("Failed to create link to %s: %s",
210*061da546Spatrick                   module_file_path.GetPath().c_str(), error.AsCString());
211*061da546Spatrick   return Status();
212*061da546Spatrick }
213*061da546Spatrick 
214*061da546Spatrick Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname,
215*061da546Spatrick                         const ModuleSpec &module_spec,
216*061da546Spatrick                         ModuleSP &cached_module_sp, bool *did_create_ptr) {
217*061da546Spatrick   const auto find_it =
218*061da546Spatrick       m_loaded_modules.find(module_spec.GetUUID().GetAsString());
219*061da546Spatrick   if (find_it != m_loaded_modules.end()) {
220*061da546Spatrick     cached_module_sp = (*find_it).second.lock();
221*061da546Spatrick     if (cached_module_sp)
222*061da546Spatrick       return Status();
223*061da546Spatrick     m_loaded_modules.erase(find_it);
224*061da546Spatrick   }
225*061da546Spatrick 
226*061da546Spatrick   const auto module_spec_dir =
227*061da546Spatrick       GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
228*061da546Spatrick   const auto module_file_path = JoinPath(
229*061da546Spatrick       module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString());
230*061da546Spatrick 
231*061da546Spatrick   if (!FileSystem::Instance().Exists(module_file_path))
232*061da546Spatrick     return Status("Module %s not found", module_file_path.GetPath().c_str());
233*061da546Spatrick   if (FileSystem::Instance().GetByteSize(module_file_path) !=
234*061da546Spatrick       module_spec.GetObjectSize())
235*061da546Spatrick     return Status("Module %s has invalid file size",
236*061da546Spatrick                   module_file_path.GetPath().c_str());
237*061da546Spatrick 
238*061da546Spatrick   // We may have already cached module but downloaded from an another host - in
239*061da546Spatrick   // this case let's create a link to it.
240*061da546Spatrick   auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname,
241*061da546Spatrick                                            module_spec.GetFileSpec(),
242*061da546Spatrick                                            module_file_path, false);
243*061da546Spatrick   if (error.Fail())
244*061da546Spatrick     return Status("Failed to create link to %s: %s",
245*061da546Spatrick                   module_file_path.GetPath().c_str(), error.AsCString());
246*061da546Spatrick 
247*061da546Spatrick   auto cached_module_spec(module_spec);
248*061da546Spatrick   cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5
249*061da546Spatrick                                         // content hash instead of real UUID.
250*061da546Spatrick   cached_module_spec.GetFileSpec() = module_file_path;
251*061da546Spatrick   cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();
252*061da546Spatrick 
253*061da546Spatrick   error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,
254*061da546Spatrick                                       nullptr, nullptr, did_create_ptr, false);
255*061da546Spatrick   if (error.Fail())
256*061da546Spatrick     return error;
257*061da546Spatrick 
258*061da546Spatrick   FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
259*061da546Spatrick   if (FileSystem::Instance().Exists(symfile_spec))
260*061da546Spatrick     cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
261*061da546Spatrick 
262*061da546Spatrick   m_loaded_modules.insert(
263*061da546Spatrick       std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));
264*061da546Spatrick 
265*061da546Spatrick   return Status();
266*061da546Spatrick }
267*061da546Spatrick 
268*061da546Spatrick Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec,
269*061da546Spatrick                               const char *hostname,
270*061da546Spatrick                               const ModuleSpec &module_spec,
271*061da546Spatrick                               const ModuleDownloader &module_downloader,
272*061da546Spatrick                               const SymfileDownloader &symfile_downloader,
273*061da546Spatrick                               lldb::ModuleSP &cached_module_sp,
274*061da546Spatrick                               bool *did_create_ptr) {
275*061da546Spatrick   const auto module_spec_dir =
276*061da546Spatrick       GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
277*061da546Spatrick   auto error = MakeDirectory(module_spec_dir);
278*061da546Spatrick   if (error.Fail())
279*061da546Spatrick     return error;
280*061da546Spatrick 
281*061da546Spatrick   ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error);
282*061da546Spatrick   if (error.Fail())
283*061da546Spatrick     return Status("Failed to lock module %s: %s",
284*061da546Spatrick                   module_spec.GetUUID().GetAsString().c_str(),
285*061da546Spatrick                   error.AsCString());
286*061da546Spatrick 
287*061da546Spatrick   const auto escaped_hostname(GetEscapedHostname(hostname));
288*061da546Spatrick   // Check local cache for a module.
289*061da546Spatrick   error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
290*061da546Spatrick               cached_module_sp, did_create_ptr);
291*061da546Spatrick   if (error.Success())
292*061da546Spatrick     return error;
293*061da546Spatrick 
294*061da546Spatrick   const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);
295*061da546Spatrick   error = module_downloader(module_spec, tmp_download_file_spec);
296*061da546Spatrick   llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());
297*061da546Spatrick   if (error.Fail())
298*061da546Spatrick     return Status("Failed to download module: %s", error.AsCString());
299*061da546Spatrick 
300*061da546Spatrick   // Put downloaded file into local module cache.
301*061da546Spatrick   error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
302*061da546Spatrick               tmp_download_file_spec, module_spec.GetFileSpec());
303*061da546Spatrick   if (error.Fail())
304*061da546Spatrick     return Status("Failed to put module into cache: %s", error.AsCString());
305*061da546Spatrick 
306*061da546Spatrick   tmp_file_remover.releaseFile();
307*061da546Spatrick   error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
308*061da546Spatrick               cached_module_sp, did_create_ptr);
309*061da546Spatrick   if (error.Fail())
310*061da546Spatrick     return error;
311*061da546Spatrick 
312*061da546Spatrick   // Fetching a symbol file for the module
313*061da546Spatrick   const auto tmp_download_sym_file_spec =
314*061da546Spatrick       JoinPath(module_spec_dir, kTempSymFileName);
315*061da546Spatrick   error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec);
316*061da546Spatrick   llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());
317*061da546Spatrick   if (error.Fail())
318*061da546Spatrick     // Failed to download a symfile but fetching the module was successful. The
319*061da546Spatrick     // module might contain the necessary symbols and the debugging is also
320*061da546Spatrick     // possible without a symfile.
321*061da546Spatrick     return Status();
322*061da546Spatrick 
323*061da546Spatrick   error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
324*061da546Spatrick               tmp_download_sym_file_spec,
325*061da546Spatrick               GetSymbolFileSpec(module_spec.GetFileSpec()));
326*061da546Spatrick   if (error.Fail())
327*061da546Spatrick     return Status("Failed to put symbol file into cache: %s",
328*061da546Spatrick                   error.AsCString());
329*061da546Spatrick 
330*061da546Spatrick   tmp_symfile_remover.releaseFile();
331*061da546Spatrick 
332*061da546Spatrick   FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
333*061da546Spatrick   cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
334*061da546Spatrick   return Status();
335*061da546Spatrick }
336