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