1 | //===-- ModuleList.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/Core/ModuleList.h" |
10 | #include "lldb/Core/Module.h" |
11 | #include "lldb/Core/ModuleSpec.h" |
12 | #include "lldb/Core/PluginManager.h" |
13 | #include "lldb/Host/FileSystem.h" |
14 | #include "lldb/Interpreter/OptionValueFileSpec.h" |
15 | #include "lldb/Interpreter/OptionValueFileSpecList.h" |
16 | #include "lldb/Interpreter/OptionValueProperties.h" |
17 | #include "lldb/Interpreter/Property.h" |
18 | #include "lldb/Symbol/ObjectFile.h" |
19 | #include "lldb/Symbol/SymbolContext.h" |
20 | #include "lldb/Symbol/TypeList.h" |
21 | #include "lldb/Symbol/VariableList.h" |
22 | #include "lldb/Utility/ArchSpec.h" |
23 | #include "lldb/Utility/ConstString.h" |
24 | #include "lldb/Utility/FileSpecList.h" |
25 | #include "lldb/Utility/LLDBLog.h" |
26 | #include "lldb/Utility/Log.h" |
27 | #include "lldb/Utility/UUID.h" |
28 | #include "lldb/lldb-defines.h" |
29 | |
30 | #if defined(_WIN32) |
31 | #include "lldb/Host/windows/PosixApi.h" |
32 | #endif |
33 | |
34 | #include "clang/Driver/Driver.h" |
35 | #include "llvm/ADT/StringRef.h" |
36 | #include "llvm/Support/FileSystem.h" |
37 | #include "llvm/Support/Threading.h" |
38 | #include "llvm/Support/raw_ostream.h" |
39 | |
40 | #include <chrono> |
41 | #include <memory> |
42 | #include <mutex> |
43 | #include <string> |
44 | #include <utility> |
45 | |
46 | namespace lldb_private { |
47 | class Function; |
48 | } |
49 | namespace lldb_private { |
50 | class RegularExpression; |
51 | } |
52 | namespace lldb_private { |
53 | class Stream; |
54 | } |
55 | namespace lldb_private { |
56 | class SymbolFile; |
57 | } |
58 | namespace lldb_private { |
59 | class Target; |
60 | } |
61 | |
62 | using namespace lldb; |
63 | using namespace lldb_private; |
64 | |
65 | namespace { |
66 | |
67 | #define LLDB_PROPERTIES_modulelist |
68 | #include "CoreProperties.inc" |
69 | |
70 | enum { |
71 | #define LLDB_PROPERTIES_modulelist |
72 | #include "CorePropertiesEnum.inc" |
73 | }; |
74 | |
75 | } // namespace |
76 | |
77 | ModuleListProperties::ModuleListProperties() { |
78 | m_collection_sp = std::make_shared<OptionValueProperties>(args: "symbols" ); |
79 | m_collection_sp->Initialize(setting_definitions: g_modulelist_properties); |
80 | m_collection_sp->SetValueChangedCallback(property_idx: ePropertySymLinkPaths, |
81 | callback: [this] { UpdateSymlinkMappings(); }); |
82 | |
83 | llvm::SmallString<128> path; |
84 | if (clang::driver::Driver::getDefaultModuleCachePath(Result&: path)) { |
85 | lldbassert(SetClangModulesCachePath(FileSpec(path))); |
86 | } |
87 | |
88 | path.clear(); |
89 | if (llvm::sys::path::cache_directory(result&: path)) { |
90 | llvm::sys::path::append(path, a: "lldb" ); |
91 | llvm::sys::path::append(path, a: "IndexCache" ); |
92 | lldbassert(SetLLDBIndexCachePath(FileSpec(path))); |
93 | } |
94 | |
95 | } |
96 | |
97 | bool ModuleListProperties::GetEnableExternalLookup() const { |
98 | const uint32_t idx = ePropertyEnableExternalLookup; |
99 | return GetPropertyAtIndexAs<bool>( |
100 | idx, g_modulelist_properties[idx].default_uint_value != 0); |
101 | } |
102 | |
103 | bool ModuleListProperties::SetEnableExternalLookup(bool new_value) { |
104 | return SetPropertyAtIndex(ePropertyEnableExternalLookup, new_value); |
105 | } |
106 | |
107 | SymbolDownload ModuleListProperties::GetSymbolAutoDownload() const { |
108 | // Backward compatibility alias. |
109 | if (GetPropertyAtIndexAs<bool>(ePropertyEnableBackgroundLookup, false)) |
110 | return eSymbolDownloadBackground; |
111 | |
112 | const uint32_t idx = ePropertyAutoDownload; |
113 | return GetPropertyAtIndexAs<lldb::SymbolDownload>( |
114 | idx, static_cast<lldb::SymbolDownload>( |
115 | g_modulelist_properties[idx].default_uint_value)); |
116 | } |
117 | |
118 | FileSpec ModuleListProperties::GetClangModulesCachePath() const { |
119 | const uint32_t idx = ePropertyClangModulesCachePath; |
120 | return GetPropertyAtIndexAs<FileSpec>(idx, default_value: {}); |
121 | } |
122 | |
123 | bool ModuleListProperties::SetClangModulesCachePath(const FileSpec &path) { |
124 | const uint32_t idx = ePropertyClangModulesCachePath; |
125 | return SetPropertyAtIndex(idx, t: path); |
126 | } |
127 | |
128 | FileSpec ModuleListProperties::GetLLDBIndexCachePath() const { |
129 | const uint32_t idx = ePropertyLLDBIndexCachePath; |
130 | return GetPropertyAtIndexAs<FileSpec>(idx, default_value: {}); |
131 | } |
132 | |
133 | bool ModuleListProperties::SetLLDBIndexCachePath(const FileSpec &path) { |
134 | const uint32_t idx = ePropertyLLDBIndexCachePath; |
135 | return SetPropertyAtIndex(idx, t: path); |
136 | } |
137 | |
138 | bool ModuleListProperties::GetEnableLLDBIndexCache() const { |
139 | const uint32_t idx = ePropertyEnableLLDBIndexCache; |
140 | return GetPropertyAtIndexAs<bool>( |
141 | idx, g_modulelist_properties[idx].default_uint_value != 0); |
142 | } |
143 | |
144 | bool ModuleListProperties::SetEnableLLDBIndexCache(bool new_value) { |
145 | return SetPropertyAtIndex(ePropertyEnableLLDBIndexCache, new_value); |
146 | } |
147 | |
148 | uint64_t ModuleListProperties::GetLLDBIndexCacheMaxByteSize() { |
149 | const uint32_t idx = ePropertyLLDBIndexCacheMaxByteSize; |
150 | return GetPropertyAtIndexAs<uint64_t>( |
151 | idx, g_modulelist_properties[idx].default_uint_value); |
152 | } |
153 | |
154 | uint64_t ModuleListProperties::GetLLDBIndexCacheMaxPercent() { |
155 | const uint32_t idx = ePropertyLLDBIndexCacheMaxPercent; |
156 | return GetPropertyAtIndexAs<uint64_t>( |
157 | idx, g_modulelist_properties[idx].default_uint_value); |
158 | } |
159 | |
160 | uint64_t ModuleListProperties::GetLLDBIndexCacheExpirationDays() { |
161 | const uint32_t idx = ePropertyLLDBIndexCacheExpirationDays; |
162 | return GetPropertyAtIndexAs<uint64_t>( |
163 | idx, g_modulelist_properties[idx].default_uint_value); |
164 | } |
165 | |
166 | void ModuleListProperties::UpdateSymlinkMappings() { |
167 | FileSpecList list = |
168 | GetPropertyAtIndexAs<FileSpecList>(ePropertySymLinkPaths, {}); |
169 | llvm::sys::ScopedWriter lock(m_symlink_paths_mutex); |
170 | const bool notify = false; |
171 | m_symlink_paths.Clear(notify); |
172 | for (auto symlink : list) { |
173 | FileSpec resolved; |
174 | Status status = FileSystem::Instance().Readlink(symlink, resolved); |
175 | if (status.Success()) |
176 | m_symlink_paths.Append(symlink.GetPath(), resolved.GetPath(), notify); |
177 | } |
178 | } |
179 | |
180 | PathMappingList ModuleListProperties::GetSymlinkMappings() const { |
181 | llvm::sys::ScopedReader lock(m_symlink_paths_mutex); |
182 | return m_symlink_paths; |
183 | } |
184 | |
185 | bool ModuleListProperties::GetLoadSymbolOnDemand() { |
186 | const uint32_t idx = ePropertyLoadSymbolOnDemand; |
187 | return GetPropertyAtIndexAs<bool>( |
188 | idx, g_modulelist_properties[idx].default_uint_value != 0); |
189 | } |
190 | |
191 | ModuleList::ModuleList() : m_modules(), m_modules_mutex() {} |
192 | |
193 | ModuleList::ModuleList(const ModuleList &rhs) : m_modules(), m_modules_mutex() { |
194 | std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex); |
195 | std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex); |
196 | m_modules = rhs.m_modules; |
197 | } |
198 | |
199 | ModuleList::ModuleList(ModuleList::Notifier *notifier) |
200 | : m_modules(), m_modules_mutex(), m_notifier(notifier) {} |
201 | |
202 | const ModuleList &ModuleList::operator=(const ModuleList &rhs) { |
203 | if (this != &rhs) { |
204 | std::lock(l1&: m_modules_mutex, l2&: rhs.m_modules_mutex); |
205 | std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex, |
206 | std::adopt_lock); |
207 | std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex, |
208 | std::adopt_lock); |
209 | m_modules = rhs.m_modules; |
210 | } |
211 | return *this; |
212 | } |
213 | |
214 | ModuleList::~ModuleList() = default; |
215 | |
216 | void ModuleList::AppendImpl(const ModuleSP &module_sp, bool use_notifier) { |
217 | if (module_sp) { |
218 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
219 | // We are required to keep the first element of the Module List as the |
220 | // executable module. So check here and if the first module is NOT an |
221 | // but the new one is, we insert this module at the beginning, rather than |
222 | // at the end. |
223 | // We don't need to do any of this if the list is empty: |
224 | if (m_modules.empty()) { |
225 | m_modules.push_back(x: module_sp); |
226 | } else { |
227 | // Since producing the ObjectFile may take some work, first check the 0th |
228 | // element, and only if that's NOT an executable look at the incoming |
229 | // ObjectFile. That way in the normal case we only look at the element |
230 | // 0 ObjectFile. |
231 | const bool elem_zero_is_executable |
232 | = m_modules[0]->GetObjectFile()->GetType() |
233 | == ObjectFile::Type::eTypeExecutable; |
234 | lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); |
235 | if (!elem_zero_is_executable && obj |
236 | && obj->GetType() == ObjectFile::Type::eTypeExecutable) { |
237 | m_modules.insert(position: m_modules.begin(), x: module_sp); |
238 | } else { |
239 | m_modules.push_back(x: module_sp); |
240 | } |
241 | } |
242 | if (use_notifier && m_notifier) |
243 | m_notifier->NotifyModuleAdded(module_list: *this, module_sp); |
244 | } |
245 | } |
246 | |
247 | void ModuleList::Append(const ModuleSP &module_sp, bool notify) { |
248 | AppendImpl(module_sp, use_notifier: notify); |
249 | } |
250 | |
251 | void ModuleList::ReplaceEquivalent( |
252 | const ModuleSP &module_sp, |
253 | llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules) { |
254 | if (module_sp) { |
255 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
256 | |
257 | // First remove any equivalent modules. Equivalent modules are modules |
258 | // whose path, platform path and architecture match. |
259 | ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(), |
260 | module_sp->GetArchitecture()); |
261 | equivalent_module_spec.GetPlatformFileSpec() = |
262 | module_sp->GetPlatformFileSpec(); |
263 | |
264 | size_t idx = 0; |
265 | while (idx < m_modules.size()) { |
266 | ModuleSP test_module_sp(m_modules[idx]); |
267 | if (test_module_sp->MatchesModuleSpec(module_ref: equivalent_module_spec)) { |
268 | if (old_modules) |
269 | old_modules->push_back(Elt: test_module_sp); |
270 | RemoveImpl(pos: m_modules.begin() + idx); |
271 | } else { |
272 | ++idx; |
273 | } |
274 | } |
275 | // Now add the new module to the list |
276 | Append(module_sp); |
277 | } |
278 | } |
279 | |
280 | bool ModuleList::AppendIfNeeded(const ModuleSP &new_module, bool notify) { |
281 | if (new_module) { |
282 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
283 | for (const ModuleSP &module_sp : m_modules) { |
284 | if (module_sp.get() == new_module.get()) |
285 | return false; // Already in the list |
286 | } |
287 | // Only push module_sp on the list if it wasn't already in there. |
288 | Append(module_sp: new_module, notify); |
289 | return true; |
290 | } |
291 | return false; |
292 | } |
293 | |
294 | void ModuleList::Append(const ModuleList &module_list) { |
295 | for (auto pos : module_list.m_modules) |
296 | Append(module_sp: pos); |
297 | } |
298 | |
299 | bool ModuleList::AppendIfNeeded(const ModuleList &module_list) { |
300 | bool any_in = false; |
301 | for (auto pos : module_list.m_modules) { |
302 | if (AppendIfNeeded(new_module: pos)) |
303 | any_in = true; |
304 | } |
305 | return any_in; |
306 | } |
307 | |
308 | bool ModuleList::RemoveImpl(const ModuleSP &module_sp, bool use_notifier) { |
309 | if (module_sp) { |
310 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
311 | collection::iterator pos, end = m_modules.end(); |
312 | for (pos = m_modules.begin(); pos != end; ++pos) { |
313 | if (pos->get() == module_sp.get()) { |
314 | m_modules.erase(position: pos); |
315 | if (use_notifier && m_notifier) |
316 | m_notifier->NotifyModuleRemoved(module_list: *this, module_sp); |
317 | return true; |
318 | } |
319 | } |
320 | } |
321 | return false; |
322 | } |
323 | |
324 | ModuleList::collection::iterator |
325 | ModuleList::RemoveImpl(ModuleList::collection::iterator pos, |
326 | bool use_notifier) { |
327 | ModuleSP module_sp(*pos); |
328 | collection::iterator retval = m_modules.erase(position: pos); |
329 | if (use_notifier && m_notifier) |
330 | m_notifier->NotifyModuleRemoved(module_list: *this, module_sp); |
331 | return retval; |
332 | } |
333 | |
334 | bool ModuleList::Remove(const ModuleSP &module_sp, bool notify) { |
335 | return RemoveImpl(module_sp, use_notifier: notify); |
336 | } |
337 | |
338 | bool ModuleList::ReplaceModule(const lldb::ModuleSP &old_module_sp, |
339 | const lldb::ModuleSP &new_module_sp) { |
340 | if (!RemoveImpl(module_sp: old_module_sp, use_notifier: false)) |
341 | return false; |
342 | AppendImpl(module_sp: new_module_sp, use_notifier: false); |
343 | if (m_notifier) |
344 | m_notifier->NotifyModuleUpdated(module_list: *this, old_module_sp, new_module_sp); |
345 | return true; |
346 | } |
347 | |
348 | bool ModuleList::RemoveIfOrphaned(const Module *module_ptr) { |
349 | if (module_ptr) { |
350 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
351 | collection::iterator pos, end = m_modules.end(); |
352 | for (pos = m_modules.begin(); pos != end; ++pos) { |
353 | if (pos->get() == module_ptr) { |
354 | if (pos->use_count() == 1) { |
355 | pos = RemoveImpl(pos); |
356 | return true; |
357 | } else |
358 | return false; |
359 | } |
360 | } |
361 | } |
362 | return false; |
363 | } |
364 | |
365 | size_t ModuleList::RemoveOrphans(bool mandatory) { |
366 | std::unique_lock<std::recursive_mutex> lock(m_modules_mutex, std::defer_lock); |
367 | |
368 | if (mandatory) { |
369 | lock.lock(); |
370 | } else { |
371 | // Not mandatory, remove orphans if we can get the mutex |
372 | if (!lock.try_lock()) |
373 | return 0; |
374 | } |
375 | size_t remove_count = 0; |
376 | // Modules might hold shared pointers to other modules, so removing one |
377 | // module might make other modules orphans. Keep removing modules until |
378 | // there are no further modules that can be removed. |
379 | bool made_progress = true; |
380 | while (made_progress) { |
381 | // Keep track if we make progress this iteration. |
382 | made_progress = false; |
383 | collection::iterator pos = m_modules.begin(); |
384 | while (pos != m_modules.end()) { |
385 | if (pos->use_count() == 1) { |
386 | pos = RemoveImpl(pos); |
387 | ++remove_count; |
388 | // We did make progress. |
389 | made_progress = true; |
390 | } else { |
391 | ++pos; |
392 | } |
393 | } |
394 | } |
395 | return remove_count; |
396 | } |
397 | |
398 | size_t ModuleList::Remove(ModuleList &module_list) { |
399 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
400 | size_t num_removed = 0; |
401 | collection::iterator pos, end = module_list.m_modules.end(); |
402 | for (pos = module_list.m_modules.begin(); pos != end; ++pos) { |
403 | if (Remove(module_sp: *pos, notify: false /* notify */)) |
404 | ++num_removed; |
405 | } |
406 | if (m_notifier) |
407 | m_notifier->NotifyModulesRemoved(module_list); |
408 | return num_removed; |
409 | } |
410 | |
411 | void ModuleList::Clear() { ClearImpl(); } |
412 | |
413 | void ModuleList::Destroy() { ClearImpl(); } |
414 | |
415 | void ModuleList::ClearImpl(bool use_notifier) { |
416 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
417 | if (use_notifier && m_notifier) |
418 | m_notifier->NotifyWillClearList(module_list: *this); |
419 | m_modules.clear(); |
420 | } |
421 | |
422 | Module *ModuleList::GetModulePointerAtIndex(size_t idx) const { |
423 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
424 | if (idx < m_modules.size()) |
425 | return m_modules[idx].get(); |
426 | return nullptr; |
427 | } |
428 | |
429 | ModuleSP ModuleList::GetModuleAtIndex(size_t idx) const { |
430 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
431 | return GetModuleAtIndexUnlocked(idx); |
432 | } |
433 | |
434 | ModuleSP ModuleList::GetModuleAtIndexUnlocked(size_t idx) const { |
435 | ModuleSP module_sp; |
436 | if (idx < m_modules.size()) |
437 | module_sp = m_modules[idx]; |
438 | return module_sp; |
439 | } |
440 | |
441 | void ModuleList::FindFunctions(ConstString name, |
442 | FunctionNameType name_type_mask, |
443 | const ModuleFunctionSearchOptions &options, |
444 | SymbolContextList &sc_list) const { |
445 | const size_t old_size = sc_list.GetSize(); |
446 | |
447 | if (name_type_mask & eFunctionNameTypeAuto) { |
448 | Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown); |
449 | |
450 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
451 | for (const ModuleSP &module_sp : m_modules) { |
452 | module_sp->FindFunctions(lookup_info, parent_decl_ctx: CompilerDeclContext(), options, |
453 | sc_list); |
454 | } |
455 | |
456 | const size_t new_size = sc_list.GetSize(); |
457 | |
458 | if (old_size < new_size) |
459 | lookup_info.Prune(sc_list, start_idx: old_size); |
460 | } else { |
461 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
462 | for (const ModuleSP &module_sp : m_modules) { |
463 | module_sp->FindFunctions(name, parent_decl_ctx: CompilerDeclContext(), name_type_mask, |
464 | options, sc_list); |
465 | } |
466 | } |
467 | } |
468 | |
469 | void ModuleList::FindFunctionSymbols(ConstString name, |
470 | lldb::FunctionNameType name_type_mask, |
471 | SymbolContextList &sc_list) { |
472 | const size_t old_size = sc_list.GetSize(); |
473 | |
474 | if (name_type_mask & eFunctionNameTypeAuto) { |
475 | Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown); |
476 | |
477 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
478 | for (const ModuleSP &module_sp : m_modules) { |
479 | module_sp->FindFunctionSymbols(name: lookup_info.GetLookupName(), |
480 | name_type_mask: lookup_info.GetNameTypeMask(), sc_list); |
481 | } |
482 | |
483 | const size_t new_size = sc_list.GetSize(); |
484 | |
485 | if (old_size < new_size) |
486 | lookup_info.Prune(sc_list, start_idx: old_size); |
487 | } else { |
488 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
489 | for (const ModuleSP &module_sp : m_modules) { |
490 | module_sp->FindFunctionSymbols(name, name_type_mask, sc_list); |
491 | } |
492 | } |
493 | } |
494 | |
495 | void ModuleList::FindFunctions(const RegularExpression &name, |
496 | const ModuleFunctionSearchOptions &options, |
497 | SymbolContextList &sc_list) { |
498 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
499 | for (const ModuleSP &module_sp : m_modules) |
500 | module_sp->FindFunctions(regex: name, options, sc_list); |
501 | } |
502 | |
503 | void ModuleList::FindCompileUnits(const FileSpec &path, |
504 | SymbolContextList &sc_list) const { |
505 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
506 | for (const ModuleSP &module_sp : m_modules) |
507 | module_sp->FindCompileUnits(path, sc_list); |
508 | } |
509 | |
510 | void ModuleList::FindGlobalVariables(ConstString name, size_t max_matches, |
511 | VariableList &variable_list) const { |
512 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
513 | for (const ModuleSP &module_sp : m_modules) { |
514 | module_sp->FindGlobalVariables(name, parent_decl_ctx: CompilerDeclContext(), max_matches, |
515 | variable_list); |
516 | } |
517 | } |
518 | |
519 | void ModuleList::FindGlobalVariables(const RegularExpression ®ex, |
520 | size_t max_matches, |
521 | VariableList &variable_list) const { |
522 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
523 | for (const ModuleSP &module_sp : m_modules) |
524 | module_sp->FindGlobalVariables(regex, max_matches, variable_list); |
525 | } |
526 | |
527 | void ModuleList::FindSymbolsWithNameAndType(ConstString name, |
528 | SymbolType symbol_type, |
529 | SymbolContextList &sc_list) const { |
530 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
531 | for (const ModuleSP &module_sp : m_modules) |
532 | module_sp->FindSymbolsWithNameAndType(name, symbol_type, sc_list); |
533 | } |
534 | |
535 | void ModuleList::FindSymbolsMatchingRegExAndType( |
536 | const RegularExpression ®ex, lldb::SymbolType symbol_type, |
537 | SymbolContextList &sc_list) const { |
538 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
539 | for (const ModuleSP &module_sp : m_modules) |
540 | module_sp->FindSymbolsMatchingRegExAndType(regex, symbol_type, sc_list); |
541 | } |
542 | |
543 | void ModuleList::FindModules(const ModuleSpec &module_spec, |
544 | ModuleList &matching_module_list) const { |
545 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
546 | for (const ModuleSP &module_sp : m_modules) { |
547 | if (module_sp->MatchesModuleSpec(module_ref: module_spec)) |
548 | matching_module_list.Append(module_sp); |
549 | } |
550 | } |
551 | |
552 | ModuleSP ModuleList::FindModule(const Module *module_ptr) const { |
553 | ModuleSP module_sp; |
554 | |
555 | // Scope for "locker" |
556 | { |
557 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
558 | collection::const_iterator pos, end = m_modules.end(); |
559 | |
560 | for (pos = m_modules.begin(); pos != end; ++pos) { |
561 | if ((*pos).get() == module_ptr) { |
562 | module_sp = (*pos); |
563 | break; |
564 | } |
565 | } |
566 | } |
567 | return module_sp; |
568 | } |
569 | |
570 | ModuleSP ModuleList::FindModule(const UUID &uuid) const { |
571 | ModuleSP module_sp; |
572 | |
573 | if (uuid.IsValid()) { |
574 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
575 | collection::const_iterator pos, end = m_modules.end(); |
576 | |
577 | for (pos = m_modules.begin(); pos != end; ++pos) { |
578 | if ((*pos)->GetUUID() == uuid) { |
579 | module_sp = (*pos); |
580 | break; |
581 | } |
582 | } |
583 | } |
584 | return module_sp; |
585 | } |
586 | |
587 | void ModuleList::FindTypes(Module *search_first, const TypeQuery &query, |
588 | TypeResults &results) const { |
589 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
590 | if (search_first) { |
591 | search_first->FindTypes(query, results); |
592 | if (results.Done(query)) |
593 | return; |
594 | } |
595 | for (const auto &module_sp : m_modules) { |
596 | if (search_first != module_sp.get()) { |
597 | module_sp->FindTypes(query, results); |
598 | if (results.Done(query)) |
599 | return; |
600 | } |
601 | } |
602 | } |
603 | |
604 | bool ModuleList::FindSourceFile(const FileSpec &orig_spec, |
605 | FileSpec &new_spec) const { |
606 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
607 | for (const ModuleSP &module_sp : m_modules) { |
608 | if (module_sp->FindSourceFile(orig_spec, new_spec)) |
609 | return true; |
610 | } |
611 | return false; |
612 | } |
613 | |
614 | void ModuleList::FindAddressesForLine(const lldb::TargetSP target_sp, |
615 | const FileSpec &file, uint32_t line, |
616 | Function *function, |
617 | std::vector<Address> &output_local, |
618 | std::vector<Address> &output_extern) { |
619 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
620 | for (const ModuleSP &module_sp : m_modules) { |
621 | module_sp->FindAddressesForLine(target_sp, file, line, function, |
622 | output_local, output_extern); |
623 | } |
624 | } |
625 | |
626 | ModuleSP ModuleList::FindFirstModule(const ModuleSpec &module_spec) const { |
627 | ModuleSP module_sp; |
628 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
629 | collection::const_iterator pos, end = m_modules.end(); |
630 | for (pos = m_modules.begin(); pos != end; ++pos) { |
631 | ModuleSP module_sp(*pos); |
632 | if (module_sp->MatchesModuleSpec(module_ref: module_spec)) |
633 | return module_sp; |
634 | } |
635 | return module_sp; |
636 | } |
637 | |
638 | size_t ModuleList::GetSize() const { |
639 | size_t size = 0; |
640 | { |
641 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
642 | size = m_modules.size(); |
643 | } |
644 | return size; |
645 | } |
646 | |
647 | void ModuleList::Dump(Stream *s) const { |
648 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
649 | for (const ModuleSP &module_sp : m_modules) |
650 | module_sp->Dump(s); |
651 | } |
652 | |
653 | void ModuleList::LogUUIDAndPaths(Log *log, const char *prefix_cstr) { |
654 | if (log != nullptr) { |
655 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
656 | collection::const_iterator pos, begin = m_modules.begin(), |
657 | end = m_modules.end(); |
658 | for (pos = begin; pos != end; ++pos) { |
659 | Module *module = pos->get(); |
660 | const FileSpec &module_file_spec = module->GetFileSpec(); |
661 | LLDB_LOGF(log, "%s[%u] %s (%s) \"%s\"" , prefix_cstr ? prefix_cstr : "" , |
662 | (uint32_t)std::distance(begin, pos), |
663 | module->GetUUID().GetAsString().c_str(), |
664 | module->GetArchitecture().GetArchitectureName(), |
665 | module_file_spec.GetPath().c_str()); |
666 | } |
667 | } |
668 | } |
669 | |
670 | bool ModuleList::ResolveFileAddress(lldb::addr_t vm_addr, |
671 | Address &so_addr) const { |
672 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
673 | for (const ModuleSP &module_sp : m_modules) { |
674 | if (module_sp->ResolveFileAddress(vm_addr, so_addr)) |
675 | return true; |
676 | } |
677 | |
678 | return false; |
679 | } |
680 | |
681 | uint32_t |
682 | ModuleList::ResolveSymbolContextForAddress(const Address &so_addr, |
683 | SymbolContextItem resolve_scope, |
684 | SymbolContext &sc) const { |
685 | // The address is already section offset so it has a module |
686 | uint32_t resolved_flags = 0; |
687 | ModuleSP module_sp(so_addr.GetModule()); |
688 | if (module_sp) { |
689 | resolved_flags = |
690 | module_sp->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc); |
691 | } else { |
692 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
693 | collection::const_iterator pos, end = m_modules.end(); |
694 | for (pos = m_modules.begin(); pos != end; ++pos) { |
695 | resolved_flags = |
696 | (*pos)->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc); |
697 | if (resolved_flags != 0) |
698 | break; |
699 | } |
700 | } |
701 | |
702 | return resolved_flags; |
703 | } |
704 | |
705 | uint32_t ModuleList::ResolveSymbolContextForFilePath( |
706 | const char *file_path, uint32_t line, bool check_inlines, |
707 | SymbolContextItem resolve_scope, SymbolContextList &sc_list) const { |
708 | FileSpec file_spec(file_path); |
709 | return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, |
710 | resolve_scope, sc_list); |
711 | } |
712 | |
713 | uint32_t ModuleList::ResolveSymbolContextsForFileSpec( |
714 | const FileSpec &file_spec, uint32_t line, bool check_inlines, |
715 | SymbolContextItem resolve_scope, SymbolContextList &sc_list) const { |
716 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
717 | for (const ModuleSP &module_sp : m_modules) { |
718 | module_sp->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, |
719 | resolve_scope, sc_list); |
720 | } |
721 | |
722 | return sc_list.GetSize(); |
723 | } |
724 | |
725 | size_t ModuleList::GetIndexForModule(const Module *module) const { |
726 | if (module) { |
727 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
728 | collection::const_iterator pos; |
729 | collection::const_iterator begin = m_modules.begin(); |
730 | collection::const_iterator end = m_modules.end(); |
731 | for (pos = begin; pos != end; ++pos) { |
732 | if ((*pos).get() == module) |
733 | return std::distance(first: begin, last: pos); |
734 | } |
735 | } |
736 | return LLDB_INVALID_INDEX32; |
737 | } |
738 | |
739 | namespace { |
740 | struct SharedModuleListInfo { |
741 | ModuleList module_list; |
742 | ModuleListProperties module_list_properties; |
743 | }; |
744 | } |
745 | static SharedModuleListInfo &GetSharedModuleListInfo() |
746 | { |
747 | static SharedModuleListInfo *g_shared_module_list_info = nullptr; |
748 | static llvm::once_flag g_once_flag; |
749 | llvm::call_once(flag&: g_once_flag, F: []() { |
750 | // NOTE: Intentionally leak the module list so a program doesn't have to |
751 | // cleanup all modules and object files as it exits. This just wastes time |
752 | // doing a bunch of cleanup that isn't required. |
753 | if (g_shared_module_list_info == nullptr) |
754 | g_shared_module_list_info = new SharedModuleListInfo(); |
755 | }); |
756 | return *g_shared_module_list_info; |
757 | } |
758 | |
759 | static ModuleList &GetSharedModuleList() { |
760 | return GetSharedModuleListInfo().module_list; |
761 | } |
762 | |
763 | ModuleListProperties &ModuleList::GetGlobalModuleListProperties() { |
764 | return GetSharedModuleListInfo().module_list_properties; |
765 | } |
766 | |
767 | bool ModuleList::ModuleIsInCache(const Module *module_ptr) { |
768 | if (module_ptr) { |
769 | ModuleList &shared_module_list = GetSharedModuleList(); |
770 | return shared_module_list.FindModule(module_ptr).get() != nullptr; |
771 | } |
772 | return false; |
773 | } |
774 | |
775 | void ModuleList::FindSharedModules(const ModuleSpec &module_spec, |
776 | ModuleList &matching_module_list) { |
777 | GetSharedModuleList().FindModules(module_spec, matching_module_list); |
778 | } |
779 | |
780 | lldb::ModuleSP ModuleList::FindSharedModule(const UUID &uuid) { |
781 | return GetSharedModuleList().FindModule(uuid); |
782 | } |
783 | |
784 | size_t ModuleList::RemoveOrphanSharedModules(bool mandatory) { |
785 | return GetSharedModuleList().RemoveOrphans(mandatory); |
786 | } |
787 | |
788 | Status |
789 | ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, |
790 | const FileSpecList *module_search_paths_ptr, |
791 | llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, |
792 | bool *did_create_ptr, bool always_create) { |
793 | ModuleList &shared_module_list = GetSharedModuleList(); |
794 | std::lock_guard<std::recursive_mutex> guard( |
795 | shared_module_list.m_modules_mutex); |
796 | char path[PATH_MAX]; |
797 | |
798 | Status error; |
799 | |
800 | module_sp.reset(); |
801 | |
802 | if (did_create_ptr) |
803 | *did_create_ptr = false; |
804 | |
805 | const UUID *uuid_ptr = module_spec.GetUUIDPtr(); |
806 | const FileSpec &module_file_spec = module_spec.GetFileSpec(); |
807 | const ArchSpec &arch = module_spec.GetArchitecture(); |
808 | |
809 | // Make sure no one else can try and get or create a module while this |
810 | // function is actively working on it by doing an extra lock on the global |
811 | // mutex list. |
812 | if (!always_create) { |
813 | ModuleList matching_module_list; |
814 | shared_module_list.FindModules(module_spec, matching_module_list); |
815 | const size_t num_matching_modules = matching_module_list.GetSize(); |
816 | |
817 | if (num_matching_modules > 0) { |
818 | for (size_t module_idx = 0; module_idx < num_matching_modules; |
819 | ++module_idx) { |
820 | module_sp = matching_module_list.GetModuleAtIndex(idx: module_idx); |
821 | |
822 | // Make sure the file for the module hasn't been modified |
823 | if (module_sp->FileHasChanged()) { |
824 | if (old_modules) |
825 | old_modules->push_back(Elt: module_sp); |
826 | |
827 | Log *log = GetLog(mask: LLDBLog::Modules); |
828 | if (log != nullptr) |
829 | LLDB_LOGF( |
830 | log, "%p '%s' module changed: removing from global module list" , |
831 | static_cast<void *>(module_sp.get()), |
832 | module_sp->GetFileSpec().GetFilename().GetCString()); |
833 | |
834 | shared_module_list.Remove(module_sp); |
835 | module_sp.reset(); |
836 | } else { |
837 | // The module matches and the module was not modified from when it |
838 | // was last loaded. |
839 | return error; |
840 | } |
841 | } |
842 | } |
843 | } |
844 | |
845 | if (module_sp) |
846 | return error; |
847 | |
848 | module_sp = std::make_shared<Module>(args: module_spec); |
849 | // Make sure there are a module and an object file since we can specify a |
850 | // valid file path with an architecture that might not be in that file. By |
851 | // getting the object file we can guarantee that the architecture matches |
852 | if (module_sp->GetObjectFile()) { |
853 | // If we get in here we got the correct arch, now we just need to verify |
854 | // the UUID if one was given |
855 | if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) { |
856 | module_sp.reset(); |
857 | } else { |
858 | if (module_sp->GetObjectFile() && |
859 | module_sp->GetObjectFile()->GetType() == |
860 | ObjectFile::eTypeStubLibrary) { |
861 | module_sp.reset(); |
862 | } else { |
863 | if (did_create_ptr) { |
864 | *did_create_ptr = true; |
865 | } |
866 | |
867 | shared_module_list.ReplaceEquivalent(module_sp, old_modules); |
868 | return error; |
869 | } |
870 | } |
871 | } else { |
872 | module_sp.reset(); |
873 | } |
874 | |
875 | if (module_search_paths_ptr) { |
876 | const auto num_directories = module_search_paths_ptr->GetSize(); |
877 | for (size_t idx = 0; idx < num_directories; ++idx) { |
878 | auto search_path_spec = module_search_paths_ptr->GetFileSpecAtIndex(idx); |
879 | FileSystem::Instance().Resolve(file_spec&: search_path_spec); |
880 | namespace fs = llvm::sys::fs; |
881 | if (!FileSystem::Instance().IsDirectory(file_spec: search_path_spec)) |
882 | continue; |
883 | search_path_spec.AppendPathComponent( |
884 | component: module_spec.GetFileSpec().GetFilename().GetStringRef()); |
885 | if (!FileSystem::Instance().Exists(file_spec: search_path_spec)) |
886 | continue; |
887 | |
888 | auto resolved_module_spec(module_spec); |
889 | resolved_module_spec.GetFileSpec() = search_path_spec; |
890 | module_sp = std::make_shared<Module>(args&: resolved_module_spec); |
891 | if (module_sp->GetObjectFile()) { |
892 | // If we get in here we got the correct arch, now we just need to |
893 | // verify the UUID if one was given |
894 | if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) { |
895 | module_sp.reset(); |
896 | } else { |
897 | if (module_sp->GetObjectFile()->GetType() == |
898 | ObjectFile::eTypeStubLibrary) { |
899 | module_sp.reset(); |
900 | } else { |
901 | if (did_create_ptr) |
902 | *did_create_ptr = true; |
903 | |
904 | shared_module_list.ReplaceEquivalent(module_sp, old_modules); |
905 | return Status(); |
906 | } |
907 | } |
908 | } else { |
909 | module_sp.reset(); |
910 | } |
911 | } |
912 | } |
913 | |
914 | // Either the file didn't exist where at the path, or no path was given, so |
915 | // we now have to use more extreme measures to try and find the appropriate |
916 | // module. |
917 | |
918 | // Fixup the incoming path in case the path points to a valid file, yet the |
919 | // arch or UUID (if one was passed in) don't match. |
920 | ModuleSpec located_binary_modulespec = |
921 | PluginManager::LocateExecutableObjectFile(module_spec); |
922 | |
923 | // Don't look for the file if it appears to be the same one we already |
924 | // checked for above... |
925 | if (located_binary_modulespec.GetFileSpec() != module_file_spec) { |
926 | if (!FileSystem::Instance().Exists( |
927 | file_spec: located_binary_modulespec.GetFileSpec())) { |
928 | located_binary_modulespec.GetFileSpec().GetPath(path, max_path_length: sizeof(path)); |
929 | if (path[0] == '\0') |
930 | module_file_spec.GetPath(path, max_path_length: sizeof(path)); |
931 | // How can this check ever be true? This branch it is false, and we |
932 | // haven't modified file_spec. |
933 | if (FileSystem::Instance().Exists( |
934 | file_spec: located_binary_modulespec.GetFileSpec())) { |
935 | std::string uuid_str; |
936 | if (uuid_ptr && uuid_ptr->IsValid()) |
937 | uuid_str = uuid_ptr->GetAsString(); |
938 | |
939 | if (arch.IsValid()) { |
940 | if (!uuid_str.empty()) |
941 | error.SetErrorStringWithFormat( |
942 | "'%s' does not contain the %s architecture and UUID %s" , path, |
943 | arch.GetArchitectureName(), uuid_str.c_str()); |
944 | else |
945 | error.SetErrorStringWithFormat( |
946 | "'%s' does not contain the %s architecture." , path, |
947 | arch.GetArchitectureName()); |
948 | } |
949 | } else { |
950 | error.SetErrorStringWithFormat("'%s' does not exist" , path); |
951 | } |
952 | if (error.Fail()) |
953 | module_sp.reset(); |
954 | return error; |
955 | } |
956 | |
957 | // Make sure no one else can try and get or create a module while this |
958 | // function is actively working on it by doing an extra lock on the global |
959 | // mutex list. |
960 | ModuleSpec platform_module_spec(module_spec); |
961 | platform_module_spec.GetFileSpec() = |
962 | located_binary_modulespec.GetFileSpec(); |
963 | platform_module_spec.GetPlatformFileSpec() = |
964 | located_binary_modulespec.GetFileSpec(); |
965 | platform_module_spec.GetSymbolFileSpec() = |
966 | located_binary_modulespec.GetSymbolFileSpec(); |
967 | ModuleList matching_module_list; |
968 | shared_module_list.FindModules(module_spec: platform_module_spec, matching_module_list); |
969 | if (!matching_module_list.IsEmpty()) { |
970 | module_sp = matching_module_list.GetModuleAtIndex(idx: 0); |
971 | |
972 | // If we didn't have a UUID in mind when looking for the object file, |
973 | // then we should make sure the modification time hasn't changed! |
974 | if (platform_module_spec.GetUUIDPtr() == nullptr) { |
975 | auto file_spec_mod_time = FileSystem::Instance().GetModificationTime( |
976 | file_spec: located_binary_modulespec.GetFileSpec()); |
977 | if (file_spec_mod_time != llvm::sys::TimePoint<>()) { |
978 | if (file_spec_mod_time != module_sp->GetModificationTime()) { |
979 | if (old_modules) |
980 | old_modules->push_back(Elt: module_sp); |
981 | shared_module_list.Remove(module_sp); |
982 | module_sp.reset(); |
983 | } |
984 | } |
985 | } |
986 | } |
987 | |
988 | if (!module_sp) { |
989 | module_sp = std::make_shared<Module>(args&: platform_module_spec); |
990 | // Make sure there are a module and an object file since we can specify a |
991 | // valid file path with an architecture that might not be in that file. |
992 | // By getting the object file we can guarantee that the architecture |
993 | // matches |
994 | if (module_sp && module_sp->GetObjectFile()) { |
995 | if (module_sp->GetObjectFile()->GetType() == |
996 | ObjectFile::eTypeStubLibrary) { |
997 | module_sp.reset(); |
998 | } else { |
999 | if (did_create_ptr) |
1000 | *did_create_ptr = true; |
1001 | |
1002 | shared_module_list.ReplaceEquivalent(module_sp, old_modules); |
1003 | } |
1004 | } else { |
1005 | located_binary_modulespec.GetFileSpec().GetPath(path, max_path_length: sizeof(path)); |
1006 | |
1007 | if (located_binary_modulespec.GetFileSpec()) { |
1008 | if (arch.IsValid()) |
1009 | error.SetErrorStringWithFormat( |
1010 | "unable to open %s architecture in '%s'" , |
1011 | arch.GetArchitectureName(), path); |
1012 | else |
1013 | error.SetErrorStringWithFormat("unable to open '%s'" , path); |
1014 | } else { |
1015 | std::string uuid_str; |
1016 | if (uuid_ptr && uuid_ptr->IsValid()) |
1017 | uuid_str = uuid_ptr->GetAsString(); |
1018 | |
1019 | if (!uuid_str.empty()) |
1020 | error.SetErrorStringWithFormat( |
1021 | "cannot locate a module for UUID '%s'" , uuid_str.c_str()); |
1022 | else |
1023 | error.SetErrorString("cannot locate a module" ); |
1024 | } |
1025 | } |
1026 | } |
1027 | } |
1028 | |
1029 | return error; |
1030 | } |
1031 | |
1032 | bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) { |
1033 | return GetSharedModuleList().Remove(module_sp); |
1034 | } |
1035 | |
1036 | bool ModuleList::RemoveSharedModuleIfOrphaned(const Module *module_ptr) { |
1037 | return GetSharedModuleList().RemoveIfOrphaned(module_ptr); |
1038 | } |
1039 | |
1040 | bool ModuleList::LoadScriptingResourcesInTarget(Target *target, |
1041 | std::list<Status> &errors, |
1042 | Stream &feedback_stream, |
1043 | bool continue_on_error) { |
1044 | if (!target) |
1045 | return false; |
1046 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
1047 | for (auto module : m_modules) { |
1048 | Status error; |
1049 | if (module) { |
1050 | if (!module->LoadScriptingResourceInTarget(target, error, |
1051 | feedback_stream)) { |
1052 | if (error.Fail() && error.AsCString()) { |
1053 | error.SetErrorStringWithFormat("unable to load scripting data for " |
1054 | "module %s - error reported was %s" , |
1055 | module->GetFileSpec() |
1056 | .GetFileNameStrippingExtension() |
1057 | .GetCString(), |
1058 | error.AsCString()); |
1059 | errors.push_back(x: error); |
1060 | |
1061 | if (!continue_on_error) |
1062 | return false; |
1063 | } |
1064 | } |
1065 | } |
1066 | } |
1067 | return errors.empty(); |
1068 | } |
1069 | |
1070 | void ModuleList::ForEach( |
1071 | std::function<bool(const ModuleSP &module_sp)> const &callback) const { |
1072 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
1073 | for (const auto &module_sp : m_modules) { |
1074 | assert(module_sp != nullptr); |
1075 | // If the callback returns false, then stop iterating and break out |
1076 | if (!callback(module_sp)) |
1077 | break; |
1078 | } |
1079 | } |
1080 | |
1081 | bool ModuleList::AnyOf( |
1082 | std::function<bool(lldb_private::Module &module_sp)> const &callback) |
1083 | const { |
1084 | std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); |
1085 | for (const auto &module_sp : m_modules) { |
1086 | assert(module_sp != nullptr); |
1087 | if (callback(*module_sp)) |
1088 | return true; |
1089 | } |
1090 | |
1091 | return false; |
1092 | } |
1093 | |
1094 | |
1095 | void ModuleList::Swap(ModuleList &other) { |
1096 | // scoped_lock locks both mutexes at once. |
1097 | std::scoped_lock<std::recursive_mutex, std::recursive_mutex> lock( |
1098 | m_modules_mutex, other.m_modules_mutex); |
1099 | m_modules.swap(x&: other.m_modules); |
1100 | } |
1101 | |