blob: 123b6876af22396104d67ed76d9263089818df19 [file] [log] [blame]
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +00001//===--------------------- 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
Zachary Turner01c32432017-02-14 19:06:07 +000010#include "lldb/Target/ModuleCache.h"
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000011
12#include "lldb/Core/Module.h"
Tamas Berghammer257e13a2015-12-10 17:08:23 +000013#include "lldb/Core/ModuleList.h"
Oleksiy Vyaloveda270e2015-03-12 18:18:03 +000014#include "lldb/Core/ModuleSpec.h"
Oleksiy Vyalov919ef9d2015-05-07 15:28:49 +000015#include "lldb/Host/File.h"
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000016#include "lldb/Host/FileSystem.h"
Oleksiy Vyalov919ef9d2015-05-07 15:28:49 +000017#include "lldb/Host/LockFile.h"
Zachary Turner6f9e6902017-03-03 20:56:28 +000018#include "lldb/Utility/Log.h"
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000019#include "llvm/Support/FileSystem.h"
Oleksiy Vyalov280d8dc2015-04-15 14:35:10 +000020#include "llvm/Support/FileUtilities.h"
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000021
22#include <assert.h>
23
24#include <cstdio>
Oleksiy Vyalov919ef9d2015-05-07 15:28:49 +000025
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000026using namespace lldb;
27using namespace lldb_private;
28
29namespace {
30
Kate Stoneb9c1b512016-09-06 20:57:50 +000031const char *kModulesSubdir = ".cache";
32const char *kLockDirName = ".lock";
33const char *kTempFileName = ".temp";
34const char *kTempSymFileName = ".symtemp";
35const char *kSymFileExtension = ".sym";
36const char *kFSIllegalChars = "\\/:*?\"<>|";
Oleksiy Vyalov3154e772016-05-24 18:09:05 +000037
Kate Stoneb9c1b512016-09-06 20:57:50 +000038std::string GetEscapedHostname(const char *hostname) {
Jason Molenda14699cf2016-10-21 02:32:08 +000039 if (hostname == nullptr)
40 hostname = "unknown";
Kate Stoneb9c1b512016-09-06 20:57:50 +000041 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;
Oleksiy Vyalov3154e772016-05-24 18:09:05 +000049}
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000050
Kate Stoneb9c1b512016-09-06 20:57:50 +000051class ModuleLock {
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +000052private:
Kate Stoneb9c1b512016-09-06 20:57:50 +000053 File m_file;
54 std::unique_ptr<lldb_private::LockFile> m_lock;
55 FileSpec m_file_spec;
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +000056
57public:
Kate Stoneb9c1b512016-09-06 20:57:50 +000058 ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Error &error);
59 void Delete();
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +000060};
61
Zachary Turner990e3cd2017-03-07 03:43:17 +000062static FileSpec JoinPath(const FileSpec &path1, const char *path2) {
Kate Stoneb9c1b512016-09-06 20:57:50 +000063 FileSpec result_spec(path1);
64 result_spec.AppendPathComponent(path2);
65 return result_spec;
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000066}
67
Zachary Turner990e3cd2017-03-07 03:43:17 +000068static 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))
Kate Stoneb9c1b512016-09-06 20:57:50 +000075 return Error("Invalid existing path");
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000076
Mehdi Aminic1edf562016-11-11 04:29:25 +000077 return Error();
Kate Stoneb9c1b512016-09-06 20:57:50 +000078 }
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000079
Kate Stoneb9c1b512016-09-06 20:57:50 +000080 return FileSystem::MakeDirectory(dir_path, eFilePermissionsDirectoryDefault);
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +000081}
82
Kate Stoneb9c1b512016-09-06 20:57:50 +000083FileSpec 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());
Oleksiy Vyalova9ea0712015-05-08 23:54:34 +000086}
87
Kate Stoneb9c1b512016-09-06 20:57:50 +000088FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {
Malcolm Parsons771ef6d2016-11-02 20:34:10 +000089 return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false);
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +000090}
91
Kate Stoneb9c1b512016-09-06 20:57:50 +000092void 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 }
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000101
Kate Stoneb9c1b512016-09-06 20:57:50 +0000102 if (!module_uuid.IsValid())
103 return;
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000104
Kate Stoneb9c1b512016-09-06 20:57:50 +0000105 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 }
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000112
Kate Stoneb9c1b512016-09-06 20:57:50 +0000113 auto link_count = FileSystem::GetHardlinkCount(sysroot_module_path_spec);
114 if (link_count == -1)
115 return;
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000116
Kate Stoneb9c1b512016-09-06 20:57:50 +0000117 if (link_count > 2) // module is referred by other hosts.
118 return;
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000119
Kate Stoneb9c1b512016-09-06 20:57:50 +0000120 const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid);
121 FileSystem::DeleteDirectory(module_spec_dir, true);
122 lock.Delete();
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000123}
124
Kate Stoneb9c1b512016-09-06 20:57:50 +0000125void 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);
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000129
Kate Stoneb9c1b512016-09-06 20:57:50 +0000130 // Remove sysroot link.
131 FileSystem::Unlink(sysroot_module_path_spec);
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000132
Kate Stoneb9c1b512016-09-06 20:57:50 +0000133 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);
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000136}
137
Kate Stoneb9c1b512016-09-06 20:57:50 +0000138Error 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)
Mehdi Aminic1edf562016-11-11 04:29:25 +0000148 return Error();
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000149
Kate Stoneb9c1b512016-09-06 20:57:50 +0000150 DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);
151 }
Oleksiy Vyalova9ea0712015-05-08 23:54:34 +0000152
Kate Stoneb9c1b512016-09-06 20:57:50 +0000153 const auto error = MakeDirectory(
154 FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false));
155 if (error.Fail())
156 return error;
Oleksiy Vyalova9ea0712015-05-08 23:54:34 +0000157
Kate Stoneb9c1b512016-09-06 20:57:50 +0000158 return FileSystem::Hardlink(sysroot_module_path_spec, local_module_spec);
Oleksiy Vyalova9ea0712015-05-08 23:54:34 +0000159}
160
Kate Stoneb9c1b512016-09-06 20:57:50 +0000161} // namespace
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000162
Kate Stoneb9c1b512016-09-06 20:57:50 +0000163ModuleLock::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;
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000169
Kate Stoneb9c1b512016-09-06 20:57:50 +0000170 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 }
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000178
Kate Stoneb9c1b512016-09-06 20:57:50 +0000179 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());
Tamas Berghammerec3f92a2015-08-12 11:10:25 +0000184}
185
Kate Stoneb9c1b512016-09-06 20:57:50 +0000186void ModuleLock::Delete() {
187 if (!m_file)
188 return;
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000189
Kate Stoneb9c1b512016-09-06 20:57:50 +0000190 m_file.Close();
191 FileSystem::Unlink(m_file_spec);
Oleksiy Vyalov2b38f332015-09-18 18:12:39 +0000192}
193
194/////////////////////////////////////////////////////////////////////////
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000195
Kate Stoneb9c1b512016-09-06 20:57:50 +0000196Error 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());
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000203
Kate Stoneb9c1b512016-09-06 20:57:50 +0000204 const auto tmp_file_path = tmp_file.GetPath();
Malcolm Parsons771ef6d2016-11-02 20:34:10 +0000205 const auto err_code =
206 llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());
Kate Stoneb9c1b512016-09-06 20:57:50 +0000207 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());
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000211
Kate Stoneb9c1b512016-09-06 20:57:50 +0000212 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());
Mehdi Aminic1edf562016-11-11 04:29:25 +0000217 return Error();
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000218}
219
Kate Stoneb9c1b512016-09-06 20:57:50 +0000220Error 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)
Mehdi Aminic1edf562016-11-11 04:29:25 +0000228 return Error();
Kate Stoneb9c1b512016-09-06 20:57:50 +0000229 m_loaded_modules.erase(find_it);
230 }
Oleksiy Vyaloveda270e2015-03-12 18:18:03 +0000231
Kate Stoneb9c1b512016-09-06 20:57:50 +0000232 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());
Oleksiy Vyaloveda270e2015-03-12 18:18:03 +0000236
Kate Stoneb9c1b512016-09-06 20:57:50 +0000237 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());
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000242
Kate Stoneb9c1b512016-09-06 20:57:50 +0000243 // 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());
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000251
Kate Stoneb9c1b512016-09-06 20:57:50 +0000252 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();
Tamas Berghammerec3f92a2015-08-12 11:10:25 +0000257
Kate Stoneb9c1b512016-09-06 20:57:50 +0000258 error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,
259 nullptr, nullptr, did_create_ptr, false);
260 if (error.Fail())
261 return error;
Tamas Berghammerec3f92a2015-08-12 11:10:25 +0000262
Kate Stoneb9c1b512016-09-06 20:57:50 +0000263 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
264 if (symfile_spec.Exists())
265 cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
Oleksiy Vyaloveda270e2015-03-12 18:18:03 +0000266
Kate Stoneb9c1b512016-09-06 20:57:50 +0000267 m_loaded_modules.insert(
268 std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));
269
Mehdi Aminic1edf562016-11-11 04:29:25 +0000270 return Error();
Oleksiy Vyalov63acdfd2015-03-10 01:15:28 +0000271}
272
Kate Stoneb9c1b512016-09-06 20:57:50 +0000273Error 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;
Oleksiy Vyalov919ef9d2015-05-07 15:28:49 +0000285
Kate Stoneb9c1b512016-09-06 20:57:50 +0000286 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());
Oleksiy Vyalov919ef9d2015-05-07 15:28:49 +0000291
Kate Stoneb9c1b512016-09-06 20:57:50 +0000292 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;
Oleksiy Vyalov280d8dc2015-04-15 14:35:10 +0000298
Kate Stoneb9c1b512016-09-06 20:57:50 +0000299 const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);
300 error = module_downloader(module_spec, tmp_download_file_spec);
Malcolm Parsons771ef6d2016-11-02 20:34:10 +0000301 llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());
Kate Stoneb9c1b512016-09-06 20:57:50 +0000302 if (error.Fail())
303 return Error("Failed to download module: %s", error.AsCString());
Oleksiy Vyalov280d8dc2015-04-15 14:35:10 +0000304
Kate Stoneb9c1b512016-09-06 20:57:50 +0000305 // 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());
Oleksiy Vyalov280d8dc2015-04-15 14:35:10 +0000310
Kate Stoneb9c1b512016-09-06 20:57:50 +0000311 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;
Tamas Berghammerec3f92a2015-08-12 11:10:25 +0000316
Kate Stoneb9c1b512016-09-06 20:57:50 +0000317 // 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);
Malcolm Parsons771ef6d2016-11-02 20:34:10 +0000321 llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());
Kate Stoneb9c1b512016-09-06 20:57:50 +0000322 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.
Mehdi Aminic1edf562016-11-11 04:29:25 +0000327 return Error();
Tamas Berghammerec3f92a2015-08-12 11:10:25 +0000328
Kate Stoneb9c1b512016-09-06 20:57:50 +0000329 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());
Tamas Berghammerec3f92a2015-08-12 11:10:25 +0000334
Kate Stoneb9c1b512016-09-06 20:57:50 +0000335 tmp_symfile_remover.releaseFile();
336
337 FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
338 cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
Mehdi Aminic1edf562016-11-11 04:29:25 +0000339 return Error();
Oleksiy Vyalov280d8dc2015-04-15 14:35:10 +0000340}