1*061da546Spatrick //===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===// 2*061da546Spatrick // 3*061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*061da546Spatrick // 7*061da546Spatrick //===----------------------------------------------------------------------===// 8*061da546Spatrick 9*061da546Spatrick #include "lldb/Target/ModuleCache.h" 10*061da546Spatrick 11*061da546Spatrick #include "lldb/Core/Module.h" 12*061da546Spatrick #include "lldb/Core/ModuleList.h" 13*061da546Spatrick #include "lldb/Core/ModuleSpec.h" 14*061da546Spatrick #include "lldb/Host/File.h" 15*061da546Spatrick #include "lldb/Host/LockFile.h" 16*061da546Spatrick #include "lldb/Utility/Log.h" 17*061da546Spatrick #include "llvm/Support/FileSystem.h" 18*061da546Spatrick #include "llvm/Support/FileUtilities.h" 19*061da546Spatrick 20*061da546Spatrick #include <assert.h> 21*061da546Spatrick 22*061da546Spatrick #include <cstdio> 23*061da546Spatrick 24*061da546Spatrick using namespace lldb; 25*061da546Spatrick using namespace lldb_private; 26*061da546Spatrick 27*061da546Spatrick namespace { 28*061da546Spatrick 29*061da546Spatrick const char *kModulesSubdir = ".cache"; 30*061da546Spatrick const char *kLockDirName = ".lock"; 31*061da546Spatrick const char *kTempFileName = ".temp"; 32*061da546Spatrick const char *kTempSymFileName = ".symtemp"; 33*061da546Spatrick const char *kSymFileExtension = ".sym"; 34*061da546Spatrick const char *kFSIllegalChars = "\\/:*?\"<>|"; 35*061da546Spatrick 36*061da546Spatrick std::string GetEscapedHostname(const char *hostname) { 37*061da546Spatrick if (hostname == nullptr) 38*061da546Spatrick hostname = "unknown"; 39*061da546Spatrick std::string result(hostname); 40*061da546Spatrick size_t size = result.size(); 41*061da546Spatrick for (size_t i = 0; i < size; ++i) { 42*061da546Spatrick if ((result[i] >= 1 && result[i] <= 31) || 43*061da546Spatrick strchr(kFSIllegalChars, result[i]) != nullptr) 44*061da546Spatrick result[i] = '_'; 45*061da546Spatrick } 46*061da546Spatrick return result; 47*061da546Spatrick } 48*061da546Spatrick 49*061da546Spatrick class ModuleLock { 50*061da546Spatrick private: 51*061da546Spatrick FileUP m_file_up; 52*061da546Spatrick std::unique_ptr<lldb_private::LockFile> m_lock; 53*061da546Spatrick FileSpec m_file_spec; 54*061da546Spatrick 55*061da546Spatrick public: 56*061da546Spatrick ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error); 57*061da546Spatrick void Delete(); 58*061da546Spatrick }; 59*061da546Spatrick 60*061da546Spatrick static FileSpec JoinPath(const FileSpec &path1, const char *path2) { 61*061da546Spatrick FileSpec result_spec(path1); 62*061da546Spatrick result_spec.AppendPathComponent(path2); 63*061da546Spatrick return result_spec; 64*061da546Spatrick } 65*061da546Spatrick 66*061da546Spatrick static Status MakeDirectory(const FileSpec &dir_path) { 67*061da546Spatrick namespace fs = llvm::sys::fs; 68*061da546Spatrick 69*061da546Spatrick return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all); 70*061da546Spatrick } 71*061da546Spatrick 72*061da546Spatrick FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { 73*061da546Spatrick const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); 74*061da546Spatrick return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); 75*061da546Spatrick } 76*061da546Spatrick 77*061da546Spatrick FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { 78*061da546Spatrick return FileSpec(module_file_spec.GetPath() + kSymFileExtension); 79*061da546Spatrick } 80*061da546Spatrick 81*061da546Spatrick void DeleteExistingModule(const FileSpec &root_dir_spec, 82*061da546Spatrick const FileSpec &sysroot_module_path_spec) { 83*061da546Spatrick Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 84*061da546Spatrick UUID module_uuid; 85*061da546Spatrick { 86*061da546Spatrick auto module_sp = 87*061da546Spatrick std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); 88*061da546Spatrick module_uuid = module_sp->GetUUID(); 89*061da546Spatrick } 90*061da546Spatrick 91*061da546Spatrick if (!module_uuid.IsValid()) 92*061da546Spatrick return; 93*061da546Spatrick 94*061da546Spatrick Status error; 95*061da546Spatrick ModuleLock lock(root_dir_spec, module_uuid, error); 96*061da546Spatrick if (error.Fail()) { 97*061da546Spatrick LLDB_LOGF(log, "Failed to lock module %s: %s", 98*061da546Spatrick module_uuid.GetAsString().c_str(), error.AsCString()); 99*061da546Spatrick } 100*061da546Spatrick 101*061da546Spatrick namespace fs = llvm::sys::fs; 102*061da546Spatrick fs::file_status st; 103*061da546Spatrick if (status(sysroot_module_path_spec.GetPath(), st)) 104*061da546Spatrick return; 105*061da546Spatrick 106*061da546Spatrick if (st.getLinkCount() > 2) // module is referred by other hosts. 107*061da546Spatrick return; 108*061da546Spatrick 109*061da546Spatrick const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); 110*061da546Spatrick llvm::sys::fs::remove_directories(module_spec_dir.GetPath()); 111*061da546Spatrick lock.Delete(); 112*061da546Spatrick } 113*061da546Spatrick 114*061da546Spatrick void DecrementRefExistingModule(const FileSpec &root_dir_spec, 115*061da546Spatrick const FileSpec &sysroot_module_path_spec) { 116*061da546Spatrick // Remove $platform/.cache/$uuid folder if nobody else references it. 117*061da546Spatrick DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); 118*061da546Spatrick 119*061da546Spatrick // Remove sysroot link. 120*061da546Spatrick llvm::sys::fs::remove(sysroot_module_path_spec.GetPath()); 121*061da546Spatrick 122*061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); 123*061da546Spatrick llvm::sys::fs::remove(symfile_spec.GetPath()); 124*061da546Spatrick } 125*061da546Spatrick 126*061da546Spatrick Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, 127*061da546Spatrick const char *hostname, 128*061da546Spatrick const FileSpec &platform_module_spec, 129*061da546Spatrick const FileSpec &local_module_spec, 130*061da546Spatrick bool delete_existing) { 131*061da546Spatrick const auto sysroot_module_path_spec = 132*061da546Spatrick JoinPath(JoinPath(root_dir_spec, hostname), 133*061da546Spatrick platform_module_spec.GetPath().c_str()); 134*061da546Spatrick if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { 135*061da546Spatrick if (!delete_existing) 136*061da546Spatrick return Status(); 137*061da546Spatrick 138*061da546Spatrick DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); 139*061da546Spatrick } 140*061da546Spatrick 141*061da546Spatrick const auto error = MakeDirectory( 142*061da546Spatrick FileSpec(sysroot_module_path_spec.GetDirectory().AsCString())); 143*061da546Spatrick if (error.Fail()) 144*061da546Spatrick return error; 145*061da546Spatrick 146*061da546Spatrick return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(), 147*061da546Spatrick sysroot_module_path_spec.GetPath()); 148*061da546Spatrick } 149*061da546Spatrick 150*061da546Spatrick } // namespace 151*061da546Spatrick 152*061da546Spatrick ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, 153*061da546Spatrick Status &error) { 154*061da546Spatrick const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); 155*061da546Spatrick error = MakeDirectory(lock_dir_spec); 156*061da546Spatrick if (error.Fail()) 157*061da546Spatrick return; 158*061da546Spatrick 159*061da546Spatrick m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); 160*061da546Spatrick 161*061da546Spatrick auto file = FileSystem::Instance().Open( 162*061da546Spatrick m_file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate | 163*061da546Spatrick File::eOpenOptionCloseOnExec); 164*061da546Spatrick if (file) 165*061da546Spatrick m_file_up = std::move(file.get()); 166*061da546Spatrick else { 167*061da546Spatrick m_file_up.reset(); 168*061da546Spatrick error = Status(file.takeError()); 169*061da546Spatrick return; 170*061da546Spatrick } 171*061da546Spatrick 172*061da546Spatrick m_lock.reset(new lldb_private::LockFile(m_file_up->GetDescriptor())); 173*061da546Spatrick error = m_lock->WriteLock(0, 1); 174*061da546Spatrick if (error.Fail()) 175*061da546Spatrick error.SetErrorStringWithFormat("Failed to lock file: %s", 176*061da546Spatrick error.AsCString()); 177*061da546Spatrick } 178*061da546Spatrick 179*061da546Spatrick void ModuleLock::Delete() { 180*061da546Spatrick if (!m_file_up) 181*061da546Spatrick return; 182*061da546Spatrick 183*061da546Spatrick m_file_up->Close(); 184*061da546Spatrick m_file_up.reset(); 185*061da546Spatrick llvm::sys::fs::remove(m_file_spec.GetPath()); 186*061da546Spatrick } 187*061da546Spatrick 188*061da546Spatrick ///////////////////////////////////////////////////////////////////////// 189*061da546Spatrick 190*061da546Spatrick Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 191*061da546Spatrick const ModuleSpec &module_spec, const FileSpec &tmp_file, 192*061da546Spatrick const FileSpec &target_file) { 193*061da546Spatrick const auto module_spec_dir = 194*061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 195*061da546Spatrick const auto module_file_path = 196*061da546Spatrick JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 197*061da546Spatrick 198*061da546Spatrick const auto tmp_file_path = tmp_file.GetPath(); 199*061da546Spatrick const auto err_code = 200*061da546Spatrick llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 201*061da546Spatrick if (err_code) 202*061da546Spatrick return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 203*061da546Spatrick module_file_path.GetPath().c_str(), 204*061da546Spatrick err_code.message().c_str()); 205*061da546Spatrick 206*061da546Spatrick const auto error = CreateHostSysRootModuleLink( 207*061da546Spatrick root_dir_spec, hostname, target_file, module_file_path, true); 208*061da546Spatrick if (error.Fail()) 209*061da546Spatrick return Status("Failed to create link to %s: %s", 210*061da546Spatrick module_file_path.GetPath().c_str(), error.AsCString()); 211*061da546Spatrick return Status(); 212*061da546Spatrick } 213*061da546Spatrick 214*061da546Spatrick Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 215*061da546Spatrick const ModuleSpec &module_spec, 216*061da546Spatrick ModuleSP &cached_module_sp, bool *did_create_ptr) { 217*061da546Spatrick const auto find_it = 218*061da546Spatrick m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 219*061da546Spatrick if (find_it != m_loaded_modules.end()) { 220*061da546Spatrick cached_module_sp = (*find_it).second.lock(); 221*061da546Spatrick if (cached_module_sp) 222*061da546Spatrick return Status(); 223*061da546Spatrick m_loaded_modules.erase(find_it); 224*061da546Spatrick } 225*061da546Spatrick 226*061da546Spatrick const auto module_spec_dir = 227*061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 228*061da546Spatrick const auto module_file_path = JoinPath( 229*061da546Spatrick module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 230*061da546Spatrick 231*061da546Spatrick if (!FileSystem::Instance().Exists(module_file_path)) 232*061da546Spatrick return Status("Module %s not found", module_file_path.GetPath().c_str()); 233*061da546Spatrick if (FileSystem::Instance().GetByteSize(module_file_path) != 234*061da546Spatrick module_spec.GetObjectSize()) 235*061da546Spatrick return Status("Module %s has invalid file size", 236*061da546Spatrick module_file_path.GetPath().c_str()); 237*061da546Spatrick 238*061da546Spatrick // We may have already cached module but downloaded from an another host - in 239*061da546Spatrick // this case let's create a link to it. 240*061da546Spatrick auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 241*061da546Spatrick module_spec.GetFileSpec(), 242*061da546Spatrick module_file_path, false); 243*061da546Spatrick if (error.Fail()) 244*061da546Spatrick return Status("Failed to create link to %s: %s", 245*061da546Spatrick module_file_path.GetPath().c_str(), error.AsCString()); 246*061da546Spatrick 247*061da546Spatrick auto cached_module_spec(module_spec); 248*061da546Spatrick cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 249*061da546Spatrick // content hash instead of real UUID. 250*061da546Spatrick cached_module_spec.GetFileSpec() = module_file_path; 251*061da546Spatrick cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 252*061da546Spatrick 253*061da546Spatrick error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 254*061da546Spatrick nullptr, nullptr, did_create_ptr, false); 255*061da546Spatrick if (error.Fail()) 256*061da546Spatrick return error; 257*061da546Spatrick 258*061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 259*061da546Spatrick if (FileSystem::Instance().Exists(symfile_spec)) 260*061da546Spatrick cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 261*061da546Spatrick 262*061da546Spatrick m_loaded_modules.insert( 263*061da546Spatrick std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 264*061da546Spatrick 265*061da546Spatrick return Status(); 266*061da546Spatrick } 267*061da546Spatrick 268*061da546Spatrick Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 269*061da546Spatrick const char *hostname, 270*061da546Spatrick const ModuleSpec &module_spec, 271*061da546Spatrick const ModuleDownloader &module_downloader, 272*061da546Spatrick const SymfileDownloader &symfile_downloader, 273*061da546Spatrick lldb::ModuleSP &cached_module_sp, 274*061da546Spatrick bool *did_create_ptr) { 275*061da546Spatrick const auto module_spec_dir = 276*061da546Spatrick GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 277*061da546Spatrick auto error = MakeDirectory(module_spec_dir); 278*061da546Spatrick if (error.Fail()) 279*061da546Spatrick return error; 280*061da546Spatrick 281*061da546Spatrick ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 282*061da546Spatrick if (error.Fail()) 283*061da546Spatrick return Status("Failed to lock module %s: %s", 284*061da546Spatrick module_spec.GetUUID().GetAsString().c_str(), 285*061da546Spatrick error.AsCString()); 286*061da546Spatrick 287*061da546Spatrick const auto escaped_hostname(GetEscapedHostname(hostname)); 288*061da546Spatrick // Check local cache for a module. 289*061da546Spatrick error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 290*061da546Spatrick cached_module_sp, did_create_ptr); 291*061da546Spatrick if (error.Success()) 292*061da546Spatrick return error; 293*061da546Spatrick 294*061da546Spatrick const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 295*061da546Spatrick error = module_downloader(module_spec, tmp_download_file_spec); 296*061da546Spatrick llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 297*061da546Spatrick if (error.Fail()) 298*061da546Spatrick return Status("Failed to download module: %s", error.AsCString()); 299*061da546Spatrick 300*061da546Spatrick // Put downloaded file into local module cache. 301*061da546Spatrick error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 302*061da546Spatrick tmp_download_file_spec, module_spec.GetFileSpec()); 303*061da546Spatrick if (error.Fail()) 304*061da546Spatrick return Status("Failed to put module into cache: %s", error.AsCString()); 305*061da546Spatrick 306*061da546Spatrick tmp_file_remover.releaseFile(); 307*061da546Spatrick error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 308*061da546Spatrick cached_module_sp, did_create_ptr); 309*061da546Spatrick if (error.Fail()) 310*061da546Spatrick return error; 311*061da546Spatrick 312*061da546Spatrick // Fetching a symbol file for the module 313*061da546Spatrick const auto tmp_download_sym_file_spec = 314*061da546Spatrick JoinPath(module_spec_dir, kTempSymFileName); 315*061da546Spatrick error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 316*061da546Spatrick llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 317*061da546Spatrick if (error.Fail()) 318*061da546Spatrick // Failed to download a symfile but fetching the module was successful. The 319*061da546Spatrick // module might contain the necessary symbols and the debugging is also 320*061da546Spatrick // possible without a symfile. 321*061da546Spatrick return Status(); 322*061da546Spatrick 323*061da546Spatrick error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec, 324*061da546Spatrick tmp_download_sym_file_spec, 325*061da546Spatrick GetSymbolFileSpec(module_spec.GetFileSpec())); 326*061da546Spatrick if (error.Fail()) 327*061da546Spatrick return Status("Failed to put symbol file into cache: %s", 328*061da546Spatrick error.AsCString()); 329*061da546Spatrick 330*061da546Spatrick tmp_symfile_remover.releaseFile(); 331*061da546Spatrick 332*061da546Spatrick FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 333*061da546Spatrick cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 334*061da546Spatrick return Status(); 335*061da546Spatrick } 336