1dda28197Spatrick //===-- ModuleCache.cpp ---------------------------------------------------===// 2061da546Spatrick // 3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick // 7061da546Spatrick //===----------------------------------------------------------------------===// 8061da546Spatrick 9061da546Spatrick #include "lldb/Target/ModuleCache.h" 10061da546Spatrick 11061da546Spatrick #include "lldb/Core/Module.h" 12061da546Spatrick #include "lldb/Core/ModuleList.h" 13061da546Spatrick #include "lldb/Core/ModuleSpec.h" 14061da546Spatrick #include "lldb/Host/File.h" 15061da546Spatrick #include "lldb/Host/LockFile.h" 16061da546Spatrick #include "lldb/Utility/Log.h" 17061da546Spatrick #include "llvm/Support/FileSystem.h" 18061da546Spatrick #include "llvm/Support/FileUtilities.h" 19061da546Spatrick 20*be691f3bSpatrick #include <cassert> 21061da546Spatrick 22061da546Spatrick #include <cstdio> 23061da546Spatrick 24061da546Spatrick using namespace lldb; 25061da546Spatrick using namespace lldb_private; 26061da546Spatrick 27061da546Spatrick namespace { 28061da546Spatrick 29061da546Spatrick const char *kModulesSubdir = ".cache"; 30061da546Spatrick const char *kLockDirName = ".lock"; 31061da546Spatrick const char *kTempFileName = ".temp"; 32061da546Spatrick const char *kTempSymFileName = ".symtemp"; 33061da546Spatrick const char *kSymFileExtension = ".sym"; 34061da546Spatrick const char *kFSIllegalChars = "\\/:*?\"<>|"; 35061da546Spatrick 36061da546Spatrick std::string GetEscapedHostname(const char *hostname) { 37061da546Spatrick if (hostname == nullptr) 38061da546Spatrick hostname = "unknown"; 39061da546Spatrick std::string result(hostname); 40061da546Spatrick size_t size = result.size(); 41061da546Spatrick for (size_t i = 0; i < size; ++i) { 42061da546Spatrick if ((result[i] >= 1 && result[i] <= 31) || 43061da546Spatrick strchr(kFSIllegalChars, result[i]) != nullptr) 44061da546Spatrick result[i] = '_'; 45061da546Spatrick } 46061da546Spatrick return result; 47061da546Spatrick } 48061da546Spatrick 49061da546Spatrick class ModuleLock { 50061da546Spatrick private: 51061da546Spatrick FileUP m_file_up; 52061da546Spatrick std::unique_ptr<lldb_private::LockFile> m_lock; 53061da546Spatrick FileSpec m_file_spec; 54061da546Spatrick 55061da546Spatrick public: 56061da546Spatrick ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error); 57061da546Spatrick void Delete(); 58061da546Spatrick }; 59061da546Spatrick 60061da546Spatrick static FileSpec JoinPath(const FileSpec &path1, const char *path2) { 61061da546Spatrick FileSpec result_spec(path1); 62061da546Spatrick result_spec.AppendPathComponent(path2); 63061da546Spatrick return result_spec; 64061da546Spatrick } 65061da546Spatrick 66061da546Spatrick static Status MakeDirectory(const FileSpec &dir_path) { 67061da546Spatrick namespace fs = llvm::sys::fs; 68061da546Spatrick 69061da546Spatrick return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all); 70061da546Spatrick } 71061da546Spatrick 72061da546Spatrick FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { 73061da546Spatrick const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); 74061da546Spatrick return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); 75061da546Spatrick } 76061da546Spatrick 77061da546Spatrick FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { 78061da546Spatrick return FileSpec(module_file_spec.GetPath() + kSymFileExtension); 79061da546Spatrick } 80061da546Spatrick 81061da546Spatrick void DeleteExistingModule(const FileSpec &root_dir_spec, 82061da546Spatrick const FileSpec &sysroot_module_path_spec) { 83061da546Spatrick Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 84061da546Spatrick UUID module_uuid; 85061da546Spatrick { 86061da546Spatrick auto module_sp = 87061da546Spatrick std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); 88061da546Spatrick module_uuid = module_sp->GetUUID(); 89061da546Spatrick } 90061da546Spatrick 91061da546Spatrick if (!module_uuid.IsValid()) 92061da546Spatrick return; 93061da546Spatrick 94061da546Spatrick Status error; 95061da546Spatrick ModuleLock lock(root_dir_spec, module_uuid, error); 96061da546Spatrick if (error.Fail()) { 97061da546Spatrick LLDB_LOGF(log, "Failed to lock module %s: %s", 98061da546Spatrick module_uuid.GetAsString().c_str(), error.AsCString()); 99061da546Spatrick } 100061da546Spatrick 101061da546Spatrick namespace fs = llvm::sys::fs; 102061da546Spatrick fs::file_status st; 103061da546Spatrick if (status(sysroot_module_path_spec.GetPath(), st)) 104061da546Spatrick return; 105061da546Spatrick 106061da546Spatrick if (st.getLinkCount() > 2) // module is referred by other hosts. 107061da546Spatrick return; 108061da546Spatrick 109061da546Spatrick const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); 110061da546Spatrick llvm::sys::fs::remove_directories(module_spec_dir.GetPath()); 111061da546Spatrick lock.Delete(); 112061da546Spatrick } 113061da546Spatrick 114061da546Spatrick void DecrementRefExistingModule(const FileSpec &root_dir_spec, 115061da546Spatrick const FileSpec &sysroot_module_path_spec) { 116061da546Spatrick // Remove $platform/.cache/$uuid folder if nobody else references it. 117061da546Spatrick DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); 118061da546Spatrick 119061da546Spatrick // Remove sysroot link. 120061da546Spatrick llvm::sys::fs::remove(sysroot_module_path_spec.GetPath()); 121061da546Spatrick 122061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); 123061da546Spatrick llvm::sys::fs::remove(symfile_spec.GetPath()); 124061da546Spatrick } 125061da546Spatrick 126061da546Spatrick Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, 127061da546Spatrick const char *hostname, 128061da546Spatrick const FileSpec &platform_module_spec, 129061da546Spatrick const FileSpec &local_module_spec, 130061da546Spatrick bool delete_existing) { 131061da546Spatrick const auto sysroot_module_path_spec = 132061da546Spatrick JoinPath(JoinPath(root_dir_spec, hostname), 133061da546Spatrick platform_module_spec.GetPath().c_str()); 134061da546Spatrick if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { 135061da546Spatrick if (!delete_existing) 136061da546Spatrick return Status(); 137061da546Spatrick 138061da546Spatrick DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); 139061da546Spatrick } 140061da546Spatrick 141061da546Spatrick const auto error = MakeDirectory( 142061da546Spatrick FileSpec(sysroot_module_path_spec.GetDirectory().AsCString())); 143061da546Spatrick if (error.Fail()) 144061da546Spatrick return error; 145061da546Spatrick 146061da546Spatrick return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(), 147061da546Spatrick sysroot_module_path_spec.GetPath()); 148061da546Spatrick } 149061da546Spatrick 150061da546Spatrick } // namespace 151061da546Spatrick 152061da546Spatrick ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, 153061da546Spatrick Status &error) { 154061da546Spatrick const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); 155061da546Spatrick error = MakeDirectory(lock_dir_spec); 156061da546Spatrick if (error.Fail()) 157061da546Spatrick return; 158061da546Spatrick 159061da546Spatrick m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); 160061da546Spatrick 161061da546Spatrick auto file = FileSystem::Instance().Open( 162061da546Spatrick m_file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate | 163061da546Spatrick File::eOpenOptionCloseOnExec); 164061da546Spatrick if (file) 165061da546Spatrick m_file_up = std::move(file.get()); 166061da546Spatrick else { 167061da546Spatrick m_file_up.reset(); 168061da546Spatrick error = Status(file.takeError()); 169061da546Spatrick return; 170061da546Spatrick } 171061da546Spatrick 172dda28197Spatrick m_lock = std::make_unique<lldb_private::LockFile>(m_file_up->GetDescriptor()); 173061da546Spatrick error = m_lock->WriteLock(0, 1); 174061da546Spatrick if (error.Fail()) 175061da546Spatrick error.SetErrorStringWithFormat("Failed to lock file: %s", 176061da546Spatrick error.AsCString()); 177061da546Spatrick } 178061da546Spatrick 179061da546Spatrick void ModuleLock::Delete() { 180061da546Spatrick if (!m_file_up) 181061da546Spatrick return; 182061da546Spatrick 183061da546Spatrick m_file_up->Close(); 184061da546Spatrick m_file_up.reset(); 185061da546Spatrick llvm::sys::fs::remove(m_file_spec.GetPath()); 186061da546Spatrick } 187061da546Spatrick 188061da546Spatrick ///////////////////////////////////////////////////////////////////////// 189061da546Spatrick 190061da546Spatrick Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 191061da546Spatrick const ModuleSpec &module_spec, const FileSpec &tmp_file, 192061da546Spatrick const FileSpec &target_file) { 193061da546Spatrick const auto module_spec_dir = 194061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 195061da546Spatrick const auto module_file_path = 196061da546Spatrick JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 197061da546Spatrick 198061da546Spatrick const auto tmp_file_path = tmp_file.GetPath(); 199061da546Spatrick const auto err_code = 200061da546Spatrick llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 201061da546Spatrick if (err_code) 202061da546Spatrick return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 203061da546Spatrick module_file_path.GetPath().c_str(), 204061da546Spatrick err_code.message().c_str()); 205061da546Spatrick 206061da546Spatrick const auto error = CreateHostSysRootModuleLink( 207061da546Spatrick root_dir_spec, hostname, target_file, module_file_path, true); 208061da546Spatrick if (error.Fail()) 209061da546Spatrick return Status("Failed to create link to %s: %s", 210061da546Spatrick module_file_path.GetPath().c_str(), error.AsCString()); 211061da546Spatrick return Status(); 212061da546Spatrick } 213061da546Spatrick 214061da546Spatrick Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 215061da546Spatrick const ModuleSpec &module_spec, 216061da546Spatrick ModuleSP &cached_module_sp, bool *did_create_ptr) { 217061da546Spatrick const auto find_it = 218061da546Spatrick m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 219061da546Spatrick if (find_it != m_loaded_modules.end()) { 220061da546Spatrick cached_module_sp = (*find_it).second.lock(); 221061da546Spatrick if (cached_module_sp) 222061da546Spatrick return Status(); 223061da546Spatrick m_loaded_modules.erase(find_it); 224061da546Spatrick } 225061da546Spatrick 226061da546Spatrick const auto module_spec_dir = 227061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 228061da546Spatrick const auto module_file_path = JoinPath( 229061da546Spatrick module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 230061da546Spatrick 231061da546Spatrick if (!FileSystem::Instance().Exists(module_file_path)) 232061da546Spatrick return Status("Module %s not found", module_file_path.GetPath().c_str()); 233061da546Spatrick if (FileSystem::Instance().GetByteSize(module_file_path) != 234061da546Spatrick module_spec.GetObjectSize()) 235061da546Spatrick return Status("Module %s has invalid file size", 236061da546Spatrick module_file_path.GetPath().c_str()); 237061da546Spatrick 238061da546Spatrick // We may have already cached module but downloaded from an another host - in 239061da546Spatrick // this case let's create a link to it. 240061da546Spatrick auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 241061da546Spatrick module_spec.GetFileSpec(), 242061da546Spatrick module_file_path, false); 243061da546Spatrick if (error.Fail()) 244061da546Spatrick return Status("Failed to create link to %s: %s", 245061da546Spatrick module_file_path.GetPath().c_str(), error.AsCString()); 246061da546Spatrick 247061da546Spatrick auto cached_module_spec(module_spec); 248061da546Spatrick cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 249061da546Spatrick // content hash instead of real UUID. 250061da546Spatrick cached_module_spec.GetFileSpec() = module_file_path; 251061da546Spatrick cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 252061da546Spatrick 253061da546Spatrick error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 254061da546Spatrick nullptr, nullptr, did_create_ptr, false); 255061da546Spatrick if (error.Fail()) 256061da546Spatrick return error; 257061da546Spatrick 258061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 259061da546Spatrick if (FileSystem::Instance().Exists(symfile_spec)) 260061da546Spatrick cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 261061da546Spatrick 262061da546Spatrick m_loaded_modules.insert( 263061da546Spatrick std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 264061da546Spatrick 265061da546Spatrick return Status(); 266061da546Spatrick } 267061da546Spatrick 268061da546Spatrick Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 269061da546Spatrick const char *hostname, 270061da546Spatrick const ModuleSpec &module_spec, 271061da546Spatrick const ModuleDownloader &module_downloader, 272061da546Spatrick const SymfileDownloader &symfile_downloader, 273061da546Spatrick lldb::ModuleSP &cached_module_sp, 274061da546Spatrick bool *did_create_ptr) { 275061da546Spatrick const auto module_spec_dir = 276061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 277061da546Spatrick auto error = MakeDirectory(module_spec_dir); 278061da546Spatrick if (error.Fail()) 279061da546Spatrick return error; 280061da546Spatrick 281061da546Spatrick ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 282061da546Spatrick if (error.Fail()) 283061da546Spatrick return Status("Failed to lock module %s: %s", 284061da546Spatrick module_spec.GetUUID().GetAsString().c_str(), 285061da546Spatrick error.AsCString()); 286061da546Spatrick 287061da546Spatrick const auto escaped_hostname(GetEscapedHostname(hostname)); 288061da546Spatrick // Check local cache for a module. 289061da546Spatrick error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 290061da546Spatrick cached_module_sp, did_create_ptr); 291061da546Spatrick if (error.Success()) 292061da546Spatrick return error; 293061da546Spatrick 294061da546Spatrick const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 295061da546Spatrick error = module_downloader(module_spec, tmp_download_file_spec); 296061da546Spatrick llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 297061da546Spatrick if (error.Fail()) 298061da546Spatrick return Status("Failed to download module: %s", error.AsCString()); 299061da546Spatrick 300061da546Spatrick // Put downloaded file into local module cache. 301061da546Spatrick error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 302061da546Spatrick tmp_download_file_spec, module_spec.GetFileSpec()); 303061da546Spatrick if (error.Fail()) 304061da546Spatrick return Status("Failed to put module into cache: %s", error.AsCString()); 305061da546Spatrick 306061da546Spatrick tmp_file_remover.releaseFile(); 307061da546Spatrick error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 308061da546Spatrick cached_module_sp, did_create_ptr); 309061da546Spatrick if (error.Fail()) 310061da546Spatrick return error; 311061da546Spatrick 312061da546Spatrick // Fetching a symbol file for the module 313061da546Spatrick const auto tmp_download_sym_file_spec = 314061da546Spatrick JoinPath(module_spec_dir, kTempSymFileName); 315061da546Spatrick error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 316061da546Spatrick llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 317061da546Spatrick if (error.Fail()) 318061da546Spatrick // Failed to download a symfile but fetching the module was successful. The 319061da546Spatrick // module might contain the necessary symbols and the debugging is also 320061da546Spatrick // possible without a symfile. 321061da546Spatrick return Status(); 322061da546Spatrick 323061da546Spatrick error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 324061da546Spatrick tmp_download_sym_file_spec, 325061da546Spatrick GetSymbolFileSpec(module_spec.GetFileSpec())); 326061da546Spatrick if (error.Fail()) 327061da546Spatrick return Status("Failed to put symbol file into cache: %s", 328061da546Spatrick error.AsCString()); 329061da546Spatrick 330061da546Spatrick tmp_symfile_remover.releaseFile(); 331061da546Spatrick 332061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 333061da546Spatrick cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 334061da546Spatrick return Status(); 335061da546Spatrick } 336