| //===-- ModuleList.cpp ----------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Core/ModuleList.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/ModuleSpec.h" | |
| #include "lldb/Core/PluginManager.h" | |
| #include "lldb/Host/FileSystem.h" | |
| #include "lldb/Interpreter/OptionValueFileSpec.h" | |
| #include "lldb/Interpreter/OptionValueFileSpecList.h" | |
| #include "lldb/Interpreter/OptionValueProperties.h" | |
| #include "lldb/Interpreter/Property.h" | |
| #include "lldb/Symbol/ObjectFile.h" | |
| #include "lldb/Symbol/SymbolContext.h" | |
| #include "lldb/Symbol/TypeList.h" | |
| #include "lldb/Symbol/VariableList.h" | |
| #include "lldb/Target/Platform.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/ArchSpec.h" | |
| #include "lldb/Utility/ConstString.h" | |
| #include "lldb/Utility/FileSpecList.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/UUID.h" | |
| #include "lldb/lldb-defines.h" | |
| #include "llvm/Support/ThreadPool.h" | |
| #if defined(_WIN32) | |
| #include "lldb/Host/windows/PosixApi.h" | |
| #endif | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/Threading.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <chrono> | |
| #include <memory> | |
| #include <mutex> | |
| #include <string> | |
| #include <utility> | |
| namespace lldb_private { | |
| class Function; | |
| } | |
| namespace lldb_private { | |
| class RegularExpression; | |
| } | |
| namespace lldb_private { | |
| class Stream; | |
| } | |
| namespace lldb_private { | |
| class SymbolFile; | |
| } | |
| namespace lldb_private { | |
| class Target; | |
| } | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| namespace { | |
| #define LLDB_PROPERTIES_modulelist | |
| #include "CoreProperties.inc" | |
| enum { | |
| #define LLDB_PROPERTIES_modulelist | |
| #include "CorePropertiesEnum.inc" | |
| }; | |
| } // namespace | |
| ModuleListProperties::ModuleListProperties() { | |
| m_collection_sp = std::make_shared<OptionValueProperties>("symbols"); | |
| m_collection_sp->Initialize(g_modulelist_properties_def); | |
| m_collection_sp->SetValueChangedCallback(ePropertySymLinkPaths, | |
| [this] { UpdateSymlinkMappings(); }); | |
| llvm::SmallString<128> path; | |
| if (llvm::sys::path::cache_directory(path)) { | |
| llvm::sys::path::append(path, "lldb"); | |
| llvm::sys::path::append(path, "IndexCache"); | |
| lldbassert(SetLLDBIndexCachePath(FileSpec(path))); | |
| } | |
| } | |
| bool ModuleListProperties::GetEnableExternalLookup() const { | |
| const uint32_t idx = ePropertyEnableExternalLookup; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_modulelist_properties[idx].default_uint_value != 0); | |
| } | |
| bool ModuleListProperties::SetEnableExternalLookup(bool new_value) { | |
| return SetPropertyAtIndex(ePropertyEnableExternalLookup, new_value); | |
| } | |
| SymbolDownload ModuleListProperties::GetSymbolAutoDownload() const { | |
| // Backward compatibility alias. | |
| if (GetPropertyAtIndexAs<bool>(ePropertyEnableBackgroundLookup, false)) | |
| return eSymbolDownloadBackground; | |
| const uint32_t idx = ePropertyAutoDownload; | |
| return GetPropertyAtIndexAs<lldb::SymbolDownload>( | |
| idx, static_cast<lldb::SymbolDownload>( | |
| g_modulelist_properties[idx].default_uint_value)); | |
| } | |
| SymbolSharedCacheUse ModuleListProperties::GetSharedCacheBinaryLoading() const { | |
| const uint32_t idx = ePropertySharedCacheBinaryLoading; | |
| return GetPropertyAtIndexAs<lldb::SymbolSharedCacheUse>( | |
| idx, static_cast<lldb::SymbolSharedCacheUse>( | |
| g_modulelist_properties[idx].default_uint_value)); | |
| } | |
| FileSpec ModuleListProperties::GetClangModulesCachePath() const { | |
| const uint32_t idx = ePropertyClangModulesCachePath; | |
| return GetPropertyAtIndexAs<FileSpec>(idx, {}); | |
| } | |
| bool ModuleListProperties::SetClangModulesCachePath(const FileSpec &path) { | |
| const uint32_t idx = ePropertyClangModulesCachePath; | |
| return SetPropertyAtIndex(idx, path); | |
| } | |
| FileSpec ModuleListProperties::GetLLDBIndexCachePath() const { | |
| const uint32_t idx = ePropertyLLDBIndexCachePath; | |
| return GetPropertyAtIndexAs<FileSpec>(idx, {}); | |
| } | |
| bool ModuleListProperties::SetLLDBIndexCachePath(const FileSpec &path) { | |
| const uint32_t idx = ePropertyLLDBIndexCachePath; | |
| return SetPropertyAtIndex(idx, path); | |
| } | |
| bool ModuleListProperties::GetEnableLLDBIndexCache() const { | |
| const uint32_t idx = ePropertyEnableLLDBIndexCache; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_modulelist_properties[idx].default_uint_value != 0); | |
| } | |
| bool ModuleListProperties::SetEnableLLDBIndexCache(bool new_value) { | |
| return SetPropertyAtIndex(ePropertyEnableLLDBIndexCache, new_value); | |
| } | |
| bool ModuleListProperties::GetEnableLLDBIndexCacheMemoryModules() const { | |
| const uint32_t idx = ePropertyEnableLLDBIndexCacheMemoryModules; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_modulelist_properties[idx].default_uint_value != 0); | |
| } | |
| bool ModuleListProperties::SetEnableLLDBIndexCacheMemoryModules( | |
| bool new_value) { | |
| return SetPropertyAtIndex(ePropertyEnableLLDBIndexCacheMemoryModules, | |
| new_value); | |
| } | |
| uint64_t ModuleListProperties::GetLLDBIndexCacheMaxByteSize() { | |
| const uint32_t idx = ePropertyLLDBIndexCacheMaxByteSize; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_modulelist_properties[idx].default_uint_value); | |
| } | |
| uint64_t ModuleListProperties::GetLLDBIndexCacheMaxPercent() { | |
| const uint32_t idx = ePropertyLLDBIndexCacheMaxPercent; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_modulelist_properties[idx].default_uint_value); | |
| } | |
| uint64_t ModuleListProperties::GetLLDBIndexCacheExpirationDays() { | |
| const uint32_t idx = ePropertyLLDBIndexCacheExpirationDays; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_modulelist_properties[idx].default_uint_value); | |
| } | |
| void ModuleListProperties::UpdateSymlinkMappings() { | |
| FileSpecList list = | |
| GetPropertyAtIndexAs<FileSpecList>(ePropertySymLinkPaths, {}); | |
| llvm::sys::ScopedWriter lock(m_symlink_paths_mutex); | |
| const bool notify = false; | |
| m_symlink_paths.Clear(notify); | |
| for (auto symlink : list) { | |
| FileSpec resolved; | |
| Status status = FileSystem::Instance().Readlink(symlink, resolved); | |
| if (status.Success()) | |
| m_symlink_paths.Append(symlink.GetPath(), resolved.GetPath(), notify); | |
| } | |
| } | |
| PathMappingList ModuleListProperties::GetSymlinkMappings() const { | |
| llvm::sys::ScopedReader lock(m_symlink_paths_mutex); | |
| return m_symlink_paths; | |
| } | |
| bool ModuleListProperties::GetLoadSymbolOnDemand() const { | |
| const uint32_t idx = ePropertyLoadSymbolOnDemand; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_modulelist_properties[idx].default_uint_value != 0); | |
| } | |
| uint64_t ModuleListProperties::GetDemangledNameInfoCacheSize() const { | |
| const uint32_t idx = ePropertyDemangledNameInfoCacheSize; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_modulelist_properties[idx].default_uint_value); | |
| } | |
| ModuleList::ModuleList() : m_modules(), m_modules_mutex() {} | |
| ModuleList::ModuleList(const ModuleList &rhs) : m_modules(), m_modules_mutex() { | |
| std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex); | |
| std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex); | |
| m_modules = rhs.m_modules; | |
| } | |
| ModuleList::ModuleList(ModuleList::Notifier *notifier) | |
| : m_modules(), m_modules_mutex(), m_notifier(notifier) {} | |
| const ModuleList &ModuleList::operator=(const ModuleList &rhs) { | |
| if (this != &rhs) { | |
| std::lock(m_modules_mutex, rhs.m_modules_mutex); | |
| std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex, | |
| std::adopt_lock); | |
| std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex, | |
| std::adopt_lock); | |
| m_modules = rhs.m_modules; | |
| } | |
| return *this; | |
| } | |
| ModuleList::~ModuleList() = default; | |
| void ModuleList::AppendImpl(const ModuleSP &module_sp, bool use_notifier) { | |
| if (!module_sp) | |
| return; | |
| { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| // We are required to keep the first element of the Module List as the | |
| // executable module. So check here and if the first module is NOT an | |
| // but the new one is, we insert this module at the beginning, rather than | |
| // at the end. | |
| // We don't need to do any of this if the list is empty: | |
| if (m_modules.empty()) { | |
| m_modules.push_back(module_sp); | |
| } else { | |
| // Since producing the ObjectFile may take some work, first check the | |
| // 0th element, and only if that's NOT an executable look at the | |
| // incoming ObjectFile. That way in the normal case we only look at the | |
| // element 0 ObjectFile. | |
| lldb_private::ObjectFile *elem_zero_obj = m_modules[0]->GetObjectFile(); | |
| const bool elem_zero_is_executable = | |
| elem_zero_obj && | |
| elem_zero_obj->GetType() == ObjectFile::Type::eTypeExecutable; | |
| lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); | |
| if (!elem_zero_is_executable && obj && | |
| obj->GetType() == ObjectFile::Type::eTypeExecutable) { | |
| m_modules.insert(m_modules.begin(), module_sp); | |
| } else { | |
| m_modules.push_back(module_sp); | |
| } | |
| } | |
| } | |
| // Release the mutex before calling the notifier to avoid deadlock | |
| // NotifyModuleAdded should be thread-safe | |
| if (use_notifier && m_notifier) | |
| m_notifier->NotifyModuleAdded(*this, module_sp); | |
| } | |
| void ModuleList::Append(const ModuleSP &module_sp, bool notify) { | |
| AppendImpl(module_sp, notify); | |
| } | |
| void ModuleList::ReplaceEquivalent( | |
| const ModuleSP &module_sp, | |
| llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules) { | |
| if (module_sp) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| // First remove any equivalent modules. Equivalent modules are modules | |
| // whose path, platform path and architecture match. | |
| ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(), | |
| module_sp->GetArchitecture()); | |
| equivalent_module_spec.GetPlatformFileSpec() = | |
| module_sp->GetPlatformFileSpec(); | |
| size_t idx = 0; | |
| while (idx < m_modules.size()) { | |
| ModuleSP test_module_sp(m_modules[idx]); | |
| if (test_module_sp->MatchesModuleSpec(equivalent_module_spec)) { | |
| if (old_modules) | |
| old_modules->push_back(test_module_sp); | |
| RemoveImpl(m_modules.begin() + idx); | |
| } else { | |
| ++idx; | |
| } | |
| } | |
| // Now add the new module to the list | |
| Append(module_sp); | |
| } | |
| } | |
| bool ModuleList::AppendIfNeeded(const ModuleSP &new_module, bool notify) { | |
| if (new_module) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| if (module_sp.get() == new_module.get()) | |
| return false; // Already in the list | |
| } | |
| // Only push module_sp on the list if it wasn't already in there. | |
| Append(new_module, notify); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void ModuleList::Append(const ModuleList &module_list) { | |
| for (auto pos : module_list.m_modules) | |
| Append(pos); | |
| } | |
| bool ModuleList::AppendIfNeeded(const ModuleList &module_list) { | |
| bool any_in = false; | |
| for (auto pos : module_list.m_modules) { | |
| if (AppendIfNeeded(pos)) | |
| any_in = true; | |
| } | |
| return any_in; | |
| } | |
| bool ModuleList::RemoveImpl(const ModuleSP &module_sp, bool use_notifier) { | |
| if (module_sp) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::iterator pos, end = m_modules.end(); | |
| for (pos = m_modules.begin(); pos != end; ++pos) { | |
| if (pos->get() == module_sp.get()) { | |
| m_modules.erase(pos); | |
| if (use_notifier && m_notifier) | |
| m_notifier->NotifyModuleRemoved(*this, module_sp); | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| ModuleList::collection::iterator | |
| ModuleList::RemoveImpl(ModuleList::collection::iterator pos, | |
| bool use_notifier) { | |
| ModuleSP module_sp(*pos); | |
| collection::iterator retval = m_modules.erase(pos); | |
| if (use_notifier && m_notifier) | |
| m_notifier->NotifyModuleRemoved(*this, module_sp); | |
| return retval; | |
| } | |
| bool ModuleList::Remove(const ModuleSP &module_sp, bool notify) { | |
| return RemoveImpl(module_sp, notify); | |
| } | |
| bool ModuleList::ReplaceModule(const lldb::ModuleSP &old_module_sp, | |
| const lldb::ModuleSP &new_module_sp) { | |
| if (!RemoveImpl(old_module_sp, false)) | |
| return false; | |
| AppendImpl(new_module_sp, false); | |
| if (m_notifier) | |
| m_notifier->NotifyModuleUpdated(*this, old_module_sp, new_module_sp); | |
| return true; | |
| } | |
| bool ModuleList::RemoveIfOrphaned(const ModuleWP module_wp) { | |
| if (auto module_sp = module_wp.lock()) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::iterator pos, end = m_modules.end(); | |
| for (pos = m_modules.begin(); pos != end; ++pos) { | |
| if (pos->get() == module_sp.get()) { | |
| // Since module_sp increases the refcount by 1, the use count should be | |
| // the regular use count + 1. | |
| constexpr long kUseCountOrphaned = kUseCountModuleListOrphaned + 1; | |
| if (pos->use_count() == kUseCountOrphaned) { | |
| pos = RemoveImpl(pos); | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| size_t ModuleList::RemoveOrphans(bool mandatory) { | |
| std::unique_lock<std::recursive_mutex> lock(m_modules_mutex, std::defer_lock); | |
| if (mandatory) { | |
| lock.lock(); | |
| } else { | |
| // Not mandatory, remove orphans if we can get the mutex | |
| if (!lock.try_lock()) | |
| return 0; | |
| } | |
| size_t remove_count = 0; | |
| // Modules might hold shared pointers to other modules, so removing one | |
| // module might make other modules orphans. Keep removing modules until | |
| // there are no further modules that can be removed. | |
| bool made_progress = true; | |
| while (made_progress) { | |
| // Keep track if we make progress this iteration. | |
| made_progress = false; | |
| collection::iterator pos = m_modules.begin(); | |
| while (pos != m_modules.end()) { | |
| if (pos->use_count() == kUseCountModuleListOrphaned) { | |
| pos = RemoveImpl(pos); | |
| ++remove_count; | |
| // We did make progress. | |
| made_progress = true; | |
| } else { | |
| ++pos; | |
| } | |
| } | |
| } | |
| return remove_count; | |
| } | |
| size_t ModuleList::Remove(ModuleList &module_list) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| size_t num_removed = 0; | |
| collection::iterator pos, end = module_list.m_modules.end(); | |
| for (pos = module_list.m_modules.begin(); pos != end; ++pos) { | |
| if (Remove(*pos, false /* notify */)) | |
| ++num_removed; | |
| } | |
| if (m_notifier) | |
| m_notifier->NotifyModulesRemoved(module_list); | |
| return num_removed; | |
| } | |
| void ModuleList::Clear() { ClearImpl(); } | |
| void ModuleList::Destroy() { ClearImpl(); } | |
| void ModuleList::ClearImpl(bool use_notifier) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| if (use_notifier && m_notifier) | |
| m_notifier->NotifyWillClearList(*this); | |
| m_modules.clear(); | |
| } | |
| Module *ModuleList::GetModulePointerAtIndex(size_t idx) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| if (idx < m_modules.size()) | |
| return m_modules[idx].get(); | |
| return nullptr; | |
| } | |
| ModuleSP ModuleList::GetModuleAtIndex(size_t idx) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| return GetModuleAtIndexUnlocked(idx); | |
| } | |
| ModuleSP ModuleList::GetModuleAtIndexUnlocked(size_t idx) const { | |
| ModuleSP module_sp; | |
| if (idx < m_modules.size()) | |
| module_sp = m_modules[idx]; | |
| return module_sp; | |
| } | |
| void ModuleList::FindFunctions(ConstString name, | |
| FunctionNameType name_type_mask, | |
| const ModuleFunctionSearchOptions &options, | |
| SymbolContextList &sc_list) const { | |
| if (name_type_mask & eFunctionNameTypeAuto) { | |
| std::vector<Module::LookupInfo> lookup_infos = | |
| Module::LookupInfo::MakeLookupInfos(name, name_type_mask, | |
| eLanguageTypeUnknown); | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const auto &lookup_info : lookup_infos) { | |
| const size_t old_size = sc_list.GetSize(); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->FindFunctions(lookup_info, CompilerDeclContext(), options, | |
| sc_list); | |
| } | |
| const size_t new_size = sc_list.GetSize(); | |
| if (old_size < new_size) | |
| lookup_info.Prune(sc_list, old_size); | |
| } | |
| } else { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->FindFunctions(name, CompilerDeclContext(), name_type_mask, | |
| options, sc_list); | |
| } | |
| } | |
| } | |
| void ModuleList::FindFunctionSymbols(ConstString name, | |
| lldb::FunctionNameType name_type_mask, | |
| SymbolContextList &sc_list) { | |
| if (name_type_mask & eFunctionNameTypeAuto) { | |
| std::vector<Module::LookupInfo> lookup_infos = | |
| Module::LookupInfo::MakeLookupInfos(name, name_type_mask, | |
| eLanguageTypeUnknown); | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const auto &lookup_info : lookup_infos) { | |
| const size_t old_size = sc_list.GetSize(); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->FindFunctionSymbols(lookup_info.GetLookupName(), | |
| lookup_info.GetNameTypeMask(), sc_list); | |
| } | |
| const size_t new_size = sc_list.GetSize(); | |
| if (old_size < new_size) | |
| lookup_info.Prune(sc_list, old_size); | |
| } | |
| } else { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->FindFunctionSymbols(name, name_type_mask, sc_list); | |
| } | |
| } | |
| } | |
| void ModuleList::FindFunctions(const RegularExpression &name, | |
| const ModuleFunctionSearchOptions &options, | |
| SymbolContextList &sc_list) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->FindFunctions(name, options, sc_list); | |
| } | |
| void ModuleList::FindCompileUnits(const FileSpec &path, | |
| SymbolContextList &sc_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->FindCompileUnits(path, sc_list); | |
| } | |
| void ModuleList::FindGlobalVariables(ConstString name, size_t max_matches, | |
| VariableList &variable_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->FindGlobalVariables(name, CompilerDeclContext(), max_matches, | |
| variable_list); | |
| } | |
| } | |
| void ModuleList::FindGlobalVariables(const RegularExpression ®ex, | |
| size_t max_matches, | |
| VariableList &variable_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->FindGlobalVariables(regex, max_matches, variable_list); | |
| } | |
| void ModuleList::FindSymbolsWithNameAndType(ConstString name, | |
| SymbolType symbol_type, | |
| SymbolContextList &sc_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->FindSymbolsWithNameAndType(name, symbol_type, sc_list); | |
| } | |
| void ModuleList::FindSymbolsMatchingRegExAndType( | |
| const RegularExpression ®ex, lldb::SymbolType symbol_type, | |
| SymbolContextList &sc_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->FindSymbolsMatchingRegExAndType(regex, symbol_type, sc_list); | |
| } | |
| void ModuleList::FindModules(const ModuleSpec &module_spec, | |
| ModuleList &matching_module_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| if (module_sp->MatchesModuleSpec(module_spec)) | |
| matching_module_list.Append(module_sp); | |
| } | |
| } | |
| ModuleSP ModuleList::FindModule(const Module *module_ptr) const { | |
| ModuleSP module_sp; | |
| // Scope for "locker" | |
| { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::const_iterator pos, end = m_modules.end(); | |
| for (pos = m_modules.begin(); pos != end; ++pos) { | |
| if ((*pos).get() == module_ptr) { | |
| module_sp = (*pos); | |
| break; | |
| } | |
| } | |
| } | |
| return module_sp; | |
| } | |
| ModuleSP ModuleList::FindModule(const UUID &uuid) const { | |
| ModuleSP module_sp; | |
| if (uuid.IsValid()) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::const_iterator pos, end = m_modules.end(); | |
| for (pos = m_modules.begin(); pos != end; ++pos) { | |
| if ((*pos)->GetUUID() == uuid) { | |
| module_sp = (*pos); | |
| break; | |
| } | |
| } | |
| } | |
| return module_sp; | |
| } | |
| ModuleSP ModuleList::FindModule(lldb::user_id_t uid) const { | |
| ModuleSP module_sp; | |
| ForEach([&](const ModuleSP &m) { | |
| if (m->GetID() == uid) { | |
| module_sp = m; | |
| return IterationAction::Stop; | |
| } | |
| return IterationAction::Continue; | |
| }); | |
| return module_sp; | |
| } | |
| void ModuleList::FindTypes(Module *search_first, const TypeQuery &query, | |
| TypeResults &results) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| if (search_first) { | |
| search_first->FindTypes(query, results); | |
| if (results.Done(query)) | |
| return; | |
| } | |
| for (const auto &module_sp : m_modules) { | |
| if (search_first != module_sp.get()) { | |
| module_sp->FindTypes(query, results); | |
| if (results.Done(query)) | |
| return; | |
| } | |
| } | |
| } | |
| bool ModuleList::FindSourceFile(const FileSpec &orig_spec, | |
| FileSpec &new_spec) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| if (module_sp->FindSourceFile(orig_spec, new_spec)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| void ModuleList::FindAddressesForLine(const lldb::TargetSP target_sp, | |
| const FileSpec &file, uint32_t line, | |
| Function *function, | |
| std::vector<Address> &output_local, | |
| std::vector<Address> &output_extern) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->FindAddressesForLine(target_sp, file, line, function, | |
| output_local, output_extern); | |
| } | |
| } | |
| ModuleSP ModuleList::FindFirstModule(const ModuleSpec &module_spec) const { | |
| ModuleSP module_sp; | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::const_iterator pos, end = m_modules.end(); | |
| for (pos = m_modules.begin(); pos != end; ++pos) { | |
| ModuleSP module_sp(*pos); | |
| if (module_sp->MatchesModuleSpec(module_spec)) | |
| return module_sp; | |
| } | |
| return module_sp; | |
| } | |
| size_t ModuleList::GetSize() const { | |
| size_t size = 0; | |
| { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| size = m_modules.size(); | |
| } | |
| return size; | |
| } | |
| void ModuleList::Dump(Stream *s) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->Dump(s); | |
| } | |
| void ModuleList::LogUUIDAndPaths(Log *log, const char *prefix_cstr) { | |
| if (log != nullptr) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::const_iterator pos, begin = m_modules.begin(), | |
| end = m_modules.end(); | |
| for (pos = begin; pos != end; ++pos) { | |
| Module *module = pos->get(); | |
| const FileSpec &module_file_spec = module->GetFileSpec(); | |
| LLDB_LOGF(log, "%s[%u] %s (%s) \"%s\"", prefix_cstr ? prefix_cstr : "", | |
| (uint32_t)std::distance(begin, pos), | |
| module->GetUUID().GetAsString().c_str(), | |
| module->GetArchitecture().GetArchitectureName(), | |
| module_file_spec.GetPath().c_str()); | |
| } | |
| } | |
| } | |
| bool ModuleList::ResolveFileAddress(lldb::addr_t vm_addr, | |
| Address &so_addr) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| if (module_sp->ResolveFileAddress(vm_addr, so_addr)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| uint32_t | |
| ModuleList::ResolveSymbolContextForAddress(const Address &so_addr, | |
| SymbolContextItem resolve_scope, | |
| SymbolContext &sc) const { | |
| // The address is already section offset so it has a module | |
| uint32_t resolved_flags = 0; | |
| ModuleSP module_sp(so_addr.GetModule()); | |
| if (module_sp) { | |
| resolved_flags = | |
| module_sp->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc); | |
| } else { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::const_iterator pos, end = m_modules.end(); | |
| for (pos = m_modules.begin(); pos != end; ++pos) { | |
| resolved_flags = | |
| (*pos)->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc); | |
| if (resolved_flags != 0) | |
| break; | |
| } | |
| } | |
| return resolved_flags; | |
| } | |
| uint32_t ModuleList::ResolveSymbolContextForFilePath( | |
| const char *file_path, uint32_t line, bool check_inlines, | |
| SymbolContextItem resolve_scope, SymbolContextList &sc_list) const { | |
| FileSpec file_spec(file_path); | |
| return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, | |
| resolve_scope, sc_list); | |
| } | |
| uint32_t ModuleList::ResolveSymbolContextsForFileSpec( | |
| const FileSpec &file_spec, uint32_t line, bool check_inlines, | |
| SymbolContextItem resolve_scope, SymbolContextList &sc_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const ModuleSP &module_sp : m_modules) { | |
| module_sp->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, | |
| resolve_scope, sc_list); | |
| } | |
| return sc_list.GetSize(); | |
| } | |
| size_t ModuleList::GetIndexForModule(const Module *module) const { | |
| if (module) { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| collection::const_iterator pos; | |
| collection::const_iterator begin = m_modules.begin(); | |
| collection::const_iterator end = m_modules.end(); | |
| for (pos = begin; pos != end; ++pos) { | |
| if ((*pos).get() == module) | |
| return std::distance(begin, pos); | |
| } | |
| } | |
| return LLDB_INVALID_INDEX32; | |
| } | |
| namespace { | |
| /// A wrapper around ModuleList for shared modules. Provides fast lookups for | |
| /// file-based ModuleSpec queries. | |
| class SharedModuleList { | |
| public: | |
| /// Finds all the modules matching the module_spec, and adds them to \p | |
| /// matching_module_list. | |
| void FindModules(const ModuleSpec &module_spec, | |
| ModuleList &matching_module_list) const { | |
| std::lock_guard<std::recursive_mutex> guard(GetMutex()); | |
| // Try map first for performance - if found, skip expensive full list | |
| // search. | |
| FindModulesInMap(module_spec, matching_module_list); | |
| if (!matching_module_list.IsEmpty()) | |
| return; | |
| m_list.FindModules(module_spec, matching_module_list); | |
| // Assert that modules were found in the list but not the map, it's | |
| // because the module_spec has no filename or the found module has a | |
| // different filename. For example, when searching by UUID and finding a | |
| // module with an alias. | |
| assert((matching_module_list.IsEmpty() || | |
| module_spec.GetFileSpec().GetFilename().empty() || | |
| module_spec.GetFileSpec().GetFilename() != | |
| matching_module_list.GetModuleAtIndex(0) | |
| ->GetFileSpec() | |
| .GetFilename()) && | |
| "Search by name not found in SharedModuleList's map"); | |
| } | |
| ModuleSP FindModule(const Module &module) { | |
| std::lock_guard<std::recursive_mutex> guard(GetMutex()); | |
| if (ModuleSP result = FindModuleInMap(module)) | |
| return result; | |
| return m_list.FindModule(&module); | |
| } | |
| // UUID searches bypass map since UUIDs aren't indexed by filename. | |
| ModuleSP FindModule(const UUID &uuid) const { | |
| return m_list.FindModule(uuid); | |
| } | |
| void Append(const ModuleSP &module_sp, bool use_notifier) { | |
| if (!module_sp) | |
| return; | |
| std::lock_guard<std::recursive_mutex> guard(GetMutex()); | |
| m_list.Append(module_sp, use_notifier); | |
| AddToMap(module_sp); | |
| } | |
| size_t RemoveOrphans(bool mandatory) { | |
| std::unique_lock<std::recursive_mutex> lock(GetMutex(), std::defer_lock); | |
| if (mandatory) { | |
| lock.lock(); | |
| } else { | |
| if (!lock.try_lock()) | |
| return 0; | |
| } | |
| size_t total_count = 0; | |
| size_t run_count; | |
| do { | |
| // Remove indexed orphans first, then remove non-indexed orphans. This | |
| // order is important because the shared count will be different if a | |
| // module is indexed or not. | |
| run_count = RemoveOrphansFromMapAndList(); | |
| run_count += m_list.RemoveOrphans(mandatory); | |
| total_count += run_count; | |
| // Because removing orphans might make new orphans, remove from both | |
| // containers until a fixed-point is reached. | |
| } while (run_count != 0); | |
| return total_count; | |
| } | |
| bool Remove(const ModuleSP &module_sp, bool use_notifier = true) { | |
| if (!module_sp) | |
| return false; | |
| std::lock_guard<std::recursive_mutex> guard(GetMutex()); | |
| RemoveFromMap(module_sp); | |
| return m_list.Remove(module_sp, use_notifier); | |
| } | |
| void ReplaceEquivalent(const ModuleSP &module_sp, | |
| llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules) { | |
| std::lock_guard<std::recursive_mutex> guard(GetMutex()); | |
| m_list.ReplaceEquivalent(module_sp, old_modules); | |
| ReplaceEquivalentInMap(module_sp); | |
| } | |
| bool RemoveIfOrphaned(const ModuleWP module_wp) { | |
| std::lock_guard<std::recursive_mutex> guard(GetMutex()); | |
| RemoveFromMap(module_wp, /*if_orphaned=*/true); | |
| return m_list.RemoveIfOrphaned(module_wp); | |
| } | |
| std::recursive_mutex &GetMutex() const { return m_list.GetMutex(); } | |
| private: | |
| ModuleSP FindModuleInMap(const Module &module) const { | |
| if (module.GetFileSpec().GetFilename().empty()) | |
| return ModuleSP(); | |
| llvm::StringRef name = module.GetFileSpec().GetFilename(); | |
| auto it = m_name_to_modules.find(ConstString(name)); | |
| if (it == m_name_to_modules.end()) | |
| return ModuleSP(); | |
| const llvm::SmallVectorImpl<ModuleSP> &vector = it->second; | |
| for (const ModuleSP &module_sp : vector) { | |
| if (module_sp.get() == &module) | |
| return module_sp; | |
| } | |
| return ModuleSP(); | |
| } | |
| void FindModulesInMap(const ModuleSpec &module_spec, | |
| ModuleList &matching_module_list) const { | |
| auto it = m_name_to_modules.find( | |
| ConstString(module_spec.GetFileSpec().GetFilename())); | |
| if (it == m_name_to_modules.end()) | |
| return; | |
| const llvm::SmallVectorImpl<ModuleSP> &vector = it->second; | |
| for (const ModuleSP &module_sp : vector) { | |
| if (module_sp->MatchesModuleSpec(module_spec)) | |
| matching_module_list.Append(module_sp); | |
| } | |
| } | |
| void AddToMap(const ModuleSP &module_sp) { | |
| llvm::StringRef name = module_sp->GetFileSpec().GetFilename(); | |
| if (name.empty()) | |
| return; | |
| m_name_to_modules[ConstString(name)].push_back(module_sp); | |
| } | |
| void RemoveFromMap(const ModuleWP module_wp, bool if_orphaned = false) { | |
| if (auto module_sp = module_wp.lock()) { | |
| ConstString name = ConstString(module_sp->GetFileSpec().GetFilename()); | |
| if (!m_name_to_modules.contains(name)) | |
| return; | |
| llvm::SmallVectorImpl<ModuleSP> &vec = m_name_to_modules[name]; | |
| for (auto *it = vec.begin(); it != vec.end(); ++it) { | |
| if (it->get() == module_sp.get()) { | |
| // Since module_sp increases the refcount by 1, the use count should | |
| // be the regular use count + 1. | |
| constexpr long kUseCountOrphaned = | |
| kUseCountSharedModuleListOrphaned + 1; | |
| if (!if_orphaned || it->use_count() == kUseCountOrphaned) { | |
| vec.erase(it); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void ReplaceEquivalentInMap(const ModuleSP &module_sp) { | |
| RemoveEquivalentModulesFromMap(module_sp); | |
| AddToMap(module_sp); | |
| } | |
| void RemoveEquivalentModulesFromMap(const ModuleSP &module_sp) { | |
| llvm::StringRef name = module_sp->GetFileSpec().GetFilename(); | |
| if (name.empty()) | |
| return; | |
| auto it = m_name_to_modules.find(ConstString(name)); | |
| if (it == m_name_to_modules.end()) | |
| return; | |
| // First remove any equivalent modules. Equivalent modules are modules | |
| // whose path, platform path and architecture match. | |
| ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(), | |
| module_sp->GetArchitecture()); | |
| equivalent_module_spec.GetPlatformFileSpec() = | |
| module_sp->GetPlatformFileSpec(); | |
| llvm::SmallVectorImpl<ModuleSP> &vec = it->second; | |
| llvm::erase_if(vec, [&equivalent_module_spec](ModuleSP &element) { | |
| return element->MatchesModuleSpec(equivalent_module_spec); | |
| }); | |
| } | |
| /// Remove orphans from the vector and return the removed modules. | |
| ModuleList RemoveOrphansFromVector(llvm::SmallVectorImpl<ModuleSP> &vec) { | |
| // remove_if moves the elements that match the condition to the end of the | |
| // container, and returns an iterator to the first element that was moved. | |
| auto *to_remove_start = llvm::remove_if(vec, [](const ModuleSP &module) { | |
| return module.use_count() == kUseCountSharedModuleListOrphaned; | |
| }); | |
| ModuleList to_remove; | |
| for (ModuleSP *it = to_remove_start; it != vec.end(); ++it) | |
| to_remove.Append(*it); | |
| vec.erase(to_remove_start, vec.end()); | |
| return to_remove; | |
| } | |
| /// Remove orphans that exist in both the map and list. This does not remove | |
| /// any orphans that exist exclusively on the list. | |
| /// | |
| /// The mutex must be locked by the caller. | |
| int RemoveOrphansFromMapAndList() { | |
| // Modules might hold shared pointers to other modules, so removing one | |
| // module might orphan other modules. Keep removing modules until | |
| // there are no further modules that can be removed. | |
| int remove_count = 0; | |
| int previous_remove_count; | |
| do { | |
| previous_remove_count = remove_count; | |
| for (auto &[name, vec] : m_name_to_modules) { | |
| if (vec.empty()) | |
| continue; | |
| ModuleList to_remove = RemoveOrphansFromVector(vec); | |
| remove_count += to_remove.GetSize(); | |
| m_list.Remove(to_remove); | |
| } | |
| // Break when fixed-point is reached. | |
| } while (previous_remove_count != remove_count); | |
| return remove_count; | |
| } | |
| ModuleList m_list; | |
| /// A hash map from a module's filename to all the modules that share that | |
| /// filename, for fast module lookups by name. | |
| llvm::DenseMap<ConstString, llvm::SmallVector<ModuleSP, 1>> m_name_to_modules; | |
| /// The use count of a module held only by m_list and m_name_to_modules. | |
| static constexpr long kUseCountSharedModuleListOrphaned = 2; | |
| }; | |
| struct SharedModuleListInfo { | |
| SharedModuleList module_list; | |
| ModuleListProperties module_list_properties; | |
| }; | |
| } // namespace | |
| static SharedModuleListInfo &GetSharedModuleListInfo() { | |
| static SharedModuleListInfo *g_shared_module_list_info = nullptr; | |
| static llvm::once_flag g_once_flag; | |
| llvm::call_once(g_once_flag, []() { | |
| // NOTE: Intentionally leak the module list so a program doesn't have to | |
| // cleanup all modules and object files as it exits. This just wastes time | |
| // doing a bunch of cleanup that isn't required. | |
| if (g_shared_module_list_info == nullptr) | |
| g_shared_module_list_info = new SharedModuleListInfo(); | |
| }); | |
| return *g_shared_module_list_info; | |
| } | |
| static SharedModuleList &GetSharedModuleList() { | |
| return GetSharedModuleListInfo().module_list; | |
| } | |
| ModuleListProperties &ModuleList::GetGlobalModuleListProperties() { | |
| return GetSharedModuleListInfo().module_list_properties; | |
| } | |
| bool ModuleList::ModuleIsInCache(const Module *module_ptr) { | |
| if (module_ptr) { | |
| SharedModuleList &shared_module_list = GetSharedModuleList(); | |
| return shared_module_list.FindModule(*module_ptr).get() != nullptr; | |
| } | |
| return false; | |
| } | |
| void ModuleList::FindSharedModules(const ModuleSpec &module_spec, | |
| ModuleList &matching_module_list) { | |
| GetSharedModuleList().FindModules(module_spec, matching_module_list); | |
| } | |
| lldb::ModuleSP ModuleList::FindSharedModule(const UUID &uuid) { | |
| return GetSharedModuleList().FindModule(uuid); | |
| } | |
| size_t ModuleList::RemoveOrphanSharedModules(bool mandatory) { | |
| return GetSharedModuleList().RemoveOrphans(mandatory); | |
| } | |
| Status | |
| ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, | |
| llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, | |
| bool *did_create_ptr, bool invoke_locate_callback, | |
| bool invoke_symbol_locators) { | |
| SharedModuleList &shared_module_list = GetSharedModuleList(); | |
| std::lock_guard<std::recursive_mutex> guard(shared_module_list.GetMutex()); | |
| char path[PATH_MAX]; | |
| Status error; | |
| module_sp.reset(); | |
| if (did_create_ptr) | |
| *did_create_ptr = false; | |
| const UUID *uuid_ptr = module_spec.GetUUIDPtr(); | |
| const FileSpec &module_file_spec = module_spec.GetFileSpec(); | |
| const ArchSpec &arch = module_spec.GetArchitecture(); | |
| // Make sure no one else can try and get or create a module while this | |
| // function is actively working on it by doing an extra lock on the global | |
| // mutex list. | |
| { | |
| ModuleList matching_module_list; | |
| shared_module_list.FindModules(module_spec, matching_module_list); | |
| const size_t num_matching_modules = matching_module_list.GetSize(); | |
| if (num_matching_modules > 0) { | |
| for (size_t module_idx = 0; module_idx < num_matching_modules; | |
| ++module_idx) { | |
| module_sp = matching_module_list.GetModuleAtIndex(module_idx); | |
| // Make sure the file for the module hasn't been modified | |
| if (module_sp->FileHasChanged()) { | |
| if (old_modules) | |
| old_modules->push_back(module_sp); | |
| Log *log = GetLog(LLDBLog::Modules); | |
| LLDB_LOG( | |
| log, | |
| "{0:x} '{1}' module changed: removing from global module list", | |
| static_cast<void *>(module_sp.get()), | |
| module_sp->GetFileSpec().GetFilename()); | |
| shared_module_list.Remove(module_sp); | |
| module_sp.reset(); | |
| } else { | |
| // The module matches and the module was not modified from when it | |
| // was last loaded. | |
| return error; | |
| } | |
| } | |
| } | |
| } | |
| if (module_sp) | |
| return error; | |
| // Try platform's locate module callback before second attempt. | |
| // The platform can come from either the Target (if available) or directly | |
| // from the ModuleSpec (useful when Target is not yet created, e.g., during | |
| // target creation for launch mode). | |
| if (invoke_locate_callback) { | |
| PlatformSP platform_sp; | |
| if (TargetSP target_sp = module_spec.GetTargetSP()) { | |
| if (target_sp->IsValid()) | |
| platform_sp = target_sp->GetPlatform(); | |
| } | |
| if (!platform_sp) | |
| platform_sp = module_spec.GetPlatformSP(); | |
| if (platform_sp) { | |
| FileSpec symbol_file_spec; | |
| platform_sp->CallLocateModuleCallbackIfSet( | |
| module_spec, module_sp, symbol_file_spec, did_create_ptr); | |
| if (module_sp) { | |
| // The callback found a module. | |
| return error; | |
| } | |
| } | |
| } | |
| module_sp = std::make_shared<Module>(module_spec); | |
| // Make sure there are a module and an object file since we can specify a | |
| // valid file path with an architecture that might not be in that file. By | |
| // getting the object file we can guarantee that the architecture matches | |
| if (module_sp->GetObjectFile()) { | |
| // If we get in here we got the correct arch, now we just need to verify | |
| // the UUID if one was given | |
| if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) { | |
| module_sp.reset(); | |
| } else { | |
| if (module_sp->GetObjectFile() && module_sp->GetObjectFile()->GetType() == | |
| ObjectFile::eTypeStubLibrary) { | |
| module_sp.reset(); | |
| } else { | |
| if (did_create_ptr) { | |
| *did_create_ptr = true; | |
| } | |
| shared_module_list.ReplaceEquivalent(module_sp, old_modules); | |
| return error; | |
| } | |
| } | |
| } else { | |
| module_sp.reset(); | |
| } | |
| // Get module search paths from the target if available. | |
| lldb::TargetSP target_sp = module_spec.GetTargetSP(); | |
| FileSpecList module_search_paths; | |
| if (target_sp) | |
| module_search_paths = target_sp->GetExecutableSearchPaths(); | |
| if (!module_search_paths.IsEmpty()) { | |
| const auto num_directories = module_search_paths.GetSize(); | |
| for (size_t idx = 0; idx < num_directories; ++idx) { | |
| auto search_path_spec = module_search_paths.GetFileSpecAtIndex(idx); | |
| FileSystem::Instance().Resolve(search_path_spec); | |
| namespace fs = llvm::sys::fs; | |
| if (!FileSystem::Instance().IsDirectory(search_path_spec)) | |
| continue; | |
| search_path_spec.AppendPathComponent( | |
| module_spec.GetFileSpec().GetFilename()); | |
| if (!FileSystem::Instance().Exists(search_path_spec)) | |
| continue; | |
| auto resolved_module_spec(module_spec); | |
| resolved_module_spec.GetFileSpec() = search_path_spec; | |
| module_sp = std::make_shared<Module>(resolved_module_spec); | |
| if (module_sp->GetObjectFile()) { | |
| // If we get in here we got the correct arch, now we just need to | |
| // verify the UUID if one was given | |
| if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) { | |
| module_sp.reset(); | |
| } else { | |
| if (module_sp->GetObjectFile()->GetType() == | |
| ObjectFile::eTypeStubLibrary) { | |
| module_sp.reset(); | |
| } else { | |
| if (did_create_ptr) | |
| *did_create_ptr = true; | |
| shared_module_list.ReplaceEquivalent(module_sp, old_modules); | |
| return Status(); | |
| } | |
| } | |
| } else { | |
| module_sp.reset(); | |
| } | |
| } | |
| } | |
| // Either the file didn't exist where at the path, or no path was given, so we | |
| // now either have to use more extreme measures to try and find the | |
| // appropriate module or end our search here. | |
| if (!invoke_symbol_locators) { | |
| std::string uuid_str; | |
| if (uuid_ptr && uuid_ptr->IsValid()) | |
| uuid_str = uuid_ptr->GetAsString(); | |
| if (!uuid_str.empty()) | |
| return Status::FromErrorStringWithFormatv( | |
| "cannot locate module for UUID '{}'", uuid_str); | |
| return Status::FromErrorString("cannot locate module"); | |
| } | |
| // Fixup the incoming path in case the path points to a valid file, yet the | |
| // arch or UUID (if one was passed in) don't match. | |
| ModuleSpec located_binary_modulespec; | |
| StatisticsMap symbol_locator_map; | |
| located_binary_modulespec = PluginManager::LocateExecutableObjectFile( | |
| module_spec, symbol_locator_map); | |
| // Don't look for the file if it appears to be the same one we already | |
| // checked for above... | |
| if (located_binary_modulespec.GetFileSpec() != module_file_spec) { | |
| if (!FileSystem::Instance().Exists( | |
| located_binary_modulespec.GetFileSpec())) { | |
| located_binary_modulespec.GetFileSpec().GetPath(path, sizeof(path)); | |
| if (path[0] == '\0') | |
| module_file_spec.GetPath(path, sizeof(path)); | |
| // How can this check ever be true? This branch it is false, and we | |
| // haven't modified file_spec. | |
| if (FileSystem::Instance().Exists( | |
| located_binary_modulespec.GetFileSpec())) { | |
| std::string uuid_str; | |
| if (uuid_ptr && uuid_ptr->IsValid()) | |
| uuid_str = uuid_ptr->GetAsString(); | |
| if (arch.IsValid()) { | |
| if (!uuid_str.empty()) | |
| error = Status::FromErrorStringWithFormat( | |
| "'%s' does not contain the %s architecture and UUID %s", path, | |
| arch.GetArchitectureName(), uuid_str.c_str()); | |
| else | |
| error = Status::FromErrorStringWithFormat( | |
| "'%s' does not contain the %s architecture.", path, | |
| arch.GetArchitectureName()); | |
| } | |
| } else { | |
| error = Status::FromErrorStringWithFormat("'%s' does not exist", path); | |
| } | |
| if (error.Fail()) | |
| module_sp.reset(); | |
| return error; | |
| } | |
| // Make sure no one else can try and get or create a module while this | |
| // function is actively working on it by doing an extra lock on the global | |
| // mutex list. | |
| ModuleSpec platform_module_spec(module_spec); | |
| platform_module_spec.GetFileSpec() = | |
| located_binary_modulespec.GetFileSpec(); | |
| platform_module_spec.GetPlatformFileSpec() = | |
| located_binary_modulespec.GetFileSpec(); | |
| platform_module_spec.GetSymbolFileSpec() = | |
| located_binary_modulespec.GetSymbolFileSpec(); | |
| ModuleList matching_module_list; | |
| shared_module_list.FindModules(platform_module_spec, matching_module_list); | |
| if (!matching_module_list.IsEmpty()) { | |
| module_sp = matching_module_list.GetModuleAtIndex(0); | |
| // If we didn't have a UUID in mind when looking for the object file, | |
| // then we should make sure the modification time hasn't changed! | |
| if (platform_module_spec.GetUUIDPtr() == nullptr) { | |
| auto file_spec_mod_time = FileSystem::Instance().GetModificationTime( | |
| located_binary_modulespec.GetFileSpec()); | |
| if (file_spec_mod_time != llvm::sys::TimePoint<>()) { | |
| if (file_spec_mod_time != module_sp->GetModificationTime()) { | |
| if (old_modules) | |
| old_modules->push_back(module_sp); | |
| shared_module_list.Remove(module_sp); | |
| module_sp.reset(); | |
| } | |
| } | |
| } | |
| } | |
| if (!module_sp) { | |
| module_sp = std::make_shared<Module>(platform_module_spec); | |
| // Make sure there are a module and an object file since we can specify a | |
| // valid file path with an architecture that might not be in that file. | |
| // By getting the object file we can guarantee that the architecture | |
| // matches | |
| if (module_sp && module_sp->GetObjectFile()) { | |
| module_sp->GetSymbolLocatorStatistics().merge(symbol_locator_map); | |
| if (module_sp->GetObjectFile()->GetType() == | |
| ObjectFile::eTypeStubLibrary) { | |
| module_sp.reset(); | |
| } else { | |
| if (did_create_ptr) | |
| *did_create_ptr = true; | |
| shared_module_list.ReplaceEquivalent(module_sp, old_modules); | |
| } | |
| } else { | |
| located_binary_modulespec.GetFileSpec().GetPath(path, sizeof(path)); | |
| if (located_binary_modulespec.GetFileSpec()) { | |
| if (arch.IsValid()) | |
| error = Status::FromErrorStringWithFormat( | |
| "unable to open %s architecture in '%s'", | |
| arch.GetArchitectureName(), path); | |
| else | |
| error = | |
| Status::FromErrorStringWithFormat("unable to open '%s'", path); | |
| } else { | |
| std::string uuid_str; | |
| if (uuid_ptr && uuid_ptr->IsValid()) | |
| uuid_str = uuid_ptr->GetAsString(); | |
| if (!uuid_str.empty()) | |
| error = Status::FromErrorStringWithFormat( | |
| "cannot locate a module for UUID '%s'", uuid_str.c_str()); | |
| else | |
| error = Status::FromErrorString("cannot locate a module"); | |
| } | |
| } | |
| } | |
| } | |
| return error; | |
| } | |
| bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) { | |
| return GetSharedModuleList().Remove(module_sp); | |
| } | |
| bool ModuleList::RemoveSharedModuleIfOrphaned(const ModuleWP module_wp) { | |
| return GetSharedModuleList().RemoveIfOrphaned(module_wp); | |
| } | |
| static bool LoadScriptingModule(const FileSpec &scripting_fspec, | |
| ScriptInterpreter &script_interpreter, | |
| Target &target, Status &error) { | |
| assert(scripting_fspec); | |
| StreamString scripting_stream; | |
| scripting_fspec.Dump(scripting_stream.AsRawOstream()); | |
| LoadScriptOptions options; | |
| return script_interpreter.LoadScriptingModule( | |
| scripting_stream.GetData(), options, error, | |
| /*module_sp*/ nullptr, /*extra_path*/ {}, target.shared_from_this()); | |
| } | |
| bool ModuleList::LoadScriptingResourceInTargetForModule(Module &module, | |
| Target &target, | |
| Status &error) { | |
| Log *log = GetLog(LLDBLog::Modules); | |
| Debugger &debugger = target.GetDebugger(); | |
| const ScriptLanguage script_language = debugger.GetScriptLanguage(); | |
| if (script_language == eScriptLanguageNone) | |
| return true; | |
| ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); | |
| if (!script_interpreter) { | |
| error = Status::FromErrorString("invalid ScriptInterpreter"); | |
| return false; | |
| } | |
| PlatformSP platform_sp = target.GetPlatform(); | |
| if (!platform_sp) { | |
| error = Status::FromErrorString("invalid Platform"); | |
| return false; | |
| } | |
| StreamString feedback_stream; | |
| llvm::SmallDenseMap<FileSpec, LoadScriptFromSymFile> file_specs = | |
| platform_sp->LocateExecutableScriptingResources(&target, module, | |
| feedback_stream); | |
| if (!feedback_stream.Empty()) | |
| debugger.ReportWarning(feedback_stream.GetString().str(), debugger.GetID()); | |
| const bool trusted = platform_sp->IsSymbolFileTrusted(module); | |
| for (const auto &[scripting_fspec, load_style] : file_specs) { | |
| if (load_style == eLoadScriptFromSymFileFalse) | |
| continue; | |
| if (!FileSystem::Instance().Exists(scripting_fspec)) | |
| continue; | |
| switch (load_style) { | |
| case eLoadScriptFromSymFileFalse: | |
| llvm_unreachable("case already handled"); | |
| case eLoadScriptFromSymFileTrue: | |
| break; | |
| case eLoadScriptFromSymFileTrusted: | |
| if (trusted) | |
| break; | |
| LLVM_FALLTHROUGH; | |
| case eLoadScriptFromSymFileWarn: | |
| debugger.ReportWarning( | |
| llvm::formatv( | |
| // clang-format off | |
| R"('{0}' contains {1} debug script. To run this script in this debug session: | |
| command script import "{2}" | |
| To run all discovered debug scripts in this session: | |
| settings set target.load-script-from-symbol-file true | |
| )", | |
| // clang-format on | |
| module.GetFileSpec().GetFileNameStrippingExtension(), | |
| trusted ? "a trusted" : "an untrusted", | |
| scripting_fspec.GetPath()), | |
| debugger.GetID()); | |
| continue; | |
| } | |
| LLDB_LOG(log, "Auto-loading {0}", scripting_fspec.GetPath()); | |
| if (!LoadScriptingModule(scripting_fspec, *script_interpreter, target, | |
| error)) { | |
| LLDB_LOG(log, "Failed to load '{0}'. Remaining scripts won't be loaded.", | |
| scripting_fspec.GetPath()); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool ModuleList::LoadScriptingResourcesInTarget(Target *target, | |
| std::list<Status> &errors, | |
| bool continue_on_error) { | |
| if (!target) | |
| return false; | |
| m_modules_mutex.lock(); | |
| // Don't hold the module list mutex while loading the scripting resources, | |
| // The initializer might do any amount of work, and having that happen while | |
| // the module list is held is asking for A/B locking problems. | |
| const ModuleList tmp_module_list(*this); | |
| m_modules_mutex.unlock(); | |
| for (auto module : tmp_module_list.ModulesNoLocking()) { | |
| if (module) { | |
| Status error; | |
| if (!LoadScriptingResourceInTargetForModule(*module, *target, error)) { | |
| if (error.Fail() && error.AsCString()) { | |
| error = Status::FromErrorStringWithFormatv( | |
| "unable to load scripting data for " | |
| "module {0} - error reported was {1}", | |
| module->GetFileSpec().GetFileNameStrippingExtension(), | |
| error.AsCString()); | |
| errors.push_back(std::move(error)); | |
| if (!continue_on_error) | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| return errors.empty(); | |
| } | |
| void ModuleList::ForEach( | |
| std::function<IterationAction(const ModuleSP &module_sp)> const &callback) | |
| const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const auto &module_sp : m_modules) { | |
| assert(module_sp != nullptr); | |
| if (callback(module_sp) == IterationAction::Stop) | |
| break; | |
| } | |
| } | |
| bool ModuleList::AnyOf( | |
| std::function<bool(lldb_private::Module &module_sp)> const &callback) | |
| const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| for (const auto &module_sp : m_modules) { | |
| assert(module_sp != nullptr); | |
| if (callback(*module_sp)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| void ModuleList::Swap(ModuleList &other) { | |
| // scoped_lock locks both mutexes at once. | |
| std::scoped_lock<std::recursive_mutex, std::recursive_mutex> lock( | |
| m_modules_mutex, other.m_modules_mutex); | |
| m_modules.swap(other.m_modules); | |
| } | |
| void ModuleList::PreloadSymbols(bool parallelize) const { | |
| std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); | |
| if (!parallelize) { | |
| for (const ModuleSP &module_sp : m_modules) | |
| module_sp->PreloadSymbols(); | |
| return; | |
| } | |
| llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool()); | |
| for (const ModuleSP &module_sp : m_modules) | |
| task_group.async([module_sp] { | |
| if (module_sp) | |
| module_sp->PreloadSymbols(); | |
| }); | |
| task_group.wait(); | |
| } |