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