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 static 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 static Error MakeDirectory(const FileSpec &dir_path) { 69 llvm::sys::fs::file_status st; 70 if (status(dir_path.GetPath(), st)) 71 return Error("Could not stat path"); 72 73 if (exists(st)) { 74 if (!is_directory(st)) 75 return Error("Invalid existing path"); 76 77 return Error(); 78 } 79 80 return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault); 81 } 82 83 FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { 84 const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir); 85 return JoinPath(modules_dir_spec, uuid.GetAsString().c_str()); 86 } 87 88 FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { 89 return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false); 90 } 91 92 void DeleteExistingModule(const FileSpec &root_dir_spec, 93 const FileSpec &sysroot_module_path_spec) { 94 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 95 UUID module_uuid; 96 { 97 auto module_sp = 98 std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec)); 99 module_uuid = module_sp->GetUUID(); 100 } 101 102 if (!module_uuid.IsValid()) 103 return; 104 105 Error error; 106 ModuleLock lock(root_dir_spec, module_uuid, error); 107 if (error.Fail()) { 108 if (log) 109 log->Printf("Failed to lock module %s: %s", 110 module_uuid.GetAsString().c_str(), error.AsCString()); 111 } 112 113 auto link_count = FileSystem::GetHardlinkCount(sysroot_module_path_spec); 114 if (link_count == -1) 115 return; 116 117 if (link_count > 2) // module is referred by other hosts. 118 return; 119 120 const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid); 121 FileSystem::DeleteDirectory(module_spec_dir, true); 122 lock.Delete(); 123 } 124 125 void DecrementRefExistingModule(const FileSpec &root_dir_spec, 126 const FileSpec &sysroot_module_path_spec) { 127 // Remove $platform/.cache/$uuid folder if nobody else references it. 128 DeleteExistingModule(root_dir_spec, sysroot_module_path_spec); 129 130 // Remove sysroot link. 131 FileSystem::Unlink(sysroot_module_path_spec); 132 133 FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec); 134 if (symfile_spec.Exists()) // delete module's symbol file if exists. 135 FileSystem::Unlink(symfile_spec); 136 } 137 138 Error CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, 139 const char *hostname, 140 const FileSpec &platform_module_spec, 141 const FileSpec &local_module_spec, 142 bool delete_existing) { 143 const auto sysroot_module_path_spec = 144 JoinPath(JoinPath(root_dir_spec, hostname), 145 platform_module_spec.GetPath().c_str()); 146 if (sysroot_module_path_spec.Exists()) { 147 if (!delete_existing) 148 return Error(); 149 150 DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); 151 } 152 153 const auto error = MakeDirectory( 154 FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false)); 155 if (error.Fail()) 156 return error; 157 158 return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec); 159 } 160 161 } // namespace 162 163 ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, 164 Error &error) { 165 const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName); 166 error = MakeDirectory(lock_dir_spec); 167 if (error.Fail()) 168 return; 169 170 m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); 171 m_file.Open(m_file_spec.GetCString(), File::eOpenOptionWrite | 172 File::eOpenOptionCanCreate | 173 File::eOpenOptionCloseOnExec); 174 if (!m_file) { 175 error.SetErrorToErrno(); 176 return; 177 } 178 179 m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor())); 180 error = m_lock->WriteLock(0, 1); 181 if (error.Fail()) 182 error.SetErrorStringWithFormat("Failed to lock file: %s", 183 error.AsCString()); 184 } 185 186 void ModuleLock::Delete() { 187 if (!m_file) 188 return; 189 190 m_file.Close(); 191 FileSystem::Unlink(m_file_spec); 192 } 193 194 ///////////////////////////////////////////////////////////////////////// 195 196 Error ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname, 197 const ModuleSpec &module_spec, const FileSpec &tmp_file, 198 const FileSpec &target_file) { 199 const auto module_spec_dir = 200 GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 201 const auto module_file_path = 202 JoinPath(module_spec_dir, target_file.GetFilename().AsCString()); 203 204 const auto tmp_file_path = tmp_file.GetPath(); 205 const auto err_code = 206 llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath()); 207 if (err_code) 208 return Error("Failed to rename file %s to %s: %s", tmp_file_path.c_str(), 209 module_file_path.GetPath().c_str(), 210 err_code.message().c_str()); 211 212 const auto error = CreateHostSysRootModuleLink( 213 root_dir_spec, hostname, target_file, module_file_path, true); 214 if (error.Fail()) 215 return Error("Failed to create link to %s: %s", 216 module_file_path.GetPath().c_str(), error.AsCString()); 217 return Error(); 218 } 219 220 Error ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, 221 const ModuleSpec &module_spec, 222 ModuleSP &cached_module_sp, bool *did_create_ptr) { 223 const auto find_it = 224 m_loaded_modules.find(module_spec.GetUUID().GetAsString()); 225 if (find_it != m_loaded_modules.end()) { 226 cached_module_sp = (*find_it).second.lock(); 227 if (cached_module_sp) 228 return Error(); 229 m_loaded_modules.erase(find_it); 230 } 231 232 const auto module_spec_dir = 233 GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 234 const auto module_file_path = JoinPath( 235 module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); 236 237 if (!module_file_path.Exists()) 238 return Error("Module %s not found", module_file_path.GetPath().c_str()); 239 if (module_file_path.GetByteSize() != module_spec.GetObjectSize()) 240 return Error("Module %s has invalid file size", 241 module_file_path.GetPath().c_str()); 242 243 // We may have already cached module but downloaded from an another host - in 244 // this case let's create a link to it. 245 auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, 246 module_spec.GetFileSpec(), 247 module_file_path, false); 248 if (error.Fail()) 249 return Error("Failed to create link to %s: %s", 250 module_file_path.GetPath().c_str(), error.AsCString()); 251 252 auto cached_module_spec(module_spec); 253 cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 254 // content hash instead of real UUID. 255 cached_module_spec.GetFileSpec() = module_file_path; 256 cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); 257 258 error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, 259 nullptr, nullptr, did_create_ptr, false); 260 if (error.Fail()) 261 return error; 262 263 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 264 if (symfile_spec.Exists()) 265 cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 266 267 m_loaded_modules.insert( 268 std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp)); 269 270 return Error(); 271 } 272 273 Error ModuleCache::GetAndPut(const FileSpec &root_dir_spec, 274 const char *hostname, 275 const ModuleSpec &module_spec, 276 const ModuleDownloader &module_downloader, 277 const SymfileDownloader &symfile_downloader, 278 lldb::ModuleSP &cached_module_sp, 279 bool *did_create_ptr) { 280 const auto module_spec_dir = 281 GetModuleDirectory(root_dir_spec, module_spec.GetUUID()); 282 auto error = MakeDirectory(module_spec_dir); 283 if (error.Fail()) 284 return error; 285 286 ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error); 287 if (error.Fail()) 288 return Error("Failed to lock module %s: %s", 289 module_spec.GetUUID().GetAsString().c_str(), 290 error.AsCString()); 291 292 const auto escaped_hostname(GetEscapedHostname(hostname)); 293 // Check local cache for a module. 294 error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 295 cached_module_sp, did_create_ptr); 296 if (error.Success()) 297 return error; 298 299 const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName); 300 error = module_downloader(module_spec, tmp_download_file_spec); 301 llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath()); 302 if (error.Fail()) 303 return Error("Failed to download module: %s", 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 Error("Failed to put module into cache: %s", error.AsCString()); 310 311 tmp_file_remover.releaseFile(); 312 error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec, 313 cached_module_sp, did_create_ptr); 314 if (error.Fail()) 315 return error; 316 317 // Fetching a symbol file for the module 318 const auto tmp_download_sym_file_spec = 319 JoinPath(module_spec_dir, kTempSymFileName); 320 error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec); 321 llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath()); 322 if (error.Fail()) 323 // Failed to download a symfile but fetching the module was successful. The 324 // module might 325 // contain the necessary symbols and the debugging is also possible without 326 // a symfile. 327 return Error(); 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 Error("Failed to put symbol file into cache: %s", error.AsCString()); 334 335 tmp_symfile_remover.releaseFile(); 336 337 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); 338 cached_module_sp->SetSymbolFileFileSpec(symfile_spec); 339 return Error(); 340 } 341