180814287SRaphael Isemann //===-- ModuleCache.cpp ---------------------------------------------------===// 201c3243fSZachary Turner // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 601c3243fSZachary Turner // 701c3243fSZachary Turner //===----------------------------------------------------------------------===// 801c3243fSZachary Turner 901c3243fSZachary Turner #include "lldb/Target/ModuleCache.h" 1001c3243fSZachary Turner 1101c3243fSZachary Turner #include "lldb/Core/Module.h" 1201c3243fSZachary Turner #include "lldb/Core/ModuleList.h" 1301c3243fSZachary Turner #include "lldb/Core/ModuleSpec.h" 1401c3243fSZachary Turner #include "lldb/Host/File.h" 1501c3243fSZachary Turner #include "lldb/Host/LockFile.h" 166f9e6901SZachary Turner #include "lldb/Utility/Log.h" 1701c3243fSZachary Turner #include "llvm/Support/FileSystem.h" 1801c3243fSZachary Turner #include "llvm/Support/FileUtilities.h" 1901c3243fSZachary Turner 2076e47d48SRaphael Isemann #include <cassert> 2101c3243fSZachary Turner 2201c3243fSZachary Turner #include <cstdio> 2301c3243fSZachary Turner 2401c3243fSZachary Turner using namespace lldb; 2501c3243fSZachary Turner using namespace lldb_private; 2601c3243fSZachary Turner 2701c3243fSZachary Turner namespace { 2801c3243fSZachary Turner 2901c3243fSZachary Turner const char *kModulesSubdir = ".cache"; 3001c3243fSZachary Turner const char *kLockDirName = ".lock"; 3101c3243fSZachary Turner const char *kTempFileName = ".temp"; 3201c3243fSZachary Turner const char *kTempSymFileName = ".symtemp"; 3301c3243fSZachary Turner const char *kSymFileExtension = ".sym"; 3401c3243fSZachary Turner const char *kFSIllegalChars = "\\/:*?\"<>|"; 3501c3243fSZachary Turner 3601c3243fSZachary Turner std::string GetEscapedHostname(const char *hostname) { 3701c3243fSZachary Turner if (hostname == nullptr) 3801c3243fSZachary Turner hostname = "unknown"; 3901c3243fSZachary Turner std::string result(hostname); 4001c3243fSZachary Turner size_t size = result.size(); 4101c3243fSZachary Turner for (size_t i = 0; i < size; ++i) { 4201c3243fSZachary Turner if ((result[i] >= 1 && result[i] <= 31) || 4301c3243fSZachary Turner strchr(kFSIllegalChars, result[i]) != nullptr) 4401c3243fSZachary Turner result[i] = '_'; 4501c3243fSZachary Turner } 4601c3243fSZachary Turner return result; 4701c3243fSZachary Turner } 4801c3243fSZachary Turner 4901c3243fSZachary Turner class ModuleLock { 5001c3243fSZachary Turner private: 512fce1137SLawrence D'Anna FileUP m_file_up; 5201c3243fSZachary Turner std::unique_ptr<lldb_private::LockFile> m_lock; 5301c3243fSZachary Turner FileSpec m_file_spec; 5401c3243fSZachary Turner 5501c3243fSZachary Turner public: 5697206d57SZachary Turner ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error); 5701c3243fSZachary Turner void Delete(); 5801c3243fSZachary Turner }; 5901c3243fSZachary Turner 607d86ee5aSZachary Turner static FileSpec JoinPath(const FileSpec &path1, const char *path2) { 6101c3243fSZachary Turner FileSpec result_spec(path1); 6201c3243fSZachary Turner result_spec.AppendPathComponent(path2); 6301c3243fSZachary Turner return result_spec; 6401c3243fSZachary Turner } 6501c3243fSZachary Turner 6697206d57SZachary Turner static Status MakeDirectory(const FileSpec &dir_path) { 677d86ee5aSZachary Turner namespace fs = llvm::sys::fs; 6801c3243fSZachary Turner 697d86ee5aSZachary Turner return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all); 7001c3243fSZachary Turner } 7101c3243fSZachary Turner 7201c3243fSZachary Turner FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { 7301c3243fSZachary Turner const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); 7401c3243fSZachary Turner return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); 7501c3243fSZachary Turner } 7601c3243fSZachary Turner 7701c3243fSZachary Turner FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { 788f3be7a3SJonas Devlieghere return FileSpec(module_file_spec.GetPath() + kSymFileExtension); 7901c3243fSZachary Turner } 8001c3243fSZachary Turner 8101c3243fSZachary Turner void DeleteExistingModule(const FileSpec &root_dir_spec, 8201c3243fSZachary Turner const FileSpec &sysroot_module_path_spec) { 8301c3243fSZachary Turner Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 8401c3243fSZachary Turner UUID module_uuid; 8501c3243fSZachary Turner { 8601c3243fSZachary Turner auto module_sp = 8701c3243fSZachary Turner std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); 8801c3243fSZachary Turner module_uuid = module_sp->GetUUID(); 8901c3243fSZachary Turner } 9001c3243fSZachary Turner 9101c3243fSZachary Turner if (!module_uuid.IsValid()) 9201c3243fSZachary Turner return; 9301c3243fSZachary Turner 9497206d57SZachary Turner Status error; 9501c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_uuid, error); 9601c3243fSZachary Turner if (error.Fail()) { 9763e5fb76SJonas Devlieghere LLDB_LOGF(log, "Failed to lock module %s: %s", 9801c3243fSZachary Turner module_uuid.GetAsString().c_str(), error.AsCString()); 9901c3243fSZachary Turner } 10001c3243fSZachary Turner 10107db3f7eSZachary Turner namespace fs = llvm::sys::fs; 10207db3f7eSZachary Turner fs::file_status st; 10307db3f7eSZachary Turner if (status(sysroot_module_path_spec.GetPath(), st)) 10401c3243fSZachary Turner return; 10501c3243fSZachary Turner 10607db3f7eSZachary Turner if (st.getLinkCount() > 2) // module is referred by other hosts. 10701c3243fSZachary Turner return; 10801c3243fSZachary Turner 10901c3243fSZachary Turner const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); 110d82067f8SZachary Turner llvm::sys::fs::remove_directories(module_spec_dir.GetPath()); 11101c3243fSZachary Turner lock.Delete(); 11201c3243fSZachary Turner } 11301c3243fSZachary Turner 11401c3243fSZachary Turner void DecrementRefExistingModule(const FileSpec &root_dir_spec, 11501c3243fSZachary Turner const FileSpec &sysroot_module_path_spec) { 11601c3243fSZachary Turner // Remove $platform/.cache/$uuid folder if nobody else references it. 11701c3243fSZachary Turner DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); 11801c3243fSZachary Turner 11901c3243fSZachary Turner // Remove sysroot link. 12007db3f7eSZachary Turner llvm::sys::fs::remove(sysroot_module_path_spec.GetPath()); 12101c3243fSZachary Turner 12201c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); 12307db3f7eSZachary Turner llvm::sys::fs::remove(symfile_spec.GetPath()); 12401c3243fSZachary Turner } 12501c3243fSZachary Turner 12697206d57SZachary Turner Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, 12701c3243fSZachary Turner const char *hostname, 12801c3243fSZachary Turner const FileSpec &platform_module_spec, 12901c3243fSZachary Turner const FileSpec &local_module_spec, 13001c3243fSZachary Turner bool delete_existing) { 13101c3243fSZachary Turner const auto sysroot_module_path_spec = 13201c3243fSZachary Turner JoinPath(JoinPath(root_dir_spec, hostname), 13301c3243fSZachary Turner platform_module_spec.GetPath().c_str()); 134dbd7fabaSJonas Devlieghere if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { 13501c3243fSZachary Turner if (!delete_existing) 13697206d57SZachary Turner return Status(); 13701c3243fSZachary Turner 13801c3243fSZachary Turner DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); 13901c3243fSZachary Turner } 14001c3243fSZachary Turner 14101c3243fSZachary Turner const auto error = MakeDirectory( 1428f3be7a3SJonas Devlieghere FileSpec(sysroot_module_path_spec.GetDirectory().AsCString())); 14301c3243fSZachary Turner if (error.Fail()) 14401c3243fSZachary Turner return error; 14501c3243fSZachary Turner 14607db3f7eSZachary Turner return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(), 14707db3f7eSZachary Turner sysroot_module_path_spec.GetPath()); 14801c3243fSZachary Turner } 14901c3243fSZachary Turner 15001c3243fSZachary Turner } // namespace 15101c3243fSZachary Turner 15201c3243fSZachary Turner ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, 15397206d57SZachary Turner Status &error) { 15401c3243fSZachary Turner const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); 15501c3243fSZachary Turner error = MakeDirectory(lock_dir_spec); 15601c3243fSZachary Turner if (error.Fail()) 15701c3243fSZachary Turner return; 15801c3243fSZachary Turner 15901c3243fSZachary Turner m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); 1602fce1137SLawrence D'Anna 1612fce1137SLawrence D'Anna auto file = FileSystem::Instance().Open( 162*14735cabSMichał Górny m_file_spec, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate | 16301c3243fSZachary Turner File::eOpenOptionCloseOnExec); 1642fce1137SLawrence D'Anna if (file) 1652fce1137SLawrence D'Anna m_file_up = std::move(file.get()); 1662fce1137SLawrence D'Anna else { 1672fce1137SLawrence D'Anna m_file_up.reset(); 1682fce1137SLawrence D'Anna error = Status(file.takeError()); 16901c3243fSZachary Turner return; 17001c3243fSZachary Turner } 17101c3243fSZachary Turner 17206412daeSJonas Devlieghere m_lock = std::make_unique<lldb_private::LockFile>(m_file_up->GetDescriptor()); 17301c3243fSZachary Turner error = m_lock->WriteLock(0, 1); 17401c3243fSZachary Turner if (error.Fail()) 17501c3243fSZachary Turner error.SetErrorStringWithFormat("Failed to lock file: %s", 17601c3243fSZachary Turner error.AsCString()); 17701c3243fSZachary Turner } 17801c3243fSZachary Turner 17901c3243fSZachary Turner void ModuleLock::Delete() { 1802fce1137SLawrence D'Anna if (!m_file_up) 18101c3243fSZachary Turner return; 18201c3243fSZachary Turner 1832fce1137SLawrence D'Anna m_file_up->Close(); 1842fce1137SLawrence D'Anna m_file_up.reset(); 18507db3f7eSZachary Turner llvm::sys::fs::remove(m_file_spec.GetPath()); 18601c3243fSZachary Turner } 18701c3243fSZachary Turner 18801c3243fSZachary Turner ///////////////////////////////////////////////////////////////////////// 18901c3243fSZachary Turner 19097206d57SZachary Turner Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 19101c3243fSZachary Turner const ModuleSpec &module_spec, const FileSpec &tmp_file, 19201c3243fSZachary Turner const FileSpec &target_file) { 19301c3243fSZachary Turner const auto module_spec_dir = 19401c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 19501c3243fSZachary Turner const auto module_file_path = 19601c3243fSZachary Turner JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 19701c3243fSZachary Turner 19801c3243fSZachary Turner const auto tmp_file_path = tmp_file.GetPath(); 19901c3243fSZachary Turner const auto err_code = 20001c3243fSZachary Turner llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 20101c3243fSZachary Turner if (err_code) 20297206d57SZachary Turner return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 20301c3243fSZachary Turner module_file_path.GetPath().c_str(), 20401c3243fSZachary Turner err_code.message().c_str()); 20501c3243fSZachary Turner 20601c3243fSZachary Turner const auto error = CreateHostSysRootModuleLink( 20701c3243fSZachary Turner root_dir_spec, hostname, target_file, module_file_path, true); 20801c3243fSZachary Turner if (error.Fail()) 20997206d57SZachary Turner return Status("Failed to create link to %s: %s", 21001c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 21197206d57SZachary Turner return Status(); 21201c3243fSZachary Turner } 21301c3243fSZachary Turner 21497206d57SZachary Turner Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 21501c3243fSZachary Turner const ModuleSpec &module_spec, 21601c3243fSZachary Turner ModuleSP &cached_module_sp, bool *did_create_ptr) { 21701c3243fSZachary Turner const auto find_it = 21801c3243fSZachary Turner m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 21901c3243fSZachary Turner if (find_it != m_loaded_modules.end()) { 22001c3243fSZachary Turner cached_module_sp = (*find_it).second.lock(); 22101c3243fSZachary Turner if (cached_module_sp) 22297206d57SZachary Turner return Status(); 22301c3243fSZachary Turner m_loaded_modules.erase(find_it); 22401c3243fSZachary Turner } 22501c3243fSZachary Turner 22601c3243fSZachary Turner const auto module_spec_dir = 22701c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 22801c3243fSZachary Turner const auto module_file_path = JoinPath( 22901c3243fSZachary Turner module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 23001c3243fSZachary Turner 231dbd7fabaSJonas Devlieghere if (!FileSystem::Instance().Exists(module_file_path)) 23297206d57SZachary Turner return Status("Module %s not found", module_file_path.GetPath().c_str()); 23359b78bcbSJonas Devlieghere if (FileSystem::Instance().GetByteSize(module_file_path) != 23459b78bcbSJonas Devlieghere module_spec.GetObjectSize()) 23597206d57SZachary Turner return Status("Module %s has invalid file size", 23601c3243fSZachary Turner module_file_path.GetPath().c_str()); 23701c3243fSZachary Turner 23801c3243fSZachary Turner // We may have already cached module but downloaded from an another host - in 23901c3243fSZachary Turner // this case let's create a link to it. 24001c3243fSZachary Turner auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 24101c3243fSZachary Turner module_spec.GetFileSpec(), 24201c3243fSZachary Turner module_file_path, false); 24301c3243fSZachary Turner if (error.Fail()) 24497206d57SZachary Turner return Status("Failed to create link to %s: %s", 24501c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 24601c3243fSZachary Turner 24701c3243fSZachary Turner auto cached_module_spec(module_spec); 24801c3243fSZachary Turner cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 24901c3243fSZachary Turner // content hash instead of real UUID. 25001c3243fSZachary Turner cached_module_spec.GetFileSpec() = module_file_path; 25101c3243fSZachary Turner cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 25201c3243fSZachary Turner 25301c3243fSZachary Turner error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 25401c3243fSZachary Turner nullptr, nullptr, did_create_ptr, false); 25501c3243fSZachary Turner if (error.Fail()) 25601c3243fSZachary Turner return error; 25701c3243fSZachary Turner 25801c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 259dbd7fabaSJonas Devlieghere if (FileSystem::Instance().Exists(symfile_spec)) 26001c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 26101c3243fSZachary Turner 26201c3243fSZachary Turner m_loaded_modules.insert( 26301c3243fSZachary Turner std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 26401c3243fSZachary Turner 26597206d57SZachary Turner return Status(); 26601c3243fSZachary Turner } 26701c3243fSZachary Turner 26897206d57SZachary Turner Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 26901c3243fSZachary Turner const char *hostname, 27001c3243fSZachary Turner const ModuleSpec &module_spec, 27101c3243fSZachary Turner const ModuleDownloader &module_downloader, 27201c3243fSZachary Turner const SymfileDownloader &symfile_downloader, 27301c3243fSZachary Turner lldb::ModuleSP &cached_module_sp, 27401c3243fSZachary Turner bool *did_create_ptr) { 27501c3243fSZachary Turner const auto module_spec_dir = 27601c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 27701c3243fSZachary Turner auto error = MakeDirectory(module_spec_dir); 27801c3243fSZachary Turner if (error.Fail()) 27901c3243fSZachary Turner return error; 28001c3243fSZachary Turner 28101c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 28201c3243fSZachary Turner if (error.Fail()) 28397206d57SZachary Turner return Status("Failed to lock module %s: %s", 28401c3243fSZachary Turner module_spec.GetUUID().GetAsString().c_str(), 28501c3243fSZachary Turner error.AsCString()); 28601c3243fSZachary Turner 28701c3243fSZachary Turner const auto escaped_hostname(GetEscapedHostname(hostname)); 28801c3243fSZachary Turner // Check local cache for a module. 28901c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 29001c3243fSZachary Turner cached_module_sp, did_create_ptr); 29101c3243fSZachary Turner if (error.Success()) 29201c3243fSZachary Turner return error; 29301c3243fSZachary Turner 29401c3243fSZachary Turner const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 29501c3243fSZachary Turner error = module_downloader(module_spec, tmp_download_file_spec); 29601c3243fSZachary Turner llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 29701c3243fSZachary Turner if (error.Fail()) 29897206d57SZachary Turner return Status("Failed to download module: %s", error.AsCString()); 29901c3243fSZachary Turner 30001c3243fSZachary Turner // Put downloaded file into local module cache. 30101c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 30201c3243fSZachary Turner tmp_download_file_spec, module_spec.GetFileSpec()); 30301c3243fSZachary Turner if (error.Fail()) 30497206d57SZachary Turner return Status("Failed to put module into cache: %s", error.AsCString()); 30501c3243fSZachary Turner 30601c3243fSZachary Turner tmp_file_remover.releaseFile(); 30701c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 30801c3243fSZachary Turner cached_module_sp, did_create_ptr); 30901c3243fSZachary Turner if (error.Fail()) 31001c3243fSZachary Turner return error; 31101c3243fSZachary Turner 31201c3243fSZachary Turner // Fetching a symbol file for the module 31301c3243fSZachary Turner const auto tmp_download_sym_file_spec = 31401c3243fSZachary Turner JoinPath(module_spec_dir, kTempSymFileName); 31501c3243fSZachary Turner error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 31601c3243fSZachary Turner llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 31701c3243fSZachary Turner if (error.Fail()) 31801c3243fSZachary Turner // Failed to download a symfile but fetching the module was successful. The 31905097246SAdrian Prantl // module might contain the necessary symbols and the debugging is also 32005097246SAdrian Prantl // possible without a symfile. 32197206d57SZachary Turner return Status(); 32201c3243fSZachary Turner 32301c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 32401c3243fSZachary Turner tmp_download_sym_file_spec, 32501c3243fSZachary Turner GetSymbolFileSpec(module_spec.GetFileSpec())); 32601c3243fSZachary Turner if (error.Fail()) 32797206d57SZachary Turner return Status("Failed to put symbol file into cache: %s", 32897206d57SZachary Turner error.AsCString()); 32901c3243fSZachary Turner 33001c3243fSZachary Turner tmp_symfile_remover.releaseFile(); 33101c3243fSZachary Turner 33201c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 33301c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 33497206d57SZachary Turner return Status(); 33501c3243fSZachary Turner } 336