brintos

brintos / llvm-project-archived public Read only

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