1 //===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "ModuleCache.h"
12 #include "lldb/Core/Log.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/ModuleList.h"
15 #include "lldb/Core/ModuleSpec.h"
16 #include "lldb/Host/File.h"
17 #include "lldb/Host/FileSystem.h"
18 #include "lldb/Host/LockFile.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/FileUtilities.h"
27 using namespace lldb_private;
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 = "\\/:*?\"<>|";
39 GetEscapedHostname(const char* hostname)
41 std::string result(hostname);
42 size_t size = result.size();
43 for (size_t i = 0; i < size; ++i)
45 if ((result[i] >=1 && result[i] <= 31) ||
46 strchr(kFSIllegalChars, result[i]) != nullptr)
56 std::unique_ptr<lldb_private::LockFile> m_lock;
60 ModuleLock (const FileSpec &root_dir_spec, const UUID &uuid, Error& error);
65 JoinPath (const FileSpec &path1, const char* path2)
67 FileSpec result_spec (path1);
68 result_spec.AppendPathComponent (path2);
73 MakeDirectory (const FileSpec &dir_path)
75 if (dir_path.Exists ())
77 if (!dir_path.IsDirectory ())
78 return Error ("Invalid existing path");
83 return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault);
87 GetModuleDirectory (const FileSpec &root_dir_spec, const UUID &uuid)
89 const auto modules_dir_spec = JoinPath (root_dir_spec, kModulesSubdir);
90 return JoinPath (modules_dir_spec, uuid.GetAsString ().c_str ());
94 GetSymbolFileSpec(const FileSpec& module_file_spec)
96 return FileSpec((module_file_spec.GetPath() + kSymFileExtension).c_str(), false);
100 DeleteExistingModule (const FileSpec &root_dir_spec, const FileSpec &sysroot_module_path_spec)
102 Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES));
105 auto module_sp = std::make_shared<Module>(ModuleSpec (sysroot_module_path_spec));
106 module_uuid = module_sp->GetUUID ();
109 if (!module_uuid.IsValid ())
113 ModuleLock lock (root_dir_spec, module_uuid, error);
117 log->Printf ("Failed to lock module %s: %s",
118 module_uuid.GetAsString ().c_str (),
122 auto link_count = FileSystem::GetHardlinkCount (sysroot_module_path_spec);
123 if (link_count == -1)
126 if (link_count > 2) // module is referred by other hosts.
129 const auto module_spec_dir = GetModuleDirectory (root_dir_spec, module_uuid);
130 FileSystem::DeleteDirectory (module_spec_dir, true);
135 DecrementRefExistingModule (const FileSpec &root_dir_spec, const FileSpec &sysroot_module_path_spec)
137 // Remove $platform/.cache/$uuid folder if nobody else references it.
138 DeleteExistingModule (root_dir_spec, sysroot_module_path_spec);
140 // Remove sysroot link.
141 FileSystem::Unlink (sysroot_module_path_spec);
143 FileSpec symfile_spec = GetSymbolFileSpec (sysroot_module_path_spec);
144 if (symfile_spec.Exists ()) // delete module's symbol file if exists.
145 FileSystem::Unlink (symfile_spec);
149 CreateHostSysRootModuleLink (const FileSpec &root_dir_spec, const char *hostname,
150 const FileSpec &platform_module_spec,
151 const FileSpec &local_module_spec,
152 bool delete_existing)
154 const auto sysroot_module_path_spec = JoinPath (
155 JoinPath (root_dir_spec, hostname), platform_module_spec.GetPath ().c_str ());
156 if (sysroot_module_path_spec.Exists())
158 if (!delete_existing)
161 DecrementRefExistingModule (root_dir_spec, sysroot_module_path_spec);
164 const auto error = MakeDirectory (FileSpec (sysroot_module_path_spec.GetDirectory ().AsCString (), false));
168 return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec);
173 ModuleLock::ModuleLock (const FileSpec &root_dir_spec, const UUID &uuid, Error& error)
175 const auto lock_dir_spec = JoinPath (root_dir_spec, kLockDirName);
176 error = MakeDirectory (lock_dir_spec);
180 m_file_spec = JoinPath (lock_dir_spec, uuid.GetAsString ().c_str ());
181 m_file.Open (m_file_spec.GetCString (),
182 File::eOpenOptionWrite | File::eOpenOptionCanCreate | File::eOpenOptionCloseOnExec);
185 error.SetErrorToErrno ();
189 m_lock.reset (new lldb_private::LockFile (m_file.GetDescriptor ()));
190 error = m_lock->WriteLock (0, 1);
192 error.SetErrorStringWithFormat ("Failed to lock file: %s", error.AsCString ());
195 void ModuleLock::Delete ()
201 FileSystem::Unlink (m_file_spec);
204 /////////////////////////////////////////////////////////////////////////
207 ModuleCache::Put (const FileSpec &root_dir_spec,
208 const char *hostname,
209 const ModuleSpec &module_spec,
210 const FileSpec &tmp_file,
211 const FileSpec &target_file)
213 const auto module_spec_dir = GetModuleDirectory (root_dir_spec, module_spec.GetUUID ());
214 const auto module_file_path = JoinPath (module_spec_dir, target_file.GetFilename ().AsCString ());
216 const auto tmp_file_path = tmp_file.GetPath ();
217 const auto err_code = llvm::sys::fs::rename (tmp_file_path.c_str (), module_file_path.GetPath ().c_str ());
219 return Error ("Failed to rename file %s to %s: %s",
220 tmp_file_path.c_str (), module_file_path.GetPath ().c_str (), err_code.message ().c_str ());
222 const auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, target_file, module_file_path, true);
224 return Error ("Failed to create link to %s: %s", module_file_path.GetPath ().c_str (), error.AsCString ());
229 ModuleCache::Get (const FileSpec &root_dir_spec,
230 const char *hostname,
231 const ModuleSpec &module_spec,
232 ModuleSP &cached_module_sp,
233 bool *did_create_ptr)
235 const auto find_it = m_loaded_modules.find (module_spec.GetUUID ().GetAsString());
236 if (find_it != m_loaded_modules.end ())
238 cached_module_sp = (*find_it).second.lock ();
239 if (cached_module_sp)
241 m_loaded_modules.erase (find_it);
244 const auto module_spec_dir = GetModuleDirectory (root_dir_spec, module_spec.GetUUID ());
245 const auto module_file_path = JoinPath (module_spec_dir, module_spec.GetFileSpec ().GetFilename ().AsCString ());
247 if (!module_file_path.Exists ())
248 return Error ("Module %s not found", module_file_path.GetPath ().c_str ());
249 if (module_file_path.GetByteSize () != module_spec.GetObjectSize ())
250 return Error ("Module %s has invalid file size", module_file_path.GetPath ().c_str ());
252 // We may have already cached module but downloaded from an another host - in this case let's create a link to it.
253 auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, module_spec.GetFileSpec(), module_file_path, false);
255 return Error ("Failed to create link to %s: %s", module_file_path.GetPath().c_str(), error.AsCString());
257 auto cached_module_spec (module_spec);
258 cached_module_spec.GetUUID ().Clear (); // Clear UUID since it may contain md5 content hash instead of real UUID.
259 cached_module_spec.GetFileSpec () = module_file_path;
260 cached_module_spec.GetPlatformFileSpec () = module_spec.GetFileSpec ();
262 error = ModuleList::GetSharedModule(cached_module_spec,
271 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec ());
272 if (symfile_spec.Exists ())
273 cached_module_sp->SetSymbolFileFileSpec (symfile_spec);
275 m_loaded_modules.insert (std::make_pair (module_spec.GetUUID ().GetAsString (), cached_module_sp));
281 ModuleCache::GetAndPut (const FileSpec &root_dir_spec,
282 const char *hostname,
283 const ModuleSpec &module_spec,
284 const ModuleDownloader &module_downloader,
285 const SymfileDownloader &symfile_downloader,
286 lldb::ModuleSP &cached_module_sp,
287 bool *did_create_ptr)
289 const auto module_spec_dir = GetModuleDirectory (root_dir_spec, module_spec.GetUUID ());
290 auto error = MakeDirectory (module_spec_dir);
294 ModuleLock lock (root_dir_spec, module_spec.GetUUID (), error);
296 return Error("Failed to lock module %s: %s", module_spec.GetUUID ().GetAsString().c_str(), error.AsCString ());
298 const auto escaped_hostname(GetEscapedHostname(hostname));
299 // Check local cache for a module.
300 error = Get (root_dir_spec, escaped_hostname.c_str(), module_spec, cached_module_sp, did_create_ptr);
301 if (error.Success ())
304 const auto tmp_download_file_spec = JoinPath (module_spec_dir, kTempFileName);
305 error = module_downloader (module_spec, tmp_download_file_spec);
306 llvm::FileRemover tmp_file_remover (tmp_download_file_spec.GetPath ().c_str ());
308 return Error("Failed to download module: %s", error.AsCString ());
310 // Put downloaded file into local module cache.
311 error = Put (root_dir_spec, escaped_hostname.c_str(), module_spec, tmp_download_file_spec, module_spec.GetFileSpec ());
313 return Error ("Failed to put module into cache: %s", error.AsCString ());
315 tmp_file_remover.releaseFile ();
316 error = Get (root_dir_spec, escaped_hostname.c_str(), module_spec, cached_module_sp, did_create_ptr);
320 // Fetching a symbol file for the module
321 const auto tmp_download_sym_file_spec = JoinPath (module_spec_dir, kTempSymFileName);
322 error = symfile_downloader (cached_module_sp, tmp_download_sym_file_spec);
323 llvm::FileRemover tmp_symfile_remover (tmp_download_sym_file_spec.GetPath ().c_str ());
325 // Failed to download a symfile but fetching the module was successful. The module might
326 // contain the neccessary symbols and the debugging is also possible without a symfile.
329 error = Put (root_dir_spec, escaped_hostname.c_str(), module_spec, tmp_download_sym_file_spec, GetSymbolFileSpec(module_spec.GetFileSpec ()));
331 return Error ("Failed to put symbol file into cache: %s", error.AsCString ());
333 tmp_symfile_remover.releaseFile();
335 FileSpec symfile_spec = GetSymbolFileSpec (cached_module_sp->GetFileSpec ());
336 cached_module_sp->SetSymbolFileFileSpec (symfile_spec);