Index: vendor/lldb/dist/include/lldb/Symbol/SymbolContext.h =================================================================== --- vendor/lldb/dist/include/lldb/Symbol/SymbolContext.h (revision 318423) +++ vendor/lldb/dist/include/lldb/Symbol/SymbolContext.h (revision 318424) @@ -1,568 +1,591 @@ //===-- SymbolContext.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_SymbolContext_h_ #define liblldb_SymbolContext_h_ // C Includes // C++ Includes #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Core/Address.h" #include "lldb/Core/Mangled.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Utility/Iterable.h" #include "lldb/lldb-private.h" namespace lldb_private { class SymbolContextScope; //---------------------------------------------------------------------- /// @class SymbolContext SymbolContext.h "lldb/Symbol/SymbolContext.h" /// @brief Defines a symbol context baton that can be handed other debug /// core functions. /// /// Many debugger functions require a context when doing lookups. This /// class provides a common structure that can be used as the result /// of a query that can contain a single result. Examples of such /// queries include /// @li Looking up a load address. //---------------------------------------------------------------------- class SymbolContext { public: //------------------------------------------------------------------ /// Default constructor. /// /// Initialize all pointer members to nullptr and all struct members /// to their default state. //------------------------------------------------------------------ SymbolContext(); //------------------------------------------------------------------ /// Construct with an object that knows how to reconstruct its /// symbol context. /// /// @param[in] sc_scope /// A symbol context scope object that knows how to reconstruct /// it's context. //------------------------------------------------------------------ explicit SymbolContext(SymbolContextScope *sc_scope); //------------------------------------------------------------------ /// Construct with module, and optional compile unit, function, /// block, line table, line entry and symbol. /// /// Initialize all pointer to the specified values. /// /// @param[in] module /// A Module pointer to the module for this context. /// /// @param[in] comp_unit /// A CompileUnit pointer to the compile unit for this context. /// /// @param[in] function /// A Function pointer to the function for this context. /// /// @param[in] block /// A Block pointer to the deepest block for this context. /// /// @param[in] line_entry /// A LineEntry pointer to the line entry for this context. /// /// @param[in] symbol /// A Symbol pointer to the symbol for this context. //------------------------------------------------------------------ explicit SymbolContext(const lldb::TargetSP &target_sp, const lldb::ModuleSP &module_sp, CompileUnit *comp_unit = nullptr, Function *function = nullptr, Block *block = nullptr, LineEntry *line_entry = nullptr, Symbol *symbol = nullptr); // This version sets the target to a NULL TargetSP if you don't know it. explicit SymbolContext(const lldb::ModuleSP &module_sp, CompileUnit *comp_unit = nullptr, Function *function = nullptr, Block *block = nullptr, LineEntry *line_entry = nullptr, Symbol *symbol = nullptr); //------------------------------------------------------------------ /// Copy constructor /// /// Makes a copy of the another SymbolContext object \a rhs. /// /// @param[in] rhs /// A const SymbolContext object reference to copy. //------------------------------------------------------------------ SymbolContext(const SymbolContext &rhs); ~SymbolContext(); //------------------------------------------------------------------ /// Assignment operator. /// /// Copies the address value from another SymbolContext object \a /// rhs into \a this object. /// /// @param[in] rhs /// A const SymbolContext object reference to copy. /// /// @return /// A const SymbolContext object reference to \a this. //------------------------------------------------------------------ const SymbolContext &operator=(const SymbolContext &rhs); //------------------------------------------------------------------ /// Clear the object's state. /// /// Resets all pointer members to nullptr, and clears any class objects /// to their default state. //------------------------------------------------------------------ void Clear(bool clear_target); //------------------------------------------------------------------ /// Dump a description of this object to a Stream. /// /// Dump a description of the contents of this object to the /// supplied stream \a s. /// /// @param[in] s /// The stream to which to dump the object description. //------------------------------------------------------------------ void Dump(Stream *s, Target *target) const; //------------------------------------------------------------------ /// Dump the stop context in this object to a Stream. /// /// Dump the best description of this object to the stream. The /// information displayed depends on the amount and quality of the /// information in this context. If a module, function, file and /// line number are available, they will be dumped. If only a /// module and function or symbol name with offset is available, /// that will be output. Else just the address at which the target /// was stopped will be displayed. /// /// @param[in] s /// The stream to which to dump the object description. /// /// @param[in] so_addr /// The resolved section offset address. /// /// @param[in] show_fullpaths /// When printing file paths (with the Module), whether the /// base name of the Module should be printed or the full path. /// /// @param[in] show_module /// Whether the module name should be printed followed by a /// grave accent "`" character. /// /// @param[in] show_inlined_frames /// If a given pc is in inlined function(s), whether the inlined /// functions should be printed on separate lines in addition to /// the concrete function containing the pc. /// /// @param[in] show_function_arguments /// If false, this method will try to elide the function argument /// types when printing the function name. This may be ambiguous /// for languages that have function overloading - but it may /// make the "function name" too long to include all the argument /// types. /// /// @param[in] show_function_name /// Normally this should be true - the function/symbol name should /// be printed. In disassembly formatting, where we want a format /// like "<*+36>", this should be false and "*" will be printed /// instead. //------------------------------------------------------------------ bool DumpStopContext(Stream *s, ExecutionContextScope *exe_scope, const Address &so_addr, bool show_fullpaths, bool show_module, bool show_inlined_frames, bool show_function_arguments, bool show_function_name) const; //------------------------------------------------------------------ /// Get the address range contained within a symbol context. /// /// Address range priority is as follows: /// - line_entry address range if line_entry is valid and /// eSymbolContextLineEntry is set in \a scope /// - block address range if block is not nullptr and eSymbolContextBlock /// is set in \a scope /// - function address range if function is not nullptr and /// eSymbolContextFunction is set in \a scope /// - symbol address range if symbol is not nullptr and /// eSymbolContextSymbol is set in \a scope /// /// @param[in] scope /// A mask of symbol context bits telling this function which /// address ranges it can use when trying to extract one from /// the valid (non-nullptr) symbol context classes. /// /// @param[in] range_idx /// The address range index to grab. Since many functions and /// blocks are not always contiguous, they may have more than /// one address range. /// /// @param[in] use_inline_block_range /// If \a scope has the eSymbolContextBlock bit set, and there /// is a valid block in the symbol context, return the block /// address range for the containing inline function block, not /// the deepest most block. This allows us to extract information /// for the address range of the inlined function block, not /// the deepest lexical block. /// /// @param[out] range /// An address range object that will be filled in if \b true /// is returned. /// /// @return /// \b True if this symbol context contains items that describe /// an address range, \b false otherwise. //------------------------------------------------------------------ bool GetAddressRange(uint32_t scope, uint32_t range_idx, bool use_inline_block_range, AddressRange &range) const; bool GetAddressRangeFromHereToEndLine(uint32_t end_line, AddressRange &range, Status &error); + + //------------------------------------------------------------------ + /// Find the best global data symbol visible from this context. + /// + /// Symbol priority is: + /// - extern symbol in the current module if there is one + /// - non-extern symbol in the current module if there is one + /// - extern symbol in the target + /// - non-extern symbol in the target + /// It is an error if the highest-priority result is ambiguous. + /// + /// @param[in] name + /// The name of the symbol to search for. + /// + /// @param[out] error + /// An error that will be populated with a message if there was an + /// ambiguous result. The error will not be populated if no result + /// was found. + /// + /// @return + /// The symbol that was found, or \b nullptr if none was found. + //------------------------------------------------------------------ + const Symbol *FindBestGlobalDataSymbol(const ConstString &name, Status &error); void GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) const; uint32_t GetResolvedMask() const; lldb::LanguageType GetLanguage() const; //------------------------------------------------------------------ /// Find a block that defines the function represented by this /// symbol context. /// /// If this symbol context points to a block that is an inlined /// function, or is contained within an inlined function, the block /// that defines the inlined function is returned. /// /// If this symbol context has no block in it, or the block is not /// itself an inlined function block or contained within one, we /// return the top level function block. /// /// This is a handy function to call when you want to get the block /// whose variable list will include the arguments for the function /// that is represented by this symbol context (whether the function /// is an inline function or not). /// /// @return /// The block object pointer that defines the function that is /// represented by this symbol context object, nullptr otherwise. //------------------------------------------------------------------ Block *GetFunctionBlock(); //------------------------------------------------------------------ /// If this symbol context represents a function that is a method, /// return true and provide information about the method. /// /// @param[out] language /// If \b true is returned, the language for the method. /// /// @param[out] is_instance_method /// If \b true is returned, \b true if this is a instance method, /// \b false if this is a static/class function. /// /// @param[out] language_object_name /// If \b true is returned, the name of the artificial variable /// for the language ("this" for C++, "self" for ObjC). /// /// @return /// \b True if this symbol context represents a function that /// is a method of a class, \b false otherwise. //------------------------------------------------------------------ bool GetFunctionMethodInfo(lldb::LanguageType &language, bool &is_instance_method, ConstString &language_object_name); //------------------------------------------------------------------ /// Sorts the types in TypeMap according to SymbolContext /// to TypeList /// //------------------------------------------------------------------ void SortTypeList(TypeMap &type_map, TypeList &type_list) const; //------------------------------------------------------------------ /// Find a name of the innermost function for the symbol context. /// /// For instance, if the symbol context contains an inlined block, /// it will return the inlined function name. /// /// @param[in] prefer_mangled /// if \btrue, then the mangled name will be returned if there /// is one. Otherwise the unmangled name will be returned if it /// is available. /// /// @return /// The name of the function represented by this symbol context. //------------------------------------------------------------------ ConstString GetFunctionName( Mangled::NamePreference preference = Mangled::ePreferDemangled) const; //------------------------------------------------------------------ /// Get the line entry that corresponds to the function. /// /// If the symbol context contains an inlined block, the line entry /// for the start address of the inlined function will be returned, /// otherwise the line entry for the start address of the function /// will be returned. This can be used after doing a /// Module::FindFunctions(...) or ModuleList::FindFunctions(...) /// call in order to get the correct line table information for /// the symbol context. /// it will return the inlined function name. /// /// @param[in] prefer_mangled /// if \btrue, then the mangled name will be returned if there /// is one. Otherwise the unmangled name will be returned if it /// is available. /// /// @return /// The name of the function represented by this symbol context. //------------------------------------------------------------------ LineEntry GetFunctionStartLineEntry() const; //------------------------------------------------------------------ /// Find the block containing the inlined block that contains this block. /// /// For instance, if the symbol context contains an inlined block, /// it will return the inlined function name. /// /// @param[in] curr_frame_pc /// The address within the block of this object. /// /// @param[out] next_frame_sc /// A new symbol context that does what the title says it does. /// /// @param[out] next_frame_addr /// This is what you should report as the PC in \a next_frame_sc. /// /// @return /// \b true if this SymbolContext specifies a block contained in an /// inlined block. If this returns \b true, \a next_frame_sc and /// \a next_frame_addr will be filled in correctly. //------------------------------------------------------------------ bool GetParentOfInlinedScope(const Address &curr_frame_pc, SymbolContext &next_frame_sc, Address &inlined_frame_addr) const; //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ lldb::TargetSP target_sp; ///< The Target for a given query lldb::ModuleSP module_sp; ///< The Module for a given query CompileUnit *comp_unit; ///< The CompileUnit for a given query Function *function; ///< The Function for a given query Block *block; ///< The Block for a given query LineEntry line_entry; ///< The LineEntry for a given query Symbol *symbol; ///< The Symbol for a given query Variable *variable; ///< The global variable matching the given query }; class SymbolContextSpecifier { public: typedef enum SpecificationType { eNothingSpecified = 0, eModuleSpecified = 1 << 0, eFileSpecified = 1 << 1, eLineStartSpecified = 1 << 2, eLineEndSpecified = 1 << 3, eFunctionSpecified = 1 << 4, eClassOrNamespaceSpecified = 1 << 5, eAddressRangeSpecified = 1 << 6 } SpecificationType; // This one produces a specifier that matches everything... SymbolContextSpecifier(const lldb::TargetSP &target_sp); ~SymbolContextSpecifier(); bool AddSpecification(const char *spec_string, SpecificationType type); bool AddLineSpecification(uint32_t line_no, SpecificationType type); void Clear(); bool SymbolContextMatches(SymbolContext &sc); bool AddressMatches(lldb::addr_t addr); void GetDescription(Stream *s, lldb::DescriptionLevel level) const; private: lldb::TargetSP m_target_sp; std::string m_module_spec; lldb::ModuleSP m_module_sp; std::unique_ptr m_file_spec_ap; size_t m_start_line; size_t m_end_line; std::string m_function_spec; std::string m_class_name; std::unique_ptr m_address_range_ap; uint32_t m_type; // Or'ed bits from SpecificationType }; //---------------------------------------------------------------------- /// @class SymbolContextList SymbolContext.h "lldb/Symbol/SymbolContext.h" /// @brief Defines a list of symbol context objects. /// /// This class provides a common structure that can be used to contain /// the result of a query that can contain a multiple results. Examples /// of such queries include: /// @li Looking up a function by name. /// @li Finding all addresses for a specified file and line number. //---------------------------------------------------------------------- class SymbolContextList { public: //------------------------------------------------------------------ /// Default constructor. /// /// Initialize with an empty list. //------------------------------------------------------------------ SymbolContextList(); //------------------------------------------------------------------ /// Destructor. //------------------------------------------------------------------ ~SymbolContextList(); //------------------------------------------------------------------ /// Append a new symbol context to the list. /// /// @param[in] sc /// A symbol context to append to the list. //------------------------------------------------------------------ void Append(const SymbolContext &sc); void Append(const SymbolContextList &sc_list); bool AppendIfUnique(const SymbolContext &sc, bool merge_symbol_into_function); bool MergeSymbolContextIntoFunctionContext(const SymbolContext &symbol_sc, uint32_t start_idx = 0, uint32_t stop_idx = UINT32_MAX); uint32_t AppendIfUnique(const SymbolContextList &sc_list, bool merge_symbol_into_function); //------------------------------------------------------------------ /// Clear the object's state. /// /// Clears the symbol context list. //------------------------------------------------------------------ void Clear(); //------------------------------------------------------------------ /// Dump a description of this object to a Stream. /// /// Dump a description of the contents of each symbol context in /// the list to the supplied stream \a s. /// /// @param[in] s /// The stream to which to dump the object description. //------------------------------------------------------------------ void Dump(Stream *s, Target *target) const; //------------------------------------------------------------------ /// Get accessor for a symbol context at index \a idx. /// /// Dump a description of the contents of each symbol context in /// the list to the supplied stream \a s. /// /// @param[in] idx /// The zero based index into the symbol context list. /// /// @param[out] sc /// A reference to the symbol context to fill in. /// /// @return /// Returns \b true if \a idx was a valid index into this /// symbol context list and \a sc was filled in, \b false /// otherwise. //------------------------------------------------------------------ bool GetContextAtIndex(size_t idx, SymbolContext &sc) const; //------------------------------------------------------------------ /// Direct reference accessor for a symbol context at index \a idx. /// /// The index \a idx must be a valid index, no error checking will /// be done to ensure that it is valid. /// /// @param[in] idx /// The zero based index into the symbol context list. /// /// @return /// A const reference to the symbol context to fill in. //------------------------------------------------------------------ SymbolContext &operator[](size_t idx) { return m_symbol_contexts[idx]; } const SymbolContext &operator[](size_t idx) const { return m_symbol_contexts[idx]; } //------------------------------------------------------------------ /// Get accessor for the last symbol context in the list. /// /// @param[out] sc /// A reference to the symbol context to fill in. /// /// @return /// Returns \b true if \a sc was filled in, \b false if the /// list is empty. //------------------------------------------------------------------ bool GetLastContext(SymbolContext &sc) const; bool RemoveContextAtIndex(size_t idx); //------------------------------------------------------------------ /// Get accessor for a symbol context list size. /// /// @return /// Returns the number of symbol context objects in the list. //------------------------------------------------------------------ uint32_t GetSize() const; uint32_t NumLineEntriesWithLine(uint32_t line) const; void GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) const; protected: typedef std::vector collection; ///< The collection type for the list. //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ collection m_symbol_contexts; ///< The list of symbol contexts. public: typedef AdaptedIterable SymbolContextIterable; SymbolContextIterable SymbolContexts() { return SymbolContextIterable(m_symbol_contexts); } }; bool operator==(const SymbolContext &lhs, const SymbolContext &rhs); bool operator!=(const SymbolContext &lhs, const SymbolContext &rhs); bool operator==(const SymbolContextList &lhs, const SymbolContextList &rhs); bool operator!=(const SymbolContextList &lhs, const SymbolContextList &rhs); } // namespace lldb_private #endif // liblldb_SymbolContext_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Makefile (revision 318424) @@ -0,0 +1,18 @@ +LEVEL := ../../../make + +LD_EXTRAS := -L. -l$(LIB_PREFIX)One -l$(LIB_PREFIX)Two +C_SOURCES := main.c + +main.o : CFLAGS_EXTRAS += -g -O0 + +include $(LEVEL)/Makefile.rules + +.PHONY: +a.out: lib_One lib_Two + +lib_%: + $(MAKE) -f $*.mk + +clean:: + $(MAKE) -f One.mk clean + $(MAKE) -f Two.mk clean Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.c (revision 318424) @@ -0,0 +1,6 @@ +#include "One.h" +#include + +void one() { + printf("One\n"); // break here +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.h =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.h (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.h (revision 318424) @@ -0,0 +1,4 @@ +#ifndef ONE_H +#define ONE_H +void one(); +#endif Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/One.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/OneConstant.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/OneConstant.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/OneConstant.c (revision 318424) @@ -0,0 +1 @@ +int __attribute__ ((visibility("hidden"))) conflicting_symbol = 11111; Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One/OneConstant.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One.mk =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One.mk (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One.mk (revision 318424) @@ -0,0 +1,12 @@ +LEVEL := ../../../make + +DYLIB_NAME := One +DYLIB_C_SOURCES := One/One.c One/OneConstant.c +DYLIB_ONLY := YES + +include $(LEVEL)/Makefile.rules + +CFLAGS_EXTRAS += -fPIC + +One/OneConstant.o: One/OneConstant.c + $(CC) $(CFLAGS_NO_DEBUG) -c $< -o $@ Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/One.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/TestConflictingSymbol.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/TestConflictingSymbol.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/TestConflictingSymbol.py (revision 318424) @@ -0,0 +1,90 @@ +"""Test that conflicting symbols in different shared libraries work correctly""" + +from __future__ import print_function + + +import os +import time +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestConflictingSymbols(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def test_conflicting_symbols(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Register our shared libraries for remote targets so they get + # automatically uploaded + environment = self.registerSharedLibrariesWithTarget( + target, ['One', 'Two']) + + One_line = line_number('One/One.c', '// break here') + Two_line = line_number('Two/Two.c', '// break here') + main_line = line_number('main.c', '// break here') + lldbutil.run_break_set_command( + self, 'breakpoint set -f One.c -l %s' % (One_line)) + lldbutil.run_break_set_command( + self, 'breakpoint set -f Two.c -l %s' % (Two_line)) + lldbutil.run_break_set_by_file_and_line( + self, 'main.c', main_line, num_expected_locations=1, loc_exact=True) + + process = target.LaunchSimple( + None, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs=[' resolved, hit count = 1']) + + # This should display correctly. + self.expect( + "expr (unsigned long long)conflicting_symbol", + "Symbol from One should be found", + substrs=[ + "11111"]) + + self.runCmd("continue", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs=[' resolved, hit count = 1']) + + self.expect( + "expr (unsigned long long)conflicting_symbol", + "Symbol from Two should be found", + substrs=[ + "22222"]) + + self.runCmd("continue", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs=[' resolved, hit count = 1']) + + self.expect( + "expr (unsigned long long)conflicting_symbol", + "An error should be printed when symbols can't be ordered", + error=True, + substrs=[ + "Multiple internal symbols"]) Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/TestConflictingSymbol.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.c (revision 318424) @@ -0,0 +1,6 @@ +#include "Two.h" +#include + +void two() { + printf("Two\n"); // break here +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.h =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.h (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.h (revision 318424) @@ -0,0 +1,4 @@ +#ifndef TWO_H +#define TWO_H +void two(); +#endif Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/Two.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/TwoConstant.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/TwoConstant.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/TwoConstant.c (revision 318424) @@ -0,0 +1 @@ +int __attribute__ ((visibility("hidden"))) conflicting_symbol = 22222; Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two/TwoConstant.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two.mk =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two.mk (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two.mk (revision 318424) @@ -0,0 +1,12 @@ +LEVEL := ../../../make + +DYLIB_NAME := Two +DYLIB_C_SOURCES := Two/Two.c Two/TwoConstant.c +DYLIB_ONLY := YES + +include $(LEVEL)/Makefile.rules + +CFLAGS_EXTRAS += -fPIC + +Two/TwoConstant.o: Two/TwoConstant.c + $(CC) $(CFLAGS_NO_DEBUG) -c $< -o $@ Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/Two.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/main.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/main.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/main.c (revision 318424) @@ -0,0 +1,11 @@ +#include "One/One.h" +#include "Two/Two.h" + +#include + +int main() { + one(); + two(); + printf("main\n"); // break here + return(0); +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/conflicting-symbol/main.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py (revision 318423) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py (revision 318424) @@ -1,2371 +1,2371 @@ """ LLDB module which provides the abstract base class of lldb test case. The concrete subclass can override lldbtest.TesBase in order to inherit the common behavior for unitest.TestCase.setUp/tearDown implemented in this file. The subclass should override the attribute mydir in order for the python runtime to locate the individual test cases when running as part of a large test suite or when running each test case as a separate python invocation. ./dotest.py provides a test driver which sets up the environment to run the entire of part of the test suite . Example: # Exercises the test suite in the types directory.... /Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types ... Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' Command invoked: python ./dotest.py -A x86_64 types compilers=['clang'] Configuration: arch=x86_64 compiler=clang ---------------------------------------------------------------------- Collected 72 tests ........................................................................ ---------------------------------------------------------------------- Ran 72 tests in 135.468s OK $ """ from __future__ import absolute_import from __future__ import print_function # System modules import abc import collections from functools import wraps import gc import glob import inspect import io import os.path import re import signal from subprocess import * import sys import time import traceback import types # Third-party modules import unittest2 from six import add_metaclass from six import StringIO as SixStringIO import six # LLDB modules import use_lldb_suite import lldb from . import configuration from . import decorators from . import lldbplatformutil from . import lldbtest_config from . import lldbutil from . import test_categories from lldbsuite.support import encoded_file from lldbsuite.support import funcutils # dosep.py starts lots and lots of dotest instances # This option helps you find if two (or more) dotest instances are using the same # directory at the same time # Enable it to cause test failures and stderr messages if dotest instances try to run in # the same directory simultaneously # it is disabled by default because it litters the test directories with # ".dirlock" files debug_confirm_directory_exclusivity = False # See also dotest.parseOptionsAndInitTestdirs(), where the environment variables # LLDB_COMMAND_TRACE and LLDB_DO_CLEANUP are set from '-t' and '-r dir' # options. # By default, traceAlways is False. if "LLDB_COMMAND_TRACE" in os.environ and os.environ[ "LLDB_COMMAND_TRACE"] == "YES": traceAlways = True else: traceAlways = False # By default, doCleanup is True. if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO": doCleanup = False else: doCleanup = True # # Some commonly used assert messages. # COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" CURRENT_EXECUTABLE_SET = "Current executable set successfully" PROCESS_IS_VALID = "Process is valid" PROCESS_KILLED = "Process is killed successfully" PROCESS_EXITED = "Process exited successfully" PROCESS_STOPPED = "Process status should be stopped" RUN_SUCCEEDED = "Process is launched successfully" RUN_COMPLETED = "Process exited successfully" BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" BREAKPOINT_CREATED = "Breakpoint created successfully" BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." OBJECT_PRINTED_CORRECTLY = "Object printed correctly" SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" STEP_OUT_SUCCEEDED = "Thread step-out succeeded" STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" VALID_BREAKPOINT = "Got a valid breakpoint" VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" VALID_FILESPEC = "Got a valid filespec" VALID_MODULE = "Got a valid module" VALID_PROCESS = "Got a valid process" VALID_SYMBOL = "Got a valid symbol" VALID_TARGET = "Got a valid target" VALID_PLATFORM = "Got a valid platform" VALID_TYPE = "Got a valid type" VALID_VARIABLE = "Got a valid variable" VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" WATCHPOINT_CREATED = "Watchpoint created successfully" def CMD_MSG(str): '''A generic "Command '%s' returns successfully" message generator.''' return "Command '%s' returns successfully" % str def COMPLETION_MSG(str_before, str_after): '''A generic message generator for the completion mechanism.''' return "'%s' successfully completes to '%s'" % (str_before, str_after) def EXP_MSG(str, actual, exe): '''A generic "'%s' returns expected result" message generator if exe. Otherwise, it generates "'%s' matches expected result" message.''' return "'%s' %s expected result, got '%s'" % ( str, 'returns' if exe else 'matches', actual.strip()) def SETTING_MSG(setting): '''A generic "Value of setting '%s' is correct" message generator.''' return "Value of setting '%s' is correct" % setting def EnvArray(): """Returns an env variable array from the os.environ map object.""" return list(map(lambda k, v: k + "=" + v, list(os.environ.keys()), list(os.environ.values()))) def line_number(filename, string_to_match): """Helper function to return the line number of the first matched string.""" with io.open(filename, mode='r', encoding="utf-8") as f: for i, line in enumerate(f): if line.find(string_to_match) != -1: # Found our match. return i + 1 raise Exception( "Unable to find '%s' within file %s" % (string_to_match, filename)) def get_line(filename, line_number): """Return the text of the line at the 1-based line number.""" with io.open(filename, mode='r', encoding="utf-8") as f: return f.readlines()[line_number - 1] def pointer_size(): """Return the pointer size of the host system.""" import ctypes a_pointer = ctypes.c_void_p(0xffff) return 8 * ctypes.sizeof(a_pointer) def is_exe(fpath): """Returns true if fpath is an executable.""" return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def which(program): """Returns the full path to a program; None otherwise.""" fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None class recording(SixStringIO): """ A nice little context manager for recording the debugger interactions into our session object. If trace flag is ON, it also emits the interactions into the stderr. """ def __init__(self, test, trace): """Create a SixStringIO instance; record the session obj and trace flag.""" SixStringIO.__init__(self) # The test might not have undergone the 'setUp(self)' phase yet, so that # the attribute 'session' might not even exist yet. self.session = getattr(test, "session", None) if test else None self.trace = trace def __enter__(self): """ Context management protocol on entry to the body of the with statement. Just return the SixStringIO object. """ return self def __exit__(self, type, value, tb): """ Context management protocol on exit from the body of the with statement. If trace is ON, it emits the recordings into stderr. Always add the recordings to our session object. And close the SixStringIO object, too. """ if self.trace: print(self.getvalue(), file=sys.stderr) if self.session: print(self.getvalue(), file=self.session) self.close() @add_metaclass(abc.ABCMeta) class _BaseProcess(object): @abc.abstractproperty def pid(self): """Returns process PID if has been launched already.""" @abc.abstractmethod def launch(self, executable, args): """Launches new process with given executable and args.""" @abc.abstractmethod def terminate(self): """Terminates previously launched process..""" class _LocalProcess(_BaseProcess): def __init__(self, trace_on): self._proc = None self._trace_on = trace_on self._delayafterterminate = 0.1 @property def pid(self): return self._proc.pid def launch(self, executable, args): self._proc = Popen( [executable] + args, stdout=open( os.devnull) if not self._trace_on else None, stdin=PIPE) def terminate(self): if self._proc.poll() is None: # Terminate _proc like it does the pexpect signals_to_try = [ sig for sig in [ 'SIGHUP', 'SIGCONT', 'SIGINT'] if sig in dir(signal)] for sig in signals_to_try: try: self._proc.send_signal(getattr(signal, sig)) time.sleep(self._delayafterterminate) if self._proc.poll() is not None: return except ValueError: pass # Windows says SIGINT is not a valid signal to send self._proc.terminate() time.sleep(self._delayafterterminate) if self._proc.poll() is not None: return self._proc.kill() time.sleep(self._delayafterterminate) def poll(self): return self._proc.poll() class _RemoteProcess(_BaseProcess): def __init__(self, install_remote): self._pid = None self._install_remote = install_remote @property def pid(self): return self._pid def launch(self, executable, args): if self._install_remote: src_path = executable dst_path = lldbutil.append_to_process_working_directory( os.path.basename(executable)) dst_file_spec = lldb.SBFileSpec(dst_path, False) err = lldb.remote_platform.Install( lldb.SBFileSpec(src_path, True), dst_file_spec) if err.Fail(): raise Exception( "remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err)) else: dst_path = executable dst_file_spec = lldb.SBFileSpec(executable, False) launch_info = lldb.SBLaunchInfo(args) launch_info.SetExecutableFile(dst_file_spec, True) launch_info.SetWorkingDirectory( lldb.remote_platform.GetWorkingDirectory()) # Redirect stdout and stderr to /dev/null launch_info.AddSuppressFileAction(1, False, True) launch_info.AddSuppressFileAction(2, False, True) err = lldb.remote_platform.Launch(launch_info) if err.Fail(): raise Exception( "remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err)) self._pid = launch_info.GetProcessID() def terminate(self): lldb.remote_platform.Kill(self._pid) # From 2.7's subprocess.check_output() convenience function. # Return a tuple (stdoutdata, stderrdata). def system(commands, **kwargs): r"""Run an os command with arguments and return its output as a byte string. If the exit code was non-zero it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute and output in the output attribute. The arguments are the same as for the Popen constructor. Example: >>> check_output(["ls", "-l", "/dev/null"]) 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' The stdout argument is not allowed as it is used internally. To capture standard error in the result, use stderr=STDOUT. >>> check_output(["/bin/sh", "-c", ... "ls -l non_existent_file ; exit 0"], ... stderr=STDOUT) 'ls: non_existent_file: No such file or directory\n' """ # Assign the sender object to variable 'test' and remove it from kwargs. test = kwargs.pop('sender', None) # [['make', 'clean', 'foo'], ['make', 'foo']] -> ['make clean foo', 'make foo'] commandList = [' '.join(x) for x in commands] output = "" error = "" for shellCommand in commandList: if 'stdout' in kwargs: raise ValueError( 'stdout argument not allowed, it will be overridden.') if 'shell' in kwargs and kwargs['shell'] == False: raise ValueError('shell=False not allowed') process = Popen( shellCommand, stdout=PIPE, stderr=PIPE, shell=True, universal_newlines=True, **kwargs) pid = process.pid this_output, this_error = process.communicate() retcode = process.poll() # Enable trace on failure return while tracking down FreeBSD buildbot # issues trace = traceAlways if not trace and retcode and sys.platform.startswith("freebsd"): trace = True with recording(test, trace) as sbuf: print(file=sbuf) print("os command:", shellCommand, file=sbuf) print("with pid:", pid, file=sbuf) print("stdout:", this_output, file=sbuf) print("stderr:", this_error, file=sbuf) print("retcode:", retcode, file=sbuf) print(file=sbuf) if retcode: cmd = kwargs.get("args") if cmd is None: cmd = shellCommand cpe = CalledProcessError(retcode, cmd) # Ensure caller can access the stdout/stderr. cpe.lldb_extensions = { "stdout_content": this_output, "stderr_content": this_error, "command": shellCommand } raise cpe output = output + this_output error = error + this_error return (output, error) def getsource_if_available(obj): """ Return the text of the source code for an object if available. Otherwise, a print representation is returned. """ import inspect try: return inspect.getsource(obj) except: return repr(obj) def builder_module(): if sys.platform.startswith("freebsd"): return __import__("builder_freebsd") if sys.platform.startswith("netbsd"): return __import__("builder_netbsd") if sys.platform.startswith("linux"): # sys.platform with Python-3.x returns 'linux', but with # Python-2.x it returns 'linux2'. return __import__("builder_linux") return __import__("builder_" + sys.platform) class Base(unittest2.TestCase): """ Abstract base for performing lldb (see TestBase) or other generic tests (see BenchBase for one example). lldbtest.Base works with the test driver to accomplish things. """ # The concrete subclass should override this attribute. mydir = None # Keep track of the old current working directory. oldcwd = None @staticmethod def compute_mydir(test_file): '''Subclasses should call this function to correctly calculate the required "mydir" attribute as follows: mydir = TestBase.compute_mydir(__file__)''' test_dir = os.path.dirname(test_file) return test_dir[len(os.environ["LLDB_TEST"]) + 1:] def TraceOn(self): """Returns True if we are in trace mode (tracing detailed test execution).""" return traceAlways @classmethod def setUpClass(cls): """ Python unittest framework class setup fixture. Do current directory manipulation. """ # Fail fast if 'mydir' attribute is not overridden. if not cls.mydir or len(cls.mydir) == 0: raise Exception("Subclasses must override the 'mydir' attribute.") # Save old working directory. cls.oldcwd = os.getcwd() # Change current working directory if ${LLDB_TEST} is defined. # See also dotest.py which sets up ${LLDB_TEST}. if ("LLDB_TEST" in os.environ): full_dir = os.path.join(os.environ["LLDB_TEST"], cls.mydir) if traceAlways: print("Change dir to:", full_dir, file=sys.stderr) os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) if debug_confirm_directory_exclusivity: import lock cls.dir_lock = lock.Lock(os.path.join(full_dir, ".dirlock")) try: cls.dir_lock.try_acquire() # write the class that owns the lock into the lock file cls.dir_lock.handle.write(cls.__name__) except IOError as ioerror: # nothing else should have this directory lock # wait here until we get a lock cls.dir_lock.acquire() # read the previous owner from the lock file lock_id = cls.dir_lock.handle.read() print( "LOCK ERROR: {} wants to lock '{}' but it is already locked by '{}'".format( cls.__name__, full_dir, lock_id), file=sys.stderr) raise ioerror # Set platform context. cls.platformContext = lldbplatformutil.createPlatformContext() @classmethod def tearDownClass(cls): """ Python unittest framework class teardown fixture. Do class-wide cleanup. """ if doCleanup: # First, let's do the platform-specific cleanup. module = builder_module() module.cleanup() # Subclass might have specific cleanup function defined. if getattr(cls, "classCleanup", None): if traceAlways: print( "Call class-specific cleanup function for class:", cls, file=sys.stderr) try: cls.classCleanup() except: exc_type, exc_value, exc_tb = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_tb) if debug_confirm_directory_exclusivity: cls.dir_lock.release() del cls.dir_lock # Restore old working directory. if traceAlways: print("Restore dir to:", cls.oldcwd, file=sys.stderr) os.chdir(cls.oldcwd) @classmethod def skipLongRunningTest(cls): """ By default, we skip long running test case. This can be overridden by passing '-l' to the test driver (dotest.py). """ if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ[ "LLDB_SKIP_LONG_RUNNING_TEST"]: return False else: return True def enableLogChannelsForCurrentTest(self): if len(lldbtest_config.channels) == 0: return # if debug channels are specified in lldbtest_config.channels, # create a new set of log files for every test log_basename = self.getLogBasenameForCurrentTest() # confirm that the file is writeable host_log_path = "{}-host.log".format(log_basename) open(host_log_path, 'w').close() log_enable = "log enable -Tpn -f {} ".format(host_log_path) for channel_with_categories in lldbtest_config.channels: channel_then_categories = channel_with_categories.split(' ', 1) channel = channel_then_categories[0] if len(channel_then_categories) > 1: categories = channel_then_categories[1] else: categories = "default" if channel == "gdb-remote" and lldb.remote_platform is None: # communicate gdb-remote categories to debugserver os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories self.ci.HandleCommand( log_enable + channel_with_categories, self.res) if not self.res.Succeeded(): raise Exception( 'log enable failed (check LLDB_LOG_OPTION env variable)') # Communicate log path name to debugserver & lldb-server # For remote debugging, these variables need to be set when starting the platform # instance. if lldb.remote_platform is None: server_log_path = "{}-server.log".format(log_basename) open(server_log_path, 'w').close() os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path # Communicate channels to lldb-server os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join( lldbtest_config.channels) self.addTearDownHook(self.disableLogChannelsForCurrentTest) def disableLogChannelsForCurrentTest(self): # close all log files that we opened for channel_and_categories in lldbtest_config.channels: # channel format - [ [ ...]] channel = channel_and_categories.split(' ', 1)[0] self.ci.HandleCommand("log disable " + channel, self.res) if not self.res.Succeeded(): raise Exception( 'log disable failed (check LLDB_LOG_OPTION env variable)') # Retrieve the server log (if any) from the remote system. It is assumed the server log # is writing to the "server.log" file in the current test directory. This can be # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote # platform. If the remote logging is not enabled, then just let the Get() command silently # fail. if lldb.remote_platform: lldb.remote_platform.Get( lldb.SBFileSpec("server.log"), lldb.SBFileSpec( self.getLogBasenameForCurrentTest() + "-server.log")) def setPlatformWorkingDir(self): if not lldb.remote_platform or not configuration.lldb_platform_working_dir: return components = [str(self.test_number)] + self.mydir.split(os.path.sep) remote_test_dir = configuration.lldb_platform_working_dir for c in components: remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c) error = lldb.remote_platform.MakeDirectory( remote_test_dir, 448) # 448 = 0o700 if error.Fail(): raise Exception("making remote directory '%s': %s" % ( remote_test_dir, error)) lldb.remote_platform.SetWorkingDirectory(remote_test_dir) # This function removes all files from the current working directory while leaving # the directories in place. The cleaup is required to reduce the disk space required # by the test suit while leaving the directories untached is neccessary because # sub-directories might belong to an other test def clean_working_directory(): # TODO: Make it working on Windows when we need it for remote debugging support # TODO: Replace the heuristic to remove the files with a logic what collects the # list of files we have to remove during test runs. shell_cmd = lldb.SBPlatformShellCommand( "rm %s/*" % remote_test_dir) lldb.remote_platform.Run(shell_cmd) self.addTearDownHook(clean_working_directory) def setUp(self): """Fixture for unittest test case setup. It works with the test driver to conditionally skip tests and does other initializations.""" #import traceback # traceback.print_stack() if "LIBCXX_PATH" in os.environ: self.libcxxPath = os.environ["LIBCXX_PATH"] else: self.libcxxPath = None if "LLDBMI_EXEC" in os.environ: self.lldbMiExec = os.environ["LLDBMI_EXEC"] else: self.lldbMiExec = None # If we spawn an lldb process for test (via pexpect), do not load the # init file unless told otherwise. if "NO_LLDBINIT" in os.environ and "NO" == os.environ["NO_LLDBINIT"]: self.lldbOption = "" else: self.lldbOption = "--no-lldbinit" # Assign the test method name to self.testMethodName. # # For an example of the use of this attribute, look at test/types dir. # There are a bunch of test cases under test/types and we don't want the # module cacheing subsystem to be confused with executable name "a.out" # used for all the test cases. self.testMethodName = self._testMethodName # This is for the case of directly spawning 'lldb'/'gdb' and interacting # with it using pexpect. self.child = None self.child_prompt = "(lldb) " # If the child is interacting with the embedded script interpreter, # there are two exits required during tear down, first to quit the # embedded script interpreter and second to quit the lldb command # interpreter. self.child_in_script_interpreter = False # These are for customized teardown cleanup. self.dict = None self.doTearDownCleanup = False # And in rare cases where there are multiple teardown cleanups. self.dicts = [] self.doTearDownCleanups = False # List of spawned subproces.Popen objects self.subprocesses = [] # List of forked process PIDs self.forkedProcessPids = [] # Create a string buffer to record the session info, to be dumped into a # test case specific file if test failure is encountered. self.log_basename = self.getLogBasenameForCurrentTest() session_file = "{}.log".format(self.log_basename) # Python 3 doesn't support unbuffered I/O in text mode. Open buffered. self.session = encoded_file.open(session_file, "utf-8", mode="w") # Optimistically set __errored__, __failed__, __expected__ to False # initially. If the test errored/failed, the session info # (self.session) is then dumped into a session specific file for # diagnosis. self.__cleanup_errored__ = False self.__errored__ = False self.__failed__ = False self.__expected__ = False # We are also interested in unexpected success. self.__unexpected__ = False # And skipped tests. self.__skipped__ = False # See addTearDownHook(self, hook) which allows the client to add a hook # function to be run during tearDown() time. self.hooks = [] # See HideStdout(self). self.sys_stdout_hidden = False if self.platformContext: # set environment variable names for finding shared libraries self.dylibPath = self.platformContext.shlib_environment_var # Create the debugger instance if necessary. try: self.dbg = lldb.DBG except AttributeError: self.dbg = lldb.SBDebugger.Create() if not self.dbg: raise Exception('Invalid debugger instance') # Retrieve the associated command interpreter instance. self.ci = self.dbg.GetCommandInterpreter() if not self.ci: raise Exception('Could not get the command interpreter') # And the result object. self.res = lldb.SBCommandReturnObject() self.setPlatformWorkingDir() self.enableLogChannelsForCurrentTest() # Initialize debug_info self.debug_info = None lib_dir = os.environ["LLDB_LIB_DIR"] self.dsym = None self.framework_dir = None self.darwinWithFramework = self.platformIsDarwin() if sys.platform.startswith("darwin"): # Handle the framework environment variable if it is set if hasattr(lldbtest_config, 'lldbFrameworkPath'): framework_path = lldbtest_config.lldbFrameworkPath # Framework dir should be the directory containing the framework self.framework_dir = framework_path[:framework_path.rfind('LLDB.framework')] # If a framework dir was not specified assume the Xcode build # directory layout where the framework is in LLDB_LIB_DIR. else: self.framework_dir = lib_dir self.dsym = os.path.join(self.framework_dir, 'LLDB.framework', 'LLDB') # If the framework binary doesn't exist, assume we didn't actually # build a framework, and fallback to standard *nix behavior by # setting framework_dir and dsym to None. if not os.path.exists(self.dsym): self.framework_dir = None self.dsym = None self.darwinWithFramework = False def setAsync(self, value): """ Sets async mode to True/False and ensures it is reset after the testcase completes.""" old_async = self.dbg.GetAsync() self.dbg.SetAsync(value) self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) def cleanupSubprocesses(self): # Ensure any subprocesses are cleaned up for p in self.subprocesses: p.terminate() del p del self.subprocesses[:] # Ensure any forked processes are cleaned up for pid in self.forkedProcessPids: if os.path.exists("/proc/" + str(pid)): os.kill(pid, signal.SIGTERM) def spawnSubprocess(self, executable, args=[], install_remote=True): """ Creates a subprocess.Popen object with the specified executable and arguments, saves it in self.subprocesses, and returns the object. NOTE: if using this function, ensure you also call: self.addTearDownHook(self.cleanupSubprocesses) otherwise the test suite will leak processes. """ proc = _RemoteProcess( install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) proc.launch(executable, args) self.subprocesses.append(proc) return proc def forkSubprocess(self, executable, args=[]): """ Fork a subprocess with its own group ID. NOTE: if using this function, ensure you also call: self.addTearDownHook(self.cleanupSubprocesses) otherwise the test suite will leak processes. """ child_pid = os.fork() if child_pid == 0: # If more I/O support is required, this can be beefed up. fd = os.open(os.devnull, os.O_RDWR) os.dup2(fd, 1) os.dup2(fd, 2) # This call causes the child to have its of group ID os.setpgid(0, 0) os.execvp(executable, [executable] + args) # Give the child time to get through the execvp() call time.sleep(0.1) self.forkedProcessPids.append(child_pid) return child_pid def HideStdout(self): """Hide output to stdout from the user. During test execution, there might be cases where we don't want to show the standard output to the user. For example, self.runCmd(r'''sc print("\n\n\tHello!\n")''') tests whether command abbreviation for 'script' works or not. There is no need to show the 'Hello' output to the user as long as the 'script' command succeeds and we are not in TraceOn() mode (see the '-t' option). In this case, the test method calls self.HideStdout(self) to redirect the sys.stdout to a null device, and restores the sys.stdout upon teardown. Note that you should only call this method at most once during a test case execution. Any subsequent call has no effect at all.""" if self.sys_stdout_hidden: return self.sys_stdout_hidden = True old_stdout = sys.stdout sys.stdout = open(os.devnull, 'w') def restore_stdout(): sys.stdout = old_stdout self.addTearDownHook(restore_stdout) # ======================================================================= # Methods for customized teardown cleanups as well as execution of hooks. # ======================================================================= def setTearDownCleanup(self, dictionary=None): """Register a cleanup action at tearDown() time with a dictinary""" self.dict = dictionary self.doTearDownCleanup = True def addTearDownCleanup(self, dictionary): """Add a cleanup action at tearDown() time with a dictinary""" self.dicts.append(dictionary) self.doTearDownCleanups = True def addTearDownHook(self, hook): """ Add a function to be run during tearDown() time. Hooks are executed in a first come first serve manner. """ if six.callable(hook): with recording(self, traceAlways) as sbuf: print( "Adding tearDown hook:", getsource_if_available(hook), file=sbuf) self.hooks.append(hook) return self def deletePexpectChild(self): # This is for the case of directly spawning 'lldb' and interacting with it # using pexpect. if self.child and self.child.isalive(): import pexpect with recording(self, traceAlways) as sbuf: print("tearing down the child process....", file=sbuf) try: if self.child_in_script_interpreter: self.child.sendline('quit()') self.child.expect_exact(self.child_prompt) self.child.sendline( 'settings set interpreter.prompt-on-quit false') self.child.sendline('quit') self.child.expect(pexpect.EOF) except (ValueError, pexpect.ExceptionPexpect): # child is already terminated pass except OSError as exception: import errno if exception.errno != errno.EIO: # unexpected error raise # child is already terminated pass finally: # Give it one final blow to make sure the child is terminated. self.child.close() def tearDown(self): """Fixture for unittest test case teardown.""" #import traceback # traceback.print_stack() self.deletePexpectChild() # Check and run any hook functions. for hook in reversed(self.hooks): with recording(self, traceAlways) as sbuf: print( "Executing tearDown hook:", getsource_if_available(hook), file=sbuf) if funcutils.requires_self(hook): hook(self) else: hook() # try the plain call and hope it works del self.hooks # Perform registered teardown cleanup. if doCleanup and self.doTearDownCleanup: self.cleanup(dictionary=self.dict) # In rare cases where there are multiple teardown cleanups added. if doCleanup and self.doTearDownCleanups: if self.dicts: for dict in reversed(self.dicts): self.cleanup(dictionary=dict) # ========================================================= # Various callbacks to allow introspection of test progress # ========================================================= def markError(self): """Callback invoked when an error (unexpected exception) errored.""" self.__errored__ = True with recording(self, False) as sbuf: # False because there's no need to write "ERROR" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("ERROR", file=sbuf) def markCleanupError(self): """Callback invoked when an error occurs while a test is cleaning up.""" self.__cleanup_errored__ = True with recording(self, False) as sbuf: # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("CLEANUP_ERROR", file=sbuf) def markFailure(self): """Callback invoked when a failure (test assertion failure) occurred.""" self.__failed__ = True with recording(self, False) as sbuf: # False because there's no need to write "FAIL" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("FAIL", file=sbuf) def markExpectedFailure(self, err, bugnumber): """Callback invoked when an expected failure/error occurred.""" self.__expected__ = True with recording(self, False) as sbuf: # False because there's no need to write "expected failure" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. if bugnumber is None: print("expected failure", file=sbuf) else: print( "expected failure (problem id:" + str(bugnumber) + ")", file=sbuf) def markSkippedTest(self): """Callback invoked when a test is skipped.""" self.__skipped__ = True with recording(self, False) as sbuf: # False because there's no need to write "skipped test" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. print("skipped test", file=sbuf) def markUnexpectedSuccess(self, bugnumber): """Callback invoked when an unexpected success occurred.""" self.__unexpected__ = True with recording(self, False) as sbuf: # False because there's no need to write "unexpected success" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. if bugnumber is None: print("unexpected success", file=sbuf) else: print( "unexpected success (problem id:" + str(bugnumber) + ")", file=sbuf) def getRerunArgs(self): return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) def getLogBasenameForCurrentTest(self, prefix=None): """ returns a partial path that can be used as the beginning of the name of multiple log files pertaining to this test /--.. """ dname = os.path.join(os.environ["LLDB_TEST"], os.environ["LLDB_SESSION_DIRNAME"]) if not os.path.isdir(dname): os.mkdir(dname) components = [] if prefix is not None: components.append(prefix) for c in configuration.session_file_format: if c == 'f': components.append(self.__class__.__module__) elif c == 'n': components.append(self.__class__.__name__) elif c == 'c': compiler = self.getCompiler() if compiler[1] == ':': compiler = compiler[2:] if os.path.altsep is not None: compiler = compiler.replace(os.path.altsep, os.path.sep) path_components = [x for x in compiler.split(os.path.sep) if x != ""] # Add at most 4 path components to avoid generating very long # filenames components.extend(path_components[-4:]) elif c == 'a': components.append(self.getArchitecture()) elif c == 'm': components.append(self.testMethodName) fname = "-".join(components) return os.path.join(dname, fname) def dumpSessionInfo(self): """ Dump the debugger interactions leading to a test error/failure. This allows for more convenient postmortem analysis. See also LLDBTestResult (dotest.py) which is a singlton class derived from TextTestResult and overwrites addError, addFailure, and addExpectedFailure methods to allow us to to mark the test instance as such. """ # We are here because self.tearDown() detected that this test instance # either errored or failed. The lldb.test_result singleton contains # two lists (erros and failures) which get populated by the unittest # framework. Look over there for stack trace information. # # The lists contain 2-tuples of TestCase instances and strings holding # formatted tracebacks. # # See http://docs.python.org/library/unittest.html#unittest.TestResult. # output tracebacks into session pairs = [] if self.__errored__: pairs = configuration.test_result.errors prefix = 'Error' elif self.__cleanup_errored__: pairs = configuration.test_result.cleanup_errors prefix = 'CleanupError' elif self.__failed__: pairs = configuration.test_result.failures prefix = 'Failure' elif self.__expected__: pairs = configuration.test_result.expectedFailures prefix = 'ExpectedFailure' elif self.__skipped__: prefix = 'SkippedTest' elif self.__unexpected__: prefix = 'UnexpectedSuccess' else: prefix = 'Success' if not self.__unexpected__ and not self.__skipped__: for test, traceback in pairs: if test is self: print(traceback, file=self.session) # put footer (timestamp/rerun instructions) into session testMethod = getattr(self, self._testMethodName) if getattr(testMethod, "__benchmarks_test__", False): benchmarks = True else: benchmarks = False import datetime print( "Session info generated @", datetime.datetime.now().ctime(), file=self.session) print( "To rerun this test, issue the following command from the 'test' directory:\n", file=self.session) print( "./dotest.py %s -v %s %s" % (self.getRunOptions(), ('+b' if benchmarks else '-t'), self.getRerunArgs()), file=self.session) self.session.close() del self.session # process the log files log_files_for_this_test = glob.glob(self.log_basename + "*") if prefix != 'Success' or lldbtest_config.log_success: # keep all log files, rename them to include prefix dst_log_basename = self.getLogBasenameForCurrentTest(prefix) for src in log_files_for_this_test: if os.path.isfile(src): dst = src.replace(self.log_basename, dst_log_basename) if os.name == "nt" and os.path.isfile(dst): # On Windows, renaming a -> b will throw an exception if b exists. On non-Windows platforms # it silently replaces the destination. Ultimately this means that atomic renames are not # guaranteed to be possible on Windows, but we need this to work anyway, so just remove the # destination first if it already exists. remove_file(dst) os.rename(src, dst) else: # success! (and we don't want log files) delete log files for log_file in log_files_for_this_test: remove_file(log_file) # ==================================================== # Config. methods supported through a plugin interface # (enables reading of the current test configuration) # ==================================================== def isMIPS(self): """Returns true if the architecture is MIPS.""" arch = self.getArchitecture() if re.match("mips", arch): return True return False def getArchitecture(self): """Returns the architecture in effect the test suite is running with.""" module = builder_module() arch = module.getArchitecture() if arch == 'amd64': arch = 'x86_64' return arch def getLldbArchitecture(self): """Returns the architecture of the lldb binary.""" if not hasattr(self, 'lldbArchitecture'): # spawn local process command = [ lldbtest_config.lldbExec, "-o", "file " + lldbtest_config.lldbExec, "-o", "quit" ] output = check_output(command) str = output.decode("utf-8") for line in str.splitlines(): m = re.search( "Current executable set to '.*' \\((.*)\\)\\.", line) if m: self.lldbArchitecture = m.group(1) break return self.lldbArchitecture def getCompiler(self): """Returns the compiler in effect the test suite is running with.""" module = builder_module() return module.getCompiler() def getCompilerBinary(self): """Returns the compiler binary the test suite is running with.""" return self.getCompiler().split()[0] def getCompilerVersion(self): """ Returns a string that represents the compiler version. Supports: llvm, clang. """ version = 'unknown' compiler = self.getCompilerBinary() version_output = system([[compiler, "-v"]])[1] for line in version_output.split(os.linesep): m = re.search('version ([0-9\.]+)', line) if m: version = m.group(1) return version def getGoCompilerVersion(self): """ Returns a string that represents the go compiler version, or None if go is not found. """ compiler = which("go") if compiler: version_output = system([[compiler, "version"]])[0] for line in version_output.split(os.linesep): m = re.search('go version (devel|go\\S+)', line) if m: return m.group(1) return None def platformIsDarwin(self): """Returns true if the OS triple for the selected platform is any valid apple OS""" return lldbplatformutil.platformIsDarwin() def hasDarwinFramework(self): return self.darwinWithFramework def getPlatform(self): """Returns the target platform the test suite is running on.""" return lldbplatformutil.getPlatform() def isIntelCompiler(self): """ Returns true if using an Intel (ICC) compiler, false otherwise. """ return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) def expectedCompilerVersion(self, compiler_version): """Returns True iff compiler_version[1] matches the current compiler version. Use compiler_version[0] to specify the operator used to determine if a match has occurred. Any operator other than the following defaults to an equality test: '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' """ if (compiler_version is None): return True operator = str(compiler_version[0]) version = compiler_version[1] if (version is None): return True if (operator == '>'): return self.getCompilerVersion() > version if (operator == '>=' or operator == '=>'): return self.getCompilerVersion() >= version if (operator == '<'): return self.getCompilerVersion() < version if (operator == '<=' or operator == '=<'): return self.getCompilerVersion() <= version if (operator == '!=' or operator == '!' or operator == 'not'): return str(version) not in str(self.getCompilerVersion()) return str(version) in str(self.getCompilerVersion()) def expectedCompiler(self, compilers): """Returns True iff any element of compilers is a sub-string of the current compiler.""" if (compilers is None): return True for compiler in compilers: if compiler in self.getCompiler(): return True return False def expectedArch(self, archs): """Returns True iff any element of archs is a sub-string of the current architecture.""" if (archs is None): return True for arch in archs: if arch in self.getArchitecture(): return True return False def getRunOptions(self): """Command line option for -A and -C to run this test again, called from self.dumpSessionInfo().""" arch = self.getArchitecture() comp = self.getCompiler() if arch: option_str = "-A " + arch else: option_str = "" if comp: option_str += " -C " + comp return option_str # ================================================== # Build methods supported through a plugin interface # ================================================== def getstdlibFlag(self): """ Returns the proper -stdlib flag, or empty if not required.""" if self.platformIsDarwin() or self.getPlatform() == "freebsd": stdlibflag = "-stdlib=libc++" else: # this includes NetBSD stdlibflag = "" return stdlibflag def getstdFlag(self): """ Returns the proper stdflag. """ if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): stdflag = "-std=c++0x" else: stdflag = "-std=c++11" return stdflag def buildDriver(self, sources, exe_name): """ Platform-specific way to build a program that links with LLDB (via the liblldb.so or LLDB.framework). """ stdflag = self.getstdFlag() stdlibflag = self.getstdlibFlag() lib_dir = os.environ["LLDB_LIB_DIR"] if self.hasDarwinFramework(): d = {'CXX_SOURCES': sources, 'EXE': exe_name, 'CFLAGS_EXTRAS': "%s %s" % (stdflag, stdlibflag), 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, 'LD_EXTRAS': "%s -Wl,-rpath,%s" % (self.dsym, self.framework_dir), } elif sys.platform.rstrip('0123456789') in ('freebsd', 'linux', 'netbsd', 'darwin') or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': d = { 'CXX_SOURCES': sources, 'EXE': exe_name, 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, stdlibflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-L%s/../lib -llldb -Wl,-rpath,%s/../lib" % (lib_dir, lib_dir)} elif sys.platform.startswith('win'): d = { 'CXX_SOURCES': sources, 'EXE': exe_name, 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, stdlibflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-L%s -lliblldb" % os.environ["LLDB_IMPLIB_DIR"]} if self.TraceOn(): print( "Building LLDB Driver (%s) from sources %s" % (exe_name, sources)) self.buildDefault(dictionary=d) def buildLibrary(self, sources, lib_name): """Platform specific way to build a default library. """ stdflag = self.getstdFlag() lib_dir = os.environ["LLDB_LIB_DIR"] if self.hasDarwinFramework(): d = {'DYLIB_CXX_SOURCES': sources, 'DYLIB_NAME': lib_name, 'CFLAGS_EXTRAS': "%s -stdlib=libc++" % stdflag, 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, 'LD_EXTRAS': "%s -Wl,-rpath,%s -dynamiclib" % (self.dsym, self.framework_dir), } elif sys.platform.rstrip('0123456789') in ('freebsd', 'linux', 'netbsd', 'darwin') or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': d = { 'DYLIB_CXX_SOURCES': sources, 'DYLIB_NAME': lib_name, 'CFLAGS_EXTRAS': "%s -I%s -fPIC" % (stdflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-shared -L%s/../lib -llldb -Wl,-rpath,%s/../lib" % (lib_dir, lib_dir)} elif self.getPlatform() == 'windows': d = { 'DYLIB_CXX_SOURCES': sources, 'DYLIB_NAME': lib_name, 'CFLAGS_EXTRAS': "%s -I%s -fPIC" % (stdflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-shared -l%s\liblldb.lib" % self.os.environ["LLDB_IMPLIB_DIR"]} if self.TraceOn(): print( "Building LLDB Library (%s) from sources %s" % (lib_name, sources)) self.buildDefault(dictionary=d) def buildProgram(self, sources, exe_name): """ Platform specific way to build an executable from C/C++ sources. """ d = {'CXX_SOURCES': sources, 'EXE': exe_name} self.buildDefault(dictionary=d) def buildDefault( self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build the default binaries.""" module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDefault( self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build default binary") def buildDsym( self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with dsym info.""" module = builder_module() if not module.buildDsym( self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dsym") def buildDwarf( self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with dwarf maps.""" module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDwarf( self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dwarf") def buildDwo( self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with dwarf maps.""" module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDwo( self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dwo") def buildGModules( self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with gmodules info.""" module = builder_module() if not module.buildGModules( self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with gmodules") def buildGo(self): """Build the default go binary. """ system([[which('go'), 'build -gcflags "-N -l" -o a.out main.go']]) def signBinary(self, binary_path): if sys.platform.startswith("darwin"): codesign_cmd = "codesign --force --sign \"%s\" %s" % ( lldbtest_config.codesign_identity, binary_path) call(codesign_cmd, shell=True) def findBuiltClang(self): """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" paths_to_try = [ "llvm-build/Release+Asserts/x86_64/Release+Asserts/bin/clang", "llvm-build/Debug+Asserts/x86_64/Debug+Asserts/bin/clang", "llvm-build/Release/x86_64/Release/bin/clang", "llvm-build/Debug/x86_64/Debug/bin/clang", ] lldb_root_path = os.path.join( os.path.dirname(__file__), "..", "..", "..", "..") for p in paths_to_try: path = os.path.join(lldb_root_path, p) if os.path.exists(path): return path # Tries to find clang at the same folder as the lldb path = os.path.join(os.path.dirname(lldbtest_config.lldbExec), "clang") if os.path.exists(path): return path return os.environ["CC"] def getBuildFlags( self, use_cpp11=True, use_libcxx=False, use_libstdcxx=False): """ Returns a dictionary (which can be provided to build* functions above) which contains OS-specific build flags. """ cflags = "" ldflags = "" # On Mac OS X, unless specifically requested to use libstdc++, use # libc++ if not use_libstdcxx and self.platformIsDarwin(): use_libcxx = True if use_libcxx and self.libcxxPath: cflags += "-stdlib=libc++ " if self.libcxxPath: libcxxInclude = os.path.join(self.libcxxPath, "include") libcxxLib = os.path.join(self.libcxxPath, "lib") if os.path.isdir(libcxxInclude) and os.path.isdir(libcxxLib): cflags += "-nostdinc++ -I%s -L%s -Wl,-rpath,%s " % ( libcxxInclude, libcxxLib, libcxxLib) if use_cpp11: cflags += "-std=" if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): cflags += "c++0x" else: cflags += "c++11" if self.platformIsDarwin() or self.getPlatform() == "freebsd": cflags += " -stdlib=libc++" elif self.getPlatform() == "netbsd": cflags += " -stdlib=libstdc++" elif "clang" in self.getCompiler(): cflags += " -stdlib=libstdc++" return {'CFLAGS_EXTRAS': cflags, 'LD_EXTRAS': ldflags, } def cleanup(self, dictionary=None): """Platform specific way to do cleanup after build.""" module = builder_module() if not module.cleanup(self, dictionary): raise Exception( "Don't know how to do cleanup with dictionary: " + dictionary) def getLLDBLibraryEnvVal(self): """ Returns the path that the OS-specific library search environment variable (self.dylibPath) should be set to in order for a program to find the LLDB library. If an environment variable named self.dylibPath is already set, the new path is appended to it and returned. """ existing_library_path = os.environ[ self.dylibPath] if self.dylibPath in os.environ else None lib_dir = os.environ["LLDB_LIB_DIR"] if existing_library_path: return "%s:%s" % (existing_library_path, lib_dir) elif sys.platform.startswith("darwin"): return os.path.join(lib_dir, 'LLDB.framework') else: return lib_dir def getLibcPlusPlusLibs(self): if self.getPlatform() in ('freebsd', 'linux', 'netbsd'): return ['libc++.so.1'] else: return ['libc++.1.dylib', 'libc++abi.dylib'] # Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. # We change the test methods to create a new test method for each test for each debug info we are # testing. The name of the new test method will be '_' and with adding # the new test method we remove the old method at the same time. This functionality can be # supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test # level by using the decorator @no_debug_info_test. class LLDBTestCaseFactory(type): def __new__(cls, name, bases, attrs): original_testcase = super( LLDBTestCaseFactory, cls).__new__( cls, name, bases, attrs) if original_testcase.NO_DEBUG_INFO_TESTCASE: return original_testcase newattrs = {} for attrname, attrvalue in attrs.items(): if attrname.startswith("test") and not getattr( attrvalue, "__no_debug_info_test__", False): target_platform = lldb.DBG.GetSelectedPlatform( ).GetTriple().split('-')[2] # If any debug info categories were explicitly tagged, assume that list to be # authoritative. If none were specified, try with all debug # info formats. all_dbginfo_categories = set( test_categories.debug_info_categories) categories = set( getattr( attrvalue, "categories", [])) & all_dbginfo_categories if not categories: categories = all_dbginfo_categories supported_categories = [ x for x in categories if test_categories.is_supported_on_platform( x, target_platform, configuration.compiler)] if "dsym" in supported_categories: @decorators.add_test_categories(["dsym"]) @wraps(attrvalue) def dsym_test_method(self, attrvalue=attrvalue): self.debug_info = "dsym" return attrvalue(self) dsym_method_name = attrname + "_dsym" dsym_test_method.__name__ = dsym_method_name newattrs[dsym_method_name] = dsym_test_method if "dwarf" in supported_categories: @decorators.add_test_categories(["dwarf"]) @wraps(attrvalue) def dwarf_test_method(self, attrvalue=attrvalue): self.debug_info = "dwarf" return attrvalue(self) dwarf_method_name = attrname + "_dwarf" dwarf_test_method.__name__ = dwarf_method_name newattrs[dwarf_method_name] = dwarf_test_method if "dwo" in supported_categories: @decorators.add_test_categories(["dwo"]) @wraps(attrvalue) def dwo_test_method(self, attrvalue=attrvalue): self.debug_info = "dwo" return attrvalue(self) dwo_method_name = attrname + "_dwo" dwo_test_method.__name__ = dwo_method_name newattrs[dwo_method_name] = dwo_test_method if "gmodules" in supported_categories: @decorators.add_test_categories(["gmodules"]) @wraps(attrvalue) def gmodules_test_method(self, attrvalue=attrvalue): self.debug_info = "gmodules" return attrvalue(self) gmodules_method_name = attrname + "_gmodules" gmodules_test_method.__name__ = gmodules_method_name newattrs[gmodules_method_name] = gmodules_test_method else: newattrs[attrname] = attrvalue return super( LLDBTestCaseFactory, cls).__new__( cls, name, bases, newattrs) # Setup the metaclass for this class to change the list of the test # methods when a new class is loaded @add_metaclass(LLDBTestCaseFactory) class TestBase(Base): """ This abstract base class is meant to be subclassed. It provides default implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), among other things. Important things for test class writers: - Overwrite the mydir class attribute, otherwise your test class won't run. It specifies the relative directory to the top level 'test' so the test harness can change to the correct working directory before running your test. - The setUp method sets up things to facilitate subsequent interactions with the debugger as part of the test. These include: - populate the test method name - create/get a debugger set with synchronous mode (self.dbg) - get the command interpreter from with the debugger (self.ci) - create a result object for use with the command interpreter (self.res) - plus other stuffs - The tearDown method tries to perform some necessary cleanup on behalf of the test to return the debugger to a good state for the next test. These include: - execute any tearDown hooks registered by the test method with TestBase.addTearDownHook(); examples can be found in settings/TestSettings.py - kill the inferior process associated with each target, if any, and, then delete the target from the debugger's target list - perform build cleanup before running the next test method in the same test class; examples of registering for this service can be found in types/TestIntegerTypes.py with the call: - self.setTearDownCleanup(dictionary=d) - Similarly setUpClass and tearDownClass perform classwise setup and teardown fixtures. The tearDownClass method invokes a default build cleanup for the entire test class; also, subclasses can implement the classmethod classCleanup(cls) to perform special class cleanup action. - The instance methods runCmd and expect are used heavily by existing test cases to send a command to the command interpreter and to perform string/pattern matching on the output of such command execution. The expect method also provides a mode to peform string/pattern matching without running a command. - The build methods buildDefault, buildDsym, and buildDwarf are used to build the binaries used during a particular test scenario. A plugin should be provided for the sys.platform running the test suite. The Mac OS X implementation is located in plugins/darwin.py. """ # Subclasses can set this to true (if they don't depend on debug info) to avoid running the # test multiple times with various debug info types. NO_DEBUG_INFO_TESTCASE = False # Maximum allowed attempts when launching the inferior process. # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. maxLaunchCount = 3 # Time to wait before the next launching attempt in second(s). # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. timeWaitNextLaunch = 1.0 # Returns the list of categories to which this test case belongs # by default, look for a ".categories" file, and read its contents # if no such file exists, traverse the hierarchy - we guarantee # a .categories to exist at the top level directory so we do not end up # looping endlessly - subclasses are free to define their own categories # in whatever way makes sense to them def getCategories(self): import inspect import os.path folder = inspect.getfile(self.__class__) folder = os.path.dirname(folder) while folder != '/': categories_file_name = os.path.join(folder, ".categories") if os.path.exists(categories_file_name): categories_file = open(categories_file_name, 'r') categories = categories_file.readline() categories_file.close() categories = str.replace(categories, '\n', '') categories = str.replace(categories, '\r', '') return categories.split(',') else: folder = os.path.dirname(folder) continue def generateSource(self, source): template = source + '.template' temp = os.path.join(os.getcwd(), template) with open(temp, 'r') as f: content = f.read() public_api_dir = os.path.join( os.environ["LLDB_SRC"], "include", "lldb", "API") # Look under the include/lldb/API directory and add #include statements # for all the SB API headers. public_headers = os.listdir(public_api_dir) # For different platforms, the include statement can vary. if self.hasDarwinFramework(): include_stmt = "'#include <%s>' % os.path.join('LLDB', header)" else: include_stmt = "'#include <%s>' % os.path.join('" + public_api_dir + "', header)" list = [eval(include_stmt) for header in public_headers if ( header.startswith("SB") and header.endswith(".h"))] includes = '\n'.join(list) new_content = content.replace('%include_SB_APIs%', includes) src = os.path.join(os.getcwd(), source) with open(src, 'w') as f: f.write(new_content) self.addTearDownHook(lambda: os.remove(src)) def setUp(self): #import traceback # traceback.print_stack() # Works with the test driver to conditionally skip tests via # decorators. Base.setUp(self) if "LLDB_MAX_LAUNCH_COUNT" in os.environ: self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: self.timeWaitNextLaunch = float( os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) # We want our debugger to be synchronous. self.dbg.SetAsync(False) # Retrieve the associated command interpreter instance. self.ci = self.dbg.GetCommandInterpreter() if not self.ci: raise Exception('Could not get the command interpreter') # And the result object. self.res = lldb.SBCommandReturnObject() def registerSharedLibrariesWithTarget(self, target, shlibs): '''If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing Any modules in the target that have their remote install file specification set will get uploaded to the remote host. This function registers the local copies of the shared libraries with the target and sets their remote install locations so they will be uploaded when the target is run. ''' if not shlibs or not self.platformContext: return None shlib_environment_var = self.platformContext.shlib_environment_var shlib_prefix = self.platformContext.shlib_prefix shlib_extension = '.' + self.platformContext.shlib_extension working_dir = self.get_process_working_directory() environment = ['%s=%s' % (shlib_environment_var, working_dir)] # Add any shared libraries to our target if remote so they get # uploaded into the working directory on the remote side for name in shlibs: # The path can be a full path to a shared library, or a make file name like "Foo" for # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a # basename like "libFoo.so". So figure out which one it is and resolve the local copy # of the shared library accordingly - if os.path.exists(name): + if os.path.isfile(name): local_shlib_path = name # name is the full path to the local shared library else: # Check relative names local_shlib_path = os.path.join( os.getcwd(), shlib_prefix + name + shlib_extension) if not os.path.exists(local_shlib_path): local_shlib_path = os.path.join( os.getcwd(), name + shlib_extension) if not os.path.exists(local_shlib_path): local_shlib_path = os.path.join(os.getcwd(), name) # Make sure we found the local shared library in the above code self.assertTrue(os.path.exists(local_shlib_path)) # Add the shared library to our target shlib_module = target.AddModule(local_shlib_path, None, None, None) if lldb.remote_platform: # We must set the remote install location if we want the shared library # to get uploaded to the remote target remote_shlib_path = lldbutil.append_to_process_working_directory( os.path.basename(local_shlib_path)) shlib_module.SetRemoteInstallFileSpec( lldb.SBFileSpec(remote_shlib_path, False)) return environment # utility methods that tests can use to access the current objects def target(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget() def process(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess() def thread(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() def frame(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() def get_process_working_directory(self): '''Get the working directory that should be used when launching processes for local or remote processes.''' if lldb.remote_platform: # Remote tests set the platform working directory up in # TestBase.setUp() return lldb.remote_platform.GetWorkingDirectory() else: # local tests change directory into each test subdirectory return os.getcwd() def tearDown(self): #import traceback # traceback.print_stack() # Ensure all the references to SB objects have gone away so that we can # be sure that all test-specific resources have been freed before we # attempt to delete the targets. gc.collect() # Delete the target(s) from the debugger as a general cleanup step. # This includes terminating the process for each target, if any. # We'd like to reuse the debugger for our next test without incurring # the initialization overhead. targets = [] for target in self.dbg: if target: targets.append(target) process = target.GetProcess() if process: rc = self.invoke(process, "Kill") self.assertTrue(rc.Success(), PROCESS_KILLED) for target in targets: self.dbg.DeleteTarget(target) # Do this last, to make sure it's in reverse order from how we setup. Base.tearDown(self) # This must be the last statement, otherwise teardown hooks or other # lines might depend on this still being active. del self.dbg def switch_to_thread_with_stop_reason(self, stop_reason): """ Run the 'thread list' command, and select the thread with stop reason as 'stop_reason'. If no such thread exists, no select action is done. """ from .lldbutil import stop_reason_to_str self.runCmd('thread list') output = self.res.GetOutput() thread_line_pattern = re.compile( "^[ *] thread #([0-9]+):.*stop reason = %s" % stop_reason_to_str(stop_reason)) for line in output.splitlines(): matched = thread_line_pattern.match(line) if matched: self.runCmd('thread select %s' % matched.group(1)) def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): """ Ask the command interpreter to handle the command and then check its return status. """ # Fail fast if 'cmd' is not meaningful. if not cmd or len(cmd) == 0: raise Exception("Bad 'cmd' parameter encountered") trace = (True if traceAlways else trace) if cmd.startswith("target create "): cmd = cmd.replace("target create ", "file ") running = (cmd.startswith("run") or cmd.startswith("process launch")) for i in range(self.maxLaunchCount if running else 1): self.ci.HandleCommand(cmd, self.res, inHistory) with recording(self, trace) as sbuf: print("runCmd:", cmd, file=sbuf) if not check: print("check of return status not required", file=sbuf) if self.res.Succeeded(): print("output:", self.res.GetOutput(), file=sbuf) else: print("runCmd failed!", file=sbuf) print(self.res.GetError(), file=sbuf) if self.res.Succeeded(): break elif running: # For process launch, wait some time before possible next try. time.sleep(self.timeWaitNextLaunch) with recording(self, trace) as sbuf: print("Command '" + cmd + "' failed!", file=sbuf) if check: self.assertTrue(self.res.Succeeded(), msg if msg else CMD_MSG(cmd)) def match( self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True): """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern Otherwise, all the arguments have the same meanings as for the expect function""" trace = (True if traceAlways else trace) if exe: # First run the command. If we are expecting error, set check=False. # Pass the assert message along since it provides more semantic # info. self.runCmd( str, msg=msg, trace=( True if trace else False), check=not error) # Then compare the output against expected strings. output = self.res.GetError() if error else self.res.GetOutput() # If error is True, the API client expects the command to fail! if error: self.assertFalse(self.res.Succeeded(), "Command '" + str + "' is expected to fail!") else: # No execution required, just compare str against the golden input. output = str with recording(self, trace) as sbuf: print("looking at:", output, file=sbuf) # The heading says either "Expecting" or "Not expecting". heading = "Expecting" if matching else "Not expecting" for pattern in patterns: # Match Objects always have a boolean value of True. match_object = re.search(pattern, output) matched = bool(match_object) with recording(self, trace) as sbuf: print("%s pattern: %s" % (heading, pattern), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) if matched: break self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, output, exe)) return match_object def expect( self, str, msg=None, patterns=None, startstr=None, endstr=None, substrs=None, trace=False, error=False, matching=True, exe=True, inHistory=False): """ Similar to runCmd; with additional expect style output matching ability. Ask the command interpreter to handle the command and then check its return status. The 'msg' parameter specifies an informational assert message. We expect the output from running the command to start with 'startstr', matches the substrings contained in 'substrs', and regexp matches the patterns contained in 'patterns'. If the keyword argument error is set to True, it signifies that the API client is expecting the command to fail. In this case, the error stream from running the command is retrieved and compared against the golden input, instead. If the keyword argument matching is set to False, it signifies that the API client is expecting the output of the command not to match the golden input. Finally, the required argument 'str' represents the lldb command to be sent to the command interpreter. In case the keyword argument 'exe' is set to False, the 'str' is treated as a string to be matched/not-matched against the golden input. """ trace = (True if traceAlways else trace) if exe: # First run the command. If we are expecting error, set check=False. # Pass the assert message along since it provides more semantic # info. self.runCmd( str, msg=msg, trace=( True if trace else False), check=not error, inHistory=inHistory) # Then compare the output against expected strings. output = self.res.GetError() if error else self.res.GetOutput() # If error is True, the API client expects the command to fail! if error: self.assertFalse(self.res.Succeeded(), "Command '" + str + "' is expected to fail!") else: # No execution required, just compare str against the golden input. if isinstance(str, lldb.SBCommandReturnObject): output = str.GetOutput() else: output = str with recording(self, trace) as sbuf: print("looking at:", output, file=sbuf) if output is None: output = "" # The heading says either "Expecting" or "Not expecting". heading = "Expecting" if matching else "Not expecting" # Start from the startstr, if specified. # If there's no startstr, set the initial state appropriately. matched = output.startswith(startstr) if startstr else ( True if matching else False) if startstr: with recording(self, trace) as sbuf: print("%s start string: %s" % (heading, startstr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) # Look for endstr, if specified. keepgoing = matched if matching else not matched if endstr: matched = output.endswith(endstr) with recording(self, trace) as sbuf: print("%s end string: %s" % (heading, endstr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) # Look for sub strings, if specified. keepgoing = matched if matching else not matched if substrs and keepgoing: for substr in substrs: matched = output.find(substr) != -1 with recording(self, trace) as sbuf: print("%s sub string: %s" % (heading, substr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) keepgoing = matched if matching else not matched if not keepgoing: break # Search for regular expression patterns, if specified. keepgoing = matched if matching else not matched if patterns and keepgoing: for pattern in patterns: # Match Objects always have a boolean value of True. matched = bool(re.search(pattern, output)) with recording(self, trace) as sbuf: print("%s pattern: %s" % (heading, pattern), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) keepgoing = matched if matching else not matched if not keepgoing: break self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, output, exe)) def invoke(self, obj, name, trace=False): """Use reflection to call a method dynamically with no argument.""" trace = (True if traceAlways else trace) method = getattr(obj, name) import inspect self.assertTrue(inspect.ismethod(method), name + "is a method name of object: " + str(obj)) result = method() with recording(self, trace) as sbuf: print(str(method) + ":", result, file=sbuf) return result def build( self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build the default binaries.""" module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if self.debug_info is None: return self.buildDefault(architecture, compiler, dictionary, clean) elif self.debug_info == "dsym": return self.buildDsym(architecture, compiler, dictionary, clean) elif self.debug_info == "dwarf": return self.buildDwarf(architecture, compiler, dictionary, clean) elif self.debug_info == "dwo": return self.buildDwo(architecture, compiler, dictionary, clean) elif self.debug_info == "gmodules": return self.buildGModules( architecture, compiler, dictionary, clean) else: self.fail("Can't build for debug info: %s" % self.debug_info) def run_platform_command(self, cmd): platform = self.dbg.GetSelectedPlatform() shell_command = lldb.SBPlatformShellCommand(cmd) err = platform.Run(shell_command) return (err, shell_command.GetStatus(), shell_command.GetOutput()) # ================================================= # Misc. helper methods for debugging test execution # ================================================= def DebugSBValue(self, val): """Debug print a SBValue object, if traceAlways is True.""" from .lldbutil import value_type_to_str if not traceAlways: return err = sys.stderr err.write(val.GetName() + ":\n") err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') err.write('\t' + "Value -> " + str(val.GetValue()) + '\n') err.write('\t' + "ValueAsUnsigned -> " + str(val.GetValueAsUnsigned()) + '\n') err.write( '\t' + "ValueType -> " + value_type_to_str( val.GetValueType()) + '\n') err.write('\t' + "Summary -> " + str(val.GetSummary()) + '\n') err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') err.write('\t' + "Location -> " + val.GetLocation() + '\n') def DebugSBType(self, type): """Debug print a SBType object, if traceAlways is True.""" if not traceAlways: return err = sys.stderr err.write(type.GetName() + ":\n") err.write('\t' + "ByteSize -> " + str(type.GetByteSize()) + '\n') err.write('\t' + "IsPointerType -> " + str(type.IsPointerType()) + '\n') err.write('\t' + "IsReferenceType -> " + str(type.IsReferenceType()) + '\n') def DebugPExpect(self, child): """Debug the spwaned pexpect object.""" if not traceAlways: return print(child) @classmethod def RemoveTempFile(cls, file): if os.path.exists(file): remove_file(file) # On Windows, the first attempt to delete a recently-touched file can fail # because of a race with antimalware scanners. This function will detect a # failure and retry. def remove_file(file, num_retries=1, sleep_duration=0.5): for i in range(num_retries + 1): try: os.remove(file) return True except: time.sleep(sleep_duration) continue return False Index: vendor/lldb/dist/source/Breakpoint/Breakpoint.cpp =================================================================== --- vendor/lldb/dist/source/Breakpoint/Breakpoint.cpp (revision 318423) +++ vendor/lldb/dist/source/Breakpoint/Breakpoint.cpp (revision 318424) @@ -1,1110 +1,1110 @@ //===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes #include "llvm/Support/Casting.h" // Project includes #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/BreakpointLocationCollection.h" #include "lldb/Breakpoint/BreakpointResolver.h" #include "lldb/Breakpoint/BreakpointResolverFileLine.h" #include "lldb/Core/Address.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/SearchFilter.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" using namespace lldb; using namespace lldb_private; using namespace llvm; const ConstString &Breakpoint::GetEventIdentifier() { static ConstString g_identifier("event-identifier.breakpoint.changed"); return g_identifier; } const char *Breakpoint::g_option_names[static_cast( Breakpoint::OptionNames::LastOptionName)]{"Names", "Hardware"}; //---------------------------------------------------------------------- // Breakpoint constructor //---------------------------------------------------------------------- Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool hardware, bool resolve_indirect_symbols) : m_being_created(true), m_hardware(hardware), m_target(target), m_filter_sp(filter_sp), m_resolver_sp(resolver_sp), m_options_up(new BreakpointOptions()), m_locations(*this), m_resolve_indirect_symbols(resolve_indirect_symbols), m_hit_count(0) { m_being_created = false; } Breakpoint::Breakpoint(Target &new_target, Breakpoint &source_bp) : m_being_created(true), m_hardware(source_bp.m_hardware), m_target(new_target), m_name_list(source_bp.m_name_list), m_options_up(new BreakpointOptions(*source_bp.m_options_up.get())), m_locations(*this), m_resolve_indirect_symbols(source_bp.m_resolve_indirect_symbols), m_hit_count(0) { // Now go through and copy the filter & resolver: m_resolver_sp = source_bp.m_resolver_sp->CopyForBreakpoint(*this); m_filter_sp = source_bp.m_filter_sp->CopyForBreakpoint(*this); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- Breakpoint::~Breakpoint() = default; //---------------------------------------------------------------------- // Serialization //---------------------------------------------------------------------- StructuredData::ObjectSP Breakpoint::SerializeToStructuredData() { // Serialize the resolver: StructuredData::DictionarySP breakpoint_dict_sp( new StructuredData::Dictionary()); StructuredData::DictionarySP breakpoint_contents_sp( new StructuredData::Dictionary()); if (!m_name_list.empty()) { StructuredData::ArraySP names_array_sp(new StructuredData::Array()); for (auto name : m_name_list) { names_array_sp->AddItem( StructuredData::StringSP(new StructuredData::String(name))); } breakpoint_contents_sp->AddItem(Breakpoint::GetKey(OptionNames::Names), names_array_sp); } breakpoint_contents_sp->AddBooleanItem( Breakpoint::GetKey(OptionNames::Hardware), m_hardware); StructuredData::ObjectSP resolver_dict_sp( m_resolver_sp->SerializeToStructuredData()); if (!resolver_dict_sp) return StructuredData::ObjectSP(); breakpoint_contents_sp->AddItem(BreakpointResolver::GetSerializationKey(), resolver_dict_sp); StructuredData::ObjectSP filter_dict_sp( m_filter_sp->SerializeToStructuredData()); if (!filter_dict_sp) return StructuredData::ObjectSP(); breakpoint_contents_sp->AddItem(SearchFilter::GetSerializationKey(), filter_dict_sp); StructuredData::ObjectSP options_dict_sp( m_options_up->SerializeToStructuredData()); if (!options_dict_sp) return StructuredData::ObjectSP(); breakpoint_contents_sp->AddItem(BreakpointOptions::GetSerializationKey(), options_dict_sp); breakpoint_dict_sp->AddItem(GetSerializationKey(), breakpoint_contents_sp); return breakpoint_dict_sp; } lldb::BreakpointSP Breakpoint::CreateFromStructuredData( Target &target, StructuredData::ObjectSP &object_data, Status &error) { BreakpointSP result_sp; StructuredData::Dictionary *breakpoint_dict = object_data->GetAsDictionary(); if (!breakpoint_dict || !breakpoint_dict->IsValid()) { error.SetErrorString("Can't deserialize from an invalid data object."); return result_sp; } StructuredData::Dictionary *resolver_dict; bool success = breakpoint_dict->GetValueForKeyAsDictionary( BreakpointResolver::GetSerializationKey(), resolver_dict); if (!success) { error.SetErrorStringWithFormat( "Breakpoint data missing toplevel resolver key"); return result_sp; } Status create_error; BreakpointResolverSP resolver_sp = BreakpointResolver::CreateFromStructuredData(*resolver_dict, create_error); if (create_error.Fail()) { error.SetErrorStringWithFormat( "Error creating breakpoint resolver from data: %s.", create_error.AsCString()); return result_sp; } StructuredData::Dictionary *filter_dict; success = breakpoint_dict->GetValueForKeyAsDictionary( SearchFilter::GetSerializationKey(), filter_dict); SearchFilterSP filter_sp; if (!success) filter_sp.reset( new SearchFilterForUnconstrainedSearches(target.shared_from_this())); else { filter_sp = SearchFilter::CreateFromStructuredData(target, *filter_dict, create_error); if (create_error.Fail()) { error.SetErrorStringWithFormat( "Error creating breakpoint filter from data: %s.", create_error.AsCString()); return result_sp; } } std::unique_ptr options_up; StructuredData::Dictionary *options_dict; success = breakpoint_dict->GetValueForKeyAsDictionary( BreakpointOptions::GetSerializationKey(), options_dict); if (success) { options_up = BreakpointOptions::CreateFromStructuredData( target, *options_dict, create_error); if (create_error.Fail()) { error.SetErrorStringWithFormat( "Error creating breakpoint options from data: %s.", create_error.AsCString()); return result_sp; } } bool hardware = false; success = breakpoint_dict->GetValueForKeyAsBoolean( Breakpoint::GetKey(OptionNames::Hardware), hardware); result_sp = target.CreateBreakpoint(filter_sp, resolver_sp, false, hardware, true); if (result_sp && options_up) { result_sp->m_options_up = std::move(options_up); } StructuredData::Array *names_array; success = breakpoint_dict->GetValueForKeyAsArray( Breakpoint::GetKey(OptionNames::Names), names_array); if (success && names_array) { size_t num_names = names_array->GetSize(); for (size_t i = 0; i < num_names; i++) { llvm::StringRef name; Status error; success = names_array->GetItemAtIndexAsString(i, name); result_sp->AddName(name, error); } } return result_sp; } bool Breakpoint::SerializedBreakpointMatchesNames( StructuredData::ObjectSP &bkpt_object_sp, std::vector &names) { if (!bkpt_object_sp) return false; StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary(); if (!bkpt_dict) return false; if (names.empty()) return true; StructuredData::Array *names_array; bool success = bkpt_dict->GetValueForKeyAsArray(GetKey(OptionNames::Names), names_array); // If there are no names, it can't match these names; if (!success) return false; size_t num_names = names_array->GetSize(); std::vector::iterator begin = names.begin(); std::vector::iterator end = names.end(); for (size_t i = 0; i < num_names; i++) { llvm::StringRef name; if (names_array->GetItemAtIndexAsString(i, name)) { if (std::find(begin, end, name) != end) { return true; } } } return false; } const lldb::TargetSP Breakpoint::GetTargetSP() { return m_target.shared_from_this(); } bool Breakpoint::IsInternal() const { return LLDB_BREAK_ID_IS_INTERNAL(m_bid); } BreakpointLocationSP Breakpoint::AddLocation(const Address &addr, bool *new_location) { return m_locations.AddLocation(addr, m_resolve_indirect_symbols, new_location); } BreakpointLocationSP Breakpoint::FindLocationByAddress(const Address &addr) { return m_locations.FindByAddress(addr); } break_id_t Breakpoint::FindLocationIDByAddress(const Address &addr) { return m_locations.FindIDByAddress(addr); } BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id) { return m_locations.FindByID(bp_loc_id); } BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index) { return m_locations.GetByIndex(index); } void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) { m_locations.RemoveInvalidLocations(arch); } // For each of the overall options we need to decide how they propagate to // the location options. This will determine the precedence of options on // the breakpoint vs. its locations. // Disable at the breakpoint level should override the location settings. // That way you can conveniently turn off a whole breakpoint without messing // up the individual settings. void Breakpoint::SetEnabled(bool enable) { if (enable == m_options_up->IsEnabled()) return; m_options_up->SetEnabled(enable); if (enable) m_locations.ResolveAllBreakpointSites(); else m_locations.ClearAllBreakpointSites(); SendBreakpointChangedEvent(enable ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); } bool Breakpoint::IsEnabled() { return m_options_up->IsEnabled(); } void Breakpoint::SetIgnoreCount(uint32_t n) { if (m_options_up->GetIgnoreCount() == n) return; m_options_up->SetIgnoreCount(n); SendBreakpointChangedEvent(eBreakpointEventTypeIgnoreChanged); } void Breakpoint::DecrementIgnoreCount() { uint32_t ignore = m_options_up->GetIgnoreCount(); if (ignore != 0) m_options_up->SetIgnoreCount(ignore - 1); } uint32_t Breakpoint::GetIgnoreCount() const { return m_options_up->GetIgnoreCount(); } bool Breakpoint::IgnoreCountShouldStop() { uint32_t ignore = GetIgnoreCount(); if (ignore != 0) { // When we get here we know the location that caused the stop doesn't have // an ignore count, // since by contract we call it first... So we don't have to find & // decrement it, we only have // to decrement our own ignore count. DecrementIgnoreCount(); return false; } else return true; } uint32_t Breakpoint::GetHitCount() const { return m_hit_count; } bool Breakpoint::IsOneShot() const { return m_options_up->IsOneShot(); } void Breakpoint::SetOneShot(bool one_shot) { m_options_up->SetOneShot(one_shot); } void Breakpoint::SetThreadID(lldb::tid_t thread_id) { if (m_options_up->GetThreadSpec()->GetTID() == thread_id) return; m_options_up->GetThreadSpec()->SetTID(thread_id); SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); } lldb::tid_t Breakpoint::GetThreadID() const { if (m_options_up->GetThreadSpecNoCreate() == nullptr) return LLDB_INVALID_THREAD_ID; else return m_options_up->GetThreadSpecNoCreate()->GetTID(); } void Breakpoint::SetThreadIndex(uint32_t index) { if (m_options_up->GetThreadSpec()->GetIndex() == index) return; m_options_up->GetThreadSpec()->SetIndex(index); SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); } uint32_t Breakpoint::GetThreadIndex() const { if (m_options_up->GetThreadSpecNoCreate() == nullptr) return 0; else return m_options_up->GetThreadSpecNoCreate()->GetIndex(); } void Breakpoint::SetThreadName(const char *thread_name) { if (m_options_up->GetThreadSpec()->GetName() != nullptr && ::strcmp(m_options_up->GetThreadSpec()->GetName(), thread_name) == 0) return; m_options_up->GetThreadSpec()->SetName(thread_name); SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); } const char *Breakpoint::GetThreadName() const { if (m_options_up->GetThreadSpecNoCreate() == nullptr) return nullptr; else return m_options_up->GetThreadSpecNoCreate()->GetName(); } void Breakpoint::SetQueueName(const char *queue_name) { if (m_options_up->GetThreadSpec()->GetQueueName() != nullptr && ::strcmp(m_options_up->GetThreadSpec()->GetQueueName(), queue_name) == 0) return; m_options_up->GetThreadSpec()->SetQueueName(queue_name); SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); } const char *Breakpoint::GetQueueName() const { if (m_options_up->GetThreadSpecNoCreate() == nullptr) return nullptr; else return m_options_up->GetThreadSpecNoCreate()->GetQueueName(); } void Breakpoint::SetCondition(const char *condition) { m_options_up->SetCondition(condition); SendBreakpointChangedEvent(eBreakpointEventTypeConditionChanged); } const char *Breakpoint::GetConditionText() const { return m_options_up->GetConditionText(); } // This function is used when "baton" doesn't need to be freed void Breakpoint::SetCallback(BreakpointHitCallback callback, void *baton, bool is_synchronous) { // The default "Baton" class will keep a copy of "baton" and won't free // or delete it when it goes goes out of scope. m_options_up->SetCallback(callback, std::make_shared(baton), is_synchronous); SendBreakpointChangedEvent(eBreakpointEventTypeCommandChanged); } // This function is used when a baton needs to be freed and therefore is // contained in a "Baton" subclass. void Breakpoint::SetCallback(BreakpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous) { m_options_up->SetCallback(callback, callback_baton_sp, is_synchronous); } void Breakpoint::ClearCallback() { m_options_up->ClearCallback(); } bool Breakpoint::InvokeCallback(StoppointCallbackContext *context, break_id_t bp_loc_id) { return m_options_up->InvokeCallback(context, GetID(), bp_loc_id); } BreakpointOptions *Breakpoint::GetOptions() { return m_options_up.get(); } void Breakpoint::ResolveBreakpoint() { if (m_resolver_sp) m_resolver_sp->ResolveBreakpoint(*m_filter_sp); } void Breakpoint::ResolveBreakpointInModules( ModuleList &module_list, BreakpointLocationCollection &new_locations) { m_locations.StartRecordingNewLocations(new_locations); m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); m_locations.StopRecordingNewLocations(); } void Breakpoint::ResolveBreakpointInModules(ModuleList &module_list, bool send_event) { if (m_resolver_sp) { // If this is not an internal breakpoint, set up to record the new // locations, then dispatch // an event with the new locations. if (!IsInternal() && send_event) { BreakpointEventData *new_locations_event = new BreakpointEventData( eBreakpointEventTypeLocationsAdded, shared_from_this()); ResolveBreakpointInModules( module_list, new_locations_event->GetBreakpointLocationCollection()); if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) { SendBreakpointChangedEvent(new_locations_event); } else delete new_locations_event; } else { m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); } } } void Breakpoint::ClearAllBreakpointSites() { m_locations.ClearAllBreakpointSites(); } //---------------------------------------------------------------------- // ModulesChanged: Pass in a list of new modules, and //---------------------------------------------------------------------- void Breakpoint::ModulesChanged(ModuleList &module_list, bool load, bool delete_locations) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Breakpoint::ModulesChanged: num_modules: %zu load: %i " "delete_locations: %i\n", module_list.GetSize(), load, delete_locations); std::lock_guard guard(module_list.GetMutex()); if (load) { // The logic for handling new modules is: // 1) If the filter rejects this module, then skip it. // 2) Run through the current location list and if there are any locations // for that module, we mark the module as "seen" and we don't try to // re-resolve // breakpoint locations for that module. // However, we do add breakpoint sites to these locations if needed. // 3) If we don't see this module in our breakpoint location list, call // ResolveInModules. ModuleList new_modules; // We'll stuff the "unseen" modules in this list, // and then resolve // them after the locations pass. Have to do it this way because // resolving breakpoints will add new locations potentially. for (ModuleSP module_sp : module_list.ModulesNoLocking()) { bool seen = false; if (!m_filter_sp->ModulePasses(module_sp)) continue; for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations()) { if (!break_loc_sp->IsEnabled()) continue; SectionSP section_sp(break_loc_sp->GetAddress().GetSection()); if (!section_sp || section_sp->GetModule() == module_sp) { if (!seen) seen = true; if (!break_loc_sp->ResolveBreakpointSite()) { if (log) log->Printf("Warning: could not set breakpoint site for " "breakpoint location %d of breakpoint %d.\n", break_loc_sp->GetID(), GetID()); } } } if (!seen) new_modules.AppendIfNeeded(module_sp); } if (new_modules.GetSize() > 0) { ResolveBreakpointInModules(new_modules); } } else { // Go through the currently set locations and if any have breakpoints in // the module list, then remove their breakpoint sites, and their locations // if asked to. BreakpointEventData *removed_locations_event; if (!IsInternal()) removed_locations_event = new BreakpointEventData( eBreakpointEventTypeLocationsRemoved, shared_from_this()); else removed_locations_event = nullptr; size_t num_modules = module_list.GetSize(); for (size_t i = 0; i < num_modules; i++) { ModuleSP module_sp(module_list.GetModuleAtIndexUnlocked(i)); if (m_filter_sp->ModulePasses(module_sp)) { size_t loc_idx = 0; size_t num_locations = m_locations.GetSize(); BreakpointLocationCollection locations_to_remove; for (loc_idx = 0; loc_idx < num_locations; loc_idx++) { BreakpointLocationSP break_loc_sp(m_locations.GetByIndex(loc_idx)); SectionSP section_sp(break_loc_sp->GetAddress().GetSection()); if (section_sp && section_sp->GetModule() == module_sp) { // Remove this breakpoint since the shared library is // unloaded, but keep the breakpoint location around // so we always get complete hit count and breakpoint // lifetime info break_loc_sp->ClearBreakpointSite(); if (removed_locations_event) { removed_locations_event->GetBreakpointLocationCollection().Add( break_loc_sp); } if (delete_locations) locations_to_remove.Add(break_loc_sp); } } if (delete_locations) { size_t num_locations_to_remove = locations_to_remove.GetSize(); for (loc_idx = 0; loc_idx < num_locations_to_remove; loc_idx++) m_locations.RemoveLocation(locations_to_remove.GetByIndex(loc_idx)); } } } SendBreakpointChangedEvent(removed_locations_event); } } namespace { static bool SymbolContextsMightBeEquivalent(SymbolContext &old_sc, SymbolContext &new_sc) { bool equivalent_scs = false; if (old_sc.module_sp.get() == new_sc.module_sp.get()) { // If these come from the same module, we can directly compare the pointers: if (old_sc.comp_unit && new_sc.comp_unit && (old_sc.comp_unit == new_sc.comp_unit)) { if (old_sc.function && new_sc.function && (old_sc.function == new_sc.function)) { equivalent_scs = true; } } else if (old_sc.symbol && new_sc.symbol && (old_sc.symbol == new_sc.symbol)) { equivalent_scs = true; } } else { // Otherwise we will compare by name... if (old_sc.comp_unit && new_sc.comp_unit) { if (FileSpec::Equal(*old_sc.comp_unit, *new_sc.comp_unit, true)) { // Now check the functions: if (old_sc.function && new_sc.function && (old_sc.function->GetName() == new_sc.function->GetName())) { equivalent_scs = true; } } } else if (old_sc.symbol && new_sc.symbol) { if (Mangled::Compare(old_sc.symbol->GetMangled(), new_sc.symbol->GetMangled()) == 0) { equivalent_scs = true; } } } return equivalent_scs; } } // anonymous namespace void Breakpoint::ModuleReplaced(ModuleSP old_module_sp, ModuleSP new_module_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Breakpoint::ModulesReplaced for %s\n", old_module_sp->GetSpecificationDescription().c_str()); // First find all the locations that are in the old module BreakpointLocationCollection old_break_locs; for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations()) { SectionSP section_sp = break_loc_sp->GetAddress().GetSection(); if (section_sp && section_sp->GetModule() == old_module_sp) { old_break_locs.Add(break_loc_sp); } } size_t num_old_locations = old_break_locs.GetSize(); if (num_old_locations == 0) { // There were no locations in the old module, so we just need to check if // there were any in the new module. ModuleList temp_list; temp_list.Append(new_module_sp); ResolveBreakpointInModules(temp_list); } else { // First search the new module for locations. // Then compare this with the old list, copy over locations that "look the // same" // Then delete the old locations. // Finally remember to post the creation event. // // Two locations are the same if they have the same comp unit & function (by // name) and there are the same number // of locations in the old function as in the new one. ModuleList temp_list; temp_list.Append(new_module_sp); BreakpointLocationCollection new_break_locs; ResolveBreakpointInModules(temp_list, new_break_locs); BreakpointLocationCollection locations_to_remove; BreakpointLocationCollection locations_to_announce; size_t num_new_locations = new_break_locs.GetSize(); if (num_new_locations > 0) { // Break out the case of one location -> one location since that's the // most common one, and there's no need // to build up the structures needed for the merge in that case. if (num_new_locations == 1 && num_old_locations == 1) { bool equivalent_locations = false; SymbolContext old_sc, new_sc; // The only way the old and new location can be equivalent is if they // have the same amount of information: BreakpointLocationSP old_loc_sp = old_break_locs.GetByIndex(0); BreakpointLocationSP new_loc_sp = new_break_locs.GetByIndex(0); if (old_loc_sp->GetAddress().CalculateSymbolContext(&old_sc) == new_loc_sp->GetAddress().CalculateSymbolContext(&new_sc)) { equivalent_locations = SymbolContextsMightBeEquivalent(old_sc, new_sc); } if (equivalent_locations) { m_locations.SwapLocation(old_loc_sp, new_loc_sp); } else { locations_to_remove.Add(old_loc_sp); locations_to_announce.Add(new_loc_sp); } } else { // We don't want to have to keep computing the SymbolContexts for these // addresses over and over, // so lets get them up front: typedef std::map IDToSCMap; IDToSCMap old_sc_map; for (size_t idx = 0; idx < num_old_locations; idx++) { SymbolContext sc; BreakpointLocationSP bp_loc_sp = old_break_locs.GetByIndex(idx); lldb::break_id_t loc_id = bp_loc_sp->GetID(); bp_loc_sp->GetAddress().CalculateSymbolContext(&old_sc_map[loc_id]); } std::map new_sc_map; for (size_t idx = 0; idx < num_new_locations; idx++) { SymbolContext sc; BreakpointLocationSP bp_loc_sp = new_break_locs.GetByIndex(idx); lldb::break_id_t loc_id = bp_loc_sp->GetID(); bp_loc_sp->GetAddress().CalculateSymbolContext(&new_sc_map[loc_id]); } // Take an element from the old Symbol Contexts while (old_sc_map.size() > 0) { lldb::break_id_t old_id = old_sc_map.begin()->first; SymbolContext &old_sc = old_sc_map.begin()->second; // Count the number of entries equivalent to this SC for the old list: std::vector old_id_vec; old_id_vec.push_back(old_id); IDToSCMap::iterator tmp_iter; for (tmp_iter = ++old_sc_map.begin(); tmp_iter != old_sc_map.end(); tmp_iter++) { if (SymbolContextsMightBeEquivalent(old_sc, tmp_iter->second)) old_id_vec.push_back(tmp_iter->first); } // Now find all the equivalent locations in the new list. std::vector new_id_vec; for (tmp_iter = new_sc_map.begin(); tmp_iter != new_sc_map.end(); tmp_iter++) { if (SymbolContextsMightBeEquivalent(old_sc, tmp_iter->second)) new_id_vec.push_back(tmp_iter->first); } // Alright, if we have the same number of potentially equivalent // locations in the old // and new modules, we'll just map them one to one in ascending ID // order (assuming the // resolver's order would match the equivalent ones. // Otherwise, we'll dump all the old ones, and just take the new ones, // erasing the elements // from both maps as we go. if (old_id_vec.size() == new_id_vec.size()) { sort(old_id_vec.begin(), old_id_vec.end()); sort(new_id_vec.begin(), new_id_vec.end()); size_t num_elements = old_id_vec.size(); for (size_t idx = 0; idx < num_elements; idx++) { BreakpointLocationSP old_loc_sp = old_break_locs.FindByIDPair(GetID(), old_id_vec[idx]); BreakpointLocationSP new_loc_sp = new_break_locs.FindByIDPair(GetID(), new_id_vec[idx]); m_locations.SwapLocation(old_loc_sp, new_loc_sp); old_sc_map.erase(old_id_vec[idx]); new_sc_map.erase(new_id_vec[idx]); } } else { for (lldb::break_id_t old_id : old_id_vec) { locations_to_remove.Add( old_break_locs.FindByIDPair(GetID(), old_id)); old_sc_map.erase(old_id); } for (lldb::break_id_t new_id : new_id_vec) { locations_to_announce.Add( new_break_locs.FindByIDPair(GetID(), new_id)); new_sc_map.erase(new_id); } } } } } // Now remove the remaining old locations, and cons up a removed locations // event. // Note, we don't put the new locations that were swapped with an old // location on the locations_to_remove // list, so we don't need to worry about telling the world about removing a // location we didn't tell them // about adding. BreakpointEventData *locations_event; if (!IsInternal()) locations_event = new BreakpointEventData( eBreakpointEventTypeLocationsRemoved, shared_from_this()); else locations_event = nullptr; for (BreakpointLocationSP loc_sp : locations_to_remove.BreakpointLocations()) { m_locations.RemoveLocation(loc_sp); if (locations_event) locations_event->GetBreakpointLocationCollection().Add(loc_sp); } SendBreakpointChangedEvent(locations_event); // And announce the new ones. if (!IsInternal()) { locations_event = new BreakpointEventData( eBreakpointEventTypeLocationsAdded, shared_from_this()); for (BreakpointLocationSP loc_sp : locations_to_announce.BreakpointLocations()) locations_event->GetBreakpointLocationCollection().Add(loc_sp); SendBreakpointChangedEvent(locations_event); } m_locations.Compact(); } } void Breakpoint::Dump(Stream *) {} size_t Breakpoint::GetNumResolvedLocations() const { // Return the number of breakpoints that are actually resolved and set // down in the inferior process. return m_locations.GetNumResolvedLocations(); } size_t Breakpoint::GetNumLocations() const { return m_locations.GetSize(); } bool Breakpoint::AddName(llvm::StringRef new_name, Status &error) { if (new_name.empty()) return false; if (!BreakpointID::StringIsBreakpointName(new_name, error)) { - error.SetErrorStringWithFormat("input name \"%s\" not a breakpoint name.", - new_name); + error.SetErrorStringWithFormatv("input name \"{0}\" not a breakpoint name.", + new_name); return false; } if (!error.Success()) return false; m_name_list.insert(new_name); return true; } void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level, bool show_locations) { assert(s != nullptr); if (!m_kind_description.empty()) { if (level == eDescriptionLevelBrief) { s->PutCString(GetBreakpointKind()); return; } else s->Printf("Kind: %s\n", GetBreakpointKind()); } const size_t num_locations = GetNumLocations(); const size_t num_resolved_locations = GetNumResolvedLocations(); // They just made the breakpoint, they don't need to be told HOW they made // it... // Also, we'll print the breakpoint number differently depending on whether // there is 1 or more locations. if (level != eDescriptionLevelInitial) { s->Printf("%i: ", GetID()); GetResolverDescription(s); GetFilterDescription(s); } switch (level) { case lldb::eDescriptionLevelBrief: case lldb::eDescriptionLevelFull: if (num_locations > 0) { s->Printf(", locations = %" PRIu64, (uint64_t)num_locations); if (num_resolved_locations > 0) s->Printf(", resolved = %" PRIu64 ", hit count = %d", (uint64_t)num_resolved_locations, GetHitCount()); } else { // Don't print the pending notification for exception resolvers since we // don't generally // know how to set them until the target is run. if (m_resolver_sp->getResolverID() != BreakpointResolver::ExceptionResolver) s->Printf(", locations = 0 (pending)"); } GetOptions()->GetDescription(s, level); if (m_precondition_sp) m_precondition_sp->GetDescription(*s, level); if (level == lldb::eDescriptionLevelFull) { if (!m_name_list.empty()) { s->EOL(); s->Indent(); s->Printf("Names:"); s->EOL(); s->IndentMore(); for (std::string name : m_name_list) { s->Indent(); s->Printf("%s\n", name.c_str()); } s->IndentLess(); } s->IndentLess(); s->EOL(); } break; case lldb::eDescriptionLevelInitial: s->Printf("Breakpoint %i: ", GetID()); if (num_locations == 0) { s->Printf("no locations (pending)."); } else if (num_locations == 1 && !show_locations) { // There is only one location, so we'll just print that location // information. GetLocationAtIndex(0)->GetDescription(s, level); } else { s->Printf("%" PRIu64 " locations.", static_cast(num_locations)); } s->EOL(); break; case lldb::eDescriptionLevelVerbose: // Verbose mode does a debug dump of the breakpoint Dump(s); s->EOL(); // s->Indent(); GetOptions()->GetDescription(s, level); break; default: break; } // The brief description is just the location name (1.2 or whatever). That's // pointless to // show in the breakpoint's description, so suppress it. if (show_locations && level != lldb::eDescriptionLevelBrief) { s->IndentMore(); for (size_t i = 0; i < num_locations; ++i) { BreakpointLocation *loc = GetLocationAtIndex(i).get(); loc->GetDescription(s, level); s->EOL(); } s->IndentLess(); } } void Breakpoint::GetResolverDescription(Stream *s) { if (m_resolver_sp) m_resolver_sp->GetDescription(s); } bool Breakpoint::GetMatchingFileLine(const ConstString &filename, uint32_t line_number, BreakpointLocationCollection &loc_coll) { // TODO: To be correct, this method needs to fill the breakpoint location // collection // with the location IDs which match the filename and line_number. // if (m_resolver_sp) { BreakpointResolverFileLine *resolverFileLine = dyn_cast(m_resolver_sp.get()); if (resolverFileLine && resolverFileLine->m_file_spec.GetFilename() == filename && resolverFileLine->m_line_number == line_number) { return true; } } return false; } void Breakpoint::GetFilterDescription(Stream *s) { m_filter_sp->GetDescription(s); } bool Breakpoint::EvaluatePrecondition(StoppointCallbackContext &context) { if (!m_precondition_sp) return true; return m_precondition_sp->EvaluatePrecondition(context); } bool Breakpoint::BreakpointPrecondition::EvaluatePrecondition( StoppointCallbackContext &context) { return true; } void Breakpoint::BreakpointPrecondition::GetDescription( Stream &stream, lldb::DescriptionLevel level) {} Status Breakpoint::BreakpointPrecondition::ConfigurePrecondition(Args &options) { Status error; error.SetErrorString("Base breakpoint precondition has no options."); return error; } void Breakpoint::SendBreakpointChangedEvent( lldb::BreakpointEventType eventKind) { if (!m_being_created && !IsInternal() && GetTarget().EventTypeHasListeners( Target::eBroadcastBitBreakpointChanged)) { BreakpointEventData *data = new Breakpoint::BreakpointEventData(eventKind, shared_from_this()); GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data); } } void Breakpoint::SendBreakpointChangedEvent(BreakpointEventData *data) { if (data == nullptr) return; if (!m_being_created && !IsInternal() && GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data); else delete data; } Breakpoint::BreakpointEventData::BreakpointEventData( BreakpointEventType sub_type, const BreakpointSP &new_breakpoint_sp) : EventData(), m_breakpoint_event(sub_type), m_new_breakpoint_sp(new_breakpoint_sp) {} Breakpoint::BreakpointEventData::~BreakpointEventData() = default; const ConstString &Breakpoint::BreakpointEventData::GetFlavorString() { static ConstString g_flavor("Breakpoint::BreakpointEventData"); return g_flavor; } const ConstString &Breakpoint::BreakpointEventData::GetFlavor() const { return BreakpointEventData::GetFlavorString(); } BreakpointSP &Breakpoint::BreakpointEventData::GetBreakpoint() { return m_new_breakpoint_sp; } BreakpointEventType Breakpoint::BreakpointEventData::GetBreakpointEventType() const { return m_breakpoint_event; } void Breakpoint::BreakpointEventData::Dump(Stream *s) const {} const Breakpoint::BreakpointEventData * Breakpoint::BreakpointEventData::GetEventDataFromEvent(const Event *event) { if (event) { const EventData *event_data = event->GetData(); if (event_data && event_data->GetFlavor() == BreakpointEventData::GetFlavorString()) return static_cast(event->GetData()); } return nullptr; } BreakpointEventType Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent( const EventSP &event_sp) { const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); if (data == nullptr) return eBreakpointEventTypeInvalidType; else return data->GetBreakpointEventType(); } BreakpointSP Breakpoint::BreakpointEventData::GetBreakpointFromEvent( const EventSP &event_sp) { BreakpointSP bp_sp; const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); if (data) bp_sp = data->m_new_breakpoint_sp; return bp_sp; } size_t Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent( const EventSP &event_sp) { const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); if (data) return data->m_locations.GetSize(); return 0; } lldb::BreakpointLocationSP Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent( const lldb::EventSP &event_sp, uint32_t bp_loc_idx) { lldb::BreakpointLocationSP bp_loc_sp; const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); if (data) { bp_loc_sp = data->m_locations.GetByIndex(bp_loc_idx); } return bp_loc_sp; } Index: vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp (revision 318423) +++ vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp (revision 318424) @@ -1,2238 +1,2150 @@ //===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ClangExpressionDeclMap.h" #include "ASTDumper.h" #include "ClangASTSource.h" #include "ClangModulesDeclVendor.h" #include "ClangPersistentVariables.h" #include "lldb/Core/Address.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Expression/Materializer.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclarationName.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" using namespace lldb; using namespace lldb_private; using namespace clang; namespace { const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars"; } // anonymous namespace ClangExpressionDeclMap::ClangExpressionDeclMap( bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, ExecutionContext &exe_ctx) : ClangASTSource(exe_ctx.GetTargetSP()), m_found_entities(), m_struct_members(), m_keep_result_in_memory(keep_result_in_memory), m_result_delegate(result_delegate), m_parser_vars(), m_struct_vars() { EnableStructVars(); } ClangExpressionDeclMap::~ClangExpressionDeclMap() { // Note: The model is now that the parser's AST context and all associated // data does not vanish until the expression has been executed. This means // that valuable lookup data (like namespaces) doesn't vanish, but DidParse(); DisableStructVars(); } bool ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx, Materializer *materializer) { ClangASTMetrics::ClearLocalCounters(); EnableParserVars(); m_parser_vars->m_exe_ctx = exe_ctx; Target *target = exe_ctx.GetTargetPtr(); if (exe_ctx.GetFramePtr()) m_parser_vars->m_sym_ctx = exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything); else if (exe_ctx.GetThreadPtr() && exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)) m_parser_vars->m_sym_ctx = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext( lldb::eSymbolContextEverything); else if (exe_ctx.GetProcessPtr()) { m_parser_vars->m_sym_ctx.Clear(true); m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); } else if (target) { m_parser_vars->m_sym_ctx.Clear(true); m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); } if (target) { m_parser_vars->m_persistent_vars = llvm::cast( target->GetPersistentExpressionStateForLanguage(eLanguageTypeC)); if (!target->GetScratchClangASTContext()) return false; } m_parser_vars->m_target_info = GetTargetInfo(); m_parser_vars->m_materializer = materializer; return true; } void ClangExpressionDeclMap::InstallCodeGenerator( clang::ASTConsumer *code_gen) { assert(m_parser_vars); m_parser_vars->m_code_gen = code_gen; } void ClangExpressionDeclMap::DidParse() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) ClangASTMetrics::DumpCounters(log); if (m_parser_vars.get()) { for (size_t entity_index = 0, num_entities = m_found_entities.GetSize(); entity_index < num_entities; ++entity_index) { ExpressionVariableSP var_sp( m_found_entities.GetVariableAtIndex(entity_index)); if (var_sp) llvm::cast(var_sp.get()) ->DisableParserVars(GetParserID()); } for (size_t pvar_index = 0, num_pvars = m_parser_vars->m_persistent_vars->GetSize(); pvar_index < num_pvars; ++pvar_index) { ExpressionVariableSP pvar_sp( m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index)); if (ClangExpressionVariable *clang_var = llvm::dyn_cast(pvar_sp.get())) clang_var->DisableParserVars(GetParserID()); } DisableParserVars(); } } // Interface for IRForTarget ClangExpressionDeclMap::TargetInfo ClangExpressionDeclMap::GetTargetInfo() { assert(m_parser_vars.get()); TargetInfo ret; ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; Process *process = exe_ctx.GetProcessPtr(); if (process) { ret.byte_order = process->GetByteOrder(); ret.address_byte_size = process->GetAddressByteSize(); } else { Target *target = exe_ctx.GetTargetPtr(); if (target) { ret.byte_order = target->GetArchitecture().GetByteOrder(); ret.address_byte_size = target->GetArchitecture().GetAddressByteSize(); } } return ret; } bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl, const ConstString &name, TypeFromParser parser_type, bool is_result, bool is_lvalue) { assert(m_parser_vars.get()); ClangASTContext *ast = llvm::dyn_cast_or_null(parser_type.GetTypeSystem()); if (ast == nullptr) return false; if (m_parser_vars->m_materializer && is_result) { Status err; ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; Target *target = exe_ctx.GetTargetPtr(); if (target == nullptr) return false; ClangASTContext *context(target->GetScratchClangASTContext()); TypeFromUser user_type(m_ast_importer_sp->DeportType( context->getASTContext(), ast->getASTContext(), parser_type.GetOpaqueQualType()), context); uint32_t offset = m_parser_vars->m_materializer->AddResultVariable( user_type, is_lvalue, m_keep_result_in_memory, m_result_delegate, err); ClangExpressionVariable *var = new ClangExpressionVariable( exe_ctx.GetBestExecutionContextScope(), name, user_type, m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size); m_found_entities.AddNewlyConstructedVariable(var); var->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = var->GetParserVars(GetParserID()); parser_vars->m_named_decl = decl; parser_vars->m_parser_type = parser_type; var->EnableJITVars(GetParserID()); ClangExpressionVariable::JITVars *jit_vars = var->GetJITVars(GetParserID()); jit_vars->m_offset = offset; return true; } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; Target *target = exe_ctx.GetTargetPtr(); if (target == NULL) return false; ClangASTContext *context(target->GetScratchClangASTContext()); TypeFromUser user_type(m_ast_importer_sp->DeportType( context->getASTContext(), ast->getASTContext(), parser_type.GetOpaqueQualType()), context); if (!user_type.GetOpaqueQualType()) { if (log) log->Printf("Persistent variable's type wasn't copied successfully"); return false; } if (!m_parser_vars->m_target_info.IsValid()) return false; ClangExpressionVariable *var = llvm::cast( m_parser_vars->m_persistent_vars ->CreatePersistentVariable( exe_ctx.GetBestExecutionContextScope(), name, user_type, m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size) .get()); if (!var) return false; var->m_frozen_sp->SetHasCompleteType(); if (is_result) var->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; else var->m_flags |= ClangExpressionVariable::EVKeepInTarget; // explicitly-declared // persistent variables should // persist if (is_lvalue) { var->m_flags |= ClangExpressionVariable::EVIsProgramReference; } else { var->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; var->m_flags |= ClangExpressionVariable::EVNeedsAllocation; } if (m_keep_result_in_memory) { var->m_flags |= ClangExpressionVariable::EVKeepInTarget; } if (log) log->Printf("Created persistent variable with flags 0x%hx", var->m_flags); var->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = var->GetParserVars(GetParserID()); parser_vars->m_named_decl = decl; parser_vars->m_parser_type = parser_type; return true; } bool ClangExpressionDeclMap::AddValueToStruct(const NamedDecl *decl, const ConstString &name, llvm::Value *value, size_t size, lldb::offset_t alignment) { assert(m_struct_vars.get()); assert(m_parser_vars.get()); bool is_persistent_variable = false; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); m_struct_vars->m_struct_laid_out = false; if (ClangExpressionVariable::FindVariableInList(m_struct_members, decl, GetParserID())) return true; ClangExpressionVariable *var(ClangExpressionVariable::FindVariableInList( m_found_entities, decl, GetParserID())); if (!var) { var = ClangExpressionVariable::FindVariableInList( *m_parser_vars->m_persistent_vars, decl, GetParserID()); is_persistent_variable = true; } if (!var) return false; if (log) log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure", static_cast(decl), name.GetCString(), var->GetName().GetCString()); // We know entity->m_parser_vars is valid because we used a parser variable // to find it ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(var)->GetParserVars(GetParserID()); parser_vars->m_llvm_value = value; if (ClangExpressionVariable::JITVars *jit_vars = llvm::cast(var)->GetJITVars(GetParserID())) { // We already laid this out; do not touch if (log) log->Printf("Already placed at 0x%llx", (unsigned long long)jit_vars->m_offset); } llvm::cast(var)->EnableJITVars(GetParserID()); ClangExpressionVariable::JITVars *jit_vars = llvm::cast(var)->GetJITVars(GetParserID()); jit_vars->m_alignment = alignment; jit_vars->m_size = size; m_struct_members.AddVariable(var->shared_from_this()); if (m_parser_vars->m_materializer) { uint32_t offset = 0; Status err; if (is_persistent_variable) { ExpressionVariableSP var_sp(var->shared_from_this()); offset = m_parser_vars->m_materializer->AddPersistentVariable( var_sp, nullptr, err); } else { if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym) offset = m_parser_vars->m_materializer->AddSymbol(*sym, err); else if (const RegisterInfo *reg_info = var->GetRegisterInfo()) offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err); else if (parser_vars->m_lldb_var) offset = m_parser_vars->m_materializer->AddVariable( parser_vars->m_lldb_var, err); } if (!err.Success()) return false; if (log) log->Printf("Placed at 0x%llx", (unsigned long long)offset); jit_vars->m_offset = offset; // TODO DoStructLayout() should not change this. } return true; } bool ClangExpressionDeclMap::DoStructLayout() { assert(m_struct_vars.get()); if (m_struct_vars->m_struct_laid_out) return true; if (!m_parser_vars->m_materializer) return false; m_struct_vars->m_struct_alignment = m_parser_vars->m_materializer->GetStructAlignment(); m_struct_vars->m_struct_size = m_parser_vars->m_materializer->GetStructByteSize(); m_struct_vars->m_struct_laid_out = true; return true; } bool ClangExpressionDeclMap::GetStructInfo(uint32_t &num_elements, size_t &size, lldb::offset_t &alignment) { assert(m_struct_vars.get()); if (!m_struct_vars->m_struct_laid_out) return false; num_elements = m_struct_members.GetSize(); size = m_struct_vars->m_struct_size; alignment = m_struct_vars->m_struct_alignment; return true; } bool ClangExpressionDeclMap::GetStructElement(const NamedDecl *&decl, llvm::Value *&value, lldb::offset_t &offset, ConstString &name, uint32_t index) { assert(m_struct_vars.get()); if (!m_struct_vars->m_struct_laid_out) return false; if (index >= m_struct_members.GetSize()) return false; ExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index)); if (!member_sp) return false; ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(member_sp.get()) ->GetParserVars(GetParserID()); ClangExpressionVariable::JITVars *jit_vars = llvm::cast(member_sp.get()) ->GetJITVars(GetParserID()); if (!parser_vars || !jit_vars || !member_sp->GetValueObject()) return false; decl = parser_vars->m_named_decl; value = parser_vars->m_llvm_value; offset = jit_vars->m_offset; name = member_sp->GetName(); return true; } bool ClangExpressionDeclMap::GetFunctionInfo(const NamedDecl *decl, uint64_t &ptr) { ClangExpressionVariable *entity(ClangExpressionVariable::FindVariableInList( m_found_entities, decl, GetParserID())); if (!entity) return false; // We know m_parser_vars is valid since we searched for the variable by // its NamedDecl ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); ptr = parser_vars->m_lldb_value.GetScalar().ULongLong(); return true; } addr_t ClangExpressionDeclMap::GetSymbolAddress(Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type, lldb_private::Module *module) { SymbolContextList sc_list; if (module) module->FindSymbolsWithNameAndType(name, symbol_type, sc_list); else target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list); const uint32_t num_matches = sc_list.GetSize(); addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; for (uint32_t i = 0; i < num_matches && (symbol_load_addr == 0 || symbol_load_addr == LLDB_INVALID_ADDRESS); i++) { SymbolContext sym_ctx; sc_list.GetContextAtIndex(i, sym_ctx); const Address sym_address = sym_ctx.symbol->GetAddress(); if (!sym_address.IsValid()) continue; switch (sym_ctx.symbol->GetType()) { case eSymbolTypeCode: case eSymbolTypeTrampoline: symbol_load_addr = sym_address.GetCallableLoadAddress(&target); break; case eSymbolTypeResolver: symbol_load_addr = sym_address.GetCallableLoadAddress(&target, true); break; case eSymbolTypeReExported: { ConstString reexport_name = sym_ctx.symbol->GetReExportedSymbolName(); if (reexport_name) { ModuleSP reexport_module_sp; ModuleSpec reexport_module_spec; reexport_module_spec.GetPlatformFileSpec() = sym_ctx.symbol->GetReExportedSymbolSharedLibrary(); if (reexport_module_spec.GetPlatformFileSpec()) { reexport_module_sp = target.GetImages().FindFirstModule(reexport_module_spec); if (!reexport_module_sp) { reexport_module_spec.GetPlatformFileSpec().GetDirectory().Clear(); reexport_module_sp = target.GetImages().FindFirstModule(reexport_module_spec); } } symbol_load_addr = GetSymbolAddress( target, process, sym_ctx.symbol->GetReExportedSymbolName(), symbol_type, reexport_module_sp.get()); } } break; case eSymbolTypeData: case eSymbolTypeRuntime: case eSymbolTypeVariable: case eSymbolTypeLocal: case eSymbolTypeParam: case eSymbolTypeInvalid: case eSymbolTypeAbsolute: case eSymbolTypeException: case eSymbolTypeSourceFile: case eSymbolTypeHeaderFile: case eSymbolTypeObjectFile: case eSymbolTypeCommonBlock: case eSymbolTypeBlock: case eSymbolTypeVariableType: case eSymbolTypeLineEntry: case eSymbolTypeLineHeader: case eSymbolTypeScopeBegin: case eSymbolTypeScopeEnd: case eSymbolTypeAdditional: case eSymbolTypeCompiler: case eSymbolTypeInstrumentation: case eSymbolTypeUndefined: case eSymbolTypeObjCClass: case eSymbolTypeObjCMetaClass: case eSymbolTypeObjCIVar: symbol_load_addr = sym_address.GetLoadAddress(&target); break; } } if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) { ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime(); if (runtime) { symbol_load_addr = runtime->LookupRuntimeSymbol(name); } } return symbol_load_addr; } addr_t ClangExpressionDeclMap::GetSymbolAddress(const ConstString &name, lldb::SymbolType symbol_type) { assert(m_parser_vars.get()); if (!m_parser_vars->m_exe_ctx.GetTargetPtr()) return false; return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), m_parser_vars->m_exe_ctx.GetProcessPtr(), name, symbol_type); } -const Symbol *ClangExpressionDeclMap::FindGlobalDataSymbol( - Target &target, const ConstString &name, lldb_private::Module *module) { - SymbolContextList sc_list; - - if (module) - module->FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); - else - target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, - sc_list); - - const uint32_t matches = sc_list.GetSize(); - for (uint32_t i = 0; i < matches; ++i) { - SymbolContext sym_ctx; - sc_list.GetContextAtIndex(i, sym_ctx); - if (sym_ctx.symbol) { - const Symbol *symbol = sym_ctx.symbol; - const Address sym_address = symbol->GetAddress(); - - if (sym_address.IsValid()) { - switch (symbol->GetType()) { - case eSymbolTypeData: - case eSymbolTypeRuntime: - case eSymbolTypeAbsolute: - case eSymbolTypeObjCClass: - case eSymbolTypeObjCMetaClass: - case eSymbolTypeObjCIVar: - if (symbol->GetDemangledNameIsSynthesized()) { - // If the demangled name was synthesized, then don't use it - // for expressions. Only let the symbol match if the mangled - // named matches for these symbols. - if (symbol->GetMangled().GetMangledName() != name) - break; - } - return symbol; - - case eSymbolTypeReExported: { - ConstString reexport_name = symbol->GetReExportedSymbolName(); - if (reexport_name) { - ModuleSP reexport_module_sp; - ModuleSpec reexport_module_spec; - reexport_module_spec.GetPlatformFileSpec() = - symbol->GetReExportedSymbolSharedLibrary(); - if (reexport_module_spec.GetPlatformFileSpec()) { - reexport_module_sp = - target.GetImages().FindFirstModule(reexport_module_spec); - if (!reexport_module_sp) { - reexport_module_spec.GetPlatformFileSpec() - .GetDirectory() - .Clear(); - reexport_module_sp = - target.GetImages().FindFirstModule(reexport_module_spec); - } - } - // Don't allow us to try and resolve a re-exported symbol if it is - // the same - // as the current symbol - if (name == symbol->GetReExportedSymbolName() && - module == reexport_module_sp.get()) - return NULL; - - return FindGlobalDataSymbol(target, - symbol->GetReExportedSymbolName(), - reexport_module_sp.get()); - } - } break; - - case eSymbolTypeCode: // We already lookup functions elsewhere - case eSymbolTypeVariable: - case eSymbolTypeLocal: - case eSymbolTypeParam: - case eSymbolTypeTrampoline: - case eSymbolTypeInvalid: - case eSymbolTypeException: - case eSymbolTypeSourceFile: - case eSymbolTypeHeaderFile: - case eSymbolTypeObjectFile: - case eSymbolTypeCommonBlock: - case eSymbolTypeBlock: - case eSymbolTypeVariableType: - case eSymbolTypeLineEntry: - case eSymbolTypeLineHeader: - case eSymbolTypeScopeBegin: - case eSymbolTypeScopeEnd: - case eSymbolTypeAdditional: - case eSymbolTypeCompiler: - case eSymbolTypeInstrumentation: - case eSymbolTypeUndefined: - case eSymbolTypeResolver: - break; - } - } - } - } - - return NULL; -} - lldb::VariableSP ClangExpressionDeclMap::FindGlobalVariable( Target &target, ModuleSP &module, const ConstString &name, CompilerDeclContext *namespace_decl, TypeFromUser *type) { VariableList vars; if (module && namespace_decl) module->FindGlobalVariables(name, namespace_decl, true, -1, vars); else target.GetImages().FindGlobalVariables(name, true, -1, vars); if (vars.GetSize()) { if (type) { for (size_t i = 0; i < vars.GetSize(); ++i) { VariableSP var_sp = vars.GetVariableAtIndex(i); if (ClangASTContext::AreTypesSame( *type, var_sp->GetType()->GetFullCompilerType())) return var_sp; } } else { return vars.GetVariableAtIndex(0); } } return VariableSP(); } ClangASTContext *ClangExpressionDeclMap::GetClangASTContext() { StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); if (frame == nullptr) return nullptr; SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); if (sym_ctx.block == nullptr) return nullptr; CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext(); if (!frame_decl_context) return nullptr; return llvm::dyn_cast_or_null( frame_decl_context.GetTypeSystem()); } // Interface for ClangASTSource void ClangExpressionDeclMap::FindExternalVisibleDecls( NameSearchContext &context) { assert(m_ast_context); ClangASTMetrics::RegisterVisibleQuery(); const ConstString name(context.m_decl_name.getAsString().c_str()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (GetImportInProgress()) { if (log && log->GetVerbose()) log->Printf("Ignoring a query during an import"); return; } static unsigned int invocation_id = 0; unsigned int current_id = invocation_id++; if (log) { if (!context.m_decl_context) log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " "'%s' in a NULL DeclContext", current_id, name.GetCString()); else if (const NamedDecl *context_named_decl = dyn_cast(context.m_decl_context)) log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " "'%s' in '%s'", current_id, name.GetCString(), context_named_decl->getNameAsString().c_str()); else log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " "'%s' in a '%s'", current_id, name.GetCString(), context.m_decl_context->getDeclKindName()); } if (const NamespaceDecl *namespace_context = dyn_cast(context.m_decl_context)) { if (namespace_context->getName().str() == std::string(g_lldb_local_vars_namespace_cstr)) { CompilerDeclContext compiler_decl_ctx( GetClangASTContext(), const_cast(static_cast( context.m_decl_context))); FindExternalVisibleDecls(context, lldb::ModuleSP(), compiler_decl_ctx, current_id); return; } ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer_sp->GetNamespaceMap(namespace_context); if (log && log->GetVerbose()) log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)", current_id, static_cast(namespace_map.get()), (int)namespace_map->size()); if (!namespace_map) return; for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); i != e; ++i) { if (log) log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s", current_id, i->second.GetName().AsCString(), i->first->GetFileSpec().GetFilename().GetCString()); FindExternalVisibleDecls(context, i->first, i->second, current_id); } } else if (isa(context.m_decl_context)) { CompilerDeclContext namespace_decl; if (log) log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id); FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl, current_id); } ClangASTSource::FindExternalVisibleDecls(context); } void ClangExpressionDeclMap::FindExternalVisibleDecls( NameSearchContext &context, lldb::ModuleSP module_sp, CompilerDeclContext &namespace_decl, unsigned int current_id) { assert(m_ast_context); std::function MaybeRegisterFunctionBody = [this](clang::FunctionDecl *copied_function_decl) { if (copied_function_decl->getBody() && m_parser_vars->m_code_gen) { DeclGroupRef decl_group_ref(copied_function_decl); m_parser_vars->m_code_gen->HandleTopLevelDecl(decl_group_ref); } }; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); SymbolContextList sc_list; const ConstString name(context.m_decl_name.getAsString().c_str()); const char *name_unique_cstr = name.GetCString(); if (name_unique_cstr == NULL) return; static ConstString id_name("id"); static ConstString Class_name("Class"); if (name == id_name || name == Class_name) return; // Only look for functions by name out in our symbols if the function // doesn't start with our phony prefix of '$' Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); SymbolContext sym_ctx; if (frame != nullptr) sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); // Try the persistent decls, which take precedence over all else. if (!namespace_decl) { do { if (!target) break; ClangASTContext *scratch_clang_ast_context = target->GetScratchClangASTContext(); if (!scratch_clang_ast_context) break; ASTContext *scratch_ast_context = scratch_clang_ast_context->getASTContext(); if (!scratch_ast_context) break; NamedDecl *persistent_decl = m_parser_vars->m_persistent_vars->GetPersistentDecl(name); if (!persistent_decl) break; Decl *parser_persistent_decl = m_ast_importer_sp->CopyDecl( m_ast_context, scratch_ast_context, persistent_decl); if (!parser_persistent_decl) break; NamedDecl *parser_named_decl = dyn_cast(parser_persistent_decl); if (!parser_named_decl) break; if (clang::FunctionDecl *parser_function_decl = llvm::dyn_cast(parser_named_decl)) { MaybeRegisterFunctionBody(parser_function_decl); } if (log) log->Printf(" CEDM::FEVD[%u] Found persistent decl %s", current_id, name.GetCString()); context.AddNamedDecl(parser_named_decl); } while (0); } if (name_unique_cstr[0] == '$' && !namespace_decl) { static ConstString g_lldb_class_name("$__lldb_class"); if (name == g_lldb_class_name) { // Clang is looking for the type of "this" if (frame == NULL) return; // Find the block that defines the function represented by "sym_ctx" Block *function_block = sym_ctx.GetFunctionBlock(); if (!function_block) return; CompilerDeclContext function_decl_ctx = function_block->GetDeclContext(); if (!function_decl_ctx) return; clang::CXXMethodDecl *method_decl = ClangASTContext::DeclContextGetAsCXXMethodDecl(function_decl_ctx); if (method_decl) { clang::CXXRecordDecl *class_decl = method_decl->getParent(); QualType class_qual_type(class_decl->getTypeForDecl(), 0); TypeFromUser class_user_type( class_qual_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&class_decl->getASTContext())); if (log) { ASTDumper ast_dumper(class_qual_type); log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); } AddThisType(context, class_user_type, current_id); if (method_decl->isInstance()) { // self is a pointer to the object QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type); TypeFromUser self_user_type( class_pointer_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); m_struct_vars->m_object_pointer_type = self_user_type; } } else { // This branch will get hit if we are executing code in the context of a // function that // claims to have an object pointer (through DW_AT_object_pointer?) but // is not formally a // method of the class. In that case, just look up the "this" variable // in the current // scope and use its type. // FIXME: This code is formally correct, but clang doesn't currently // emit DW_AT_object_pointer // for C++ so it hasn't actually been tested. VariableList *vars = frame->GetVariableList(false); lldb::VariableSP this_var = vars->FindVariable(ConstString("this")); if (this_var && this_var->IsInScope(frame) && this_var->LocationIsValidForFrame(frame)) { Type *this_type = this_var->GetType(); if (!this_type) return; TypeFromUser pointee_type = this_type->GetForwardCompilerType().GetPointeeType(); if (pointee_type.IsValid()) { if (log) { ASTDumper ast_dumper(pointee_type); log->Printf(" FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); } AddThisType(context, pointee_type, current_id); TypeFromUser this_user_type(this_type->GetFullCompilerType()); m_struct_vars->m_object_pointer_type = this_user_type; return; } } } return; } static ConstString g_lldb_objc_class_name("$__lldb_objc_class"); if (name == g_lldb_objc_class_name) { // Clang is looking for the type of "*self" if (!frame) return; SymbolContext sym_ctx = frame->GetSymbolContext( lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); // Find the block that defines the function represented by "sym_ctx" Block *function_block = sym_ctx.GetFunctionBlock(); if (!function_block) return; CompilerDeclContext function_decl_ctx = function_block->GetDeclContext(); if (!function_decl_ctx) return; clang::ObjCMethodDecl *method_decl = ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx); if (method_decl) { ObjCInterfaceDecl *self_interface = method_decl->getClassInterface(); if (!self_interface) return; const clang::Type *interface_type = self_interface->getTypeForDecl(); if (!interface_type) return; // This is unlikely, but we have seen crashes where this // occurred TypeFromUser class_user_type( QualType(interface_type, 0).getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); if (log) { ASTDumper ast_dumper(interface_type); log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); } AddOneType(context, class_user_type, current_id); if (method_decl->isInstanceMethod()) { // self is a pointer to the object QualType class_pointer_type = method_decl->getASTContext().getObjCObjectPointerType( QualType(interface_type, 0)); TypeFromUser self_user_type( class_pointer_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); m_struct_vars->m_object_pointer_type = self_user_type; } else { // self is a Class pointer QualType class_type = method_decl->getASTContext().getObjCClassType(); TypeFromUser self_user_type( class_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); m_struct_vars->m_object_pointer_type = self_user_type; } return; } else { // This branch will get hit if we are executing code in the context of a // function that // claims to have an object pointer (through DW_AT_object_pointer?) but // is not formally a // method of the class. In that case, just look up the "self" variable // in the current // scope and use its type. VariableList *vars = frame->GetVariableList(false); lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); if (self_var && self_var->IsInScope(frame) && self_var->LocationIsValidForFrame(frame)) { Type *self_type = self_var->GetType(); if (!self_type) return; CompilerType self_clang_type = self_type->GetFullCompilerType(); if (ClangASTContext::IsObjCClassType(self_clang_type)) { return; } else if (ClangASTContext::IsObjCObjectPointerType( self_clang_type)) { self_clang_type = self_clang_type.GetPointeeType(); if (!self_clang_type) return; if (log) { ASTDumper ast_dumper(self_type->GetFullCompilerType()); log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); } TypeFromUser class_user_type(self_clang_type); AddOneType(context, class_user_type, current_id); TypeFromUser self_user_type(self_type->GetFullCompilerType()); m_struct_vars->m_object_pointer_type = self_user_type; return; } } } return; } if (name == ConstString(g_lldb_local_vars_namespace_cstr)) { CompilerDeclContext frame_decl_context = sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext() : CompilerDeclContext(); if (frame_decl_context) { ClangASTContext *ast = llvm::dyn_cast_or_null( frame_decl_context.GetTypeSystem()); if (ast) { clang::NamespaceDecl *namespace_decl = ClangASTContext::GetUniqueNamespaceDeclaration( m_ast_context, name_unique_cstr, nullptr); if (namespace_decl) { context.AddNamedDecl(namespace_decl); clang::DeclContext *clang_decl_ctx = clang::Decl::castToDeclContext(namespace_decl); clang_decl_ctx->setHasExternalVisibleStorage(true); context.m_found.local_vars_nsp = true; } } } return; } // any other $__lldb names should be weeded out now if (!::strncmp(name_unique_cstr, "$__lldb", sizeof("$__lldb") - 1)) return; ExpressionVariableSP pvar_sp( m_parser_vars->m_persistent_vars->GetVariable(name)); if (pvar_sp) { AddOneVariable(context, pvar_sp, current_id); return; } const char *reg_name(&name.GetCString()[1]); if (m_parser_vars->m_exe_ctx.GetRegisterContext()) { const RegisterInfo *reg_info( m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName( reg_name)); if (reg_info) { if (log) log->Printf(" CEDM::FEVD[%u] Found register %s", current_id, reg_info->name); AddOneRegister(context, reg_info, current_id); } } } else { ValueObjectSP valobj; VariableSP var; bool local_var_lookup = !namespace_decl || (namespace_decl.GetName() == ConstString(g_lldb_local_vars_namespace_cstr)); if (frame && local_var_lookup) { CompilerDeclContext compiler_decl_context = sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext() : CompilerDeclContext(); if (compiler_decl_context) { // Make sure that the variables are parsed so that we have the // declarations. VariableListSP vars = frame->GetInScopeVariableList(true); for (size_t i = 0; i < vars->GetSize(); i++) vars->GetVariableAtIndex(i)->GetDecl(); // Search for declarations matching the name. Do not include imported // decls // in the search if we are looking for decls in the artificial namespace // $__lldb_local_vars. std::vector found_decls = compiler_decl_context.FindDeclByName(name, namespace_decl.IsValid()); bool variable_found = false; for (CompilerDecl decl : found_decls) { for (size_t vi = 0, ve = vars->GetSize(); vi != ve; ++vi) { VariableSP candidate_var = vars->GetVariableAtIndex(vi); if (candidate_var->GetDecl() == decl) { var = candidate_var; break; } } if (var && !variable_found) { variable_found = true; valobj = ValueObjectVariable::Create(frame, var); AddOneVariable(context, var, valobj, current_id); context.m_found.variable = true; } } if (variable_found) return; } } if (target) { var = FindGlobalVariable(*target, module_sp, name, &namespace_decl, NULL); if (var) { valobj = ValueObjectVariable::Create(target, var); AddOneVariable(context, var, valobj, current_id); context.m_found.variable = true; return; } } std::vector decls_from_modules; if (target) { if (ClangModulesDeclVendor *decl_vendor = target->GetClangModulesDeclVendor()) { decl_vendor->FindDecls(name, false, UINT32_MAX, decls_from_modules); } } const bool include_inlines = false; const bool append = false; if (namespace_decl && module_sp) { const bool include_symbols = false; module_sp->FindFunctions(name, &namespace_decl, eFunctionNameTypeBase, include_symbols, include_inlines, append, sc_list); } else if (target && !namespace_decl) { const bool include_symbols = true; // TODO Fix FindFunctions so that it doesn't return // instance methods for eFunctionNameTypeBase. target->GetImages().FindFunctions(name, eFunctionNameTypeFull, include_symbols, include_inlines, append, sc_list); } // If we found more than one function, see if we can use the // frame's decl context to remove functions that are shadowed // by other functions which match in type but are nearer in scope. // // AddOneFunction will not add a function whose type has already been // added, so if there's another function in the list with a matching // type, check to see if their decl context is a parent of the current // frame's or was imported via a and using statement, and pick the // best match according to lookup rules. if (sc_list.GetSize() > 1) { // Collect some info about our frame's context. StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); SymbolContext frame_sym_ctx; if (frame != nullptr) frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); CompilerDeclContext frame_decl_context = frame_sym_ctx.block != nullptr ? frame_sym_ctx.block->GetDeclContext() : CompilerDeclContext(); // We can't do this without a compiler decl context for our frame. if (frame_decl_context) { clang::DeclContext *frame_decl_ctx = (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext(); ClangASTContext *ast = llvm::dyn_cast_or_null( frame_decl_context.GetTypeSystem()); // Structure to hold the info needed when comparing function // declarations. struct FuncDeclInfo { ConstString m_name; CompilerType m_copied_type; uint32_t m_decl_lvl; SymbolContext m_sym_ctx; }; // First, symplify things by looping through the symbol contexts // to remove unwanted functions and separate out the functions we // want to compare and prune into a separate list. // Cache the info needed about the function declarations in a // vector for efficiency. SymbolContextList sc_sym_list; uint32_t num_indices = sc_list.GetSize(); std::vector fdi_cache; fdi_cache.reserve(num_indices); for (uint32_t index = 0; index < num_indices; ++index) { FuncDeclInfo fdi; SymbolContext sym_ctx; sc_list.GetContextAtIndex(index, sym_ctx); // We don't know enough about symbols to compare them, // but we should keep them in the list. Function *function = sym_ctx.function; if (!function) { sc_sym_list.Append(sym_ctx); continue; } // Filter out functions without declaration contexts, as well as // class/instance methods, since they'll be skipped in the // code that follows anyway. CompilerDeclContext func_decl_context = function->GetDeclContext(); if (!func_decl_context || func_decl_context.IsClassMethod(nullptr, nullptr, nullptr)) continue; // We can only prune functions for which we can copy the type. CompilerType func_clang_type = function->GetType()->GetFullCompilerType(); CompilerType copied_func_type = GuardedCopyType(func_clang_type); if (!copied_func_type) { sc_sym_list.Append(sym_ctx); continue; } fdi.m_sym_ctx = sym_ctx; fdi.m_name = function->GetName(); fdi.m_copied_type = copied_func_type; fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL; if (fdi.m_copied_type && func_decl_context) { // Call CountDeclLevels to get the number of parent scopes we // have to look through before we find the function declaration. // When comparing functions of the same type, the one with a // lower count will be closer to us in the lookup scope and // shadows the other. clang::DeclContext *func_decl_ctx = (clang::DeclContext *)func_decl_context.GetOpaqueDeclContext(); fdi.m_decl_lvl = ast->CountDeclLevels( frame_decl_ctx, func_decl_ctx, &fdi.m_name, &fdi.m_copied_type); } fdi_cache.emplace_back(fdi); } // Loop through the functions in our cache looking for matching types, // then compare their scope levels to see which is closer. std::multimap matches; for (const FuncDeclInfo &fdi : fdi_cache) { const CompilerType t = fdi.m_copied_type; auto q = matches.find(t); if (q != matches.end()) { if (q->second->m_decl_lvl > fdi.m_decl_lvl) // This function is closer; remove the old set. matches.erase(t); else if (q->second->m_decl_lvl < fdi.m_decl_lvl) // The functions in our set are closer - skip this one. continue; } matches.insert(std::make_pair(t, &fdi)); } // Loop through our matches and add their symbol contexts to our list. SymbolContextList sc_func_list; for (const auto &q : matches) sc_func_list.Append(q.second->m_sym_ctx); // Rejoin the lists with the functions in front. sc_list = sc_func_list; sc_list.Append(sc_sym_list); } } if (sc_list.GetSize()) { Symbol *extern_symbol = NULL; Symbol *non_extern_symbol = NULL; for (uint32_t index = 0, num_indices = sc_list.GetSize(); index < num_indices; ++index) { SymbolContext sym_ctx; sc_list.GetContextAtIndex(index, sym_ctx); if (sym_ctx.function) { CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext(); if (!decl_ctx) continue; // Filter out class/instance methods. if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr)) continue; AddOneFunction(context, sym_ctx.function, NULL, current_id); context.m_found.function_with_type_info = true; context.m_found.function = true; } else if (sym_ctx.symbol) { if (sym_ctx.symbol->GetType() == eSymbolTypeReExported && target) { sym_ctx.symbol = sym_ctx.symbol->ResolveReExportedSymbol(*target); if (sym_ctx.symbol == NULL) continue; } if (sym_ctx.symbol->IsExternal()) extern_symbol = sym_ctx.symbol; else non_extern_symbol = sym_ctx.symbol; } } if (!context.m_found.function_with_type_info) { for (clang::NamedDecl *decl : decls_from_modules) { if (llvm::isa(decl)) { clang::NamedDecl *copied_decl = llvm::cast_or_null(m_ast_importer_sp->CopyDecl( m_ast_context, &decl->getASTContext(), decl)); if (copied_decl) { context.AddNamedDecl(copied_decl); context.m_found.function_with_type_info = true; } } } } if (!context.m_found.function_with_type_info) { if (extern_symbol) { AddOneFunction(context, NULL, extern_symbol, current_id); context.m_found.function = true; } else if (non_extern_symbol) { AddOneFunction(context, NULL, non_extern_symbol, current_id); context.m_found.function = true; } } } if (!context.m_found.function_with_type_info) { // Try the modules next. do { if (ClangModulesDeclVendor *modules_decl_vendor = m_target->GetClangModulesDeclVendor()) { bool append = false; uint32_t max_matches = 1; std::vector decls; if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls)) break; clang::NamedDecl *const decl_from_modules = decls[0]; if (llvm::isa(decl_from_modules)) { if (log) { log->Printf(" CAS::FEVD[%u] Matching function found for " "\"%s\" in the modules", current_id, name.GetCString()); } clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl( m_ast_context, &decl_from_modules->getASTContext(), decl_from_modules); clang::FunctionDecl *copied_function_decl = copied_decl ? dyn_cast(copied_decl) : nullptr; if (!copied_function_decl) { if (log) log->Printf(" CAS::FEVD[%u] - Couldn't export a function " "declaration from the modules", current_id); break; } MaybeRegisterFunctionBody(copied_function_decl); context.AddNamedDecl(copied_function_decl); context.m_found.function_with_type_info = true; context.m_found.function = true; } else if (llvm::isa(decl_from_modules)) { if (log) { log->Printf(" CAS::FEVD[%u] Matching variable found for " "\"%s\" in the modules", current_id, name.GetCString()); } clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl( m_ast_context, &decl_from_modules->getASTContext(), decl_from_modules); clang::VarDecl *copied_var_decl = copied_decl ? dyn_cast_or_null(copied_decl) : nullptr; if (!copied_var_decl) { if (log) log->Printf(" CAS::FEVD[%u] - Couldn't export a variable " "declaration from the modules", current_id); break; } context.AddNamedDecl(copied_var_decl); context.m_found.variable = true; } } } while (0); } if (target && !context.m_found.variable && !namespace_decl) { // We couldn't find a non-symbol variable for this. Now we'll hunt for // a generic // data symbol, and -- if it is found -- treat it as a variable. - - const Symbol *data_symbol = FindGlobalDataSymbol(*target, name); - + Status error; + + const Symbol *data_symbol = + m_parser_vars->m_sym_ctx.FindBestGlobalDataSymbol(name, error); + + if (!error.Success()) { + const unsigned diag_id = + m_ast_context->getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Level::Error, "%0"); + m_ast_context->getDiagnostics().Report(diag_id) << error.AsCString(); + } + if (data_symbol) { std::string warning("got name from symbols: "); warning.append(name.AsCString()); const unsigned diag_id = m_ast_context->getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Level::Warning, "%0"); m_ast_context->getDiagnostics().Report(diag_id) << warning.c_str(); AddOneGenericVariable(context, *data_symbol, current_id); context.m_found.variable = true; } } } } // static opaque_compiler_type_t // MaybePromoteToBlockPointerType //( // ASTContext *ast_context, // opaque_compiler_type_t candidate_type //) //{ // if (!candidate_type) // return candidate_type; // // QualType candidate_qual_type = QualType::getFromOpaquePtr(candidate_type); // // const PointerType *candidate_pointer_type = // dyn_cast(candidate_qual_type); // // if (!candidate_pointer_type) // return candidate_type; // // QualType pointee_qual_type = candidate_pointer_type->getPointeeType(); // // const RecordType *pointee_record_type = // dyn_cast(pointee_qual_type); // // if (!pointee_record_type) // return candidate_type; // // RecordDecl *pointee_record_decl = pointee_record_type->getDecl(); // // if (!pointee_record_decl->isRecord()) // return candidate_type; // // if // (!pointee_record_decl->getName().startswith(llvm::StringRef("__block_literal_"))) // return candidate_type; // // QualType generic_function_type = // ast_context->getFunctionNoProtoType(ast_context->UnknownAnyTy); // QualType block_pointer_type = // ast_context->getBlockPointerType(generic_function_type); // // return block_pointer_type.getAsOpaquePtr(); //} bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var, lldb_private::Value &var_location, TypeFromUser *user_type, TypeFromParser *parser_type) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); Type *var_type = var->GetType(); if (!var_type) { if (log) log->PutCString("Skipped a definition because it has no type"); return false; } CompilerType var_clang_type = var_type->GetFullCompilerType(); if (!var_clang_type) { if (log) log->PutCString("Skipped a definition because it has no Clang type"); return false; } ClangASTContext *clang_ast = llvm::dyn_cast_or_null( var_type->GetForwardCompilerType().GetTypeSystem()); if (!clang_ast) { if (log) log->PutCString("Skipped a definition because it has no Clang AST"); return false; } ASTContext *ast = clang_ast->getASTContext(); if (!ast) { if (log) log->PutCString( "There is no AST context for the current execution context"); return false; } // var_clang_type = MaybePromoteToBlockPointerType (ast, var_clang_type); DWARFExpression &var_location_expr = var->LocationExpression(); Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); Status err; if (var->GetLocationIsConstantValueData()) { DataExtractor const_value_extractor; if (var_location_expr.GetExpressionData(const_value_extractor)) { var_location = Value(const_value_extractor.GetDataStart(), const_value_extractor.GetByteSize()); var_location.SetValueType(Value::eValueTypeHostAddress); } else { if (log) log->Printf("Error evaluating constant variable: %s", err.AsCString()); return false; } } CompilerType type_to_use = GuardedCopyType(var_clang_type); if (!type_to_use) { if (log) log->Printf( "Couldn't copy a variable's type into the parser's AST context"); return false; } if (parser_type) *parser_type = TypeFromParser(type_to_use); if (var_location.GetContextType() == Value::eContextTypeInvalid) var_location.SetCompilerType(type_to_use); if (var_location.GetValueType() == Value::eValueTypeFileAddress) { SymbolContext var_sc; var->CalculateSymbolContext(&var_sc); if (!var_sc.module_sp) return false; Address so_addr(var_location.GetScalar().ULongLong(), var_sc.module_sp->GetSectionList()); lldb::addr_t load_addr = so_addr.GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { var_location.GetScalar() = load_addr; var_location.SetValueType(Value::eValueTypeLoadAddress); } } if (user_type) *user_type = TypeFromUser(var_clang_type); return true; } void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, VariableSP var, ValueObjectSP valobj, unsigned int current_id) { assert(m_parser_vars.get()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); TypeFromUser ut; TypeFromParser pt; Value var_location; if (!GetVariableValue(var, var_location, &ut, &pt)) return; clang::QualType parser_opaque_type = QualType::getFromOpaquePtr(pt.GetOpaqueQualType()); if (parser_opaque_type.isNull()) return; if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) { if (const TagType *tag_type = dyn_cast(parser_type)) CompleteType(tag_type->getDecl()); if (const ObjCObjectPointerType *objc_object_ptr_type = dyn_cast(parser_type)) CompleteType(objc_object_ptr_type->getInterfaceDecl()); } bool is_reference = pt.IsReferenceType(); NamedDecl *var_decl = NULL; if (is_reference) var_decl = context.AddVarDecl(pt); else var_decl = context.AddVarDecl(pt.GetLValueReferenceType()); std::string decl_name(context.m_decl_name.getAsString()); ConstString entity_name(decl_name.c_str()); ClangExpressionVariable *entity(new ClangExpressionVariable(valobj)); m_found_entities.AddNewlyConstructedVariable(entity); assert(entity); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); parser_vars->m_parser_type = pt; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_value = var_location; parser_vars->m_lldb_var = var; if (is_reference) entity->m_flags |= ClangExpressionVariable::EVTypeIsReference; if (log) { ASTDumper orig_dumper(ut.GetOpaqueQualType()); ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s (original %s)", current_id, decl_name.c_str(), ast_dumper.GetCString(), orig_dumper.GetCString()); } } void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, ExpressionVariableSP &pvar_sp, unsigned int current_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); TypeFromUser user_type( llvm::cast(pvar_sp.get())->GetTypeFromUser()); TypeFromParser parser_type(GuardedCopyType(user_type)); if (!parser_type.GetOpaqueQualType()) { if (log) log->Printf(" CEDM::FEVD[%u] Couldn't import type for pvar %s", current_id, pvar_sp->GetName().GetCString()); return; } NamedDecl *var_decl = context.AddVarDecl(parser_type.GetLValueReferenceType()); llvm::cast(pvar_sp.get()) ->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(pvar_sp.get()) ->GetParserVars(GetParserID()); parser_vars->m_parser_type = parser_type; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_value.Clear(); if (log) { ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%u] Added pvar %s, returned %s", current_id, pvar_sp->GetName().GetCString(), ast_dumper.GetCString()); } } void ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol, unsigned int current_id) { assert(m_parser_vars.get()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); if (target == NULL) return; ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); TypeFromUser user_type( ClangASTContext::GetBasicType(scratch_ast_context, eBasicTypeVoid) .GetPointerType() .GetLValueReferenceType()); TypeFromParser parser_type( ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid) .GetPointerType() .GetLValueReferenceType()); NamedDecl *var_decl = context.AddVarDecl(parser_type); std::string decl_name(context.m_decl_name.getAsString()); ConstString entity_name(decl_name.c_str()); ClangExpressionVariable *entity(new ClangExpressionVariable( m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), entity_name, user_type, m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size)); m_found_entities.AddNewlyConstructedVariable(entity); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); const Address symbol_address = symbol.GetAddress(); lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target); // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, // user_type.GetOpaqueQualType()); parser_vars->m_lldb_value.SetCompilerType(user_type); parser_vars->m_lldb_value.GetScalar() = symbol_load_addr; parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); parser_vars->m_parser_type = parser_type; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_sym = &symbol; if (log) { ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s", current_id, decl_name.c_str(), ast_dumper.GetCString()); } } bool ClangExpressionDeclMap::ResolveUnknownTypes() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); ClangASTContext *scratch_ast_context = target->GetScratchClangASTContext(); for (size_t index = 0, num_entities = m_found_entities.GetSize(); index < num_entities; ++index) { ExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index); ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(entity.get()) ->GetParserVars(GetParserID()); if (entity->m_flags & ClangExpressionVariable::EVUnknownType) { const NamedDecl *named_decl = parser_vars->m_named_decl; const VarDecl *var_decl = dyn_cast(named_decl); if (!var_decl) { if (log) log->Printf("Entity of unknown type does not have a VarDecl"); return false; } if (log) { ASTDumper ast_dumper(const_cast(var_decl)); log->Printf("Variable of unknown type now has Decl %s", ast_dumper.GetCString()); } QualType var_type = var_decl->getType(); TypeFromParser parser_type( var_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&var_decl->getASTContext())); lldb::opaque_compiler_type_t copied_type = m_ast_importer_sp->CopyType( scratch_ast_context->getASTContext(), &var_decl->getASTContext(), var_type.getAsOpaquePtr()); if (!copied_type) { if (log) log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't " "import the type for a variable"); return (bool)lldb::ExpressionVariableSP(); } TypeFromUser user_type(copied_type, scratch_ast_context); // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, // user_type.GetOpaqueQualType()); parser_vars->m_lldb_value.SetCompilerType(user_type); parser_vars->m_parser_type = parser_type; entity->SetCompilerType(user_type); entity->m_flags &= ~(ClangExpressionVariable::EVUnknownType); } } return true; } void ClangExpressionDeclMap::AddOneRegister(NameSearchContext &context, const RegisterInfo *reg_info, unsigned int current_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); CompilerType clang_type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( m_ast_context, reg_info->encoding, reg_info->byte_size * 8); if (!clang_type) { if (log) log->Printf(" Tried to add a type for %s, but couldn't get one", context.m_decl_name.getAsString().c_str()); return; } TypeFromParser parser_clang_type(clang_type); NamedDecl *var_decl = context.AddVarDecl(parser_clang_type); ClangExpressionVariable *entity(new ClangExpressionVariable( m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size)); m_found_entities.AddNewlyConstructedVariable(entity); std::string decl_name(context.m_decl_name.getAsString()); entity->SetName(ConstString(decl_name.c_str())); entity->SetRegisterInfo(reg_info); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); parser_vars->m_parser_type = parser_clang_type; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_value.Clear(); entity->m_flags |= ClangExpressionVariable::EVBareRegister; if (log) { ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%d] Added register %s, returned %s", current_id, context.m_decl_name.getAsString().c_str(), ast_dumper.GetCString()); } } void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context, Function *function, Symbol *symbol, unsigned int current_id) { assert(m_parser_vars.get()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); NamedDecl *function_decl = NULL; Address fun_address; CompilerType function_clang_type; bool is_indirect_function = false; if (function) { Type *function_type = function->GetType(); const auto lang = function->GetCompileUnit()->GetLanguage(); const auto name = function->GetMangled().GetMangledName().AsCString(); const bool extern_c = (Language::LanguageIsC(lang) && !CPlusPlusLanguage::IsCPPMangledName(name)) || (Language::LanguageIsObjC(lang) && !Language::LanguageIsCPlusPlus(lang)); if (!extern_c) { TypeSystem *type_system = function->GetDeclContext().GetTypeSystem(); if (ClangASTContext *src_ast = llvm::dyn_cast(type_system)) { clang::DeclContext *src_decl_context = (clang::DeclContext *)function->GetDeclContext() .GetOpaqueDeclContext(); clang::FunctionDecl *src_function_decl = llvm::dyn_cast_or_null(src_decl_context); if (src_function_decl && src_function_decl->getTemplateSpecializationInfo()) { clang::FunctionTemplateDecl *function_template = src_function_decl->getTemplateSpecializationInfo()->getTemplate(); clang::FunctionTemplateDecl *copied_function_template = llvm::dyn_cast_or_null( m_ast_importer_sp->CopyDecl(m_ast_context, src_ast->getASTContext(), function_template)); if (copied_function_template) { if (log) { ASTDumper ast_dumper((clang::Decl *)copied_function_template); StreamString ss; function->DumpSymbolContext(&ss); log->Printf(" CEDM::FEVD[%u] Imported decl for function template" " %s (description %s), returned %s", current_id, copied_function_template->getNameAsString().c_str(), ss.GetData(), ast_dumper.GetCString()); } context.AddNamedDecl(copied_function_template); } } else if (src_function_decl) { if (clang::FunctionDecl *copied_function_decl = llvm::dyn_cast_or_null( m_ast_importer_sp->CopyDecl(m_ast_context, src_ast->getASTContext(), src_function_decl))) { if (log) { ASTDumper ast_dumper((clang::Decl *)copied_function_decl); StreamString ss; function->DumpSymbolContext(&ss); log->Printf(" CEDM::FEVD[%u] Imported decl for function %s " "(description %s), returned %s", current_id, copied_function_decl->getNameAsString().c_str(), ss.GetData(), ast_dumper.GetCString()); } context.AddNamedDecl(copied_function_decl); return; } else { if (log) { log->Printf(" Failed to import the function decl for '%s'", src_function_decl->getName().str().c_str()); } } } } } if (!function_type) { if (log) log->PutCString(" Skipped a function because it has no type"); return; } function_clang_type = function_type->GetFullCompilerType(); if (!function_clang_type) { if (log) log->PutCString(" Skipped a function because it has no Clang type"); return; } fun_address = function->GetAddressRange().GetBaseAddress(); CompilerType copied_function_type = GuardedCopyType(function_clang_type); if (copied_function_type) { function_decl = context.AddFunDecl(copied_function_type, extern_c); if (!function_decl) { if (log) { log->Printf( " Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}", function_type->GetName().GetCString(), function_type->GetID()); } return; } } else { // We failed to copy the type we found if (log) { log->Printf(" Failed to import the function type '%s' {0x%8.8" PRIx64 "} into the expression parser AST contenxt", function_type->GetName().GetCString(), function_type->GetID()); } return; } } else if (symbol) { fun_address = symbol->GetAddress(); function_decl = context.AddGenericFunDecl(); is_indirect_function = symbol->IsIndirect(); } else { if (log) log->PutCString(" AddOneFunction called with no function and no symbol"); return; } Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); lldb::addr_t load_addr = fun_address.GetCallableLoadAddress(target, is_indirect_function); ClangExpressionVariable *entity(new ClangExpressionVariable( m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size)); m_found_entities.AddNewlyConstructedVariable(entity); std::string decl_name(context.m_decl_name.getAsString()); entity->SetName(ConstString(decl_name.c_str())); entity->SetCompilerType(function_clang_type); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); if (load_addr != LLDB_INVALID_ADDRESS) { parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); parser_vars->m_lldb_value.GetScalar() = load_addr; } else { // We have to try finding a file address. lldb::addr_t file_addr = fun_address.GetFileAddress(); parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress); parser_vars->m_lldb_value.GetScalar() = file_addr; } parser_vars->m_named_decl = function_decl; parser_vars->m_llvm_value = NULL; if (log) { ASTDumper ast_dumper(function_decl); StreamString ss; fun_address.Dump(&ss, m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), Address::DumpStyleResolvedDescription); log->Printf( " CEDM::FEVD[%u] Found %s function %s (description %s), returned %s", current_id, (function ? "specific" : "generic"), decl_name.c_str(), ss.GetData(), ast_dumper.GetCString()); } } void ClangExpressionDeclMap::AddThisType(NameSearchContext &context, TypeFromUser &ut, unsigned int current_id) { CompilerType copied_clang_type = GuardedCopyType(ut); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (!copied_clang_type) { if (log) log->Printf( "ClangExpressionDeclMap::AddThisType - Couldn't import the type"); return; } if (copied_clang_type.IsAggregateType() && copied_clang_type.GetCompleteType()) { CompilerType void_clang_type = ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid); CompilerType void_ptr_clang_type = void_clang_type.GetPointerType(); CompilerType method_type = ClangASTContext::CreateFunctionType( m_ast_context, void_clang_type, &void_ptr_clang_type, 1, false, 0); const bool is_virtual = false; const bool is_static = false; const bool is_inline = false; const bool is_explicit = false; const bool is_attr_used = true; const bool is_artificial = false; CXXMethodDecl *method_decl = ClangASTContext::GetASTContext(m_ast_context) ->AddMethodToCXXRecordType( copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", method_type, lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); if (log) { ASTDumper method_ast_dumper((clang::Decl *)method_decl); ASTDumper type_ast_dumper(copied_clang_type); log->Printf(" CEDM::AddThisType Added function $__lldb_expr " "(description %s) for this type %s", method_ast_dumper.GetCString(), type_ast_dumper.GetCString()); } } if (!copied_clang_type.IsValid()) return; TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo( QualType::getFromOpaquePtr(copied_clang_type.GetOpaqueQualType())); if (!type_source_info) return; // Construct a typedef type because if "*this" is a templated type we can't // just return ClassTemplateSpecializationDecls in response to name queries. // Using a typedef makes this much more robust. TypedefDecl *typedef_decl = TypedefDecl::Create( *m_ast_context, m_ast_context->getTranslationUnitDecl(), SourceLocation(), SourceLocation(), context.m_decl_name.getAsIdentifierInfo(), type_source_info); if (!typedef_decl) return; context.AddNamedDecl(typedef_decl); return; } void ClangExpressionDeclMap::AddOneType(NameSearchContext &context, TypeFromUser &ut, unsigned int current_id) { CompilerType copied_clang_type = GuardedCopyType(ut); if (!copied_clang_type) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) log->Printf( "ClangExpressionDeclMap::AddOneType - Couldn't import the type"); return; } context.AddTypeDecl(copied_clang_type); } Index: vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h =================================================================== --- vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h (revision 318423) +++ vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h (revision 318424) @@ -1,639 +1,621 @@ //===-- ClangExpressionDeclMap.h --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ClangExpressionDeclMap_h_ #define liblldb_ClangExpressionDeclMap_h_ // C Includes #include #include // C++ Includes #include #include "ClangASTSource.h" #include "ClangExpressionVariable.h" // Other libraries and framework includes // Project includes #include "lldb/Core/ClangForward.h" #include "lldb/Core/Value.h" #include "lldb/Expression/Materializer.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/TaggedASTType.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/lldb-public.h" #include "clang/AST/Decl.h" #include "llvm/ADT/DenseMap.h" namespace lldb_private { //---------------------------------------------------------------------- /// @class ClangExpressionDeclMap ClangExpressionDeclMap.h /// "lldb/Expression/ClangExpressionDeclMap.h" /// @brief Manages named entities that are defined in LLDB's debug information. /// /// The Clang parser uses the ClangASTSource as an interface to request named /// entities from outside an expression. The ClangASTSource reports back, /// listing /// all possible objects corresponding to a particular name. But it in turn /// relies on ClangExpressionDeclMap, which performs several important /// functions. /// /// First, it records what variables and functions were looked up and what Decls /// were returned for them. /// /// Second, it constructs a struct on behalf of IRForTarget, recording which /// variables should be placed where and relaying this information back so that /// IRForTarget can generate context-independent code. /// /// Third, it "materializes" this struct on behalf of the expression command, /// finding the current values of each variable and placing them into the /// struct so that it can be passed to the JITted version of the IR. /// /// Fourth and finally, it "dematerializes" the struct after the JITted code has /// has executed, placing the new values back where it found the old ones. //---------------------------------------------------------------------- class ClangExpressionDeclMap : public ClangASTSource { public: //------------------------------------------------------------------ /// Constructor /// /// Initializes class variables. /// /// @param[in] keep_result_in_memory /// If true, inhibits the normal deallocation of the memory for /// the result persistent variable, and instead marks the variable /// as persisting. /// /// @param[in] delegate /// If non-NULL, use this delegate to report result values. This /// allows the client ClangUserExpression to report a result. /// /// @param[in] exe_ctx /// The execution context to use when parsing. //------------------------------------------------------------------ ClangExpressionDeclMap( bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, ExecutionContext &exe_ctx); //------------------------------------------------------------------ /// Destructor //------------------------------------------------------------------ ~ClangExpressionDeclMap() override; //------------------------------------------------------------------ /// Enable the state needed for parsing and IR transformation. /// /// @param[in] exe_ctx /// The execution context to use when finding types for variables. /// Also used to find a "scratch" AST context to store result types. /// /// @param[in] materializer /// If non-NULL, the materializer to populate with information about /// the variables to use /// /// @return /// True if parsing is possible; false if it is unsafe to continue. //------------------------------------------------------------------ bool WillParse(ExecutionContext &exe_ctx, Materializer *materializer); void InstallCodeGenerator(clang::ASTConsumer *code_gen); //------------------------------------------------------------------ /// [Used by ClangExpressionParser] For each variable that had an unknown /// type at the beginning of parsing, determine its final type now. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ bool ResolveUnknownTypes(); //------------------------------------------------------------------ /// Disable the state needed for parsing and IR transformation. //------------------------------------------------------------------ void DidParse(); //------------------------------------------------------------------ /// [Used by IRForTarget] Add a variable to the list of persistent /// variables for the process. /// /// @param[in] decl /// The Clang declaration for the persistent variable, used for /// lookup during parsing. /// /// @param[in] name /// The name of the persistent variable, usually $something. /// /// @param[in] type /// The type of the variable, in the Clang parser's context. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ bool AddPersistentVariable(const clang::NamedDecl *decl, const ConstString &name, TypeFromParser type, bool is_result, bool is_lvalue); //------------------------------------------------------------------ /// [Used by IRForTarget] Add a variable to the struct that needs to /// be materialized each time the expression runs. /// /// @param[in] decl /// The Clang declaration for the variable. /// /// @param[in] name /// The name of the variable. /// /// @param[in] value /// The LLVM IR value for this variable. /// /// @param[in] size /// The size of the variable in bytes. /// /// @param[in] alignment /// The required alignment of the variable in bytes. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ bool AddValueToStruct(const clang::NamedDecl *decl, const ConstString &name, llvm::Value *value, size_t size, lldb::offset_t alignment); //------------------------------------------------------------------ /// [Used by IRForTarget] Finalize the struct, laying out the position /// of each object in it. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ bool DoStructLayout(); //------------------------------------------------------------------ /// [Used by IRForTarget] Get general information about the laid-out /// struct after DoStructLayout() has been called. /// /// @param[out] num_elements /// The number of elements in the struct. /// /// @param[out] size /// The size of the struct, in bytes. /// /// @param[out] alignment /// The alignment of the struct, in bytes. /// /// @return /// True if the information could be retrieved; false otherwise. //------------------------------------------------------------------ bool GetStructInfo(uint32_t &num_elements, size_t &size, lldb::offset_t &alignment); //------------------------------------------------------------------ /// [Used by IRForTarget] Get specific information about one field /// of the laid-out struct after DoStructLayout() has been called. /// /// @param[out] decl /// The parsed Decl for the field, as generated by ClangASTSource /// on ClangExpressionDeclMap's behalf. In the case of the result /// value, this will have the name $__lldb_result even if the /// result value ends up having the name $1. This is an /// implementation detail of IRForTarget. /// /// @param[out] value /// The IR value for the field (usually a GlobalVariable). In /// the case of the result value, this will have the correct /// name ($1, for instance). This is an implementation detail /// of IRForTarget. /// /// @param[out] offset /// The offset of the field from the beginning of the struct. /// As long as the struct is aligned according to its required /// alignment, this offset will align the field correctly. /// /// @param[out] name /// The name of the field as used in materialization. /// /// @param[in] index /// The index of the field about which information is requested. /// /// @return /// True if the information could be retrieved; false otherwise. //------------------------------------------------------------------ bool GetStructElement(const clang::NamedDecl *&decl, llvm::Value *&value, lldb::offset_t &offset, ConstString &name, uint32_t index); //------------------------------------------------------------------ /// [Used by IRForTarget] Get information about a function given its /// Decl. /// /// @param[in] decl /// The parsed Decl for the Function, as generated by ClangASTSource /// on ClangExpressionDeclMap's behalf. /// /// @param[out] ptr /// The absolute address of the function in the target. /// /// @return /// True if the information could be retrieved; false otherwise. //------------------------------------------------------------------ bool GetFunctionInfo(const clang::NamedDecl *decl, uint64_t &ptr); //------------------------------------------------------------------ /// [Used by IRForTarget] Get the address of a symbol given nothing /// but its name. /// /// @param[in] target /// The target to find the symbol in. If not provided, /// then the current parsing context's Target. /// /// @param[in] process /// The process to use. For Objective-C symbols, the process's /// Objective-C language runtime may be queried if the process /// is non-NULL. /// /// @param[in] name /// The name of the symbol. /// /// @param[in] module /// The module to limit the search to. This can be NULL /// /// @return /// Valid load address for the symbol //------------------------------------------------------------------ lldb::addr_t GetSymbolAddress(Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type, Module *module = NULL); lldb::addr_t GetSymbolAddress(const ConstString &name, lldb::SymbolType symbol_type); //------------------------------------------------------------------ /// [Used by IRInterpreter] Get basic target information. /// /// @param[out] byte_order /// The byte order of the target. /// /// @param[out] address_byte_size /// The size of a pointer in bytes. /// /// @return /// True if the information could be determined; false /// otherwise. //------------------------------------------------------------------ struct TargetInfo { lldb::ByteOrder byte_order; size_t address_byte_size; TargetInfo() : byte_order(lldb::eByteOrderInvalid), address_byte_size(0) {} bool IsValid() { return (byte_order != lldb::eByteOrderInvalid && address_byte_size != 0); } }; TargetInfo GetTargetInfo(); //------------------------------------------------------------------ /// [Used by ClangASTSource] Find all entities matching a given name, /// using a NameSearchContext to make Decls for them. /// /// @param[in] context /// The NameSearchContext that can construct Decls for this name. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ void FindExternalVisibleDecls(NameSearchContext &context) override; //------------------------------------------------------------------ /// Find all entities matching a given name in a given module/namespace, /// using a NameSearchContext to make Decls for them. /// /// @param[in] context /// The NameSearchContext that can construct Decls for this name. /// /// @param[in] module /// If non-NULL, the module to query. /// /// @param[in] namespace_decl /// If valid and module is non-NULL, the parent namespace. /// /// @param[in] name /// The name as a plain C string. The NameSearchContext contains /// a DeclarationName for the name so at first the name may seem /// redundant, but ClangExpressionDeclMap operates in RTTI land so /// it can't access DeclarationName. /// /// @param[in] current_id /// The ID for the current FindExternalVisibleDecls invocation, /// for logging purposes. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ void FindExternalVisibleDecls(NameSearchContext &context, lldb::ModuleSP module, CompilerDeclContext &namespace_decl, unsigned int current_id); private: ExpressionVariableList m_found_entities; ///< All entities that were looked up for the parser. ExpressionVariableList m_struct_members; ///< All entities that need to be placed in the struct. bool m_keep_result_in_memory; ///< True if result persistent variables ///generated by this expression should stay in ///memory. Materializer::PersistentVariableDelegate *m_result_delegate; ///< If non-NULL, used to report expression results to ///ClangUserExpression. //---------------------------------------------------------------------- /// The following values should not live beyond parsing //---------------------------------------------------------------------- class ParserVars { public: ParserVars() {} Target *GetTarget() { if (m_exe_ctx.GetTargetPtr()) return m_exe_ctx.GetTargetPtr(); else if (m_sym_ctx.target_sp) m_sym_ctx.target_sp.get(); return NULL; } ExecutionContext m_exe_ctx; ///< The execution context to use when parsing. SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables ///and types. ClangPersistentVariables *m_persistent_vars = nullptr; ///< The persistent variables for the process. bool m_enable_lookups = false; ///< Set to true during parsing if we have ///found the first "$__lldb" name. TargetInfo m_target_info; ///< Basic information about the target. Materializer *m_materializer = nullptr; ///< If non-NULL, the materializer ///to use when reporting used ///variables. clang::ASTConsumer *m_code_gen = nullptr; ///< If non-NULL, a code generator ///that receives new top-level ///functions. private: DISALLOW_COPY_AND_ASSIGN(ParserVars); }; std::unique_ptr m_parser_vars; //---------------------------------------------------------------------- /// Activate parser-specific variables //---------------------------------------------------------------------- void EnableParserVars() { if (!m_parser_vars.get()) m_parser_vars = llvm::make_unique(); } //---------------------------------------------------------------------- /// Deallocate parser-specific variables //---------------------------------------------------------------------- void DisableParserVars() { m_parser_vars.reset(); } //---------------------------------------------------------------------- /// The following values contain layout information for the materialized /// struct, but are not specific to a single materialization //---------------------------------------------------------------------- struct StructVars { StructVars() : m_struct_alignment(0), m_struct_size(0), m_struct_laid_out(false), m_result_name(), m_object_pointer_type(NULL, NULL) {} lldb::offset_t m_struct_alignment; ///< The alignment of the struct in bytes. size_t m_struct_size; ///< The size of the struct in bytes. bool m_struct_laid_out; ///< True if the struct has been laid out and the ///layout is valid (that is, no new fields have been ///added since). ConstString m_result_name; ///< The name of the result variable ($1, for example) TypeFromUser m_object_pointer_type; ///< The type of the "this" variable, if ///one exists }; std::unique_ptr m_struct_vars; //---------------------------------------------------------------------- /// Activate struct variables //---------------------------------------------------------------------- void EnableStructVars() { if (!m_struct_vars.get()) m_struct_vars.reset(new struct StructVars); } //---------------------------------------------------------------------- /// Deallocate struct variables //---------------------------------------------------------------------- void DisableStructVars() { m_struct_vars.reset(); } //---------------------------------------------------------------------- /// Get this parser's ID for use in extracting parser- and JIT-specific /// data from persistent variables. //---------------------------------------------------------------------- uint64_t GetParserID() { return (uint64_t) this; } //------------------------------------------------------------------ - /// Given a target, find a data symbol that has the given name. - /// - /// @param[in] target - /// The target to use as the basis for the search. - /// - /// @param[in] name - /// The name as a plain C string. - /// - /// @param[in] module - /// The module to limit the search to. This can be NULL - /// - /// @return - /// The LLDB Symbol found, or NULL if none was found. - //------------------------------------------------------------------ - const Symbol *FindGlobalDataSymbol(Target &target, const ConstString &name, - Module *module = NULL); - - //------------------------------------------------------------------ /// Given a target, find a variable that matches the given name and /// type. /// /// @param[in] target /// The target to use as a basis for finding the variable. /// /// @param[in] module /// If non-NULL, the module to search. /// /// @param[in] name /// The name as a plain C string. /// /// @param[in] namespace_decl /// If non-NULL and module is non-NULL, the parent namespace. /// /// @param[in] type /// The required type for the variable. This function may be called /// during parsing, in which case we don't know its type; hence the /// default. /// /// @return /// The LLDB Variable found, or NULL if none was found. //------------------------------------------------------------------ lldb::VariableSP FindGlobalVariable(Target &target, lldb::ModuleSP &module, const ConstString &name, CompilerDeclContext *namespace_decl, TypeFromUser *type = NULL); //------------------------------------------------------------------ /// Get the value of a variable in a given execution context and return /// the associated Types if needed. /// /// @param[in] var /// The variable to evaluate. /// /// @param[out] var_location /// The variable location value to fill in /// /// @param[out] found_type /// The type of the found value, as it was found in the user process. /// This is only useful when the variable is being inspected on behalf /// of the parser, hence the default. /// /// @param[out] parser_type /// The type of the found value, as it was copied into the parser's /// AST context. This is only useful when the variable is being /// inspected on behalf of the parser, hence the default. /// /// @param[in] decl /// The Decl to be looked up. /// /// @return /// Return true if the value was successfully filled in. //------------------------------------------------------------------ bool GetVariableValue(lldb::VariableSP &var, lldb_private::Value &var_location, TypeFromUser *found_type = NULL, TypeFromParser *parser_type = NULL); //------------------------------------------------------------------ /// Use the NameSearchContext to generate a Decl for the given LLDB /// Variable, and put it in the Tuple list. /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] var /// The LLDB Variable that needs a Decl. /// /// @param[in] valobj /// The LLDB ValueObject for that variable. //------------------------------------------------------------------ void AddOneVariable(NameSearchContext &context, lldb::VariableSP var, lldb::ValueObjectSP valobj, unsigned int current_id); //------------------------------------------------------------------ /// Use the NameSearchContext to generate a Decl for the given /// persistent variable, and put it in the list of found entities. /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] pvar /// The persistent variable that needs a Decl. /// /// @param[in] current_id /// The ID of the current invocation of FindExternalVisibleDecls /// for logging purposes. //------------------------------------------------------------------ void AddOneVariable(NameSearchContext &context, lldb::ExpressionVariableSP &pvar_sp, unsigned int current_id); //------------------------------------------------------------------ /// Use the NameSearchContext to generate a Decl for the given LLDB /// symbol (treated as a variable), and put it in the list of found /// entities. /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] var /// The LLDB Variable that needs a Decl. //------------------------------------------------------------------ void AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol, unsigned int current_id); //------------------------------------------------------------------ /// Use the NameSearchContext to generate a Decl for the given /// function. (Functions are not placed in the Tuple list.) Can /// handle both fully typed functions and generic functions. /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] fun /// The Function that needs to be created. If non-NULL, this is /// a fully-typed function. /// /// @param[in] sym /// The Symbol that corresponds to a function that needs to be /// created with generic type (unitptr_t foo(...)). //------------------------------------------------------------------ void AddOneFunction(NameSearchContext &context, Function *fun, Symbol *sym, unsigned int current_id); //------------------------------------------------------------------ /// Use the NameSearchContext to generate a Decl for the given /// register. /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] reg_info /// The information corresponding to that register. //------------------------------------------------------------------ void AddOneRegister(NameSearchContext &context, const RegisterInfo *reg_info, unsigned int current_id); //------------------------------------------------------------------ /// Use the NameSearchContext to generate a Decl for the given /// type. (Types are not placed in the Tuple list.) /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] type /// The type that needs to be created. //------------------------------------------------------------------ void AddOneType(NameSearchContext &context, TypeFromUser &type, unsigned int current_id); //------------------------------------------------------------------ /// Generate a Decl for "*this" and add a member function declaration /// to it for the expression, then report it. /// /// @param[in] context /// The NameSearchContext to use when constructing the Decl. /// /// @param[in] type /// The type for *this. //------------------------------------------------------------------ void AddThisType(NameSearchContext &context, TypeFromUser &type, unsigned int current_id); ClangASTContext *GetClangASTContext(); }; } // namespace lldb_private #endif // liblldb_ClangExpressionDeclMap_h_ Index: vendor/lldb/dist/source/Symbol/SymbolContext.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/SymbolContext.cpp (revision 318423) +++ vendor/lldb/dist/source/Symbol/SymbolContext.cpp (revision 318424) @@ -1,1249 +1,1406 @@ //===-- SymbolContext.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Symbol/SymbolContext.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" #include "lldb/Host/StringConvert.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Variable.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Log.h" using namespace lldb; using namespace lldb_private; SymbolContext::SymbolContext() : target_sp(), module_sp(), comp_unit(nullptr), function(nullptr), block(nullptr), line_entry(), symbol(nullptr), variable(nullptr) {} SymbolContext::SymbolContext(const ModuleSP &m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) : target_sp(), module_sp(m), comp_unit(cu), function(f), block(b), line_entry(), symbol(s), variable(nullptr) { if (le) line_entry = *le; } SymbolContext::SymbolContext(const TargetSP &t, const ModuleSP &m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) : target_sp(t), module_sp(m), comp_unit(cu), function(f), block(b), line_entry(), symbol(s), variable(nullptr) { if (le) line_entry = *le; } SymbolContext::SymbolContext(const SymbolContext &rhs) : target_sp(rhs.target_sp), module_sp(rhs.module_sp), comp_unit(rhs.comp_unit), function(rhs.function), block(rhs.block), line_entry(rhs.line_entry), symbol(rhs.symbol), variable(rhs.variable) {} SymbolContext::SymbolContext(SymbolContextScope *sc_scope) : target_sp(), module_sp(), comp_unit(nullptr), function(nullptr), block(nullptr), line_entry(), symbol(nullptr), variable(nullptr) { sc_scope->CalculateSymbolContext(this); } SymbolContext::~SymbolContext() {} const SymbolContext &SymbolContext::operator=(const SymbolContext &rhs) { if (this != &rhs) { target_sp = rhs.target_sp; module_sp = rhs.module_sp; comp_unit = rhs.comp_unit; function = rhs.function; block = rhs.block; line_entry = rhs.line_entry; symbol = rhs.symbol; variable = rhs.variable; } return *this; } void SymbolContext::Clear(bool clear_target) { if (clear_target) target_sp.reset(); module_sp.reset(); comp_unit = nullptr; function = nullptr; block = nullptr; line_entry.Clear(); symbol = nullptr; variable = nullptr; } bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope, const Address &addr, bool show_fullpaths, bool show_module, bool show_inlined_frames, bool show_function_arguments, bool show_function_name) const { bool dumped_something = false; if (show_module && module_sp) { if (show_fullpaths) *s << module_sp->GetFileSpec(); else *s << module_sp->GetFileSpec().GetFilename(); s->PutChar('`'); dumped_something = true; } if (function != nullptr) { SymbolContext inline_parent_sc; Address inline_parent_addr; if (show_function_name == false) { s->Printf("<"); dumped_something = true; } else { ConstString name; if (show_function_arguments == false) name = function->GetNameNoArguments(); if (!name) name = function->GetName(); if (name) name.Dump(s); } if (addr.IsValid()) { const addr_t function_offset = addr.GetOffset() - function->GetAddressRange().GetBaseAddress().GetOffset(); if (show_function_name == false) { // Print +offset even if offset is 0 dumped_something = true; s->Printf("+%" PRIu64 ">", function_offset); } else if (function_offset) { dumped_something = true; s->Printf(" + %" PRIu64, function_offset); } } if (GetParentOfInlinedScope(addr, inline_parent_sc, inline_parent_addr)) { dumped_something = true; Block *inlined_block = block->GetContainingInlinedBlock(); const InlineFunctionInfo *inlined_block_info = inlined_block->GetInlinedFunctionInfo(); s->Printf( " [inlined] %s", inlined_block_info->GetName(function->GetLanguage()).GetCString()); lldb_private::AddressRange block_range; if (inlined_block->GetRangeContainingAddress(addr, block_range)) { const addr_t inlined_function_offset = addr.GetOffset() - block_range.GetBaseAddress().GetOffset(); if (inlined_function_offset) { s->Printf(" + %" PRIu64, inlined_function_offset); } } const Declaration &call_site = inlined_block_info->GetCallSite(); if (call_site.IsValid()) { s->PutCString(" at "); call_site.DumpStopContext(s, show_fullpaths); } if (show_inlined_frames) { s->EOL(); s->Indent(); const bool show_function_name = true; return inline_parent_sc.DumpStopContext( s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames, show_function_arguments, show_function_name); } } else { if (line_entry.IsValid()) { dumped_something = true; s->PutCString(" at "); if (line_entry.DumpStopContext(s, show_fullpaths)) dumped_something = true; } } } else if (symbol != nullptr) { if (show_function_name == false) { s->Printf("<"); dumped_something = true; } else if (symbol->GetName()) { dumped_something = true; if (symbol->GetType() == eSymbolTypeTrampoline) s->PutCString("symbol stub for: "); symbol->GetName().Dump(s); } if (addr.IsValid() && symbol->ValueIsAddress()) { const addr_t symbol_offset = addr.GetOffset() - symbol->GetAddressRef().GetOffset(); if (show_function_name == false) { // Print +offset even if offset is 0 dumped_something = true; s->Printf("+%" PRIu64 ">", symbol_offset); } else if (symbol_offset) { dumped_something = true; s->Printf(" + %" PRIu64, symbol_offset); } } } else if (addr.IsValid()) { addr.Dump(s, exe_scope, Address::DumpStyleModuleWithFileAddress); dumped_something = true; } return dumped_something; } void SymbolContext::GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) const { if (module_sp) { s->Indent(" Module: file = \""); module_sp->GetFileSpec().Dump(s); *s << '"'; if (module_sp->GetArchitecture().IsValid()) s->Printf(", arch = \"%s\"", module_sp->GetArchitecture().GetArchitectureName()); s->EOL(); } if (comp_unit != nullptr) { s->Indent("CompileUnit: "); comp_unit->GetDescription(s, level); s->EOL(); } if (function != nullptr) { s->Indent(" Function: "); function->GetDescription(s, level, target); s->EOL(); Type *func_type = function->GetType(); if (func_type) { s->Indent(" FuncType: "); func_type->GetDescription(s, level, false); s->EOL(); } } if (block != nullptr) { std::vector blocks; blocks.push_back(block); Block *parent_block = block->GetParent(); while (parent_block) { blocks.push_back(parent_block); parent_block = parent_block->GetParent(); } std::vector::reverse_iterator pos; std::vector::reverse_iterator begin = blocks.rbegin(); std::vector::reverse_iterator end = blocks.rend(); for (pos = begin; pos != end; ++pos) { if (pos == begin) s->Indent(" Blocks: "); else s->Indent(" "); (*pos)->GetDescription(s, function, level, target); s->EOL(); } } if (line_entry.IsValid()) { s->Indent(" LineEntry: "); line_entry.GetDescription(s, level, comp_unit, target, false); s->EOL(); } if (symbol != nullptr) { s->Indent(" Symbol: "); symbol->GetDescription(s, level, target); s->EOL(); } if (variable != nullptr) { s->Indent(" Variable: "); s->Printf("id = {0x%8.8" PRIx64 "}, ", variable->GetID()); switch (variable->GetScope()) { case eValueTypeVariableGlobal: s->PutCString("kind = global, "); break; case eValueTypeVariableStatic: s->PutCString("kind = static, "); break; case eValueTypeVariableArgument: s->PutCString("kind = argument, "); break; case eValueTypeVariableLocal: s->PutCString("kind = local, "); break; case eValueTypeVariableThreadLocal: s->PutCString("kind = thread local, "); break; default: break; } s->Printf("name = \"%s\"\n", variable->GetName().GetCString()); } } uint32_t SymbolContext::GetResolvedMask() const { uint32_t resolved_mask = 0; if (target_sp) resolved_mask |= eSymbolContextTarget; if (module_sp) resolved_mask |= eSymbolContextModule; if (comp_unit) resolved_mask |= eSymbolContextCompUnit; if (function) resolved_mask |= eSymbolContextFunction; if (block) resolved_mask |= eSymbolContextBlock; if (line_entry.IsValid()) resolved_mask |= eSymbolContextLineEntry; if (symbol) resolved_mask |= eSymbolContextSymbol; if (variable) resolved_mask |= eSymbolContextVariable; return resolved_mask; } void SymbolContext::Dump(Stream *s, Target *target) const { *s << this << ": "; s->Indent(); s->PutCString("SymbolContext"); s->IndentMore(); s->EOL(); s->IndentMore(); s->Indent(); *s << "Module = " << module_sp.get() << ' '; if (module_sp) module_sp->GetFileSpec().Dump(s); s->EOL(); s->Indent(); *s << "CompileUnit = " << comp_unit; if (comp_unit != nullptr) *s << " {0x" << comp_unit->GetID() << "} " << *(static_cast(comp_unit)); s->EOL(); s->Indent(); *s << "Function = " << function; if (function != nullptr) { *s << " {0x" << function->GetID() << "} " << function->GetType()->GetName() << ", address-range = "; function->GetAddressRange().Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); s->EOL(); s->Indent(); Type *func_type = function->GetType(); if (func_type) { *s << " Type = "; func_type->Dump(s, false); } } s->EOL(); s->Indent(); *s << "Block = " << block; if (block != nullptr) *s << " {0x" << block->GetID() << '}'; // Dump the block and pass it a negative depth to we print all the parent // blocks // if (block != NULL) // block->Dump(s, function->GetFileAddress(), INT_MIN); s->EOL(); s->Indent(); *s << "LineEntry = "; line_entry.Dump(s, target, true, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, true); s->EOL(); s->Indent(); *s << "Symbol = " << symbol; if (symbol != nullptr && symbol->GetMangled()) *s << ' ' << symbol->GetName().AsCString(); s->EOL(); *s << "Variable = " << variable; if (variable != nullptr) { *s << " {0x" << variable->GetID() << "} " << variable->GetType()->GetName(); s->EOL(); } s->IndentLess(); s->IndentLess(); } bool lldb_private::operator==(const SymbolContext &lhs, const SymbolContext &rhs) { return lhs.function == rhs.function && lhs.symbol == rhs.symbol && lhs.module_sp.get() == rhs.module_sp.get() && lhs.comp_unit == rhs.comp_unit && lhs.target_sp.get() == rhs.target_sp.get() && LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0 && lhs.variable == rhs.variable; } bool lldb_private::operator!=(const SymbolContext &lhs, const SymbolContext &rhs) { return lhs.function != rhs.function || lhs.symbol != rhs.symbol || lhs.module_sp.get() != rhs.module_sp.get() || lhs.comp_unit != rhs.comp_unit || lhs.target_sp.get() != rhs.target_sp.get() || LineEntry::Compare(lhs.line_entry, rhs.line_entry) != 0 || lhs.variable != rhs.variable; } bool SymbolContext::GetAddressRange(uint32_t scope, uint32_t range_idx, bool use_inline_block_range, AddressRange &range) const { if ((scope & eSymbolContextLineEntry) && line_entry.IsValid()) { range = line_entry.range; return true; } if ((scope & eSymbolContextBlock) && (block != nullptr)) { if (use_inline_block_range) { Block *inline_block = block->GetContainingInlinedBlock(); if (inline_block) return inline_block->GetRangeAtIndex(range_idx, range); } else { return block->GetRangeAtIndex(range_idx, range); } } if ((scope & eSymbolContextFunction) && (function != nullptr)) { if (range_idx == 0) { range = function->GetAddressRange(); return true; } } if ((scope & eSymbolContextSymbol) && (symbol != nullptr)) { if (range_idx == 0) { if (symbol->ValueIsAddress()) { range.GetBaseAddress() = symbol->GetAddressRef(); range.SetByteSize(symbol->GetByteSize()); return true; } } } range.Clear(); return false; } LanguageType SymbolContext::GetLanguage() const { LanguageType lang; if (function && (lang = function->GetLanguage()) != eLanguageTypeUnknown) { return lang; } else if (variable && (lang = variable->GetLanguage()) != eLanguageTypeUnknown) { return lang; } else if (symbol && (lang = symbol->GetLanguage()) != eLanguageTypeUnknown) { return lang; } else if (comp_unit && (lang = comp_unit->GetLanguage()) != eLanguageTypeUnknown) { return lang; } else if (symbol) { // If all else fails, try to guess the language from the name. return symbol->GetMangled().GuessLanguage(); } return eLanguageTypeUnknown; } bool SymbolContext::GetParentOfInlinedScope(const Address &curr_frame_pc, SymbolContext &next_frame_sc, Address &next_frame_pc) const { next_frame_sc.Clear(false); next_frame_pc.Clear(); if (block) { // const addr_t curr_frame_file_addr = curr_frame_pc.GetFileAddress(); // In order to get the parent of an inlined function we first need to // see if we are in an inlined block as "this->block" could be an // inlined block, or a parent of "block" could be. So lets check if // this block or one of this blocks parents is an inlined function. Block *curr_inlined_block = block->GetContainingInlinedBlock(); if (curr_inlined_block) { // "this->block" is contained in an inline function block, so to // get the scope above the inlined block, we get the parent of the // inlined block itself Block *next_frame_block = curr_inlined_block->GetParent(); // Now calculate the symbol context of the containing block next_frame_block->CalculateSymbolContext(&next_frame_sc); // If we get here we weren't able to find the return line entry using the // nesting of the blocks and // the line table. So just use the call site info from our inlined block. AddressRange range; if (curr_inlined_block->GetRangeContainingAddress(curr_frame_pc, range)) { // To see there this new frame block it, we need to look at the // call site information from const InlineFunctionInfo *curr_inlined_block_inlined_info = curr_inlined_block->GetInlinedFunctionInfo(); next_frame_pc = range.GetBaseAddress(); next_frame_sc.line_entry.range.GetBaseAddress() = next_frame_pc; next_frame_sc.line_entry.file = curr_inlined_block_inlined_info->GetCallSite().GetFile(); next_frame_sc.line_entry.original_file = curr_inlined_block_inlined_info->GetCallSite().GetFile(); next_frame_sc.line_entry.line = curr_inlined_block_inlined_info->GetCallSite().GetLine(); next_frame_sc.line_entry.column = curr_inlined_block_inlined_info->GetCallSite().GetColumn(); return true; } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS)); if (log) { log->Printf( "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64, curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); } #ifdef LLDB_CONFIGURATION_DEBUG else { ObjectFile *objfile = NULL; if (module_sp) { SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); if (symbol_vendor) { SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); if (symbol_file) objfile = symbol_file->GetObjectFile(); } } if (objfile) { Host::SystemLog( Host::eSystemLogWarning, "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 " in %s\n", curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress(), objfile->GetFileSpec().GetPath().c_str()); } else { Host::SystemLog( Host::eSystemLogWarning, "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 "\n", curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); } } #endif } } } return false; } Block *SymbolContext::GetFunctionBlock() { if (function) { if (block) { // If this symbol context has a block, check to see if this block // is itself, or is contained within a block with inlined function // information. If so, then the inlined block is the block that // defines the function. Block *inlined_block = block->GetContainingInlinedBlock(); if (inlined_block) return inlined_block; // The block in this symbol context is not inside an inlined // block, so the block that defines the function is the function's // top level block, which is returned below. } // There is no block information in this symbol context, so we must // assume that the block that is desired is the top level block of // the function itself. return &function->GetBlock(true); } return nullptr; } bool SymbolContext::GetFunctionMethodInfo(lldb::LanguageType &language, bool &is_instance_method, ConstString &language_object_name) { Block *function_block = GetFunctionBlock(); if (function_block) { CompilerDeclContext decl_ctx = function_block->GetDeclContext(); if (decl_ctx) return decl_ctx.IsClassMethod(&language, &is_instance_method, &language_object_name); } return false; } void SymbolContext::SortTypeList(TypeMap &type_map, TypeList &type_list) const { Block *curr_block = block; bool isInlinedblock = false; if (curr_block != nullptr && curr_block->GetContainingInlinedBlock() != nullptr) isInlinedblock = true; //---------------------------------------------------------------------- // Find all types that match the current block if we have one and put // them first in the list. Keep iterating up through all blocks. //---------------------------------------------------------------------- while (curr_block != nullptr && !isInlinedblock) { type_map.ForEach( [curr_block, &type_list](const lldb::TypeSP &type_sp) -> bool { SymbolContextScope *scs = type_sp->GetSymbolContextScope(); if (scs && curr_block == scs->CalculateSymbolContextBlock()) type_list.Insert(type_sp); return true; // Keep iterating }); // Remove any entries that are now in "type_list" from "type_map" // since we can't remove from type_map while iterating type_list.ForEach([&type_map](const lldb::TypeSP &type_sp) -> bool { type_map.Remove(type_sp); return true; // Keep iterating }); curr_block = curr_block->GetParent(); } //---------------------------------------------------------------------- // Find all types that match the current function, if we have onem, and // put them next in the list. //---------------------------------------------------------------------- if (function != nullptr && !type_map.Empty()) { const size_t old_type_list_size = type_list.GetSize(); type_map.ForEach([this, &type_list](const lldb::TypeSP &type_sp) -> bool { SymbolContextScope *scs = type_sp->GetSymbolContextScope(); if (scs && function == scs->CalculateSymbolContextFunction()) type_list.Insert(type_sp); return true; // Keep iterating }); // Remove any entries that are now in "type_list" from "type_map" // since we can't remove from type_map while iterating const size_t new_type_list_size = type_list.GetSize(); if (new_type_list_size > old_type_list_size) { for (size_t i = old_type_list_size; i < new_type_list_size; ++i) type_map.Remove(type_list.GetTypeAtIndex(i)); } } //---------------------------------------------------------------------- // Find all types that match the current compile unit, if we have one, // and put them next in the list. //---------------------------------------------------------------------- if (comp_unit != nullptr && !type_map.Empty()) { const size_t old_type_list_size = type_list.GetSize(); type_map.ForEach([this, &type_list](const lldb::TypeSP &type_sp) -> bool { SymbolContextScope *scs = type_sp->GetSymbolContextScope(); if (scs && comp_unit == scs->CalculateSymbolContextCompileUnit()) type_list.Insert(type_sp); return true; // Keep iterating }); // Remove any entries that are now in "type_list" from "type_map" // since we can't remove from type_map while iterating const size_t new_type_list_size = type_list.GetSize(); if (new_type_list_size > old_type_list_size) { for (size_t i = old_type_list_size; i < new_type_list_size; ++i) type_map.Remove(type_list.GetTypeAtIndex(i)); } } //---------------------------------------------------------------------- // Find all types that match the current module, if we have one, and put // them next in the list. //---------------------------------------------------------------------- if (module_sp && !type_map.Empty()) { const size_t old_type_list_size = type_list.GetSize(); type_map.ForEach([this, &type_list](const lldb::TypeSP &type_sp) -> bool { SymbolContextScope *scs = type_sp->GetSymbolContextScope(); if (scs && module_sp == scs->CalculateSymbolContextModule()) type_list.Insert(type_sp); return true; // Keep iterating }); // Remove any entries that are now in "type_list" from "type_map" // since we can't remove from type_map while iterating const size_t new_type_list_size = type_list.GetSize(); if (new_type_list_size > old_type_list_size) { for (size_t i = old_type_list_size; i < new_type_list_size; ++i) type_map.Remove(type_list.GetTypeAtIndex(i)); } } //---------------------------------------------------------------------- // Any types that are left get copied into the list an any order. //---------------------------------------------------------------------- if (!type_map.Empty()) { type_map.ForEach([&type_list](const lldb::TypeSP &type_sp) -> bool { type_list.Insert(type_sp); return true; // Keep iterating }); } } ConstString SymbolContext::GetFunctionName(Mangled::NamePreference preference) const { if (function) { if (block) { Block *inlined_block = block->GetContainingInlinedBlock(); if (inlined_block) { const InlineFunctionInfo *inline_info = inlined_block->GetInlinedFunctionInfo(); if (inline_info) return inline_info->GetName(function->GetLanguage()); } } return function->GetMangled().GetName(function->GetLanguage(), preference); } else if (symbol && symbol->ValueIsAddress()) { return symbol->GetMangled().GetName(symbol->GetLanguage(), preference); } else { // No function, return an empty string. return ConstString(); } } LineEntry SymbolContext::GetFunctionStartLineEntry() const { LineEntry line_entry; Address start_addr; if (block) { Block *inlined_block = block->GetContainingInlinedBlock(); if (inlined_block) { if (inlined_block->GetStartAddress(start_addr)) { if (start_addr.CalculateSymbolContextLineEntry(line_entry)) return line_entry; } return LineEntry(); } } if (function) { if (function->GetAddressRange() .GetBaseAddress() .CalculateSymbolContextLineEntry(line_entry)) return line_entry; } return LineEntry(); } bool SymbolContext::GetAddressRangeFromHereToEndLine(uint32_t end_line, AddressRange &range, Status &error) { if (!line_entry.IsValid()) { error.SetErrorString("Symbol context has no line table."); return false; } range = line_entry.range; if (line_entry.line > end_line) { error.SetErrorStringWithFormat( "end line option %d must be after the current line: %d", end_line, line_entry.line); return false; } uint32_t line_index = 0; bool found = false; while (1) { LineEntry this_line; line_index = comp_unit->FindLineEntry(line_index, line_entry.line, nullptr, false, &this_line); if (line_index == UINT32_MAX) break; if (LineEntry::Compare(this_line, line_entry) == 0) { found = true; break; } } LineEntry end_entry; if (!found) { // Can't find the index of the SymbolContext's line entry in the // SymbolContext's CompUnit. error.SetErrorString( "Can't find the current line entry in the CompUnit - can't process " "the end-line option"); return false; } line_index = comp_unit->FindLineEntry(line_index, end_line, nullptr, false, &end_entry); if (line_index == UINT32_MAX) { error.SetErrorStringWithFormat( "could not find a line table entry corresponding " "to end line number %d", end_line); return false; } Block *func_block = GetFunctionBlock(); if (func_block && func_block->GetRangeIndexContainingAddress( end_entry.range.GetBaseAddress()) == UINT32_MAX) { error.SetErrorStringWithFormat( "end line number %d is not contained within the current function.", end_line); return false; } lldb::addr_t range_size = end_entry.range.GetBaseAddress().GetFileAddress() - range.GetBaseAddress().GetFileAddress(); range.SetByteSize(range_size); return true; } +const Symbol * +SymbolContext::FindBestGlobalDataSymbol(const ConstString &name, Status &error) { + error.Clear(); + + if (!target_sp) { + return nullptr; + } + + Target &target = *target_sp; + Module *module = module_sp.get(); + + auto ProcessMatches = [this, &name, &target, module] + (SymbolContextList &sc_list, Status &error) -> const Symbol* { + llvm::SmallVector external_symbols; + llvm::SmallVector internal_symbols; + const uint32_t matches = sc_list.GetSize(); + for (uint32_t i = 0; i < matches; ++i) { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(i, sym_ctx); + if (sym_ctx.symbol) { + const Symbol *symbol = sym_ctx.symbol; + const Address sym_address = symbol->GetAddress(); + + if (sym_address.IsValid()) { + switch (symbol->GetType()) { + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeAbsolute: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + if (symbol->GetDemangledNameIsSynthesized()) { + // If the demangled name was synthesized, then don't use it + // for expressions. Only let the symbol match if the mangled + // named matches for these symbols. + if (symbol->GetMangled().GetMangledName() != name) + break; + } + if (symbol->IsExternal()) { + external_symbols.push_back(symbol); + } else { + internal_symbols.push_back(symbol); + } + break; + case eSymbolTypeReExported: { + ConstString reexport_name = symbol->GetReExportedSymbolName(); + if (reexport_name) { + ModuleSP reexport_module_sp; + ModuleSpec reexport_module_spec; + reexport_module_spec.GetPlatformFileSpec() = + symbol->GetReExportedSymbolSharedLibrary(); + if (reexport_module_spec.GetPlatformFileSpec()) { + reexport_module_sp = + target.GetImages().FindFirstModule(reexport_module_spec); + if (!reexport_module_sp) { + reexport_module_spec.GetPlatformFileSpec() + .GetDirectory() + .Clear(); + reexport_module_sp = + target.GetImages().FindFirstModule(reexport_module_spec); + } + } + // Don't allow us to try and resolve a re-exported symbol if it is + // the same as the current symbol + if (name == symbol->GetReExportedSymbolName() && + module == reexport_module_sp.get()) + return nullptr; + + return FindBestGlobalDataSymbol( + symbol->GetReExportedSymbolName(), error); + } + } break; + + case eSymbolTypeCode: // We already lookup functions elsewhere + case eSymbolTypeVariable: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeTrampoline: + case eSymbolTypeInvalid: + case eSymbolTypeException: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeUndefined: + case eSymbolTypeResolver: + break; + } + } + } + } + + if (external_symbols.size() > 1) { + StreamString ss; + ss.Printf("Multiple external symbols found for '%s'\n", name.AsCString()); + for (const Symbol *symbol : external_symbols) { + symbol->GetDescription(&ss, eDescriptionLevelFull, &target); + } + ss.PutChar('\n'); + error.SetErrorString(ss.GetData()); + return nullptr; + } else if (external_symbols.size()) { + return external_symbols[0]; + } else if (internal_symbols.size() > 1) { + StreamString ss; + ss.Printf("Multiple internal symbols found for '%s'\n", name.AsCString()); + for (const Symbol *symbol : internal_symbols) { + symbol->GetDescription(&ss, eDescriptionLevelVerbose, &target); + ss.PutChar('\n'); + } + error.SetErrorString(ss.GetData()); + return nullptr; + } else if (internal_symbols.size()) { + return internal_symbols[0]; + } else { + return nullptr; + } + }; + + if (module) { + SymbolContextList sc_list; + module->FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); + const Symbol *const module_symbol = ProcessMatches(sc_list, error); + + if (!error.Success()) { + return nullptr; + } else if (module_symbol) { + return module_symbol; + } + } + + { + SymbolContextList sc_list; + target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, + sc_list); + const Symbol *const target_symbol = ProcessMatches(sc_list, error); + + if (!error.Success()) { + return nullptr; + } else if (target_symbol) { + return target_symbol; + } + } + + return nullptr; // no error; we just didn't find anything +} + + //---------------------------------------------------------------------- // // SymbolContextSpecifier // //---------------------------------------------------------------------- SymbolContextSpecifier::SymbolContextSpecifier(const TargetSP &target_sp) : m_target_sp(target_sp), m_module_spec(), m_module_sp(), m_file_spec_ap(), m_start_line(0), m_end_line(0), m_function_spec(), m_class_name(), m_address_range_ap(), m_type(eNothingSpecified) {} SymbolContextSpecifier::~SymbolContextSpecifier() {} bool SymbolContextSpecifier::AddLineSpecification(uint32_t line_no, SpecificationType type) { bool return_value = true; switch (type) { case eNothingSpecified: Clear(); break; case eLineStartSpecified: m_start_line = line_no; m_type |= eLineStartSpecified; break; case eLineEndSpecified: m_end_line = line_no; m_type |= eLineEndSpecified; break; default: return_value = false; break; } return return_value; } bool SymbolContextSpecifier::AddSpecification(const char *spec_string, SpecificationType type) { bool return_value = true; switch (type) { case eNothingSpecified: Clear(); break; case eModuleSpecified: { // See if we can find the Module, if so stick it in the SymbolContext. FileSpec module_file_spec(spec_string, false); ModuleSpec module_spec(module_file_spec); lldb::ModuleSP module_sp( m_target_sp->GetImages().FindFirstModule(module_spec)); m_type |= eModuleSpecified; if (module_sp) m_module_sp = module_sp; else m_module_spec.assign(spec_string); } break; case eFileSpecified: // CompUnits can't necessarily be resolved here, since an inlined function // might show up in // a number of CompUnits. Instead we just convert to a FileSpec and store // it away. m_file_spec_ap.reset(new FileSpec(spec_string, false)); m_type |= eFileSpecified; break; case eLineStartSpecified: m_start_line = StringConvert::ToSInt32(spec_string, 0, 0, &return_value); if (return_value) m_type |= eLineStartSpecified; break; case eLineEndSpecified: m_end_line = StringConvert::ToSInt32(spec_string, 0, 0, &return_value); if (return_value) m_type |= eLineEndSpecified; break; case eFunctionSpecified: m_function_spec.assign(spec_string); m_type |= eFunctionSpecified; break; case eClassOrNamespaceSpecified: Clear(); m_class_name.assign(spec_string); m_type = eClassOrNamespaceSpecified; break; case eAddressRangeSpecified: // Not specified yet... break; } return return_value; } void SymbolContextSpecifier::Clear() { m_module_spec.clear(); m_file_spec_ap.reset(); m_function_spec.clear(); m_class_name.clear(); m_start_line = 0; m_end_line = 0; m_address_range_ap.reset(); m_type = eNothingSpecified; } bool SymbolContextSpecifier::SymbolContextMatches(SymbolContext &sc) { if (m_type == eNothingSpecified) return true; if (m_target_sp.get() != sc.target_sp.get()) return false; if (m_type & eModuleSpecified) { if (sc.module_sp) { if (m_module_sp.get() != nullptr) { if (m_module_sp.get() != sc.module_sp.get()) return false; } else { FileSpec module_file_spec(m_module_spec, false); if (!FileSpec::Equal(module_file_spec, sc.module_sp->GetFileSpec(), false)) return false; } } } if (m_type & eFileSpecified) { if (m_file_spec_ap.get()) { // If we don't have a block or a comp_unit, then we aren't going to match // a source file. if (sc.block == nullptr && sc.comp_unit == nullptr) return false; // Check if the block is present, and if so is it inlined: bool was_inlined = false; if (sc.block != nullptr) { const InlineFunctionInfo *inline_info = sc.block->GetInlinedFunctionInfo(); if (inline_info != nullptr) { was_inlined = true; if (!FileSpec::Equal(inline_info->GetDeclaration().GetFile(), *(m_file_spec_ap.get()), false)) return false; } } // Next check the comp unit, but only if the SymbolContext was not // inlined. if (!was_inlined && sc.comp_unit != nullptr) { if (!FileSpec::Equal(*(sc.comp_unit), *(m_file_spec_ap.get()), false)) return false; } } } if (m_type & eLineStartSpecified || m_type & eLineEndSpecified) { if (sc.line_entry.line < m_start_line || sc.line_entry.line > m_end_line) return false; } if (m_type & eFunctionSpecified) { // First check the current block, and if it is inlined, get the inlined // function name: bool was_inlined = false; ConstString func_name(m_function_spec.c_str()); if (sc.block != nullptr) { const InlineFunctionInfo *inline_info = sc.block->GetInlinedFunctionInfo(); if (inline_info != nullptr) { was_inlined = true; const Mangled &name = inline_info->GetMangled(); if (!name.NameMatches(func_name, sc.function->GetLanguage())) return false; } } // If it wasn't inlined, check the name in the function or symbol: if (!was_inlined) { if (sc.function != nullptr) { if (!sc.function->GetMangled().NameMatches(func_name, sc.function->GetLanguage())) return false; } else if (sc.symbol != nullptr) { if (!sc.symbol->GetMangled().NameMatches(func_name, sc.symbol->GetLanguage())) return false; } } } return true; } bool SymbolContextSpecifier::AddressMatches(lldb::addr_t addr) { if (m_type & eAddressRangeSpecified) { } else { Address match_address(addr, nullptr); SymbolContext sc; m_target_sp->GetImages().ResolveSymbolContextForAddress( match_address, eSymbolContextEverything, sc); return SymbolContextMatches(sc); } return true; } void SymbolContextSpecifier::GetDescription( Stream *s, lldb::DescriptionLevel level) const { char path_str[PATH_MAX + 1]; if (m_type == eNothingSpecified) { s->Printf("Nothing specified.\n"); } if (m_type == eModuleSpecified) { s->Indent(); if (m_module_sp) { m_module_sp->GetFileSpec().GetPath(path_str, PATH_MAX); s->Printf("Module: %s\n", path_str); } else s->Printf("Module: %s\n", m_module_spec.c_str()); } if (m_type == eFileSpecified && m_file_spec_ap.get() != nullptr) { m_file_spec_ap->GetPath(path_str, PATH_MAX); s->Indent(); s->Printf("File: %s", path_str); if (m_type == eLineStartSpecified) { s->Printf(" from line %" PRIu64 "", (uint64_t)m_start_line); if (m_type == eLineEndSpecified) s->Printf("to line %" PRIu64 "", (uint64_t)m_end_line); else s->Printf("to end"); } else if (m_type == eLineEndSpecified) { s->Printf(" from start to line %" PRIu64 "", (uint64_t)m_end_line); } s->Printf(".\n"); } if (m_type == eLineStartSpecified) { s->Indent(); s->Printf("From line %" PRIu64 "", (uint64_t)m_start_line); if (m_type == eLineEndSpecified) s->Printf("to line %" PRIu64 "", (uint64_t)m_end_line); else s->Printf("to end"); s->Printf(".\n"); } else if (m_type == eLineEndSpecified) { s->Printf("From start to line %" PRIu64 ".\n", (uint64_t)m_end_line); } if (m_type == eFunctionSpecified) { s->Indent(); s->Printf("Function: %s.\n", m_function_spec.c_str()); } if (m_type == eClassOrNamespaceSpecified) { s->Indent(); s->Printf("Class name: %s.\n", m_class_name.c_str()); } if (m_type == eAddressRangeSpecified && m_address_range_ap.get() != nullptr) { s->Indent(); s->PutCString("Address range: "); m_address_range_ap->Dump(s, m_target_sp.get(), Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); s->PutCString("\n"); } } //---------------------------------------------------------------------- // // SymbolContextList // //---------------------------------------------------------------------- SymbolContextList::SymbolContextList() : m_symbol_contexts() {} SymbolContextList::~SymbolContextList() {} void SymbolContextList::Append(const SymbolContext &sc) { m_symbol_contexts.push_back(sc); } void SymbolContextList::Append(const SymbolContextList &sc_list) { collection::const_iterator pos, end = sc_list.m_symbol_contexts.end(); for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos) m_symbol_contexts.push_back(*pos); } uint32_t SymbolContextList::AppendIfUnique(const SymbolContextList &sc_list, bool merge_symbol_into_function) { uint32_t unique_sc_add_count = 0; collection::const_iterator pos, end = sc_list.m_symbol_contexts.end(); for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos) { if (AppendIfUnique(*pos, merge_symbol_into_function)) ++unique_sc_add_count; } return unique_sc_add_count; } bool SymbolContextList::AppendIfUnique(const SymbolContext &sc, bool merge_symbol_into_function) { collection::iterator pos, end = m_symbol_contexts.end(); for (pos = m_symbol_contexts.begin(); pos != end; ++pos) { if (*pos == sc) return false; } if (merge_symbol_into_function && sc.symbol != nullptr && sc.comp_unit == nullptr && sc.function == nullptr && sc.block == nullptr && sc.line_entry.IsValid() == false) { if (sc.symbol->ValueIsAddress()) { for (pos = m_symbol_contexts.begin(); pos != end; ++pos) { // Don't merge symbols into inlined function symbol contexts if (pos->block && pos->block->GetContainingInlinedBlock()) continue; if (pos->function) { if (pos->function->GetAddressRange().GetBaseAddress() == sc.symbol->GetAddressRef()) { // Do we already have a function with this symbol? if (pos->symbol == sc.symbol) return false; if (pos->symbol == nullptr) { pos->symbol = sc.symbol; return false; } } } } } } m_symbol_contexts.push_back(sc); return true; } bool SymbolContextList::MergeSymbolContextIntoFunctionContext( const SymbolContext &symbol_sc, uint32_t start_idx, uint32_t stop_idx) { if (symbol_sc.symbol != nullptr && symbol_sc.comp_unit == nullptr && symbol_sc.function == nullptr && symbol_sc.block == nullptr && symbol_sc.line_entry.IsValid() == false) { if (symbol_sc.symbol->ValueIsAddress()) { const size_t end = std::min(m_symbol_contexts.size(), stop_idx); for (size_t i = start_idx; i < end; ++i) { const SymbolContext &function_sc = m_symbol_contexts[i]; // Don't merge symbols into inlined function symbol contexts if (function_sc.block && function_sc.block->GetContainingInlinedBlock()) continue; if (function_sc.function) { if (function_sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddressRef()) { // Do we already have a function with this symbol? if (function_sc.symbol == symbol_sc.symbol) return true; // Already have a symbol context with this symbol, // return true if (function_sc.symbol == nullptr) { // We successfully merged this symbol into an existing symbol // context m_symbol_contexts[i].symbol = symbol_sc.symbol; return true; } } } } } } return false; } void SymbolContextList::Clear() { m_symbol_contexts.clear(); } void SymbolContextList::Dump(Stream *s, Target *target) const { *s << this << ": "; s->Indent(); s->PutCString("SymbolContextList"); s->EOL(); s->IndentMore(); collection::const_iterator pos, end = m_symbol_contexts.end(); for (pos = m_symbol_contexts.begin(); pos != end; ++pos) { // pos->Dump(s, target); pos->GetDescription(s, eDescriptionLevelVerbose, target); } s->IndentLess(); } bool SymbolContextList::GetContextAtIndex(size_t idx, SymbolContext &sc) const { if (idx < m_symbol_contexts.size()) { sc = m_symbol_contexts[idx]; return true; } return false; } bool SymbolContextList::GetLastContext(SymbolContext &sc) const { if (!m_symbol_contexts.empty()) { sc = m_symbol_contexts.back(); return true; } return false; } bool SymbolContextList::RemoveContextAtIndex(size_t idx) { if (idx < m_symbol_contexts.size()) { m_symbol_contexts.erase(m_symbol_contexts.begin() + idx); return true; } return false; } uint32_t SymbolContextList::GetSize() const { return m_symbol_contexts.size(); } uint32_t SymbolContextList::NumLineEntriesWithLine(uint32_t line) const { uint32_t match_count = 0; const size_t size = m_symbol_contexts.size(); for (size_t idx = 0; idx < size; ++idx) { if (m_symbol_contexts[idx].line_entry.line == line) ++match_count; } return match_count; } void SymbolContextList::GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) const { const size_t size = m_symbol_contexts.size(); for (size_t idx = 0; idx < size; ++idx) m_symbol_contexts[idx].GetDescription(s, level, target); } bool lldb_private::operator==(const SymbolContextList &lhs, const SymbolContextList &rhs) { const uint32_t size = lhs.GetSize(); if (size != rhs.GetSize()) return false; SymbolContext lhs_sc; SymbolContext rhs_sc; for (uint32_t i = 0; i < size; ++i) { lhs.GetContextAtIndex(i, lhs_sc); rhs.GetContextAtIndex(i, rhs_sc); if (lhs_sc != rhs_sc) return false; } return true; } bool lldb_private::operator!=(const SymbolContextList &lhs, const SymbolContextList &rhs) { return !(lhs == rhs); }