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/FileSystem.h" 1701c3243fSZachary Turner #include "lldb/Host/LockFile.h" 18*6f9e6901SZachary Turner #include "lldb/Utility/Log.h" 1901c3243fSZachary Turner #include "llvm/Support/FileSystem.h" 2001c3243fSZachary Turner #include "llvm/Support/FileUtilities.h" 2101c3243fSZachary Turner 2201c3243fSZachary Turner #include <assert.h> 2301c3243fSZachary Turner 2401c3243fSZachary Turner #include <cstdio> 2501c3243fSZachary Turner 2601c3243fSZachary Turner using namespace lldb; 2701c3243fSZachary Turner using namespace lldb_private; 2801c3243fSZachary Turner 2901c3243fSZachary Turner namespace { 3001c3243fSZachary Turner 3101c3243fSZachary Turner const char *kModulesSubdir = ".cache"; 3201c3243fSZachary Turner const char *kLockDirName = ".lock"; 3301c3243fSZachary Turner const char *kTempFileName = ".temp"; 3401c3243fSZachary Turner const char *kTempSymFileName = ".symtemp"; 3501c3243fSZachary Turner const char *kSymFileExtension = ".sym"; 3601c3243fSZachary Turner const char *kFSIllegalChars = "\\/:*?\"<>|"; 3701c3243fSZachary Turner 3801c3243fSZachary Turner std::string GetEscapedHostname(const char *hostname) { 3901c3243fSZachary Turner if (hostname == nullptr) 4001c3243fSZachary Turner hostname = "unknown"; 4101c3243fSZachary Turner std::string result(hostname); 4201c3243fSZachary Turner size_t size = result.size(); 4301c3243fSZachary Turner for (size_t i = 0; i < size; ++i) { 4401c3243fSZachary Turner if ((result[i] >= 1 && result[i] <= 31) || 4501c3243fSZachary Turner strchr(kFSIllegalChars, result[i]) != nullptr) 4601c3243fSZachary Turner result[i] = '_'; 4701c3243fSZachary Turner } 4801c3243fSZachary Turner return result; 4901c3243fSZachary Turner } 5001c3243fSZachary Turner 5101c3243fSZachary Turner class ModuleLock { 5201c3243fSZachary Turner private: 5301c3243fSZachary Turner File m_file; 5401c3243fSZachary Turner std::unique_ptr<lldb_private::LockFile> m_lock; 5501c3243fSZachary Turner FileSpec m_file_spec; 5601c3243fSZachary Turner 5701c3243fSZachary Turner public: 5801c3243fSZachary Turner ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Error &error); 5901c3243fSZachary Turner void Delete(); 6001c3243fSZachary Turner }; 6101c3243fSZachary Turner 6201c3243fSZachary Turner FileSpec JoinPath(const FileSpec &path1, const char *path2) { 6301c3243fSZachary Turner FileSpec result_spec(path1); 6401c3243fSZachary Turner result_spec.AppendPathComponent(path2); 6501c3243fSZachary Turner return result_spec; 6601c3243fSZachary Turner } 6701c3243fSZachary Turner 6801c3243fSZachary Turner Error MakeDirectory(const FileSpec &dir_path) { 6901c3243fSZachary Turner if (dir_path.Exists()) { 7001c3243fSZachary Turner if (!dir_path.IsDirectory()) 7101c3243fSZachary Turner return Error("Invalid existing path"); 7201c3243fSZachary Turner 7301c3243fSZachary Turner return Error(); 7401c3243fSZachary Turner } 7501c3243fSZachary Turner 7601c3243fSZachary Turner return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault); 7701c3243fSZachary Turner } 7801c3243fSZachary Turner 7901c3243fSZachary Turner FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { 8001c3243fSZachary Turner const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); 8101c3243fSZachary Turner return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); 8201c3243fSZachary Turner } 8301c3243fSZachary Turner 8401c3243fSZachary Turner FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { 8501c3243fSZachary Turner return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false); 8601c3243fSZachary Turner } 8701c3243fSZachary Turner 8801c3243fSZachary Turner void DeleteExistingModule(const FileSpec &root_dir_spec, 8901c3243fSZachary Turner const FileSpec &sysroot_module_path_spec) { 9001c3243fSZachary Turner Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 9101c3243fSZachary Turner UUID module_uuid; 9201c3243fSZachary Turner { 9301c3243fSZachary Turner auto module_sp = 9401c3243fSZachary Turner std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); 9501c3243fSZachary Turner module_uuid = module_sp->GetUUID(); 9601c3243fSZachary Turner } 9701c3243fSZachary Turner 9801c3243fSZachary Turner if (!module_uuid.IsValid()) 9901c3243fSZachary Turner return; 10001c3243fSZachary Turner 10101c3243fSZachary Turner Error error; 10201c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_uuid, error); 10301c3243fSZachary Turner if (error.Fail()) { 10401c3243fSZachary Turner if (log) 10501c3243fSZachary Turner log->Printf("Failed to lock module %s: %s", 10601c3243fSZachary Turner module_uuid.GetAsString().c_str(), error.AsCString()); 10701c3243fSZachary Turner } 10801c3243fSZachary Turner 10901c3243fSZachary Turner auto link_count = FileSystem::GetHardlinkCount(sysroot_module_path_spec); 11001c3243fSZachary Turner if (link_count == -1) 11101c3243fSZachary Turner return; 11201c3243fSZachary Turner 11301c3243fSZachary Turner if (link_count > 2) // module is referred by other hosts. 11401c3243fSZachary Turner return; 11501c3243fSZachary Turner 11601c3243fSZachary Turner const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); 11701c3243fSZachary Turner FileSystem::DeleteDirectory(module_spec_dir, true); 11801c3243fSZachary Turner lock.Delete(); 11901c3243fSZachary Turner } 12001c3243fSZachary Turner 12101c3243fSZachary Turner void DecrementRefExistingModule(const FileSpec &root_dir_spec, 12201c3243fSZachary Turner const FileSpec &sysroot_module_path_spec) { 12301c3243fSZachary Turner // Remove $platform/.cache/$uuid folder if nobody else references it. 12401c3243fSZachary Turner DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); 12501c3243fSZachary Turner 12601c3243fSZachary Turner // Remove sysroot link. 12701c3243fSZachary Turner FileSystem::Unlink(sysroot_module_path_spec); 12801c3243fSZachary Turner 12901c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); 13001c3243fSZachary Turner if (symfile_spec.Exists()) // delete module's symbol file if exists. 13101c3243fSZachary Turner FileSystem::Unlink(symfile_spec); 13201c3243fSZachary Turner } 13301c3243fSZachary Turner 13401c3243fSZachary Turner Error CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, 13501c3243fSZachary Turner const char *hostname, 13601c3243fSZachary Turner const FileSpec &platform_module_spec, 13701c3243fSZachary Turner const FileSpec &local_module_spec, 13801c3243fSZachary Turner bool delete_existing) { 13901c3243fSZachary Turner const auto sysroot_module_path_spec = 14001c3243fSZachary Turner JoinPath(JoinPath(root_dir_spec, hostname), 14101c3243fSZachary Turner platform_module_spec.GetPath().c_str()); 14201c3243fSZachary Turner if (sysroot_module_path_spec.Exists()) { 14301c3243fSZachary Turner if (!delete_existing) 14401c3243fSZachary Turner return Error(); 14501c3243fSZachary Turner 14601c3243fSZachary Turner DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); 14701c3243fSZachary Turner } 14801c3243fSZachary Turner 14901c3243fSZachary Turner const auto error = MakeDirectory( 15001c3243fSZachary Turner FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false)); 15101c3243fSZachary Turner if (error.Fail()) 15201c3243fSZachary Turner return error; 15301c3243fSZachary Turner 15401c3243fSZachary Turner return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec); 15501c3243fSZachary Turner } 15601c3243fSZachary Turner 15701c3243fSZachary Turner } // namespace 15801c3243fSZachary Turner 15901c3243fSZachary Turner ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, 16001c3243fSZachary Turner Error &error) { 16101c3243fSZachary Turner const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); 16201c3243fSZachary Turner error = MakeDirectory(lock_dir_spec); 16301c3243fSZachary Turner if (error.Fail()) 16401c3243fSZachary Turner return; 16501c3243fSZachary Turner 16601c3243fSZachary Turner m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); 16701c3243fSZachary Turner m_file.Open(m_file_spec.GetCString(), File::eOpenOptionWrite | 16801c3243fSZachary Turner File::eOpenOptionCanCreate | 16901c3243fSZachary Turner File::eOpenOptionCloseOnExec); 17001c3243fSZachary Turner if (!m_file) { 17101c3243fSZachary Turner error.SetErrorToErrno(); 17201c3243fSZachary Turner return; 17301c3243fSZachary Turner } 17401c3243fSZachary Turner 17501c3243fSZachary Turner m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor())); 17601c3243fSZachary Turner error = m_lock->WriteLock(0, 1); 17701c3243fSZachary Turner if (error.Fail()) 17801c3243fSZachary Turner error.SetErrorStringWithFormat("Failed to lock file: %s", 17901c3243fSZachary Turner error.AsCString()); 18001c3243fSZachary Turner } 18101c3243fSZachary Turner 18201c3243fSZachary Turner void ModuleLock::Delete() { 18301c3243fSZachary Turner if (!m_file) 18401c3243fSZachary Turner return; 18501c3243fSZachary Turner 18601c3243fSZachary Turner m_file.Close(); 18701c3243fSZachary Turner FileSystem::Unlink(m_file_spec); 18801c3243fSZachary Turner } 18901c3243fSZachary Turner 19001c3243fSZachary Turner ///////////////////////////////////////////////////////////////////////// 19101c3243fSZachary Turner 19201c3243fSZachary Turner Error ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 19301c3243fSZachary Turner const ModuleSpec &module_spec, const FileSpec &tmp_file, 19401c3243fSZachary Turner const FileSpec &target_file) { 19501c3243fSZachary Turner const auto module_spec_dir = 19601c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 19701c3243fSZachary Turner const auto module_file_path = 19801c3243fSZachary Turner JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 19901c3243fSZachary Turner 20001c3243fSZachary Turner const auto tmp_file_path = tmp_file.GetPath(); 20101c3243fSZachary Turner const auto err_code = 20201c3243fSZachary Turner llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 20301c3243fSZachary Turner if (err_code) 20401c3243fSZachary Turner return Error("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 20501c3243fSZachary Turner module_file_path.GetPath().c_str(), 20601c3243fSZachary Turner err_code.message().c_str()); 20701c3243fSZachary Turner 20801c3243fSZachary Turner const auto error = CreateHostSysRootModuleLink( 20901c3243fSZachary Turner root_dir_spec, hostname, target_file, module_file_path, true); 21001c3243fSZachary Turner if (error.Fail()) 21101c3243fSZachary Turner return Error("Failed to create link to %s: %s", 21201c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 21301c3243fSZachary Turner return Error(); 21401c3243fSZachary Turner } 21501c3243fSZachary Turner 21601c3243fSZachary Turner Error ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 21701c3243fSZachary Turner const ModuleSpec &module_spec, 21801c3243fSZachary Turner ModuleSP &cached_module_sp, bool *did_create_ptr) { 21901c3243fSZachary Turner const auto find_it = 22001c3243fSZachary Turner m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 22101c3243fSZachary Turner if (find_it != m_loaded_modules.end()) { 22201c3243fSZachary Turner cached_module_sp = (*find_it).second.lock(); 22301c3243fSZachary Turner if (cached_module_sp) 22401c3243fSZachary Turner return Error(); 22501c3243fSZachary Turner m_loaded_modules.erase(find_it); 22601c3243fSZachary Turner } 22701c3243fSZachary Turner 22801c3243fSZachary Turner const auto module_spec_dir = 22901c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 23001c3243fSZachary Turner const auto module_file_path = JoinPath( 23101c3243fSZachary Turner module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 23201c3243fSZachary Turner 23301c3243fSZachary Turner if (!module_file_path.Exists()) 23401c3243fSZachary Turner return Error("Module %s not found", module_file_path.GetPath().c_str()); 23501c3243fSZachary Turner if (module_file_path.GetByteSize() != module_spec.GetObjectSize()) 23601c3243fSZachary Turner return Error("Module %s has invalid file size", 23701c3243fSZachary Turner module_file_path.GetPath().c_str()); 23801c3243fSZachary Turner 23901c3243fSZachary Turner // We may have already cached module but downloaded from an another host - in 24001c3243fSZachary Turner // this case let's create a link to it. 24101c3243fSZachary Turner auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 24201c3243fSZachary Turner module_spec.GetFileSpec(), 24301c3243fSZachary Turner module_file_path, false); 24401c3243fSZachary Turner if (error.Fail()) 24501c3243fSZachary Turner return Error("Failed to create link to %s: %s", 24601c3243fSZachary Turner module_file_path.GetPath().c_str(), error.AsCString()); 24701c3243fSZachary Turner 24801c3243fSZachary Turner auto cached_module_spec(module_spec); 24901c3243fSZachary Turner cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 25001c3243fSZachary Turner // content hash instead of real UUID. 25101c3243fSZachary Turner cached_module_spec.GetFileSpec() = module_file_path; 25201c3243fSZachary Turner cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 25301c3243fSZachary Turner 25401c3243fSZachary Turner error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 25501c3243fSZachary Turner nullptr, nullptr, did_create_ptr, false); 25601c3243fSZachary Turner if (error.Fail()) 25701c3243fSZachary Turner return error; 25801c3243fSZachary Turner 25901c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 26001c3243fSZachary Turner if (symfile_spec.Exists()) 26101c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 26201c3243fSZachary Turner 26301c3243fSZachary Turner m_loaded_modules.insert( 26401c3243fSZachary Turner std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 26501c3243fSZachary Turner 26601c3243fSZachary Turner return Error(); 26701c3243fSZachary Turner } 26801c3243fSZachary Turner 26901c3243fSZachary Turner Error ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 27001c3243fSZachary Turner const char *hostname, 27101c3243fSZachary Turner const ModuleSpec &module_spec, 27201c3243fSZachary Turner const ModuleDownloader &module_downloader, 27301c3243fSZachary Turner const SymfileDownloader &symfile_downloader, 27401c3243fSZachary Turner lldb::ModuleSP &cached_module_sp, 27501c3243fSZachary Turner bool *did_create_ptr) { 27601c3243fSZachary Turner const auto module_spec_dir = 27701c3243fSZachary Turner GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 27801c3243fSZachary Turner auto error = MakeDirectory(module_spec_dir); 27901c3243fSZachary Turner if (error.Fail()) 28001c3243fSZachary Turner return error; 28101c3243fSZachary Turner 28201c3243fSZachary Turner ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 28301c3243fSZachary Turner if (error.Fail()) 28401c3243fSZachary Turner return Error("Failed to lock module %s: %s", 28501c3243fSZachary Turner module_spec.GetUUID().GetAsString().c_str(), 28601c3243fSZachary Turner error.AsCString()); 28701c3243fSZachary Turner 28801c3243fSZachary Turner const auto escaped_hostname(GetEscapedHostname(hostname)); 28901c3243fSZachary Turner // Check local cache for a module. 29001c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 29101c3243fSZachary Turner cached_module_sp, did_create_ptr); 29201c3243fSZachary Turner if (error.Success()) 29301c3243fSZachary Turner return error; 29401c3243fSZachary Turner 29501c3243fSZachary Turner const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 29601c3243fSZachary Turner error = module_downloader(module_spec, tmp_download_file_spec); 29701c3243fSZachary Turner llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 29801c3243fSZachary Turner if (error.Fail()) 29901c3243fSZachary Turner return Error("Failed to download module: %s", error.AsCString()); 30001c3243fSZachary Turner 30101c3243fSZachary Turner // Put downloaded file into local module cache. 30201c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 30301c3243fSZachary Turner tmp_download_file_spec, module_spec.GetFileSpec()); 30401c3243fSZachary Turner if (error.Fail()) 30501c3243fSZachary Turner return Error("Failed to put module into cache: %s", error.AsCString()); 30601c3243fSZachary Turner 30701c3243fSZachary Turner tmp_file_remover.releaseFile(); 30801c3243fSZachary Turner error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 30901c3243fSZachary Turner cached_module_sp, did_create_ptr); 31001c3243fSZachary Turner if (error.Fail()) 31101c3243fSZachary Turner return error; 31201c3243fSZachary Turner 31301c3243fSZachary Turner // Fetching a symbol file for the module 31401c3243fSZachary Turner const auto tmp_download_sym_file_spec = 31501c3243fSZachary Turner JoinPath(module_spec_dir, kTempSymFileName); 31601c3243fSZachary Turner error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 31701c3243fSZachary Turner llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 31801c3243fSZachary Turner if (error.Fail()) 31901c3243fSZachary Turner // Failed to download a symfile but fetching the module was successful. The 32001c3243fSZachary Turner // module might 32101c3243fSZachary Turner // contain the necessary symbols and the debugging is also possible without 32201c3243fSZachary Turner // a symfile. 32301c3243fSZachary Turner return Error(); 32401c3243fSZachary Turner 32501c3243fSZachary Turner error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 32601c3243fSZachary Turner tmp_download_sym_file_spec, 32701c3243fSZachary Turner GetSymbolFileSpec(module_spec.GetFileSpec())); 32801c3243fSZachary Turner if (error.Fail()) 32901c3243fSZachary Turner return Error("Failed to put symbol file into cache: %s", error.AsCString()); 33001c3243fSZachary Turner 33101c3243fSZachary Turner tmp_symfile_remover.releaseFile(); 33201c3243fSZachary Turner 33301c3243fSZachary Turner FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 33401c3243fSZachary Turner cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 33501c3243fSZachary Turner return Error(); 33601c3243fSZachary Turner } 337