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