diff --git a/include/lldb/Symbol/SymbolFileOnDemand.h b/include/lldb/Symbol/SymbolFileOnDemand.h new file mode 100644 --- /dev/null +++ b/include/lldb/Symbol/SymbolFileOnDemand.h @@ -0,0 +1,204 @@ +//===-- 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/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"; } + + Log *GetLog() const { return ::lldb_private::GetLog(LLDBLog::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; + + // TODO: other public functions. +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/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,209 @@ +//===-- 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/packages/Python/lldbsuite/test/builders/builder.py b/lldb/packages/Python/lldbsuite/test/builders/builder.py --- a/lldb/packages/Python/lldbsuite/test/builders/builder.py +++ b/lldb/packages/Python/lldbsuite/test/builders/builder.py @@ -129,6 +129,8 @@ return ["MAKE_DSYM=NO", "MAKE_DWO=YES"] if debug_info == "gmodules": return ["MAKE_DSYM=NO", "MAKE_GMODULES=YES"] + if debug_info == "ondemand": + return ["MAKE_DSYM=NO"] return None def getBuildCommand(self, debug_info, architecture=None, compiler=None, diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -710,6 +710,11 @@ """Return absolute path to a file in the test's source directory.""" return os.path.join(self.getSourceDir(), name) + def getTestCategories(self): + test_method = getattr(self, self._testMethodName) + if test_method is not None and hasattr(test_method, "categories"): + return test_method.categories + @classmethod def setUpCommands(cls): commands = [ @@ -1796,6 +1801,10 @@ for s in self.setUpCommands(): self.runCmd(s) + test_categories = self.getTestCategories() + if test_categories is not None and 'ondemand' in test_categories: + self.runCmd("settings set symbols.load-on-demand true") + if "LLDB_MAX_LAUNCH_COUNT" in os.environ: self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) diff --git a/lldb/packages/Python/lldbsuite/test/test_categories.py b/lldb/packages/Python/lldbsuite/test/test_categories.py --- a/lldb/packages/Python/lldbsuite/test/test_categories.py +++ b/lldb/packages/Python/lldbsuite/test/test_categories.py @@ -15,7 +15,7 @@ debug_info_categories = [ - 'dwarf', 'dwo', 'dsym', 'gmodules' + 'dwarf', 'dwo', 'dsym', 'gmodules', 'ondemand' ] all_categories = { @@ -42,6 +42,7 @@ 'std-module': 'Tests related to importing the std module', 'stresstest': 'Tests related to stressing lldb limits', 'watchpoint': 'Watchpoint-related tests', + 'ondemand': 'Tests that can be run with symbol on-demand loading', } 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 breakpoint 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,8 @@ if (!symfile) return resolved_flags; + symfile->SetLoadDebugInfoEnabled(); + // Resolve the compile unit, function, block, line table or line entry if // requested. if (resolve_scope & eSymbolContextCompUnit || 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,15 @@ } } if (best_symfile_up) { + 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,611 @@ +//===-- 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() { + if (!this->m_debug_info_enabled) { + Log *log = GetLog(); + LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__); + if (log) { + uint32_t abilities = m_sym_file_impl->CalculateAbilities(); + if (abilities != 0) { + LLDB_LOG(log, "{0} abilities would return if hydrated.", abilities); + } + } + return 0; + } + 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("symtab_stripped", 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,15 @@ 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 +267,9 @@ {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded}, {"totalDebugInfoIndexSavedToCache", debug_index_saved}, {"totalDebugInfoByteSize", debug_info_size}, + {"totalModuleCount", num_modules}, + {"totalDebugInfoEnabledModules", num_debug_info_enabled_modules}, + {"totalSymbolTableStrippedModules", 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/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py b/lldb/test/API/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py --- a/lldb/test/API/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py @@ -5,6 +5,7 @@ import lldb +from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbsuite.test.lldbutil as lldbutil @@ -13,6 +14,10 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf( + debug_info="ondemand", + bugnumber="symbolic function for inline function won't work in on-demand mode", + ) def test(self): self.build() diff --git a/lldb/test/API/functionalities/find-line-entry/TestFindLineEntry.py b/lldb/test/API/functionalities/find-line-entry/TestFindLineEntry.py --- a/lldb/test/API/functionalities/find-line-entry/TestFindLineEntry.py +++ b/lldb/test/API/functionalities/find-line-entry/TestFindLineEntry.py @@ -3,6 +3,7 @@ """ import lldb +from lldbsuite.test.decorators import * import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -10,6 +11,10 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf( + debug_info="ondemand", + bugnumber="Line table parsing is disabled in on-demand mode", + ) def test_compile_unit_find_line_entry_index(self): """ Test the CompileUnit LineEntryIndex lookup API """ self.build() diff --git a/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py b/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py --- a/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py +++ b/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py @@ -31,6 +31,7 @@ self._check_type(target, "InheritsFromTwo") @skipIf(bugnumber="pr46284", debug_info="gmodules") + @skipIf(bugnumber="FindFirstType() won't work in on-demand", debug_info="ondemand") @skipIfWindows # Clang emits type info even with -flimit-debug-info # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call # by-value functions. @@ -70,6 +71,7 @@ self.expect_expr("shadowed_one.one", result_value="142") @skipIf(bugnumber="pr46284", debug_info="gmodules") + @skipIf(bugnumber="FindFirstType() won't work in on-demand", debug_info="ondemand") @skipIfWindows # Clang emits type info even with -flimit-debug-info # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call # by-value functions. @@ -113,6 +115,7 @@ self.expect_expr("get_two().member", result_value="224") @skipIf(bugnumber="pr46284", debug_info="gmodules") + @skipIf(bugnumber="FindFirstType() won't work in on-demand", debug_info="ondemand") @skipIfWindows # Clang emits type info even with -flimit-debug-info # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call # by-value functions. diff --git a/lldb/test/API/functionalities/show_location/TestShowLocationDwarf5.py b/lldb/test/API/functionalities/show_location/TestShowLocationDwarf5.py --- a/lldb/test/API/functionalities/show_location/TestShowLocationDwarf5.py +++ b/lldb/test/API/functionalities/show_location/TestShowLocationDwarf5.py @@ -11,6 +11,8 @@ @skipIf(archs="aarch64", oslist="linux", bugnumber="https://bugs.llvm.org/show_bug.cgi?id=44180") + @skipIf(debug_info="ondemand", + bugnumber="yaml generated binary does not have symbol table") def test_source_map(self): # Set the target soure map to map "./" to the current test directory. yaml_path = os.path.join(self.getSourceDir(), "a.yaml") diff --git a/lldb/test/API/functionalities/type_get_module/TestTypeGetModule.py b/lldb/test/API/functionalities/type_get_module/TestTypeGetModule.py --- a/lldb/test/API/functionalities/type_get_module/TestTypeGetModule.py +++ b/lldb/test/API/functionalities/type_get_module/TestTypeGetModule.py @@ -61,6 +61,8 @@ self.assertTrue(result.IsValid()) return result + @skipIf(debug_info="ondemand", + bugnumber="Can't find type in on-demand mode") def test(self): self.build() target = lldbutil.run_to_breakpoint_make_target(self) @@ -72,7 +74,7 @@ comp_unit = self.find_comp_unit(exe_module, 'compile_unit1.c') cu_type = self.find_type(comp_unit.GetTypes(), 'compile_unit1_type') self.assertEqual(exe_module, cu_type.GetModule()) - + comp_unit = self.find_comp_unit(exe_module, 'compile_unit2.c') cu_type = self.find_type(comp_unit.GetTypes(), 'compile_unit2_type') self.assertEqual(exe_module, cu_type.GetModule()) diff --git a/lldb/test/API/lang/c/enum_types/TestEnumTypes.py b/lldb/test/API/lang/c/enum_types/TestEnumTypes.py --- a/lldb/test/API/lang/c/enum_types/TestEnumTypes.py +++ b/lldb/test/API/lang/c/enum_types/TestEnumTypes.py @@ -125,7 +125,7 @@ def check_enum_members(self, members): name_matches = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "kNumDays"] value_matches = [-3, -2, -1, 0, 1, 2, 3, 4] - + # First test that the list of members from the type works num_matches = len(name_matches) self.assertEqual(len(members), num_matches, "enum_members returns the right number of elements") @@ -134,7 +134,9 @@ self.assertTrue(member.IsValid(), "Got a valid member for idx: %d"%(idx)) self.assertEqual(member.name, name_matches[idx], "Name matches for %d"%(idx)) self.assertEqual(member.signed, value_matches[idx], "Value matches for %d"%(idx)) - + + @skipIf(debug_info="ondemand", + bugnumber="Can't find type in on-demand mode") def test_api(self): """Test that the SBTypeEnumMember API's work correctly for enum_test_days""" self.build() @@ -158,4 +160,3 @@ self.assertTrue(check_member.IsValid(), "Got a valid member for %s."%(name)) self.assertEqual(name, check_member.name, "Got back the right name") self.assertEqual(member.unsigned, check_member.unsigned) - diff --git a/lldb/test/API/lang/c/global_variables/TestGlobalVariables.py b/lldb/test/API/lang/c/global_variables/TestGlobalVariables.py --- a/lldb/test/API/lang/c/global_variables/TestGlobalVariables.py +++ b/lldb/test/API/lang/c/global_variables/TestGlobalVariables.py @@ -117,4 +117,3 @@ VARIABLES_DISPLAYED_CORRECTLY, matching=False, substrs=["can't be resolved"]) - diff --git a/lldb/test/API/lang/c/shared_lib/TestSharedLib.py b/lldb/test/API/lang/c/shared_lib/TestSharedLib.py --- a/lldb/test/API/lang/c/shared_lib/TestSharedLib.py +++ b/lldb/test/API/lang/c/shared_lib/TestSharedLib.py @@ -34,14 +34,20 @@ "expression GetMeASubFoo(my_foo_ptr)", startstr="(sub_foo *) $") + @skipIf(debug_info="ondemand", + bugnumber="debug info is expected to not load for shared library") def test_expr(self): """Test that types work when defined in a shared library and forward-declared in the main executable""" self.common_test_expr(True) + @skipIf(debug_info="ondemand", + bugnumber="debug info is expected to not load for shared library") def test_expr_no_preload(self): """Test that types work when defined in a shared library and forward-declared in the main executable, but with preloading disabled""" self.common_test_expr(False) + @skipIf(debug_info="ondemand", + bugnumber="debug info is expected to not load for shared library") @expectedFailure("llvm.org/PR36712") def test_frame_variable(self): """Test that types work when defined in a shared library and forward-declared in the main executable""" diff --git a/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py b/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py --- a/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py +++ b/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py @@ -13,6 +13,8 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf(debug_info="ondemand", + bugnumber="debug info is expected to not load for shared library") @expectedFailureAll(oslist=["windows"]) def test_expr(self): """Test that types work when defined in a shared library and forwa/d-declared in the main executable""" @@ -32,6 +34,8 @@ "(sub_foo)", "other_element = 3"]) + @skipIf(debug_info="ondemand", + bugnumber="debug info is expected to not load for shared library") @expectedFailureAll(oslist=["windows"]) @expectedFailure("llvm.org/PR36712") def test_frame_variable(self): diff --git a/lldb/test/API/lang/c/sizeof/TestCSizeof.py b/lldb/test/API/lang/c/sizeof/TestCSizeof.py --- a/lldb/test/API/lang/c/sizeof/TestCSizeof.py +++ b/lldb/test/API/lang/c/sizeof/TestCSizeof.py @@ -7,6 +7,8 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf(debug_info="ondemand", + bugnumber="type info is not loaded by default in on-demand mode") def test(self): self.build() self.createTestTarget() diff --git a/lldb/test/API/lang/c/unicode/TestUnicodeSymbols.py b/lldb/test/API/lang/c/unicode/TestUnicodeSymbols.py --- a/lldb/test/API/lang/c/unicode/TestUnicodeSymbols.py +++ b/lldb/test/API/lang/c/unicode/TestUnicodeSymbols.py @@ -9,6 +9,8 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf(debug_info="ondemand", + bugnumber="type info is not loaded by default in on-demand mode") @skipIf(compiler="clang", compiler_version=['<', '7.0']) def test_union_members(self): self.build() diff --git a/lldb/test/API/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py b/lldb/test/API/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py --- a/lldb/test/API/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py +++ b/lldb/test/API/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py @@ -28,6 +28,13 @@ num_locations)) return bkpt + @skipIf( + debug_info="ondemand", + bugnumber=""" + Function name breakpoint filter by compile unit would disable + searching in symbol table. + """, + ) def test_cpp_breakpoint_cmds(self): """Test a sequence of breakpoint command add, list, and delete.""" self.build() 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/lang/cpp/namespace_definitions/TestNamespaceDefinitions.py b/lldb/test/API/lang/cpp/namespace_definitions/TestNamespaceDefinitions.py --- a/lldb/test/API/lang/cpp/namespace_definitions/TestNamespaceDefinitions.py +++ b/lldb/test/API/lang/cpp/namespace_definitions/TestNamespaceDefinitions.py @@ -13,6 +13,10 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf( + debug_info="ondemand", + bugnumber="Type in shared library is not parsed in on-demand mode", + ) # See also llvm.org/pr28948 @expectedFailureAll( bugnumber="llvm.org/pr50814", diff --git a/lldb/test/API/lang/cpp/sizeof/TestCPPSizeof.py b/lldb/test/API/lang/cpp/sizeof/TestCPPSizeof.py --- a/lldb/test/API/lang/cpp/sizeof/TestCPPSizeof.py +++ b/lldb/test/API/lang/cpp/sizeof/TestCPPSizeof.py @@ -7,6 +7,10 @@ mydir = TestBase.compute_mydir(__file__) + @skipIf( + debug_info="ondemand", + bugnumber="Type is not parsed in on-demand mode", + ) def test(self): self.build() self.createTestTarget() diff --git a/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py b/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py --- a/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py +++ b/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py @@ -53,6 +53,6 @@ self.do_test_template_function(True) @skipIfWindows - @expectedFailureAll(debug_info=["dwarf", "gmodules", "dwo"]) + @expectedFailureAll(debug_info=["dwarf", "gmodules", "dwo", "ondemand"]) def test_template_function_without_cast(self): self.do_test_template_function(False) 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 foo_h__ +#define foo_h__ + +extern void foo(void); + +#endif // foo_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.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 6 +# CHECK: where = {{.*}}`foo() + {{.*}} at lib.cpp:6 + +breakpoint list +# CHECK: file = 'lib.cpp', line = 6 + +run +# CHECK: stop reason = breakpoint +# CHECK: foo() at lib.cpp:6 + +bt +# CHECK: {{.*}}`foo() at lib.cpp:6 +# CHECK: at shared.cpp:7 + +breakpoint set -f shared.cpp -l 8 +# CHECK: at shared.cpp:8 + +breakpoint list +# CHECK: file = 'lib.cpp', line = 6 +# 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 2 +# CHECK: where = {{.*}}`bar(int, int) + {{.*}} at basic.cpp:2 + +breakpoint list +# CHECK: file = 'basic.cpp' + +run +# CHECK: stop reason = breakpoint + +bt +# CHECK: {{.*}}`bar(x=33, y=78) at basic.cpp:2 +# CHECK: {{.*}}`foo(x=33, y=78) at basic.cpp:6 +# CHECK: {{.*}}`main(argc=1, argv={{.*}}) at basic.cpp:10 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:2 + +breakpoint list +# CHECK: where = {{.*}}`bar(int, int) + {{.*}} at basic.cpp:2 + +run +# CHECK: stop reason = breakpoint + +bt +# CHECK: {{.*}}`bar(x=33, y=78) at basic.cpp:2 +# CHECK: {{.*}}`foo(x=33, y=78) at basic.cpp:6 +# CHECK: {{.*}}`main(argc=1, argv={{.*}}) at basic.cpp:10