101c3243fSZachary Turner //===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===// 201c3243fSZachary Turner // 301c3243fSZachary Turner // The LLVM Compiler Infrastructure 401c3243fSZachary Turner // 501c3243fSZachary Turner // This file is distributed under the University of Illinois Open Source 601c3243fSZachary Turner // License. See LICENSE.TXT for details. 701c3243fSZachary Turner // 801c3243fSZachary Turner //===----------------------------------------------------------------------===// 901c3243fSZachary Turner 1001c3243fSZachary Turner #include "lldb/Target/ModuleCache.h" 1101c3243fSZachary Turner 1201c3243fSZachary Turner #include "lldb/Core/Module.h" 1301c3243fSZachary Turner #include "lldb/Core/ModuleList.h" 1401c3243fSZachary Turner #include "lldb/Core/ModuleSpec.h" 1501c3243fSZachary Turner #include "lldb/Host/File.h" 1601c3243fSZachary Turner #include "lldb/Host/LockFile.h" 176f9e6901SZachary Turner #include "lldb/Utility/Log.h" 1801c3243fSZachary Turner #include "llvm/Support/FileSystem.h" 1901c3243fSZachary Turner #include "llvm/Support/FileUtilities.h" 2001c3243fSZachary Turner 2101c3243fSZachary Turner #include <assert.h> 2201c3243fSZachary Turner 2301c3243fSZachary Turner #include <cstdio> 2401c3243fSZachary Turner 2501c3243fSZachary Turner using namespace lldb; 2601c3243fSZachary Turner using namespace lldb_private; 2701c3243fSZachary Turner 2801c3243fSZachary Turner namespace { 2901c3243fSZachary Turner 3001c3243fSZachary Turner const char *kModulesSubdir = ".cache"; 3101c3243fSZachary Turner const char *kLockDirName = ".lock"; 3201c3243fSZachary Turner const char *kTempFileName = ".temp"; 3301c3243fSZachary Turner const char *kTempSymFileName = ".symtemp"; 3401c3243fSZachary Turner const char *kSymFileExtension = ".sym"; 3501c3243fSZachary Turner const char *kFSIllegalChars = "\\/:*?\"<>|"; 3601c3243fSZachary Turner 3701c3243fSZachary Turner std::string GetEscapedHostname(const char *hostname) { 3801c3243fSZachary Turner if (hostname == nullptr) 3901c3243fSZachary Turner hostname = "unknown"; 4001c3243fSZachary Turner std::string result(hostname); 4101c3243fSZachary Turner size_t size = result.size(); 4201c3243fSZachary Turner for (size_t i = 0; i < size; ++i) { 4301c3243fSZachary Turner if ((result[i] >= 1 && result[i] <= 31) || 4401c3243fSZachary Turner strchr(kFSIllegalChars, result[i]) != nullptr) 4501c3243fSZachary Turner result[i] = '_'; 4601c3243fSZachary Turner } 4701c3243fSZachary Turner return result; 4801c3243fSZachary Turner } 4901c3243fSZachary Turner 5001c3243fSZachary Turner class ModuleLock { 5101c3243fSZachary Turner private: 5201c3243fSZachary Turner File m_file; 5301c3243fSZachary Turner std::unique_ptr<lldb_private::LockFile> m_lock; 5401c3243fSZachary Turner FileSpec m_file_spec; 5501c3243fSZachary Turner 5601c3243fSZachary Turner public: 5797206d57SZachary Turner ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error); 5801c3243fSZachary Turner void Delete(); 5901c3243fSZachary Turner }; 6001c3243fSZachary Turner 617d86ee5aSZachary Turner static FileSpec JoinPath(const FileSpec &path1, const char *path2) { 6201c3243fSZachary Turner FileSpec result_spec(path1); 6301c3243fSZachary Turner result_spec.AppendPathComponent(path2); 6401c3243fSZachary Turner return result_spec; 6501c3243fSZachary Turner } 6601c3243fSZachary Turner 6797206d57SZachary Turner static Status MakeDirectory(const FileSpec &dir_path) { 687d86ee5aSZachary Turner namespace fs = llvm::sys::fs; 6901c3243fSZachary Turner 707d86ee5aSZachary Turner return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all); 7101c3243fSZachary Turner } 7201c3243fSZachary Turner 7301c3243fSZachary Turner FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { 7401c3243fSZachary Turner const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); 7501c3243fSZachary Turner return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); 7601c3243fSZachary Turner } 7701c3243fSZachary Turner 7801c3243fSZachary Turner FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { 7901c3243fSZachary Turner return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false); 8001c3243fSZachary Turner } 8101c3243fSZachary Turner 8201c3243fSZachary Turner void DeleteExistingModule(const FileSpec &root_dir_spec, 8301c3243fSZachary Turner const FileSpec &sysroot_module_path_spec) { 8401c3243fSZachary Turner Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 8501c3243fSZachary Turner UUID module_uuid; 8601c3243fSZachary Turner { 8701c3243fSZachary Turner auto module_sp = 8801c3243fSZachary Turner std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); 8901c3243fSZachary Turner module_uuid = module_sp->GetUUID(); 9001c3243fSZachary Turner } 9101c3243fSZachary Turner 9201c3243fSZachary Turner if (!module_uuid.IsValid()) 9301c3243fSZachary Turner return; 9401c3243fSZachary Turner 9597206d57SZachary Turner Status error; 9601c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_uuid, error); 9701c3243fSZachary Turner if (error.Fail()) { 9801c3243fSZachary Turner if (log) 9901c3243fSZachary Turner log->Printf("Failed to lock module %s: %s", 10001c3243fSZachary Turner module_uuid.GetAsString().c_str(), error.AsCString()); 10101c3243fSZachary Turner } 10201c3243fSZachary Turner 10307db3f7eSZachary Turner namespace fs = llvm::sys::fs; 10407db3f7eSZachary Turner fs::file_status st; 10507db3f7eSZachary Turner if (status(sysroot_module_path_spec.GetPath(), st)) 10601c3243fSZachary Turner return; 10701c3243fSZachary Turner 10807db3f7eSZachary Turner if (st.getLinkCount() > 2) // module is referred by other hosts. 10901c3243fSZachary Turner return; 11001c3243fSZachary Turner 11101c3243fSZachary Turner const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); 112d82067f8SZachary Turner llvm::sys::fs::remove_directories(module_spec_dir.GetPath()); 11301c3243fSZachary Turner lock.Delete(); 11401c3243fSZachary Turner } 11501c3243fSZachary Turner 11601c3243fSZachary Turner void DecrementRefExistingModule(const FileSpec &root_dir_spec, 11701c3243fSZachary Turner const FileSpec &sysroot_module_path_spec) { 11801c3243fSZachary Turner // Remove $platform/.cache/$uuid folder if nobody else references it. 11901c3243fSZachary Turner DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); 12001c3243fSZachary Turner 12101c3243fSZachary Turner // Remove sysroot link. 12207db3f7eSZachary Turner llvm::sys::fs::remove(sysroot_module_path_spec.GetPath()); 12301c3243fSZachary Turner 12401c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); 12507db3f7eSZachary Turner llvm::sys::fs::remove(symfile_spec.GetPath()); 12601c3243fSZachary Turner } 12701c3243fSZachary Turner 12897206d57SZachary Turner Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, 12901c3243fSZachary Turner const char *hostname, 13001c3243fSZachary Turner const FileSpec &platform_module_spec, 13101c3243fSZachary Turner const FileSpec &local_module_spec, 13201c3243fSZachary Turner bool delete_existing) { 13301c3243fSZachary Turner const auto sysroot_module_path_spec = 13401c3243fSZachary Turner JoinPath(JoinPath(root_dir_spec, hostname), 13501c3243fSZachary Turner platform_module_spec.GetPath().c_str()); 136*dbd7fabaSJonas Devlieghere if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { 13701c3243fSZachary Turner if (!delete_existing) 13897206d57SZachary Turner return Status(); 13901c3243fSZachary Turner 14001c3243fSZachary Turner DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); 14101c3243fSZachary Turner } 14201c3243fSZachary Turner 14301c3243fSZachary Turner const auto error = MakeDirectory( 14401c3243fSZachary Turner FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false)); 14501c3243fSZachary Turner if (error.Fail()) 14601c3243fSZachary Turner return error; 14701c3243fSZachary Turner 14807db3f7eSZachary Turner return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(), 14907db3f7eSZachary Turner sysroot_module_path_spec.GetPath()); 15001c3243fSZachary Turner } 15101c3243fSZachary Turner 15201c3243fSZachary Turner } // namespace 15301c3243fSZachary Turner 15401c3243fSZachary Turner ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, 15597206d57SZachary Turner Status &error) { 15601c3243fSZachary Turner const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); 15701c3243fSZachary Turner error = MakeDirectory(lock_dir_spec); 15801c3243fSZachary Turner if (error.Fail()) 15901c3243fSZachary Turner return; 16001c3243fSZachary Turner 16101c3243fSZachary Turner m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); 16201c3243fSZachary Turner m_file.Open(m_file_spec.GetCString(), File::eOpenOptionWrite | 16301c3243fSZachary Turner File::eOpenOptionCanCreate | 16401c3243fSZachary Turner File::eOpenOptionCloseOnExec); 16501c3243fSZachary Turner if (!m_file) { 16601c3243fSZachary Turner error.SetErrorToErrno(); 16701c3243fSZachary Turner return; 16801c3243fSZachary Turner } 16901c3243fSZachary Turner 17001c3243fSZachary Turner m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor())); 17101c3243fSZachary Turner error = m_lock->WriteLock(0, 1); 17201c3243fSZachary Turner if (error.Fail()) 17301c3243fSZachary Turner error.SetErrorStringWithFormat("Failed to lock file: %s", 17401c3243fSZachary Turner error.AsCString()); 17501c3243fSZachary Turner } 17601c3243fSZachary Turner 17701c3243fSZachary Turner void ModuleLock::Delete() { 17801c3243fSZachary Turner if (!m_file) 17901c3243fSZachary Turner return; 18001c3243fSZachary Turner 18101c3243fSZachary Turner m_file.Close(); 18207db3f7eSZachary Turner llvm::sys::fs::remove(m_file_spec.GetPath()); 18301c3243fSZachary Turner } 18401c3243fSZachary Turner 18501c3243fSZachary Turner ///////////////////////////////////////////////////////////////////////// 18601c3243fSZachary Turner 18797206d57SZachary Turner Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 18801c3243fSZachary Turner const ModuleSpec &module_spec, const FileSpec &tmp_file, 18901c3243fSZachary Turner const FileSpec &target_file) { 19001c3243fSZachary Turner const auto module_spec_dir = 19101c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 19201c3243fSZachary Turner const auto module_file_path = 19301c3243fSZachary Turner JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 19401c3243fSZachary Turner 19501c3243fSZachary Turner const auto tmp_file_path = tmp_file.GetPath(); 19601c3243fSZachary Turner const auto err_code = 19701c3243fSZachary Turner llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 19801c3243fSZachary Turner if (err_code) 19997206d57SZachary Turner return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 20001c3243fSZachary Turner module_file_path.GetPath().c_str(), 20101c3243fSZachary Turner err_code.message().c_str()); 20201c3243fSZachary Turner 20301c3243fSZachary Turner const auto error = CreateHostSysRootModuleLink( 20401c3243fSZachary Turner root_dir_spec, hostname, target_file, module_file_path, true); 20501c3243fSZachary Turner if (error.Fail()) 20697206d57SZachary Turner return Status("Failed to create link to %s: %s", 20701c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 20897206d57SZachary Turner return Status(); 20901c3243fSZachary Turner } 21001c3243fSZachary Turner 21197206d57SZachary Turner Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 21201c3243fSZachary Turner const ModuleSpec &module_spec, 21301c3243fSZachary Turner ModuleSP &cached_module_sp, bool *did_create_ptr) { 21401c3243fSZachary Turner const auto find_it = 21501c3243fSZachary Turner m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 21601c3243fSZachary Turner if (find_it != m_loaded_modules.end()) { 21701c3243fSZachary Turner cached_module_sp = (*find_it).second.lock(); 21801c3243fSZachary Turner if (cached_module_sp) 21997206d57SZachary Turner return Status(); 22001c3243fSZachary Turner m_loaded_modules.erase(find_it); 22101c3243fSZachary Turner } 22201c3243fSZachary Turner 22301c3243fSZachary Turner const auto module_spec_dir = 22401c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 22501c3243fSZachary Turner const auto module_file_path = JoinPath( 22601c3243fSZachary Turner module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 22701c3243fSZachary Turner 228*dbd7fabaSJonas Devlieghere if (!FileSystem::Instance().Exists(module_file_path)) 22997206d57SZachary Turner return Status("Module %s not found", module_file_path.GetPath().c_str()); 23059b78bcbSJonas Devlieghere if (FileSystem::Instance().GetByteSize(module_file_path) != 23159b78bcbSJonas Devlieghere module_spec.GetObjectSize()) 23297206d57SZachary Turner return Status("Module %s has invalid file size", 23301c3243fSZachary Turner module_file_path.GetPath().c_str()); 23401c3243fSZachary Turner 23501c3243fSZachary Turner // We may have already cached module but downloaded from an another host - in 23601c3243fSZachary Turner // this case let's create a link to it. 23701c3243fSZachary Turner auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 23801c3243fSZachary Turner module_spec.GetFileSpec(), 23901c3243fSZachary Turner module_file_path, false); 24001c3243fSZachary Turner if (error.Fail()) 24197206d57SZachary Turner return Status("Failed to create link to %s: %s", 24201c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 24301c3243fSZachary Turner 24401c3243fSZachary Turner auto cached_module_spec(module_spec); 24501c3243fSZachary Turner cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 24601c3243fSZachary Turner // content hash instead of real UUID. 24701c3243fSZachary Turner cached_module_spec.GetFileSpec() = module_file_path; 24801c3243fSZachary Turner cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 24901c3243fSZachary Turner 25001c3243fSZachary Turner error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 25101c3243fSZachary Turner nullptr, nullptr, did_create_ptr, false); 25201c3243fSZachary Turner if (error.Fail()) 25301c3243fSZachary Turner return error; 25401c3243fSZachary Turner 25501c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 256*dbd7fabaSJonas Devlieghere if (FileSystem::Instance().Exists(symfile_spec)) 25701c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 25801c3243fSZachary Turner 25901c3243fSZachary Turner m_loaded_modules.insert( 26001c3243fSZachary Turner std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 26101c3243fSZachary Turner 26297206d57SZachary Turner return Status(); 26301c3243fSZachary Turner } 26401c3243fSZachary Turner 26597206d57SZachary Turner Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 26601c3243fSZachary Turner const char *hostname, 26701c3243fSZachary Turner const ModuleSpec &module_spec, 26801c3243fSZachary Turner const ModuleDownloader &module_downloader, 26901c3243fSZachary Turner const SymfileDownloader &symfile_downloader, 27001c3243fSZachary Turner lldb::ModuleSP &cached_module_sp, 27101c3243fSZachary Turner bool *did_create_ptr) { 27201c3243fSZachary Turner const auto module_spec_dir = 27301c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 27401c3243fSZachary Turner auto error = MakeDirectory(module_spec_dir); 27501c3243fSZachary Turner if (error.Fail()) 27601c3243fSZachary Turner return error; 27701c3243fSZachary Turner 27801c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 27901c3243fSZachary Turner if (error.Fail()) 28097206d57SZachary Turner return Status("Failed to lock module %s: %s", 28101c3243fSZachary Turner module_spec.GetUUID().GetAsString().c_str(), 28201c3243fSZachary Turner error.AsCString()); 28301c3243fSZachary Turner 28401c3243fSZachary Turner const auto escaped_hostname(GetEscapedHostname(hostname)); 28501c3243fSZachary Turner // Check local cache for a module. 28601c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 28701c3243fSZachary Turner cached_module_sp, did_create_ptr); 28801c3243fSZachary Turner if (error.Success()) 28901c3243fSZachary Turner return error; 29001c3243fSZachary Turner 29101c3243fSZachary Turner const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 29201c3243fSZachary Turner error = module_downloader(module_spec, tmp_download_file_spec); 29301c3243fSZachary Turner llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 29401c3243fSZachary Turner if (error.Fail()) 29597206d57SZachary Turner return Status("Failed to download module: %s", error.AsCString()); 29601c3243fSZachary Turner 29701c3243fSZachary Turner // Put downloaded file into local module cache. 29801c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 29901c3243fSZachary Turner tmp_download_file_spec, module_spec.GetFileSpec()); 30001c3243fSZachary Turner if (error.Fail()) 30197206d57SZachary Turner return Status("Failed to put module into cache: %s", error.AsCString()); 30201c3243fSZachary Turner 30301c3243fSZachary Turner tmp_file_remover.releaseFile(); 30401c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 30501c3243fSZachary Turner cached_module_sp, did_create_ptr); 30601c3243fSZachary Turner if (error.Fail()) 30701c3243fSZachary Turner return error; 30801c3243fSZachary Turner 30901c3243fSZachary Turner // Fetching a symbol file for the module 31001c3243fSZachary Turner const auto tmp_download_sym_file_spec = 31101c3243fSZachary Turner JoinPath(module_spec_dir, kTempSymFileName); 31201c3243fSZachary Turner error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 31301c3243fSZachary Turner llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 31401c3243fSZachary Turner if (error.Fail()) 31501c3243fSZachary Turner // Failed to download a symfile but fetching the module was successful. The 31605097246SAdrian Prantl // module might contain the necessary symbols and the debugging is also 31705097246SAdrian Prantl // possible without a symfile. 31897206d57SZachary Turner return Status(); 31901c3243fSZachary Turner 32001c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 32101c3243fSZachary Turner tmp_download_sym_file_spec, 32201c3243fSZachary Turner GetSymbolFileSpec(module_spec.GetFileSpec())); 32301c3243fSZachary Turner if (error.Fail()) 32497206d57SZachary Turner return Status("Failed to put symbol file into cache: %s", 32597206d57SZachary Turner error.AsCString()); 32601c3243fSZachary Turner 32701c3243fSZachary Turner tmp_symfile_remover.releaseFile(); 32801c3243fSZachary Turner 32901c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 33001c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 33197206d57SZachary Turner return Status(); 33201c3243fSZachary Turner } 333