Path: blob/main/contrib/llvm-project/lldb/source/Target/ModuleCache.cpp
39587 views
//===-- ModuleCache.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Target/ModuleCache.h"910#include "lldb/Core/Module.h"11#include "lldb/Core/ModuleList.h"12#include "lldb/Core/ModuleSpec.h"13#include "lldb/Host/File.h"14#include "lldb/Host/LockFile.h"15#include "lldb/Utility/LLDBLog.h"16#include "lldb/Utility/Log.h"17#include "llvm/Support/FileSystem.h"18#include "llvm/Support/FileUtilities.h"1920#include <cassert>2122#include <cstdio>2324using namespace lldb;25using namespace lldb_private;2627namespace {2829const char *kModulesSubdir = ".cache";30const char *kLockDirName = ".lock";31const char *kTempFileName = ".temp";32const char *kTempSymFileName = ".symtemp";33const char *kSymFileExtension = ".sym";34const char *kFSIllegalChars = "\\/:*?\"<>|";3536std::string GetEscapedHostname(const char *hostname) {37if (hostname == nullptr)38hostname = "unknown";39std::string result(hostname);40size_t size = result.size();41for (size_t i = 0; i < size; ++i) {42if ((result[i] >= 1 && result[i] <= 31) ||43strchr(kFSIllegalChars, result[i]) != nullptr)44result[i] = '_';45}46return result;47}4849class ModuleLock {50private:51FileUP m_file_up;52std::unique_ptr<lldb_private::LockFile> m_lock;53FileSpec m_file_spec;5455public:56ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error);57void Delete();58};5960static FileSpec JoinPath(const FileSpec &path1, const char *path2) {61FileSpec result_spec(path1);62result_spec.AppendPathComponent(path2);63return result_spec;64}6566static Status MakeDirectory(const FileSpec &dir_path) {67namespace fs = llvm::sys::fs;6869return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all);70}7172FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) {73const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir);74return JoinPath(modules_dir_spec, uuid.GetAsString().c_str());75}7677FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {78return FileSpec(module_file_spec.GetPath() + kSymFileExtension);79}8081void DeleteExistingModule(const FileSpec &root_dir_spec,82const FileSpec &sysroot_module_path_spec) {83Log *log = GetLog(LLDBLog::Modules);84UUID module_uuid;85{86auto module_sp =87std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec));88module_uuid = module_sp->GetUUID();89}9091if (!module_uuid.IsValid())92return;9394Status error;95ModuleLock lock(root_dir_spec, module_uuid, error);96if (error.Fail()) {97LLDB_LOGF(log, "Failed to lock module %s: %s",98module_uuid.GetAsString().c_str(), error.AsCString());99}100101namespace fs = llvm::sys::fs;102fs::file_status st;103if (status(sysroot_module_path_spec.GetPath(), st))104return;105106if (st.getLinkCount() > 2) // module is referred by other hosts.107return;108109const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid);110llvm::sys::fs::remove_directories(module_spec_dir.GetPath());111lock.Delete();112}113114void DecrementRefExistingModule(const FileSpec &root_dir_spec,115const FileSpec &sysroot_module_path_spec) {116// Remove $platform/.cache/$uuid folder if nobody else references it.117DeleteExistingModule(root_dir_spec, sysroot_module_path_spec);118119// Remove sysroot link.120llvm::sys::fs::remove(sysroot_module_path_spec.GetPath());121122FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec);123llvm::sys::fs::remove(symfile_spec.GetPath());124}125126Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec,127const char *hostname,128const FileSpec &platform_module_spec,129const FileSpec &local_module_spec,130bool delete_existing) {131const auto sysroot_module_path_spec =132JoinPath(JoinPath(root_dir_spec, hostname),133platform_module_spec.GetPath().c_str());134if (FileSystem::Instance().Exists(sysroot_module_path_spec)) {135if (!delete_existing)136return Status();137138DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);139}140141const auto error = MakeDirectory(142FileSpec(sysroot_module_path_spec.GetDirectory().AsCString()));143if (error.Fail())144return error;145146return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(),147sysroot_module_path_spec.GetPath());148}149150} // namespace151152ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid,153Status &error) {154const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName);155error = MakeDirectory(lock_dir_spec);156if (error.Fail())157return;158159m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str());160161auto file = FileSystem::Instance().Open(162m_file_spec, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate |163File::eOpenOptionCloseOnExec);164if (file)165m_file_up = std::move(file.get());166else {167m_file_up.reset();168error = Status(file.takeError());169return;170}171172m_lock = std::make_unique<lldb_private::LockFile>(m_file_up->GetDescriptor());173error = m_lock->WriteLock(0, 1);174if (error.Fail())175error.SetErrorStringWithFormat("Failed to lock file: %s",176error.AsCString());177}178179void ModuleLock::Delete() {180if (!m_file_up)181return;182183m_file_up->Close();184m_file_up.reset();185llvm::sys::fs::remove(m_file_spec.GetPath());186}187188/////////////////////////////////////////////////////////////////////////189190Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname,191const ModuleSpec &module_spec, const FileSpec &tmp_file,192const FileSpec &target_file) {193const auto module_spec_dir =194GetModuleDirectory(root_dir_spec, module_spec.GetUUID());195const auto module_file_path =196JoinPath(module_spec_dir, target_file.GetFilename().AsCString());197198const auto tmp_file_path = tmp_file.GetPath();199const auto err_code =200llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());201if (err_code)202return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(),203module_file_path.GetPath().c_str(),204err_code.message().c_str());205206const auto error = CreateHostSysRootModuleLink(207root_dir_spec, hostname, target_file, module_file_path, true);208if (error.Fail())209return Status("Failed to create link to %s: %s",210module_file_path.GetPath().c_str(), error.AsCString());211return Status();212}213214Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname,215const ModuleSpec &module_spec,216ModuleSP &cached_module_sp, bool *did_create_ptr) {217const auto find_it =218m_loaded_modules.find(module_spec.GetUUID().GetAsString());219if (find_it != m_loaded_modules.end()) {220cached_module_sp = (*find_it).second.lock();221if (cached_module_sp)222return Status();223m_loaded_modules.erase(find_it);224}225226const auto module_spec_dir =227GetModuleDirectory(root_dir_spec, module_spec.GetUUID());228const auto module_file_path = JoinPath(229module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString());230231if (!FileSystem::Instance().Exists(module_file_path))232return Status("Module %s not found", module_file_path.GetPath().c_str());233if (FileSystem::Instance().GetByteSize(module_file_path) !=234module_spec.GetObjectSize())235return Status("Module %s has invalid file size",236module_file_path.GetPath().c_str());237238// We may have already cached module but downloaded from an another host - in239// this case let's create a link to it.240auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname,241module_spec.GetFileSpec(),242module_file_path, false);243if (error.Fail())244return Status("Failed to create link to %s: %s",245module_file_path.GetPath().c_str(), error.AsCString());246247auto cached_module_spec(module_spec);248cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5249// content hash instead of real UUID.250cached_module_spec.GetFileSpec() = module_file_path;251cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();252253error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,254nullptr, nullptr, did_create_ptr, false);255if (error.Fail())256return error;257258FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());259if (FileSystem::Instance().Exists(symfile_spec))260cached_module_sp->SetSymbolFileFileSpec(symfile_spec);261262m_loaded_modules.insert(263std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));264265return Status();266}267268Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec,269const char *hostname,270const ModuleSpec &module_spec,271const ModuleDownloader &module_downloader,272const SymfileDownloader &symfile_downloader,273lldb::ModuleSP &cached_module_sp,274bool *did_create_ptr) {275const auto module_spec_dir =276GetModuleDirectory(root_dir_spec, module_spec.GetUUID());277auto error = MakeDirectory(module_spec_dir);278if (error.Fail())279return error;280281ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error);282if (error.Fail())283return Status("Failed to lock module %s: %s",284module_spec.GetUUID().GetAsString().c_str(),285error.AsCString());286287const auto escaped_hostname(GetEscapedHostname(hostname));288// Check local cache for a module.289error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,290cached_module_sp, did_create_ptr);291if (error.Success())292return error;293294const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);295error = module_downloader(module_spec, tmp_download_file_spec);296llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());297if (error.Fail())298return Status("Failed to download module: %s", error.AsCString());299300// Put downloaded file into local module cache.301error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,302tmp_download_file_spec, module_spec.GetFileSpec());303if (error.Fail())304return Status("Failed to put module into cache: %s", error.AsCString());305306tmp_file_remover.releaseFile();307error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,308cached_module_sp, did_create_ptr);309if (error.Fail())310return error;311312// Fetching a symbol file for the module313const auto tmp_download_sym_file_spec =314JoinPath(module_spec_dir, kTempSymFileName);315error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec);316llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());317if (error.Fail())318// Failed to download a symfile but fetching the module was successful. The319// module might contain the necessary symbols and the debugging is also320// possible without a symfile.321return Status();322323error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,324tmp_download_sym_file_spec,325GetSymbolFileSpec(module_spec.GetFileSpec()));326if (error.Fail())327return Status("Failed to put symbol file into cache: %s",328error.AsCString());329330tmp_symfile_remover.releaseFile();331332FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());333cached_module_sp->SetSymbolFileFileSpec(symfile_spec);334return Status();335}336337338