]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Utility/ModuleCache.cpp
Merge ACPICA 20161222.
[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 const char* kFSIllegalChars = "\\/:*?\"<>|";
37
38 std::string
39 GetEscapedHostname(const char* hostname)
40 {
41     std::string result(hostname);
42     size_t size = result.size();
43     for (size_t i = 0; i < size; ++i)
44     {
45         if ((result[i] >=1 && result[i] <= 31) ||
46             strchr(kFSIllegalChars, result[i]) != nullptr)
47             result[i] = '_';
48     }
49     return result;
50 }
51
52 class ModuleLock
53 {
54 private:
55     File m_file;
56     std::unique_ptr<lldb_private::LockFile> m_lock;
57     FileSpec m_file_spec;
58
59 public:
60     ModuleLock (const FileSpec &root_dir_spec, const UUID &uuid, Error& error);
61     void Delete ();
62 };
63
64 FileSpec
65 JoinPath (const FileSpec &path1, const char* path2)
66 {
67     FileSpec result_spec (path1);
68     result_spec.AppendPathComponent (path2);
69     return result_spec;
70 }
71
72 Error
73 MakeDirectory (const FileSpec &dir_path)
74 {
75     if (dir_path.Exists ())
76     {
77         if (!dir_path.IsDirectory ())
78             return Error ("Invalid existing path");
79
80         return Error ();
81     }
82
83     return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault);
84 }
85
86 FileSpec
87 GetModuleDirectory (const FileSpec &root_dir_spec, const UUID &uuid)
88 {
89     const auto modules_dir_spec = JoinPath (root_dir_spec, kModulesSubdir);
90     return JoinPath (modules_dir_spec, uuid.GetAsString ().c_str ());
91 }
92
93 FileSpec
94 GetSymbolFileSpec(const FileSpec& module_file_spec)
95 {
96     return FileSpec((module_file_spec.GetPath() + kSymFileExtension).c_str(), false);
97 }
98
99 void
100 DeleteExistingModule (const FileSpec &root_dir_spec, const FileSpec &sysroot_module_path_spec)
101 {
102     Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES));
103     UUID module_uuid;
104     {
105         auto module_sp = std::make_shared<Module>(ModuleSpec (sysroot_module_path_spec));
106         module_uuid = module_sp->GetUUID ();
107     }
108
109     if (!module_uuid.IsValid ())
110         return;
111
112     Error error;
113     ModuleLock lock (root_dir_spec,  module_uuid, error);
114     if (error.Fail ())
115     {
116         if (log)
117             log->Printf ("Failed to lock module %s: %s",
118                          module_uuid.GetAsString ().c_str (),
119                          error.AsCString ());
120     }
121
122     auto link_count = FileSystem::GetHardlinkCount (sysroot_module_path_spec);
123     if (link_count == -1)
124         return;
125
126     if (link_count > 2)  // module is referred by other hosts.
127         return;
128
129     const auto module_spec_dir = GetModuleDirectory (root_dir_spec, module_uuid);
130     FileSystem::DeleteDirectory (module_spec_dir, true);
131     lock.Delete();
132 }
133
134 void
135 DecrementRefExistingModule (const FileSpec &root_dir_spec, const FileSpec &sysroot_module_path_spec)
136 {
137     // Remove $platform/.cache/$uuid folder if nobody else references it.
138     DeleteExistingModule (root_dir_spec, sysroot_module_path_spec);
139
140     // Remove sysroot link.
141     FileSystem::Unlink (sysroot_module_path_spec);
142
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);
146 }
147
148 Error
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)
153 {
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())
157     {
158         if (!delete_existing)
159             return Error ();
160
161         DecrementRefExistingModule (root_dir_spec, sysroot_module_path_spec);
162     }
163
164     const auto error = MakeDirectory (FileSpec (sysroot_module_path_spec.GetDirectory ().AsCString (), false));
165     if (error.Fail ())
166         return error;
167
168     return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec);
169 }
170
171 }  // namespace
172
173 ModuleLock::ModuleLock (const FileSpec &root_dir_spec, const UUID &uuid, Error& error)
174 {
175     const auto lock_dir_spec = JoinPath (root_dir_spec, kLockDirName);
176     error = MakeDirectory (lock_dir_spec);
177     if (error.Fail ())
178         return;
179
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);
183     if (!m_file)
184     {
185         error.SetErrorToErrno ();
186         return;
187     }
188
189     m_lock.reset (new lldb_private::LockFile (m_file.GetDescriptor ()));
190     error = m_lock->WriteLock (0, 1);
191     if (error.Fail ())
192         error.SetErrorStringWithFormat ("Failed to lock file: %s", error.AsCString ());
193 }
194
195 void ModuleLock::Delete ()
196 {
197     if (!m_file)
198         return;
199
200     m_file.Close ();
201     FileSystem::Unlink (m_file_spec);
202 }
203
204 /////////////////////////////////////////////////////////////////////////
205
206 Error
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)
212 {
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 ());
215
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 ());
218     if (err_code)
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 ());
221
222     const auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname, target_file, module_file_path, true);
223     if (error.Fail ())
224         return Error ("Failed to create link to %s: %s", module_file_path.GetPath ().c_str (), error.AsCString ());
225     return Error ();
226 }
227
228 Error
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)
234 {
235     const auto find_it = m_loaded_modules.find (module_spec.GetUUID ().GetAsString());
236     if (find_it != m_loaded_modules.end ())
237     {
238         cached_module_sp = (*find_it).second.lock ();
239         if (cached_module_sp)
240             return Error ();
241         m_loaded_modules.erase (find_it);
242     }
243
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 ());
246
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 ());
251
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);
254     if (error.Fail ())
255         return Error ("Failed to create link to %s: %s", module_file_path.GetPath().c_str(), error.AsCString());
256
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 ();
261     
262     error = ModuleList::GetSharedModule(cached_module_spec,
263                                         cached_module_sp,
264                                         nullptr,
265                                         nullptr,
266                                         did_create_ptr,
267                                         false);
268     if (error.Fail())
269         return error;
270
271     FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec ());
272     if (symfile_spec.Exists ())
273         cached_module_sp->SetSymbolFileFileSpec (symfile_spec);
274
275     m_loaded_modules.insert (std::make_pair (module_spec.GetUUID ().GetAsString (), cached_module_sp));
276
277     return Error ();
278 }
279
280 Error
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)
288 {
289     const auto module_spec_dir = GetModuleDirectory (root_dir_spec, module_spec.GetUUID ());
290     auto error = MakeDirectory (module_spec_dir);
291     if (error.Fail ())
292         return error;
293
294     ModuleLock lock (root_dir_spec,  module_spec.GetUUID (), error);
295     if (error.Fail ())
296         return Error("Failed to lock module %s: %s", module_spec.GetUUID ().GetAsString().c_str(), error.AsCString ());
297
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 ())
302         return error;
303
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 ());
307     if (error.Fail ())
308         return Error("Failed to download module: %s", error.AsCString ());
309
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 ());
312     if (error.Fail ())
313         return Error ("Failed to put module into cache: %s", error.AsCString ());
314
315     tmp_file_remover.releaseFile ();
316     error = Get (root_dir_spec, escaped_hostname.c_str(), module_spec, cached_module_sp, did_create_ptr);
317     if (error.Fail ())
318         return error;
319
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 ());
324     if (error.Fail ())
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.
327         return Error ();
328
329     error = Put (root_dir_spec, escaped_hostname.c_str(), module_spec, tmp_download_sym_file_spec, GetSymbolFileSpec(module_spec.GetFileSpec ()));
330     if (error.Fail ())
331         return Error ("Failed to put symbol file into cache: %s", error.AsCString ());
332     
333     tmp_symfile_remover.releaseFile();
334
335     FileSpec symfile_spec = GetSymbolFileSpec (cached_module_sp->GetFileSpec ());
336     cached_module_sp->SetSymbolFileFileSpec (symfile_spec);
337     return Error ();
338 }