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
GetEscapedHostname(const char * hostname)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:
575517e702SDimitry Andric ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error);
58f678e45dSDimitry Andric void Delete();
59f678e45dSDimitry Andric };
60f678e45dSDimitry Andric
JoinPath(const FileSpec & path1,const char * path2)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
MakeDirectory(const FileSpec & dir_path)675517e702SDimitry Andric static Status 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
GetModuleDirectory(const FileSpec & root_dir_spec,const UUID & uuid)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
GetSymbolFileSpec(const FileSpec & module_file_spec)78f678e45dSDimitry Andric FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {
79*b5893f02SDimitry Andric return FileSpec(module_file_spec.GetPath() + kSymFileExtension);
80f678e45dSDimitry Andric }
81f678e45dSDimitry Andric
DeleteExistingModule(const FileSpec & root_dir_spec,const FileSpec & sysroot_module_path_spec)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
955517e702SDimitry Andric Status 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
DecrementRefExistingModule(const FileSpec & root_dir_spec,const FileSpec & sysroot_module_path_spec)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
CreateHostSysRootModuleLink(const FileSpec & root_dir_spec,const char * hostname,const FileSpec & platform_module_spec,const FileSpec & local_module_spec,bool delete_existing)1285517e702SDimitry Andric Status 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());
136*b5893f02SDimitry Andric if (FileSystem::Instance().Exists(sysroot_module_path_spec)) {
137f678e45dSDimitry Andric if (!delete_existing)
1385517e702SDimitry Andric return Status();
139f678e45dSDimitry Andric
140f678e45dSDimitry Andric DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);
141f678e45dSDimitry Andric }
142f678e45dSDimitry Andric
143f678e45dSDimitry Andric const auto error = MakeDirectory(
144*b5893f02SDimitry Andric FileSpec(sysroot_module_path_spec.GetDirectory().AsCString()));
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
ModuleLock(const FileSpec & root_dir_spec,const UUID & uuid,Status & error)154f678e45dSDimitry Andric ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid,
1555517e702SDimitry Andric Status &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());
162*b5893f02SDimitry Andric FileSystem::Instance().Open(m_file, m_file_spec,
163*b5893f02SDimitry Andric File::eOpenOptionWrite |
164f678e45dSDimitry Andric File::eOpenOptionCanCreate |
165f678e45dSDimitry Andric File::eOpenOptionCloseOnExec);
166f678e45dSDimitry Andric if (!m_file) {
167f678e45dSDimitry Andric error.SetErrorToErrno();
168f678e45dSDimitry Andric return;
169f678e45dSDimitry Andric }
170f678e45dSDimitry Andric
171f678e45dSDimitry Andric m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor()));
172f678e45dSDimitry Andric error = m_lock->WriteLock(0, 1);
173f678e45dSDimitry Andric if (error.Fail())
174f678e45dSDimitry Andric error.SetErrorStringWithFormat("Failed to lock file: %s",
175f678e45dSDimitry Andric error.AsCString());
176f678e45dSDimitry Andric }
177f678e45dSDimitry Andric
Delete()178f678e45dSDimitry Andric void ModuleLock::Delete() {
179f678e45dSDimitry Andric if (!m_file)
180f678e45dSDimitry Andric return;
181f678e45dSDimitry Andric
182f678e45dSDimitry Andric m_file.Close();
183f678e45dSDimitry Andric llvm::sys::fs::remove(m_file_spec.GetPath());
184f678e45dSDimitry Andric }
185f678e45dSDimitry Andric
186f678e45dSDimitry Andric /////////////////////////////////////////////////////////////////////////
187f678e45dSDimitry Andric
Put(const FileSpec & root_dir_spec,const char * hostname,const ModuleSpec & module_spec,const FileSpec & tmp_file,const FileSpec & target_file)1885517e702SDimitry Andric Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname,
189f678e45dSDimitry Andric const ModuleSpec &module_spec, const FileSpec &tmp_file,
190f678e45dSDimitry Andric const FileSpec &target_file) {
191f678e45dSDimitry Andric const auto module_spec_dir =
192f678e45dSDimitry Andric GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
193f678e45dSDimitry Andric const auto module_file_path =
194f678e45dSDimitry Andric JoinPath(module_spec_dir, target_file.GetFilename().AsCString());
195f678e45dSDimitry Andric
196f678e45dSDimitry Andric const auto tmp_file_path = tmp_file.GetPath();
197f678e45dSDimitry Andric const auto err_code =
198f678e45dSDimitry Andric llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());
199f678e45dSDimitry Andric if (err_code)
2005517e702SDimitry Andric return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(),
201f678e45dSDimitry Andric module_file_path.GetPath().c_str(),
202f678e45dSDimitry Andric err_code.message().c_str());
203f678e45dSDimitry Andric
204f678e45dSDimitry Andric const auto error = CreateHostSysRootModuleLink(
205f678e45dSDimitry Andric root_dir_spec, hostname, target_file, module_file_path, true);
206f678e45dSDimitry Andric if (error.Fail())
2075517e702SDimitry Andric return Status("Failed to create link to %s: %s",
208f678e45dSDimitry Andric module_file_path.GetPath().c_str(), error.AsCString());
2095517e702SDimitry Andric return Status();
210f678e45dSDimitry Andric }
211f678e45dSDimitry Andric
Get(const FileSpec & root_dir_spec,const char * hostname,const ModuleSpec & module_spec,ModuleSP & cached_module_sp,bool * did_create_ptr)2125517e702SDimitry Andric Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname,
213f678e45dSDimitry Andric const ModuleSpec &module_spec,
214f678e45dSDimitry Andric ModuleSP &cached_module_sp, bool *did_create_ptr) {
215f678e45dSDimitry Andric const auto find_it =
216f678e45dSDimitry Andric m_loaded_modules.find(module_spec.GetUUID().GetAsString());
217f678e45dSDimitry Andric if (find_it != m_loaded_modules.end()) {
218f678e45dSDimitry Andric cached_module_sp = (*find_it).second.lock();
219f678e45dSDimitry Andric if (cached_module_sp)
2205517e702SDimitry Andric return Status();
221f678e45dSDimitry Andric m_loaded_modules.erase(find_it);
222f678e45dSDimitry Andric }
223f678e45dSDimitry Andric
224f678e45dSDimitry Andric const auto module_spec_dir =
225f678e45dSDimitry Andric GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
226f678e45dSDimitry Andric const auto module_file_path = JoinPath(
227f678e45dSDimitry Andric module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString());
228f678e45dSDimitry Andric
229*b5893f02SDimitry Andric if (!FileSystem::Instance().Exists(module_file_path))
2305517e702SDimitry Andric return Status("Module %s not found", module_file_path.GetPath().c_str());
231*b5893f02SDimitry Andric if (FileSystem::Instance().GetByteSize(module_file_path) !=
232*b5893f02SDimitry Andric module_spec.GetObjectSize())
2335517e702SDimitry Andric return Status("Module %s has invalid file size",
234f678e45dSDimitry Andric module_file_path.GetPath().c_str());
235f678e45dSDimitry Andric
236f678e45dSDimitry Andric // We may have already cached module but downloaded from an another host - in
237f678e45dSDimitry Andric // this case let's create a link to it.
238f678e45dSDimitry Andric auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname,
239f678e45dSDimitry Andric module_spec.GetFileSpec(),
240f678e45dSDimitry Andric module_file_path, false);
241f678e45dSDimitry Andric if (error.Fail())
2425517e702SDimitry Andric return Status("Failed to create link to %s: %s",
243f678e45dSDimitry Andric module_file_path.GetPath().c_str(), error.AsCString());
244f678e45dSDimitry Andric
245f678e45dSDimitry Andric auto cached_module_spec(module_spec);
246f678e45dSDimitry Andric cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5
247f678e45dSDimitry Andric // content hash instead of real UUID.
248f678e45dSDimitry Andric cached_module_spec.GetFileSpec() = module_file_path;
249f678e45dSDimitry Andric cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();
250f678e45dSDimitry Andric
251f678e45dSDimitry Andric error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,
252f678e45dSDimitry Andric nullptr, nullptr, did_create_ptr, false);
253f678e45dSDimitry Andric if (error.Fail())
254f678e45dSDimitry Andric return error;
255f678e45dSDimitry Andric
256f678e45dSDimitry Andric FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
257*b5893f02SDimitry Andric if (FileSystem::Instance().Exists(symfile_spec))
258f678e45dSDimitry Andric cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
259f678e45dSDimitry Andric
260f678e45dSDimitry Andric m_loaded_modules.insert(
261f678e45dSDimitry Andric std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));
262f678e45dSDimitry Andric
2635517e702SDimitry Andric return Status();
264f678e45dSDimitry Andric }
265f678e45dSDimitry Andric
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)2665517e702SDimitry Andric Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec,
267f678e45dSDimitry Andric const char *hostname,
268f678e45dSDimitry Andric const ModuleSpec &module_spec,
269f678e45dSDimitry Andric const ModuleDownloader &module_downloader,
270f678e45dSDimitry Andric const SymfileDownloader &symfile_downloader,
271f678e45dSDimitry Andric lldb::ModuleSP &cached_module_sp,
272f678e45dSDimitry Andric bool *did_create_ptr) {
273f678e45dSDimitry Andric const auto module_spec_dir =
274f678e45dSDimitry Andric GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
275f678e45dSDimitry Andric auto error = MakeDirectory(module_spec_dir);
276f678e45dSDimitry Andric if (error.Fail())
277f678e45dSDimitry Andric return error;
278f678e45dSDimitry Andric
279f678e45dSDimitry Andric ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error);
280f678e45dSDimitry Andric if (error.Fail())
2815517e702SDimitry Andric return Status("Failed to lock module %s: %s",
282f678e45dSDimitry Andric module_spec.GetUUID().GetAsString().c_str(),
283f678e45dSDimitry Andric error.AsCString());
284f678e45dSDimitry Andric
285f678e45dSDimitry Andric const auto escaped_hostname(GetEscapedHostname(hostname));
286f678e45dSDimitry Andric // Check local cache for a module.
287f678e45dSDimitry Andric error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
288f678e45dSDimitry Andric cached_module_sp, did_create_ptr);
289f678e45dSDimitry Andric if (error.Success())
290f678e45dSDimitry Andric return error;
291f678e45dSDimitry Andric
292f678e45dSDimitry Andric const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);
293f678e45dSDimitry Andric error = module_downloader(module_spec, tmp_download_file_spec);
294f678e45dSDimitry Andric llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());
295f678e45dSDimitry Andric if (error.Fail())
2965517e702SDimitry Andric return Status("Failed to download module: %s", error.AsCString());
297f678e45dSDimitry Andric
298f678e45dSDimitry Andric // Put downloaded file into local module cache.
299f678e45dSDimitry Andric error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
300f678e45dSDimitry Andric tmp_download_file_spec, module_spec.GetFileSpec());
301f678e45dSDimitry Andric if (error.Fail())
3025517e702SDimitry Andric return Status("Failed to put module into cache: %s", error.AsCString());
303f678e45dSDimitry Andric
304f678e45dSDimitry Andric tmp_file_remover.releaseFile();
305f678e45dSDimitry Andric error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
306f678e45dSDimitry Andric cached_module_sp, did_create_ptr);
307f678e45dSDimitry Andric if (error.Fail())
308f678e45dSDimitry Andric return error;
309f678e45dSDimitry Andric
310f678e45dSDimitry Andric // Fetching a symbol file for the module
311f678e45dSDimitry Andric const auto tmp_download_sym_file_spec =
312f678e45dSDimitry Andric JoinPath(module_spec_dir, kTempSymFileName);
313f678e45dSDimitry Andric error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec);
314f678e45dSDimitry Andric llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());
315f678e45dSDimitry Andric if (error.Fail())
316f678e45dSDimitry Andric // Failed to download a symfile but fetching the module was successful. The
3174ba319b5SDimitry Andric // module might contain the necessary symbols and the debugging is also
3184ba319b5SDimitry Andric // possible without a symfile.
3195517e702SDimitry Andric return Status();
320f678e45dSDimitry Andric
321f678e45dSDimitry Andric error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
322f678e45dSDimitry Andric tmp_download_sym_file_spec,
323f678e45dSDimitry Andric GetSymbolFileSpec(module_spec.GetFileSpec()));
324f678e45dSDimitry Andric if (error.Fail())
3255517e702SDimitry Andric return Status("Failed to put symbol file into cache: %s",
3265517e702SDimitry Andric error.AsCString());
327f678e45dSDimitry Andric
328f678e45dSDimitry Andric tmp_symfile_remover.releaseFile();
329f678e45dSDimitry Andric
330f678e45dSDimitry Andric FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
331f678e45dSDimitry Andric cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
3325517e702SDimitry Andric return Status();
333f678e45dSDimitry Andric }
334