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