diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h --- a/lldb/include/lldb/Core/ModuleList.h +++ b/lldb/include/lldb/Core/ModuleList.h @@ -68,6 +68,8 @@ FileSpec GetLLDBIndexCachePath() const; bool SetLLDBIndexCachePath(const FileSpec &path); + bool GetLoadSymbolOnDemand(); + PathMappingList GetSymlinkMappings() const; }; diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -40,6 +40,11 @@ /// LLVM RTTI support. static char ID; +public: + /// Required to call protected member functions from + /// SymbolFileOnDemand via forwarding. + friend class SymbolFileOnDemand; + public: /// LLVM RTTI support. /// \{ @@ -72,6 +77,10 @@ ~SymbolFile() override = default; + /// SymbolFileOnDemand class overrides this to return the underlying + /// backing SymbolFile implementation that loads on-demand. + virtual SymbolFile *GetBackingSymbolFile() { return this; } + /// Get a mask of what this symbol file supports for the object file /// that it was constructed with. /// @@ -123,6 +132,16 @@ /// prior to any other functions in the SymbolFile protocol. virtual void InitializeObject() {} + /// Whether debug info will be loaded or not. + /// + /// It will be true for most implementations except SymbolFileOnDemand. + virtual bool GetLoadDebugInfoEnabled() { return true; } + + /// Specify debug info should be loaded. + /// + /// It will be no-op for most implementations except SymbolFileOnDemand. + virtual void SetLoadDebugInfoEnabled() { return; } + // Compile Unit function calls // Approach 1 - iterator uint32_t GetNumCompileUnits(); @@ -353,9 +372,7 @@ bool GetDebugInfoIndexWasSavedToCache() const { return m_index_was_saved_to_cache; } - void SetDebugInfoIndexWasSavedToCache() { - m_index_was_saved_to_cache = true; - } + void SetDebugInfoIndexWasSavedToCache() { m_index_was_saved_to_cache = true; } /// \} protected: diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h @@ -0,0 +1,208 @@ +//===-- SymbolFileOnDemand.h ------------------------------------*- C++-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SYMBOL_SYMBOLFILEONDEMAND_H +#define LLDB_SYMBOL_SYMBOLFILEONDEMAND_H + +#include +#include + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/Statistics.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Flags.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// SymbolFileOnDemand wraps a real SymbolFile by providing +/// on demand symbol parsing/indexing to improve performance. +/// By default SymbolFileOnDemand will skip load the underlying +/// symbols. Any client can on demand hydrate the underlying +/// SymbolFile via SetForceLoad() API. +class SymbolFileOnDemand : public lldb_private::SymbolFile { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFile::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + SymbolFileOnDemand(lldb::ObjectFileSP objfile_sp, + std::unique_ptr &&symbol_file); + virtual ~SymbolFileOnDemand() override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return "ondemand"; } + + bool GetLoadDebugInfoEnabled() override { return m_debug_info_enabled; } + + void SetLoadDebugInfoEnabled() override; + + SymbolFile *GetBackingSymbolFile() override { return m_sym_file_impl.get(); } + + uint32_t CalculateAbilities() override; + + std::recursive_mutex &GetModuleMutex() const override; + + lldb::LanguageType + ParseLanguage(lldb_private::CompileUnit &comp_unit) override; + + lldb_private::XcodeSDK + ParseXcodeSDK(lldb_private::CompileUnit &comp_unit) override; + + void InitializeObject() override; + + size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override; + + bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override; + + bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override; + + bool ForEachExternalModule( + lldb_private::CompileUnit &, llvm::DenseSet &, + llvm::function_ref) override; + + bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit, + lldb_private::FileSpecList &support_files) override; + + bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; + + bool ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector &imported_modules) override; + + size_t ParseBlocksRecursive(lldb_private::Function &func) override; + + size_t + ParseVariablesForContext(const lldb_private::SymbolContext &sc) override; + + lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + llvm::Optional GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override; + + bool CompleteType(lldb_private::CompilerType &compiler_type) override; + + lldb_private::CompilerDecl GetDeclForUID(lldb::user_id_t uid) override; + + lldb_private::CompilerDeclContext + GetDeclContextForUID(lldb::user_id_t uid) override; + + lldb_private::CompilerDeclContext + GetDeclContextContainingUID(lldb::user_id_t uid) override; + + void + ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override; + + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) override; + + uint32_t ResolveSymbolContext( + const lldb_private::SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContextList &sc_list) override; + + void Dump(lldb_private::Stream &s) override; + void DumpClangAST(lldb_private::Stream &s) override; + + void + FindGlobalVariables(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + void FindGlobalVariables(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + void FindFunctions(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + lldb::FunctionNameType name_type_mask, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) override; + + void FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) override; + + void GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector &mangled_names) override; + + void + FindTypes(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + llvm::DenseSet &searched_symbol_files, + lldb_private::TypeMap &types) override; + + void FindTypes(llvm::ArrayRef pattern, + lldb_private::LanguageSet languages, + llvm::DenseSet &searched_symbol_files, + lldb_private::TypeMap &types) override; + + void GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + + llvm::Expected + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + lldb_private::CompilerDeclContext FindNamespace( + lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx) override; + + std::vector> + ParseCallEdgesInFunction(UserID func_id) override; + + lldb::UnwindPlanSP + GetUnwindPlan(const Address &address, + const RegisterInfoResolver &resolver) override; + + llvm::Expected GetParameterStackSize(Symbol &symbol) override; + + void PreloadSymbols() override; + + uint64_t GetDebugInfoSize() override; + lldb_private::StatsDuration::Duration GetDebugInfoParseTime() override; + lldb_private::StatsDuration::Duration GetDebugInfoIndexTime() override; + +private: + Log *GetLog() const { return ::lldb_private::GetLog(LLDBLog::OnDemand); } + + ConstString GetSymbolFileName() { + return GetObjectFile()->GetFileSpec().GetFilename(); + } + +protected: + uint32_t CalculateNumCompileUnits() override; + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + lldb_private::TypeList &GetTypeList() override; + +private: + bool m_debug_info_enabled = false; + bool m_preload_symbols = false; + std::unique_ptr m_sym_file_impl; +}; +} // namespace lldb_private + +#endif // LLDB_SYMBOL_SYMBOLFILEONDEMAND_H diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h --- a/lldb/include/lldb/Target/Statistics.h +++ b/lldb/include/lldb/Target/Statistics.h @@ -116,6 +116,8 @@ bool symtab_saved_to_cache = false; bool debug_info_index_loaded_from_cache = false; bool debug_info_index_saved_to_cache = false; + bool debug_info_enabled = true; + bool symtab_stripped = false; }; struct ConstStringStats { diff --git a/lldb/include/lldb/Utility/LLDBLog.h b/lldb/include/lldb/Utility/LLDBLog.h --- a/lldb/include/lldb/Utility/LLDBLog.h +++ b/lldb/include/lldb/Utility/LLDBLog.h @@ -47,6 +47,7 @@ Types = Log::ChannelFlag<28>, Unwind = Log::ChannelFlag<29>, Watchpoints = Log::ChannelFlag<30>, + OnDemand = Log::ChannelFlag<31>, LLVM_MARK_AS_BITMASK_ENUM(Watchpoints), }; diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -33,6 +33,10 @@ Global, DefaultUnsignedValue<7>, Desc<"The expiration time in days for a file. When a file hasn't been accessed for the specified amount of days, it is removed from the cache. A value of 0 disables the expiration-based pruning.">; + def LoadSymbolOnDemand: Property<"load-on-demand", "Boolean">, + Global, + DefaultFalse, + Desc<"Enable on demand symbol loading in LLDB. LLDB will load debug info on demand for each module based on various conditions (e.g. matched breakpoint, resolved stack frame addresses and matched global variables/function symbols in symbol table) to improve performance.">; } let Definition = "debugger" in { diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -469,6 +469,11 @@ if (!symfile) return resolved_flags; + if (resolve_scope & + (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock | + eSymbolContextLineEntry | eSymbolContextVariable)) + symfile->SetLoadDebugInfoEnabled(); + // Resolve the compile unit, function, block, line table or line entry if // requested. if (resolve_scope & eSymbolContextCompUnit || @@ -1381,7 +1386,6 @@ // Now let the symbol file preload its data and the symbol table will be // available without needing to take the module lock. sym_file->PreloadSymbols(); - } void Module::SetSymbolFileFileSpec(const FileSpec &file) { @@ -1687,6 +1691,9 @@ return nullptr; // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function - static DataFileCache *g_data_file_cache = new DataFileCache(ModuleList::GetGlobalModuleListProperties().GetLLDBIndexCachePath().GetPath()); + static DataFileCache *g_data_file_cache = + new DataFileCache(ModuleList::GetGlobalModuleListProperties() + .GetLLDBIndexCachePath() + .GetPath()); return g_data_file_cache; } diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -180,6 +180,12 @@ return m_symlink_paths; } +bool ModuleListProperties::GetLoadSymbolOnDemand() { + const uint32_t idx = ePropertyLoadSymbolOnDemand; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_modulelist_properties[idx].default_uint_value != 0); +} + ModuleList::ModuleList() : m_modules(), m_modules_mutex() {} ModuleList::ModuleList(const ModuleList &rhs) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp @@ -68,7 +68,8 @@ const DWARFIndex &index, llvm::function_ref callback, llvm::StringRef name) : m_index(index), - m_dwarf(*llvm::cast(index.m_module.GetSymbolFile())), + m_dwarf(*llvm::cast( + index.m_module.GetSymbolFile()->GetBackingSymbolFile())), m_callback(callback), m_name(name) {} bool DWARFIndex::DIERefCallbackImpl::operator()(DIERef ref) const { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -65,8 +65,8 @@ llvm::Optional ref = ToDIERef(entry); if (!ref) return true; - SymbolFileDWARF &dwarf = - *llvm::cast(m_module.GetSymbolFile()); + SymbolFileDWARF &dwarf = *llvm::cast( + m_module.GetSymbolFile()->GetBackingSymbolFile()); DWARFDIE die = dwarf.GetDIE(*ref); if (!die) return true; diff --git a/lldb/source/Symbol/CMakeLists.txt b/lldb/source/Symbol/CMakeLists.txt --- a/lldb/source/Symbol/CMakeLists.txt +++ b/lldb/source/Symbol/CMakeLists.txt @@ -27,6 +27,7 @@ Symbol.cpp SymbolContext.cpp SymbolFile.cpp + SymbolFileOnDemand.cpp SymbolVendor.cpp Symtab.cpp Type.cpp diff --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp --- a/lldb/source/Symbol/CompileUnit.cpp +++ b/lldb/source/Symbol/CompileUnit.cpp @@ -287,6 +287,9 @@ if (num_file_indexes == 0) return; + // Found a matching source file in this compile unit load its debug info. + GetModule()->GetSymbolFile()->SetLoadDebugInfoEnabled(); + LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table == nullptr) { @@ -312,7 +315,7 @@ line_idx = line_table->FindLineEntryIndexByFileIndex( 0, file_indexes, src_location_spec, &line_entry); } - + // If "exact == true", then "found_line" will be the same as "line". If // "exact == false", the "found_line" will be the closest line entry // with a line number greater than "line" and we will use this for our diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp --- a/lldb/source/Symbol/SymbolFile.cpp +++ b/lldb/source/Symbol/SymbolFile.cpp @@ -12,6 +12,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFileOnDemand.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" @@ -79,6 +80,23 @@ } } if (best_symfile_up) { + // If symbol on-demand is enabled the winning symbol file parser is + // wrapped with SymbolFileOnDemand so that hydration of the debug info + // can be controlled to improve performance. + // + // Currently the supported on-demand symbol files include: + // executables, shared libraries and debug info files. + // + // To reduce unnecessary wrapping files with zero debug info are skipped. + ObjectFile::Type obj_file_type = objfile_sp->CalculateType(); + if (ModuleList::GetGlobalModuleListProperties().GetLoadSymbolOnDemand() && + best_symfile_up->GetDebugInfoSize() > 0 && + (obj_file_type == ObjectFile::eTypeExecutable || + obj_file_type == ObjectFile::eTypeSharedLibrary || + obj_file_type == ObjectFile::eTypeDebugInfo)) { + best_symfile_up = std::make_unique( + objfile_sp, std::move(best_symfile_up)); + } // Let the winning symbol file parser initialize itself more completely // now that it has been chosen best_symfile_up->InitializeObject(); diff --git a/lldb/source/Symbol/SymbolFileOnDemand.cpp b/lldb/source/Symbol/SymbolFileOnDemand.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Symbol/SymbolFileOnDemand.cpp @@ -0,0 +1,596 @@ +//===-- SymbolFileDWARFDebugMap.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/Symbol/SymbolFileOnDemand.h" + +#include "lldb/Core/Module.h" +#include "lldb/Symbol/SymbolFile.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +char SymbolFileOnDemand::ID; + +SymbolFileOnDemand::SymbolFileOnDemand( + lldb::ObjectFileSP objfile_sp, std::unique_ptr &&symbol_file) + : SymbolFile(std::move(objfile_sp)), + m_sym_file_impl(std::move(symbol_file)) {} + +SymbolFileOnDemand::~SymbolFileOnDemand() {} + +uint32_t SymbolFileOnDemand::CalculateAbilities() { + // Explicitly allow ability checking to pass though. + // This should be a cheap operation. + return m_sym_file_impl->CalculateAbilities(); +} + +std::recursive_mutex &SymbolFileOnDemand::GetModuleMutex() const { + return m_sym_file_impl->GetModuleMutex(); +} + +void SymbolFileOnDemand::InitializeObject() { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->InitializeObject(); +} + +lldb::LanguageType SymbolFileOnDemand::ParseLanguage(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + lldb::LanguageType langType = m_sym_file_impl->ParseLanguage(comp_unit); + if (langType != eLanguageTypeUnknown) + LLDB_LOG(log, "Language {0} would return if hydrated.", + eLanguageTypeUnknown); + } + return eLanguageTypeUnknown; + } + return m_sym_file_impl->ParseLanguage(comp_unit); +} + +XcodeSDK SymbolFileOnDemand::ParseXcodeSDK(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + XcodeSDK defaultValue{}; + if (log) { + XcodeSDK sdk = m_sym_file_impl->ParseXcodeSDK(comp_unit); + if (!(sdk == defaultValue)) + LLDB_LOG(log, "SDK {0} would return if hydrated.", sdk.GetString()); + } + return defaultValue; + } + return m_sym_file_impl->ParseXcodeSDK(comp_unit); +} + +size_t SymbolFileOnDemand::ParseFunctions(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return 0; + } + return m_sym_file_impl->ParseFunctions(comp_unit); +} + +bool SymbolFileOnDemand::ParseLineTable(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return false; + } + return m_sym_file_impl->ParseLineTable(comp_unit); +} + +bool SymbolFileOnDemand::ParseDebugMacros(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return false; + } + return m_sym_file_impl->ParseDebugMacros(comp_unit); +} + +bool SymbolFileOnDemand::ForEachExternalModule( + CompileUnit &comp_unit, + llvm::DenseSet &visited_symbol_files, + llvm::function_ref lambda) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + // Not early exit. + return false; + } + return m_sym_file_impl->ForEachExternalModule(comp_unit, visited_symbol_files, + lambda); +} + +bool SymbolFileOnDemand::ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) { + LLDB_LOG(GetLog(), + "[{0}] {1} is not skipped: explicitly allowed to support breakpoint", + GetSymbolFileName(), __FUNCTION__); + // Explicitly allow this API through to support source line breakpoint. + return m_sym_file_impl->ParseSupportFiles(comp_unit, support_files); +} + +bool SymbolFileOnDemand::ParseIsOptimized(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + bool optimized = m_sym_file_impl->ParseIsOptimized(comp_unit); + if (optimized) { + LLDB_LOG(log, "Optimized would return if hydrated."); + } + } + return false; + } + return m_sym_file_impl->ParseIsOptimized(comp_unit); +} + +size_t SymbolFileOnDemand::ParseTypes(CompileUnit &comp_unit) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return 0; + } + return m_sym_file_impl->ParseTypes(comp_unit); +} + +bool SymbolFileOnDemand::ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector &imported_modules) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + std::vector tmp_imported_modules; + bool succeed = + m_sym_file_impl->ParseImportedModules(sc, tmp_imported_modules); + if (succeed) + LLDB_LOG(log, "{0} imported modules would be parsed if hydrated.", + tmp_imported_modules.size()); + } + return false; + } + return m_sym_file_impl->ParseImportedModules(sc, imported_modules); +} + +size_t SymbolFileOnDemand::ParseBlocksRecursive(Function &func) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return 0; + } + return m_sym_file_impl->ParseBlocksRecursive(func); +} + +size_t SymbolFileOnDemand::ParseVariablesForContext(const SymbolContext &sc) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return 0; + } + return m_sym_file_impl->ParseVariablesForContext(sc); +} + +Type *SymbolFileOnDemand::ResolveTypeUID(lldb::user_id_t type_uid) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + Type *resolved_type = m_sym_file_impl->ResolveTypeUID(type_uid); + if (resolved_type) + LLDB_LOG(log, "Type would be parsed for {0} if hydrated.", type_uid); + } + return nullptr; + } + return m_sym_file_impl->ResolveTypeUID(type_uid); +} + +llvm::Optional +SymbolFileOnDemand::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return llvm::None; + } + return m_sym_file_impl->GetDynamicArrayInfoForUID(type_uid, exe_ctx); +} + +bool SymbolFileOnDemand::CompleteType(CompilerType &compiler_type) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return false; + } + return m_sym_file_impl->CompleteType(compiler_type); +} + +CompilerDecl SymbolFileOnDemand::GetDeclForUID(lldb::user_id_t type_uid) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + CompilerDecl parsed_decl = m_sym_file_impl->GetDeclForUID(type_uid); + if (parsed_decl != CompilerDecl()) { + LLDB_LOG(log, "CompilerDecl {0} would be parsed for {1} if hydrated.", + parsed_decl.GetName(), type_uid); + } + } + return CompilerDecl(); + } + return m_sym_file_impl->GetDeclForUID(type_uid); +} + +CompilerDeclContext +SymbolFileOnDemand::GetDeclContextForUID(lldb::user_id_t type_uid) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return CompilerDeclContext(); + } + return m_sym_file_impl->GetDeclContextForUID(type_uid); +} + +CompilerDeclContext +SymbolFileOnDemand::GetDeclContextContainingUID(lldb::user_id_t type_uid) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return CompilerDeclContext(); + } + return m_sym_file_impl->GetDeclContextContainingUID(type_uid); +} + +void SymbolFileOnDemand::ParseDeclsForContext(CompilerDeclContext decl_ctx) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->ParseDeclsForContext(decl_ctx); +} + +uint32_t +SymbolFileOnDemand::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return 0; + } + return m_sym_file_impl->ResolveSymbolContext(so_addr, resolve_scope, sc); +} + +uint32_t SymbolFileOnDemand::ResolveSymbolContext( + const SourceLocationSpec &src_location_spec, + SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return 0; + } + return m_sym_file_impl->ResolveSymbolContext(src_location_spec, resolve_scope, + sc_list); +} + +void SymbolFileOnDemand::Dump(lldb_private::Stream &s) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->Dump(s); +} + +void SymbolFileOnDemand::DumpClangAST(lldb_private::Stream &s) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->DumpClangAST(s); +} + +void SymbolFileOnDemand::FindGlobalVariables(const RegularExpression ®ex, + uint32_t max_matches, + VariableList &variables) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->FindGlobalVariables(regex, max_matches, variables); +} + +void SymbolFileOnDemand::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + Symtab *symtab = GetSymtab(); + if (!symtab) { + LLDB_LOG(log, "[{0}] {1} is skipped - fail to get symtab", + GetSymbolFileName(), __FUNCTION__); + return; + } + Symbol *sym = symtab->FindFirstSymbolWithNameAndType( + name, eSymbolTypeData, Symtab::eDebugAny, Symtab::eVisibilityAny); + if (!sym) { + LLDB_LOG(log, "[{0}] {1} is skipped - fail to find match in symtab", + GetSymbolFileName(), __FUNCTION__); + return; + } + LLDB_LOG(log, "[{0}] {1} is NOT skipped - found match in symtab", + GetSymbolFileName(), __FUNCTION__); + + // Found match in symbol table hydrate debug info and + // allow the FindGlobalVariables to go through. + SetLoadDebugInfoEnabled(); + } + return m_sym_file_impl->FindGlobalVariables(name, parent_decl_ctx, + max_matches, variables); +} + +void SymbolFileOnDemand::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + SymbolContextList &sc_list) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + Symtab *symtab = GetSymtab(); + if (!symtab) { + LLDB_LOG(log, "[{0}] {1} is skipped - fail to get symtab", + GetSymbolFileName(), __FUNCTION__); + return; + } + std::vector symbol_indexes; + symtab->AppendSymbolIndexesMatchingRegExAndType( + regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny, + symbol_indexes); + if (symbol_indexes.empty()) { + LLDB_LOG(log, "[{0}] {1} is skipped - fail to find match in symtab", + GetSymbolFileName(), __FUNCTION__); + return; + } + LLDB_LOG(log, "[{0}] {1} is NOT skipped - found match in symtab", + GetSymbolFileName(), __FUNCTION__); + + // Found match in symbol table hydrate debug info and + // allow the FindFucntions to go through. + SetLoadDebugInfoEnabled(); + } + return m_sym_file_impl->FindFunctions(regex, include_inlines, sc_list); +} + +void SymbolFileOnDemand::FindFunctions( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + FunctionNameType name_type_mask, bool include_inlines, + SymbolContextList &sc_list) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + + Symtab *symtab = GetSymtab(); + if (!symtab) { + LLDB_LOG(log, "[{0}] {1}({2}) is skipped - fail to get symtab", + GetSymbolFileName(), __FUNCTION__, name); + return; + } + + SymbolContextList sc_list_helper; + symtab->FindFunctionSymbols(name, name_type_mask, sc_list_helper); + if (sc_list_helper.GetSize() == 0) { + LLDB_LOG(log, "[{0}] {1}({2}) is skipped - fail to find match in symtab", + GetSymbolFileName(), __FUNCTION__, name); + return; + } + LLDB_LOG(log, "[{0}] {1}({2}) is NOT skipped - found match in symtab", + GetSymbolFileName(), __FUNCTION__, name); + + // Found match in symbol table hydrate debug info and + // allow the FindFucntions to go through. + SetLoadDebugInfoEnabled(); + } + return m_sym_file_impl->FindFunctions(name, parent_decl_ctx, name_type_mask, + include_inlines, sc_list); +} + +void SymbolFileOnDemand::GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector &mangled_names) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1}({2}) is skipped", GetSymbolFileName(), + __FUNCTION__, scope_qualified_name); + return; + } + return m_sym_file_impl->GetMangledNamesForFunction(scope_qualified_name, + mangled_names); +} + +void SymbolFileOnDemand::FindTypes( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + llvm::DenseSet &searched_symbol_files, + TypeMap &types) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1}({2}) is skipped", GetSymbolFileName(), + __FUNCTION__, name); + return; + } + return m_sym_file_impl->FindTypes(name, parent_decl_ctx, max_matches, + searched_symbol_files, types); +} + +void SymbolFileOnDemand::FindTypes( + llvm::ArrayRef pattern, LanguageSet languages, + llvm::DenseSet &searched_symbol_files, TypeMap &types) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->FindTypes(pattern, languages, searched_symbol_files, + types); +} + +void SymbolFileOnDemand::GetTypes(SymbolContextScope *sc_scope, + TypeClass type_mask, TypeList &type_list) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->GetTypes(sc_scope, type_mask, type_list); +} + +llvm::Expected +SymbolFileOnDemand::GetTypeSystemForLanguage(LanguageType language) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped for language type {2}", + GetSymbolFileName(), __FUNCTION__, language); + return SymbolFile::GetTypeSystemForLanguage(language); + } + return m_sym_file_impl->GetTypeSystemForLanguage(language); +} + +CompilerDeclContext +SymbolFileOnDemand::FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1}({2}) is skipped", GetSymbolFileName(), + __FUNCTION__, name); + return SymbolFile::FindNamespace(name, parent_decl_ctx); + } + return m_sym_file_impl->FindNamespace(name, parent_decl_ctx); +} + +std::vector> +SymbolFileOnDemand::ParseCallEdgesInFunction(UserID func_id) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + std::vector> call_edges = + m_sym_file_impl->ParseCallEdgesInFunction(func_id); + if (call_edges.size() > 0) { + LLDB_LOG(log, "{0} call edges would be parsed for {1} if hydrated.", + call_edges.size(), func_id.GetID()); + } + } + return {}; + } + return m_sym_file_impl->ParseCallEdgesInFunction(func_id); +} + +lldb::UnwindPlanSP +SymbolFileOnDemand::GetUnwindPlan(const Address &address, + const RegisterInfoResolver &resolver) { + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return nullptr; + } + return m_sym_file_impl->GetUnwindPlan(address, resolver); +} + +llvm::Expected +SymbolFileOnDemand::GetParameterStackSize(Symbol &symbol) { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + llvm::Expected stack_size = + m_sym_file_impl->GetParameterStackSize(symbol); + if (stack_size) { + LLDB_LOG(log, "{0} stack size would return for symbol {1} if hydrated.", + *stack_size, symbol.GetName()); + } + } + return SymbolFile::GetParameterStackSize(symbol); + } + return m_sym_file_impl->GetParameterStackSize(symbol); +} + +void SymbolFileOnDemand::PreloadSymbols() { + m_preload_symbols = true; + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return; + } + return m_sym_file_impl->PreloadSymbols(); +} + +uint64_t SymbolFileOnDemand::GetDebugInfoSize() { + // Always return the real debug info size. + LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(), + __FUNCTION__); + return m_sym_file_impl->GetDebugInfoSize(); +} + +StatsDuration::Duration SymbolFileOnDemand::GetDebugInfoParseTime() { + // Always return the real parse time. + LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(), + __FUNCTION__); + return m_sym_file_impl->GetDebugInfoParseTime(); +} + +StatsDuration::Duration SymbolFileOnDemand::GetDebugInfoIndexTime() { + // Always return the real index time. + LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(), + __FUNCTION__); + return m_sym_file_impl->GetDebugInfoIndexTime(); +} + +uint32_t SymbolFileOnDemand::CalculateNumCompileUnits() { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is not skipped to support breakpoint hydration", + GetSymbolFileName(), __FUNCTION__); + // Explicitly allow this API through. + return m_sym_file_impl->CalculateNumCompileUnits(); +} + +CompUnitSP SymbolFileOnDemand::ParseCompileUnitAtIndex(uint32_t cu_idx) { + LLDB_LOG( + GetLog(), + "[{0}] {1} for cu_idx {2} is not skipped to support breakpoint hydration", + GetSymbolFileName(), __FUNCTION__, cu_idx); + // Explicitly allow this API through to support source line breakpoint. + return m_sym_file_impl->ParseCompileUnitAtIndex(cu_idx); +} + +TypeList &SymbolFileOnDemand::GetTypeList() { + // assert(false && "Should never be called"); + if (!this->m_debug_info_enabled) { + LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(), + __FUNCTION__); + return SymbolFile::GetTypeList(); + } + return m_sym_file_impl->GetTypeList(); +} + +void SymbolFileOnDemand::SetLoadDebugInfoEnabled() { + if (m_debug_info_enabled) + return; + LLDB_LOG(GetLog(), "[{0}] Hydrate debug info", GetSymbolFileName()); + m_debug_info_enabled = true; + InitializeObject(); + if (m_preload_symbols) + PreloadSymbols(); +} diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -62,6 +62,8 @@ debug_info_index_loaded_from_cache); module.try_emplace("debugInfoIndexSavedToCache", debug_info_index_saved_to_cache); + module.try_emplace("debugInfoEnabled", debug_info_enabled); + module.try_emplace("symbolTableStripped", symtab_stripped); if (!symfile_path.empty()) module.try_emplace("symbolFilePath", symfile_path); @@ -183,7 +185,9 @@ std::vector modules; std::lock_guard guard( Module::GetAllocationModuleCollectionMutex()); - const size_t num_modules = Module::GetNumberAllocatedModules(); + const uint64_t num_modules = Module::GetNumberAllocatedModules(); + uint32_t num_debug_info_enabled_modules = 0; + uint32_t num_stripped_modules = 0; for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { Module *module = Module::GetAllocatedModuleAtIndex(image_idx); ModuleStats module_stat; @@ -227,6 +231,13 @@ ModuleList symbol_modules = sym_file->GetDebugInfoModules(); for (const auto &symbol_module: symbol_modules.Modules()) module_stat.symfile_modules.push_back((intptr_t)symbol_module.get()); + module_stat.symtab_stripped = module->GetObjectFile()->IsStripped(); + if (module_stat.symtab_stripped) + ++num_stripped_modules; + module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() && + module_stat.debug_info_size > 0; + if (module_stat.debug_info_enabled) + ++num_debug_info_enabled_modules; } symtab_parse_time += module_stat.symtab_parse_time; symtab_index_time += module_stat.symtab_index_time; @@ -254,6 +265,9 @@ {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded}, {"totalDebugInfoIndexSavedToCache", debug_index_saved}, {"totalDebugInfoByteSize", debug_info_size}, + {"totalModuleCount", num_modules}, + {"totalDebugInfoEnabled", num_debug_info_enabled_modules}, + {"totalSymbolTableStripped", num_stripped_modules}, }; return std::move(global_stats); } diff --git a/lldb/source/Utility/LLDBLog.cpp b/lldb/source/Utility/LLDBLog.cpp --- a/lldb/source/Utility/LLDBLog.cpp +++ b/lldb/source/Utility/LLDBLog.cpp @@ -60,6 +60,9 @@ {{"types"}, {"log type system related activities"}, LLDBLog::Types}, {{"unwind"}, {"log stack unwind activities"}, LLDBLog::Unwind}, {{"watch"}, {"log watchpoint related activities"}, LLDBLog::Watchpoints}, + {{"on-demand"}, + {"log symbol on-demand related activities"}, + LLDBLog::OnDemand}, }; static Log::Channel g_log_channel(g_categories, diff --git a/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py b/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py --- a/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py +++ b/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py @@ -37,6 +37,10 @@ self.expect_expr("var_below_" + suffix, result_type=enum_name, result_value="-3") self.expect_expr("var_above_" + suffix, result_type=enum_name, result_value="1") + @skipIf( + debug_info="ondemand", + bugnumber="Type lookup is expected to fail in on-demand mode", + ) @skipIf(dwarf_version=['<', '4']) def test(self): self.build() diff --git a/lldb/test/API/symbol_ondemand/breakpoint_language/Makefile b/lldb/test/API/symbol_ondemand/breakpoint_language/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_language/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := c_lang.c +CXX_SOURCES := main.cpp cpp_lang.cpp + +include Makefile.rules diff --git a/lldb/test/API/symbol_ondemand/breakpoint_language/TestBreakpointLanguage.py b/lldb/test/API/symbol_ondemand/breakpoint_language/TestBreakpointLanguage.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_language/TestBreakpointLanguage.py @@ -0,0 +1,134 @@ +""" +Test that the language option for breakpoints works correctly +with symbol load on-demand. +""" + + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + + +class TestBreakpointLanguage(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def check_location_file(self, bp, loc, test_name): + bp_loc = bp.GetLocationAtIndex(loc) + addr = bp_loc.GetAddress() + comp_unit = addr.GetCompileUnit() + comp_name = comp_unit.GetFileSpec().GetFilename() + return comp_name == test_name + + def test_regex_breakpoint_language(self): + """Test that the name regex breakpoint commands obey the language filter.""" + + self.build() + + # Load symbols on-demand + self.runCmd("settings set symbols.load-on-demand true") + + # Create a target by the debugger. + exe = self.getBuildArtifact("a.out") + error = lldb.SBError() + # Don't read in dependencies so we don't come across false matches that + # add unwanted breakpoint hits. + self.target = self.dbg.CreateTarget(exe, None, None, False, error) + self.assertTrue(self.target, VALID_TARGET) + + cpp_bp = self.target.BreakpointCreateByRegex( + "func_from", + lldb.eLanguageTypeC_plus_plus, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + cpp_bp.GetNumLocations(), 1, + "Only one C++ symbol matches") + self.assertTrue(self.check_location_file(cpp_bp, 0, "cpp_lang.cpp")) + + c_bp = self.target.BreakpointCreateByRegex( + "func_from", + lldb.eLanguageTypeC, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + c_bp.GetNumLocations(), 1, + "Only one C symbol matches") + self.assertTrue(self.check_location_file(c_bp, 0, "c_lang.c")) + + objc_bp = self.target.BreakpointCreateByRegex( + "func_from", + lldb.eLanguageTypeObjC, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + objc_bp.GetNumLocations(), 0, + "No ObjC symbol matches") + + def test_by_name_breakpoint_language(self): + """Test that the name regex breakpoint commands obey the language filter.""" + + self.build() + + # Load symbols on-demand + self.runCmd("settings set symbols.load-on-demand true") + + # Create a target by the debugger. + exe = self.getBuildArtifact("a.out") + error = lldb.SBError() + # Don't read in dependencies so we don't come across false matches that + # add unwanted breakpoint hits. + self.target = self.dbg.CreateTarget(exe, None, None, False, error) + self.assertTrue(self.target, VALID_TARGET) + + cpp_bp = self.target.BreakpointCreateByName( + "func_from_cpp", + lldb.eFunctionNameTypeAuto, + lldb.eLanguageTypeC_plus_plus, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + cpp_bp.GetNumLocations(), 1, + "Only one C++ symbol matches") + self.assertTrue(self.check_location_file(cpp_bp, 0, "cpp_lang.cpp")) + + no_cpp_bp = self.target.BreakpointCreateByName( + "func_from_c", + lldb.eFunctionNameTypeAuto, + lldb.eLanguageTypeC_plus_plus, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + no_cpp_bp.GetNumLocations(), 0, + "And the C one doesn't match") + + c_bp = self.target.BreakpointCreateByName( + "func_from_c", + lldb.eFunctionNameTypeAuto, + lldb.eLanguageTypeC, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + c_bp.GetNumLocations(), 1, + "Only one C symbol matches") + self.assertTrue(self.check_location_file(c_bp, 0, "c_lang.c")) + + no_c_bp = self.target.BreakpointCreateByName( + "func_from_cpp", + lldb.eFunctionNameTypeAuto, + lldb.eLanguageTypeC, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + no_c_bp.GetNumLocations(), 0, + "And the C++ one doesn't match") + + objc_bp = self.target.BreakpointCreateByName( + "func_from_cpp", + lldb.eFunctionNameTypeAuto, + lldb.eLanguageTypeObjC, + lldb.SBFileSpecList(), + lldb.SBFileSpecList()) + self.assertEqual( + objc_bp.GetNumLocations(), 0, + "No ObjC symbol matches") diff --git a/lldb/test/API/symbol_ondemand/breakpoint_language/c_lang.c b/lldb/test/API/symbol_ondemand/breakpoint_language/c_lang.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_language/c_lang.c @@ -0,0 +1 @@ +int func_from_c() { return 5; } diff --git a/lldb/test/API/symbol_ondemand/breakpoint_language/cpp_lang.cpp b/lldb/test/API/symbol_ondemand/breakpoint_language/cpp_lang.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_language/cpp_lang.cpp @@ -0,0 +1 @@ +int func_from_cpp() { return 10; } diff --git a/lldb/test/API/symbol_ondemand/breakpoint_language/main.cpp b/lldb/test/API/symbol_ondemand/breakpoint_language/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_language/main.cpp @@ -0,0 +1,9 @@ +#include +extern "C" int func_from_c(); +extern int func_from_cpp(); + +int main() { + func_from_c(); + func_from_cpp(); + return 0; +} diff --git a/lldb/test/API/symbol_ondemand/breakpoint_source_regex/Makefile b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py @@ -0,0 +1,35 @@ +""" +Test source text regex breakpoint hydrates module debug info +in symbol on-demand mode. +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestSourceTextRegexBreakpoint(TestBase): + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + self.build() + + # Load symbols on-demand + self.runCmd("settings set symbols.load-on-demand true") + + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp( + self, "Set break point at this line.") + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + frame = self.frame() + self.assertTrue(frame.IsValid()) + self.assertEqual(frame.GetLineEntry().GetFileSpec().GetFilename(), "main.cpp") + self.assertEqual(frame.GetLineEntry().GetLine(), 4) diff --git a/lldb/test/API/symbol_ondemand/breakpoint_source_regex/main.cpp b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/main.cpp @@ -0,0 +1,10 @@ +#include + +void foo() { + printf("hello world from foo"); // Set break point at this line. +} + +int main() { + foo(); + return 0; +} diff --git a/lldb/test/Shell/SymbolFile/OnDemand/Inputs/basic.cpp b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/basic.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/basic.cpp @@ -0,0 +1,5 @@ +int bar(int x, int y) { return x + y + 5; } + +int foo(int x, int y) { return bar(x, y) + 12; } + +int main(int argc, char **argv) { return foo(33, 78); } diff --git a/lldb/test/Shell/SymbolFile/OnDemand/Inputs/lib.h b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/lib.h new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/lib.h @@ -0,0 +1,6 @@ +#ifndef lib_h__ +#define lib_h__ + +extern void foo(void); + +#endif // lib_h__ diff --git a/lldb/test/Shell/SymbolFile/OnDemand/Inputs/lib.cpp b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/lib.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/lib.cpp @@ -0,0 +1,4 @@ +#include + +int global_foo = 321; +void foo(void) { puts("Hello, I am a shared library"); } diff --git a/lldb/test/Shell/SymbolFile/OnDemand/Inputs/shared.cpp b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/shared.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/Inputs/shared.cpp @@ -0,0 +1,9 @@ +#include "lib.h" +#include + +int global_shared = 897; +int main(void) { + puts("This is a shared library test..."); + foo(); + return 0; +} diff --git a/lldb/test/Shell/SymbolFile/OnDemand/shared-lib-globals-hydration.test b/lldb/test/Shell/SymbolFile/OnDemand/shared-lib-globals-hydration.test new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/shared-lib-globals-hydration.test @@ -0,0 +1,32 @@ +# Test shows that global variables works as expected in multiple shared libraries with LLDB on demand symbol loading. +# When there is a global variable query matches in symbol table this will promote debug info hydration. + +# REQUIRES: system-linux + +# RUN: mkdir -p %t +# RUN: cd %t +# RUN: %clang_host -shared -g -fpic %p/Inputs/lib.cpp -o libFoo.so +# RUN: %clang_host -L%t -g -o shared.out %p/Inputs/shared.cpp -lFoo -Wl,-rpath=%t +# RUN: %lldb -b -O "settings set symbols.load-on-demand true" -s %s shared.out | FileCheck %s + +breakpoint list +# CHECK: No breakpoints currently set + +breakpoint set -f shared.cpp -l 8 +# CHECK: at shared.cpp:8 + +breakpoint list +# CHECK: file = 'shared.cpp', line = 8 + +run +# CHECK: stop reason = breakpoint +# CHECK: at shared.cpp:8 + +target variable --shlib shared.out +# CHECK: (int) ::global_shared = 897 + +target variable global_foo --shlib libFoo.so +# CHECK: (int) global_foo = 321 + +target variable --shlib libFoo.so +# CHECK: (int) ::global_foo = 321 diff --git a/lldb/test/Shell/SymbolFile/OnDemand/shared-lib-globals.test b/lldb/test/Shell/SymbolFile/OnDemand/shared-lib-globals.test new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/shared-lib-globals.test @@ -0,0 +1,29 @@ +# Test shows that global variables works as expected in multiple shared libraries with LLDB on demand symbol loading. +# The global varialbes in non-hydrated shared library libFoo.so can't be found. + +# REQUIRES: system-linux + +# RUN: mkdir -p %t +# RUN: cd %t +# RUN: %clang_host -shared -g -fpic %p/Inputs/lib.cpp -o libFoo.so +# RUN: %clang_host -L%t -g -o shared.out %p/Inputs/shared.cpp -lFoo -Wl,-rpath=%t +# RUN: %lldb -b -O "settings set symbols.load-on-demand true" -s %s shared.out | FileCheck %s + +breakpoint list +# CHECK: No breakpoints currently set + +breakpoint set -f shared.cpp -l 8 +# CHECK: at shared.cpp:8 + +breakpoint list +# CHECK: file = 'shared.cpp', line = 8 + +run +# CHECK: stop reason = breakpoint +# CHECK: at shared.cpp:8 + +target variable --shlib shared.out +# CHECK: (int) ::global_shared = 897 + +target variable --shlib libFoo.so +# CHECK-NOT: global_foo diff --git a/lldb/test/Shell/SymbolFile/OnDemand/shared-lib.test b/lldb/test/Shell/SymbolFile/OnDemand/shared-lib.test new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/shared-lib.test @@ -0,0 +1,37 @@ +# Test shows that source line breakpoint works in multiple shared libraries with LLDB on demand symbol loading. + +# REQUIRES: system-linux + +# RUN: mkdir -p %t +# RUN: cd %t +# RUN: %clang_host -shared -g -fpic %p/Inputs/lib.cpp -o libFoo.so +# RUN: %clang_host -L%t -g -o shared.out %p/Inputs/shared.cpp -lFoo -Wl,-rpath=%t +# RUN: %lldb -b -O "settings set symbols.load-on-demand true" -s %s shared.out | FileCheck %s + +breakpoint list +# CHECK: No breakpoints currently set + +breakpoint set -f lib.cpp -l 4 +# CHECK: where = {{.*}}`foo() + {{.*}} at lib.cpp:4 + +breakpoint list +# CHECK: file = 'lib.cpp', line = 4 + +run +# CHECK: stop reason = breakpoint +# CHECK: foo() at lib.cpp:4 + +bt +# CHECK: {{.*}}`foo() at lib.cpp:4 +# CHECK: at shared.cpp:7 + +breakpoint set -f shared.cpp -l 8 +# CHECK: at shared.cpp:8 + +breakpoint list +# CHECK: file = 'lib.cpp', line = 4 +# CHECK: file = 'shared.cpp', line = 8 + +continue +# CHECK: stop reason = breakpoint +# CHECK: at shared.cpp:8 diff --git a/lldb/test/Shell/SymbolFile/OnDemand/source-breakpoint.test b/lldb/test/Shell/SymbolFile/OnDemand/source-breakpoint.test new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/source-breakpoint.test @@ -0,0 +1,23 @@ +# Test shows that source line breakpoint works with LLDB on demand symbol loading. + +# RUN: mkdir -p %t +# RUN: cd %t +# RUN: %build %p/Inputs/basic.cpp -o basic.out +# RUN: %lldb -b -O "settings set symbols.load-on-demand true" -s %s basic.out | FileCheck %s + +breakpoint list +# CHECK: No breakpoints currently set + +breakpoint set -f basic.cpp -l 1 +# CHECK: where = {{.*}}`bar(int, int) + {{.*}} at basic.cpp:1 + +breakpoint list +# CHECK: file = 'basic.cpp' + +run +# CHECK: stop reason = breakpoint + +bt +# CHECK: {{.*}}`bar(x=33, y=78) at basic.cpp:1 +# CHECK: {{.*}}`foo(x=33, y=78) at basic.cpp:3 +# CHECK: {{.*}}`main(argc=1, argv={{.*}}) at basic.cpp:5 diff --git a/lldb/test/Shell/SymbolFile/OnDemand/symbolic-breakpoint.test b/lldb/test/Shell/SymbolFile/OnDemand/symbolic-breakpoint.test new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/OnDemand/symbolic-breakpoint.test @@ -0,0 +1,24 @@ + +# Test shows that symbolic function breakpoint works with LLDB on demand symbol loading. + +# RUN: mkdir -p %t +# RUN: cd %t +# RUN: %build %p/Inputs/basic.cpp -o basic.out +# RUN: %lldb -b -O "settings set symbols.load-on-demand true" -s %s basic.out | FileCheck %s + +breakpoint list +# CHECK: No breakpoints currently set + +b bar +# CHECK: where = {{.*}}`bar(int, int) + {{.*}} at basic.cpp:1 + +breakpoint list +# CHECK: where = {{.*}}`bar(int, int) + {{.*}} at basic.cpp:1 + +run +# CHECK: stop reason = breakpoint + +bt +# CHECK: {{.*}}`bar(x=33, y=78) at basic.cpp:1 +# CHECK: {{.*}}`foo(x=33, y=78) at basic.cpp:3 +# CHECK: {{.*}}`main(argc=1, argv={{.*}}) at basic.cpp:5