101c3243fSZachary Turner //===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===// 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 2001c3243fSZachary Turner #include <assert.h> 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: 5101c3243fSZachary Turner File m_file; 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()) { 97*63e5fb76SJonas 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()); 16050bc1ed2SJonas Devlieghere FileSystem::Instance().Open(m_file, m_file_spec, 16150bc1ed2SJonas Devlieghere File::eOpenOptionWrite | 16201c3243fSZachary Turner File::eOpenOptionCanCreate | 16301c3243fSZachary Turner File::eOpenOptionCloseOnExec); 16401c3243fSZachary Turner if (!m_file) { 16501c3243fSZachary Turner error.SetErrorToErrno(); 16601c3243fSZachary Turner return; 16701c3243fSZachary Turner } 16801c3243fSZachary Turner 16901c3243fSZachary Turner m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor())); 17001c3243fSZachary Turner error = m_lock->WriteLock(0, 1); 17101c3243fSZachary Turner if (error.Fail()) 17201c3243fSZachary Turner error.SetErrorStringWithFormat("Failed to lock file: %s", 17301c3243fSZachary Turner error.AsCString()); 17401c3243fSZachary Turner } 17501c3243fSZachary Turner 17601c3243fSZachary Turner void ModuleLock::Delete() { 17701c3243fSZachary Turner if (!m_file) 17801c3243fSZachary Turner return; 17901c3243fSZachary Turner 18001c3243fSZachary Turner m_file.Close(); 18107db3f7eSZachary Turner llvm::sys::fs::remove(m_file_spec.GetPath()); 18201c3243fSZachary Turner } 18301c3243fSZachary Turner 18401c3243fSZachary Turner ///////////////////////////////////////////////////////////////////////// 18501c3243fSZachary Turner 18697206d57SZachary Turner Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 18701c3243fSZachary Turner const ModuleSpec &module_spec, const FileSpec &tmp_file, 18801c3243fSZachary Turner const FileSpec &target_file) { 18901c3243fSZachary Turner const auto module_spec_dir = 19001c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 19101c3243fSZachary Turner const auto module_file_path = 19201c3243fSZachary Turner JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 19301c3243fSZachary Turner 19401c3243fSZachary Turner const auto tmp_file_path = tmp_file.GetPath(); 19501c3243fSZachary Turner const auto err_code = 19601c3243fSZachary Turner llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 19701c3243fSZachary Turner if (err_code) 19897206d57SZachary Turner return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 19901c3243fSZachary Turner module_file_path.GetPath().c_str(), 20001c3243fSZachary Turner err_code.message().c_str()); 20101c3243fSZachary Turner 20201c3243fSZachary Turner const auto error = CreateHostSysRootModuleLink( 20301c3243fSZachary Turner root_dir_spec, hostname, target_file, module_file_path, true); 20401c3243fSZachary Turner if (error.Fail()) 20597206d57SZachary Turner return Status("Failed to create link to %s: %s", 20601c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 20797206d57SZachary Turner return Status(); 20801c3243fSZachary Turner } 20901c3243fSZachary Turner 21097206d57SZachary Turner Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 21101c3243fSZachary Turner const ModuleSpec &module_spec, 21201c3243fSZachary Turner ModuleSP &cached_module_sp, bool *did_create_ptr) { 21301c3243fSZachary Turner const auto find_it = 21401c3243fSZachary Turner m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 21501c3243fSZachary Turner if (find_it != m_loaded_modules.end()) { 21601c3243fSZachary Turner cached_module_sp = (*find_it).second.lock(); 21701c3243fSZachary Turner if (cached_module_sp) 21897206d57SZachary Turner return Status(); 21901c3243fSZachary Turner m_loaded_modules.erase(find_it); 22001c3243fSZachary Turner } 22101c3243fSZachary Turner 22201c3243fSZachary Turner const auto module_spec_dir = 22301c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 22401c3243fSZachary Turner const auto module_file_path = JoinPath( 22501c3243fSZachary Turner module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 22601c3243fSZachary Turner 227dbd7fabaSJonas Devlieghere if (!FileSystem::Instance().Exists(module_file_path)) 22897206d57SZachary Turner return Status("Module %s not found", module_file_path.GetPath().c_str()); 22959b78bcbSJonas Devlieghere if (FileSystem::Instance().GetByteSize(module_file_path) != 23059b78bcbSJonas Devlieghere module_spec.GetObjectSize()) 23197206d57SZachary Turner return Status("Module %s has invalid file size", 23201c3243fSZachary Turner module_file_path.GetPath().c_str()); 23301c3243fSZachary Turner 23401c3243fSZachary Turner // We may have already cached module but downloaded from an another host - in 23501c3243fSZachary Turner // this case let's create a link to it. 23601c3243fSZachary Turner auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 23701c3243fSZachary Turner module_spec.GetFileSpec(), 23801c3243fSZachary Turner module_file_path, false); 23901c3243fSZachary Turner if (error.Fail()) 24097206d57SZachary Turner return Status("Failed to create link to %s: %s", 24101c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 24201c3243fSZachary Turner 24301c3243fSZachary Turner auto cached_module_spec(module_spec); 24401c3243fSZachary Turner cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 24501c3243fSZachary Turner // content hash instead of real UUID. 24601c3243fSZachary Turner cached_module_spec.GetFileSpec() = module_file_path; 24701c3243fSZachary Turner cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 24801c3243fSZachary Turner 24901c3243fSZachary Turner error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 25001c3243fSZachary Turner nullptr, nullptr, did_create_ptr, false); 25101c3243fSZachary Turner if (error.Fail()) 25201c3243fSZachary Turner return error; 25301c3243fSZachary Turner 25401c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 255dbd7fabaSJonas Devlieghere if (FileSystem::Instance().Exists(symfile_spec)) 25601c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 25701c3243fSZachary Turner 25801c3243fSZachary Turner m_loaded_modules.insert( 25901c3243fSZachary Turner std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 26001c3243fSZachary Turner 26197206d57SZachary Turner return Status(); 26201c3243fSZachary Turner } 26301c3243fSZachary Turner 26497206d57SZachary Turner Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 26501c3243fSZachary Turner const char *hostname, 26601c3243fSZachary Turner const ModuleSpec &module_spec, 26701c3243fSZachary Turner const ModuleDownloader &module_downloader, 26801c3243fSZachary Turner const SymfileDownloader &symfile_downloader, 26901c3243fSZachary Turner lldb::ModuleSP &cached_module_sp, 27001c3243fSZachary Turner bool *did_create_ptr) { 27101c3243fSZachary Turner const auto module_spec_dir = 27201c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 27301c3243fSZachary Turner auto error = MakeDirectory(module_spec_dir); 27401c3243fSZachary Turner if (error.Fail()) 27501c3243fSZachary Turner return error; 27601c3243fSZachary Turner 27701c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 27801c3243fSZachary Turner if (error.Fail()) 27997206d57SZachary Turner return Status("Failed to lock module %s: %s", 28001c3243fSZachary Turner module_spec.GetUUID().GetAsString().c_str(), 28101c3243fSZachary Turner error.AsCString()); 28201c3243fSZachary Turner 28301c3243fSZachary Turner const auto escaped_hostname(GetEscapedHostname(hostname)); 28401c3243fSZachary Turner // Check local cache for a module. 28501c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 28601c3243fSZachary Turner cached_module_sp, did_create_ptr); 28701c3243fSZachary Turner if (error.Success()) 28801c3243fSZachary Turner return error; 28901c3243fSZachary Turner 29001c3243fSZachary Turner const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 29101c3243fSZachary Turner error = module_downloader(module_spec, tmp_download_file_spec); 29201c3243fSZachary Turner llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 29301c3243fSZachary Turner if (error.Fail()) 29497206d57SZachary Turner return Status("Failed to download module: %s", error.AsCString()); 29501c3243fSZachary Turner 29601c3243fSZachary Turner // Put downloaded file into local module cache. 29701c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 29801c3243fSZachary Turner tmp_download_file_spec, module_spec.GetFileSpec()); 29901c3243fSZachary Turner if (error.Fail()) 30097206d57SZachary Turner return Status("Failed to put module into cache: %s", error.AsCString()); 30101c3243fSZachary Turner 30201c3243fSZachary Turner tmp_file_remover.releaseFile(); 30301c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 30401c3243fSZachary Turner cached_module_sp, did_create_ptr); 30501c3243fSZachary Turner if (error.Fail()) 30601c3243fSZachary Turner return error; 30701c3243fSZachary Turner 30801c3243fSZachary Turner // Fetching a symbol file for the module 30901c3243fSZachary Turner const auto tmp_download_sym_file_spec = 31001c3243fSZachary Turner JoinPath(module_spec_dir, kTempSymFileName); 31101c3243fSZachary Turner error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 31201c3243fSZachary Turner llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 31301c3243fSZachary Turner if (error.Fail()) 31401c3243fSZachary Turner // Failed to download a symfile but fetching the module was successful. The 31505097246SAdrian Prantl // module might contain the necessary symbols and the debugging is also 31605097246SAdrian Prantl // possible without a symfile. 31797206d57SZachary Turner return Status(); 31801c3243fSZachary Turner 31901c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 32001c3243fSZachary Turner tmp_download_sym_file_spec, 32101c3243fSZachary Turner GetSymbolFileSpec(module_spec.GetFileSpec())); 32201c3243fSZachary Turner if (error.Fail()) 32397206d57SZachary Turner return Status("Failed to put symbol file into cache: %s", 32497206d57SZachary Turner error.AsCString()); 32501c3243fSZachary Turner 32601c3243fSZachary Turner tmp_symfile_remover.releaseFile(); 32701c3243fSZachary Turner 32801c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 32901c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 33097206d57SZachary Turner return Status(); 33101c3243fSZachary Turner } 332