diff options
Diffstat (limited to 'lldb')
104 files changed, 2591 insertions, 1161 deletions
diff --git a/lldb/bindings/interface/SBSection.i b/lldb/bindings/interface/SBSection.i index b86d4e99c5ea..a138d81825b5 100644 --- a/lldb/bindings/interface/SBSection.i +++ b/lldb/bindings/interface/SBSection.i @@ -105,6 +105,9 @@ public: uint32_t GetTargetByteSize (); + uint32_t + GetAlignment (); + bool GetDescription (lldb::SBStream &description); @@ -138,6 +141,7 @@ public: data = property(GetSectionData, None, doc='''A read only property that returns an lldb object that represents the bytes for this section (lldb.SBData) for this section.''') type = property(GetSectionType, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eSectionType") that represents the type of this section (code, data, etc.).''') target_byte_size = property(GetTargetByteSize, None, doc='''A read only property that returns the size of a target byte represented by this section as a number of host bytes.''') + alignment = property(GetAlignment, None, doc='''A read only property that returns the alignment of this section as a number of host bytes.''') %} #endif diff --git a/lldb/bindings/interface/SBTrace.i b/lldb/bindings/interface/SBTrace.i index 0f5bf0ecc8d9..0d74881a3f3d 100644 --- a/lldb/bindings/interface/SBTrace.i +++ b/lldb/bindings/interface/SBTrace.i @@ -17,6 +17,8 @@ public: const char *GetStartConfigurationHelp(); + SBFileSpec SaveToDisk(SBError &error, const SBFileSpec &bundle_dir, bool compact = false); + SBError Start(const SBStructuredData &configuration); SBError Start(const SBThread &thread, const SBStructuredData &configuration); diff --git a/lldb/include/lldb/API/SBSection.h b/lldb/include/lldb/API/SBSection.h index d722dbe4ff1f..94c6614ecfa9 100644 --- a/lldb/include/lldb/API/SBSection.h +++ b/lldb/include/lldb/API/SBSection.h @@ -76,6 +76,12 @@ public: /// The number of host (8-bit) bytes needed to hold a target byte uint32_t GetTargetByteSize(); + /// Return the alignment of the section in bytes + /// + /// \return + /// The alignment of the section in bytes + uint32_t GetAlignment(); + bool operator==(const lldb::SBSection &rhs); bool operator!=(const lldb::SBSection &rhs); diff --git a/lldb/include/lldb/API/SBTrace.h b/lldb/include/lldb/API/SBTrace.h index d5cf30f56637..19d759013955 100644 --- a/lldb/include/lldb/API/SBTrace.h +++ b/lldb/include/lldb/API/SBTrace.h @@ -25,6 +25,28 @@ public: static SBTrace LoadTraceFromFile(SBError &error, SBDebugger &debugger, const SBFileSpec &trace_description_file); + /// Save the trace to the specified directory, which will be created if + /// needed. This will also create a a file \a <directory>/trace.json with the + /// main properties of the trace session, along with others files which + /// contain the actual trace data. The trace.json file can be used later as + /// input for the "trace load" command to load the trace in LLDB, or for the + /// method \a SBDebugger.LoadTraceFromFile(). + /// + /// \param[out] error + /// This will be set with an error in case of failures. + /// + /// \param[in] directory + /// The directory where the trace files will be saved. + /// + /// \param[in] compact + /// Try not to save to disk information irrelevant to the traced processes. + /// Each trace plug-in implements this in a different fashion. + /// + /// \return + /// A \a SBFileSpec pointing to the bundle description file. + SBFileSpec SaveToDisk(SBError &error, const SBFileSpec &bundle_dir, + bool compact = false); + /// \return /// A description of the parameters to use for the \a SBTrace::Start /// method, or \b null if the object is invalid. diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 2dd1153031a6..2cb983c40d19 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -79,6 +79,12 @@ public: return m_comment.c_str(); } + /// \return + /// The control flow kind of this instruction, or + /// eInstructionControlFlowKindUnknown if the instruction + /// can't be classified. + lldb::InstructionControlFlowKind GetControlFlowKind(const ArchSpec &arch); + virtual void CalculateMnemonicOperandsAndComment(const ExecutionContext *exe_ctx) = 0; @@ -105,6 +111,9 @@ public: /// \param[in] show_bytes /// Whether the bytes of the assembly instruction should be printed. /// + /// \param[in] show_control_flow_kind + /// Whether the control flow kind of the instruction should be printed. + /// /// \param[in] max_opcode_byte_size /// The size (in bytes) of the largest instruction in the list that /// we are printing (for text justification/alignment purposes) @@ -140,7 +149,8 @@ public: /// so this method can properly align the instruction opcodes. /// May be 0 to indicate no indentation/alignment of the opcodes. virtual void Dump(Stream *s, uint32_t max_opcode_byte_size, bool show_address, - bool show_bytes, const ExecutionContext *exe_ctx, + bool show_bytes, bool show_control_flow_kind, + const ExecutionContext *exe_ctx, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, const FormatEntity::Entry *disassembly_addr_format, @@ -320,7 +330,7 @@ public: void Append(lldb::InstructionSP &inst_sp); void Dump(Stream *s, bool show_address, bool show_bytes, - const ExecutionContext *exe_ctx); + bool show_control_flow_kind, const ExecutionContext *exe_ctx); private: typedef std::vector<lldb::InstructionSP> collection; @@ -375,7 +385,8 @@ public: eOptionMarkPCSourceLine = (1u << 2), // Mark the source line that contains // the current PC (mixed mode only) eOptionMarkPCAddress = - (1u << 3) // Mark the disassembly line the contains the PC + (1u << 3), // Mark the disassembly line the contains the PC + eOptionShowControlFlowKind = (1u << 4), }; enum HexImmediateStyle { diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h index 96a0e8e02da1..49e51d51f211 100644 --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -42,49 +42,14 @@ public: /// \param[in] data /// A data extractor configured to read the DWARF location expression's /// bytecode. - DWARFExpression(lldb::ModuleSP module, const DataExtractor &data, - const DWARFUnit *dwarf_cu); + DWARFExpression(const DataExtractor &data); /// Destructor virtual ~DWARFExpression(); - /// Print the description of the expression to a stream - /// - /// \param[in] s - /// The stream to print to. - /// - /// \param[in] level - /// The level of verbosity to use. - /// - /// \param[in] abi - /// An optional ABI plug-in that can be used to resolve register - /// names. - void GetDescription(Stream *s, lldb::DescriptionLevel level, ABI *abi) const; - /// Return true if the location expression contains data bool IsValid() const; - /// Return true if a location list was provided - bool IsLocationList() const; - - /// Search for a load address in the location list - /// - /// \param[in] func_load_addr - /// The actual address of the function containing this location list. - /// - /// \param[in] addr - /// The address to resolve - /// - /// \return - /// True if IsLocationList() is true and the address was found; - /// false otherwise. - // bool - // LocationListContainsLoadAddress (Process* process, const Address &addr) - // const; - // - bool LocationListContainsAddress(lldb::addr_t func_load_addr, - lldb::addr_t addr) const; - /// If a location is not a location list, return true if the location /// contains a DW_OP_addr () opcode in the stream that matches \a file_addr. /// If file_addr is LLDB_INVALID_ADDRESS, the this function will return true @@ -93,6 +58,9 @@ public: /// static variable since there is no other indication from DWARF debug /// info. /// + /// \param[in] dwarf_cu + /// The dwarf unit this expression belongs to. + /// /// \param[in] op_addr_idx /// The DW_OP_addr index to retrieve in case there is more than /// one DW_OP_addr opcode in the location byte stream. @@ -104,36 +72,22 @@ public: /// \return /// LLDB_INVALID_ADDRESS if the location doesn't contain a /// DW_OP_addr for \a op_addr_idx, otherwise a valid file address - lldb::addr_t GetLocation_DW_OP_addr(uint32_t op_addr_idx, bool &error) const; + lldb::addr_t GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, + uint32_t op_addr_idx, bool &error) const; bool Update_DW_OP_addr(lldb::addr_t file_addr); void UpdateValue(uint64_t const_value, lldb::offset_t const_value_byte_size, uint8_t addr_byte_size); - void SetModule(const lldb::ModuleSP &module) { m_module_wp = module; } - bool ContainsThreadLocalStorage() const; bool LinkThreadLocalStorage( - lldb::ModuleSP new_module_sp, std::function<lldb::addr_t(lldb::addr_t file_addr)> const &link_address_callback); - /// Tells the expression that it refers to a location list. - /// - /// \param[in] cu_file_addr - /// The base address to use for interpreting relative location list - /// entries. - /// \param[in] func_file_addr - /// The file address of the function containing this location list. This - /// address will be used to relocate the location list on the fly (in - /// conjuction with the func_load_addr arguments). - void SetLocationListAddresses(lldb::addr_t cu_file_addr, - lldb::addr_t func_file_addr); - /// Return the call-frame-info style register kind - int GetRegisterKind(); + lldb::RegisterKind GetRegisterKind() const; /// Set the call-frame-info style register kind /// @@ -141,20 +95,6 @@ public: /// The register kind. void SetRegisterKind(lldb::RegisterKind reg_kind); - /// Wrapper for the static evaluate function that accepts an - /// ExecutionContextScope instead of an ExecutionContext and uses member - /// variables to populate many operands - bool Evaluate(ExecutionContextScope *exe_scope, lldb::addr_t func_load_addr, - const Value *initial_value_ptr, const Value *object_address_ptr, - Value &result, Status *error_ptr) const; - - /// Wrapper for the static evaluate function that uses member variables to - /// populate many operands - bool Evaluate(ExecutionContext *exe_ctx, RegisterContext *reg_ctx, - lldb::addr_t loclist_base_load_addr, - const Value *initial_value_ptr, const Value *object_address_ptr, - Value &result, Status *error_ptr) const; - /// Evaluate a DWARF location expression in a particular context /// /// \param[in] exe_ctx @@ -194,72 +134,32 @@ public: /// True on success; false otherwise. If error_ptr is non-NULL, /// details of the failure are provided through it. static bool Evaluate(ExecutionContext *exe_ctx, RegisterContext *reg_ctx, - lldb::ModuleSP opcode_ctx, const DataExtractor &opcodes, + lldb::ModuleSP module_sp, const DataExtractor &opcodes, const DWARFUnit *dwarf_cu, const lldb::RegisterKind reg_set, const Value *initial_value_ptr, const Value *object_address_ptr, Value &result, Status *error_ptr); + static bool ParseDWARFLocationList(const DWARFUnit *dwarf_cu, + const DataExtractor &data, + DWARFExpressionList *loc_list); + bool GetExpressionData(DataExtractor &data) const { data = m_data; return data.GetByteSize() > 0; } - bool DumpLocationForAddress(Stream *s, lldb::DescriptionLevel level, - lldb::addr_t func_load_addr, lldb::addr_t address, - ABI *abi); - - bool DumpLocations(Stream *s, lldb::DescriptionLevel level, - lldb::addr_t func_load_addr, lldb::addr_t addr, ABI *abi); - - bool GetLocationExpressions( - lldb::addr_t load_function_start, - llvm::function_ref<bool(llvm::DWARFLocationExpression)> callback) const; + void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const; - bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op); - - llvm::Optional<DataExtractor> - GetLocationExpression(lldb::addr_t load_function_start, - lldb::addr_t addr) const; + bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const; private: - /// Pretty-prints the location expression to a stream - /// - /// \param[in] s - /// The stream to use for pretty-printing. - /// - /// \param[in] data - /// The data extractor. - /// - /// \param[in] level - /// The level of detail to use in pretty-printing. - /// - /// \param[in] abi - /// An optional ABI plug-in that can be used to resolve register - /// names. - void DumpLocation(Stream *s, const DataExtractor &data, - lldb::DescriptionLevel level, ABI *abi) const; - - /// Module which defined this expression. - lldb::ModuleWP m_module_wp; - /// A data extractor capable of reading opcode bytes DataExtractor m_data; - /// The DWARF compile unit this expression belongs to. It is used to evaluate - /// values indexing into the .debug_addr section (e.g. DW_OP_GNU_addr_index, - /// DW_OP_GNU_const_index) - const DWARFUnit *m_dwarf_cu = nullptr; - /// One of the defines that starts with LLDB_REGKIND_ lldb::RegisterKind m_reg_kind = lldb::eRegisterKindDWARF; - - struct LoclistAddresses { - lldb::addr_t cu_file_addr; - lldb::addr_t func_file_addr; - }; - llvm::Optional<LoclistAddresses> m_loclist_addresses; }; } // namespace lldb_private diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h new file mode 100644 index 000000000000..a8f2a7126e3c --- /dev/null +++ b/lldb/include/lldb/Expression/DWARFExpressionList.h @@ -0,0 +1,151 @@ +//===-- DWARFExpressionList.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_EXPRESSION_DWARFEXPRESSIONLIST_H +#define LLDB_EXPRESSION_DWARFEXPRESSIONLIST_H + +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/lldb-private.h" +#include "llvm/ADT/Optional.h" + +class DWARFUnit; + +namespace lldb_private { + +/// \class DWARFExpressionList DWARFExpressionList.h +/// "lldb/Expression/DWARFExpressionList.h" Encapsulates a range map from file +/// address range to a single DWARF location expression. +class DWARFExpressionList { +public: + DWARFExpressionList() = default; + + DWARFExpressionList(lldb::ModuleSP module_sp, const DWARFUnit *dwarf_cu, + lldb::addr_t func_file_addr) + : m_module_wp(module_sp), m_dwarf_cu(dwarf_cu), + m_func_file_addr(func_file_addr) {} + + DWARFExpressionList(lldb::ModuleSP module_sp, DWARFExpression expr, + const DWARFUnit *dwarf_cu) + : m_module_wp(module_sp), m_dwarf_cu(dwarf_cu) { + AddExpression(0, LLDB_INVALID_ADDRESS, expr); + } + + /// Return true if the location expression contains data + bool IsValid() const { return !m_exprs.IsEmpty(); } + + void Clear() { m_exprs.Clear(); } + + // Return true if the location expression is always valid. + bool IsAlwaysValidSingleExpr() const; + + bool AddExpression(lldb::addr_t base, lldb::addr_t end, DWARFExpression expr); + + /// Get the expression data at the file address. + bool GetExpressionData(DataExtractor &data, + lldb::addr_t func_load_addr = LLDB_INVALID_ADDRESS, + lldb::addr_t file_addr = 0) const; + + /// Sort m_expressions. + void Sort() { m_exprs.Sort(); } + + void SetFuncFileAddress(lldb::addr_t func_file_addr) { + m_func_file_addr = func_file_addr; + } + + lldb::addr_t GetFuncFileAddress() { return m_func_file_addr; } + + const DWARFExpression *GetExpressionAtAddress(lldb::addr_t func_load_addr, + lldb::addr_t load_addr) const; + + const DWARFExpression *GetAlwaysValidExpr() const; + + DWARFExpression *GetMutableExpressionAtAddress( + lldb::addr_t func_load_addr = LLDB_INVALID_ADDRESS, + lldb::addr_t load_addr = 0); + + size_t GetSize() const { return m_exprs.GetSize(); } + + bool ContainsThreadLocalStorage() const; + + bool LinkThreadLocalStorage( + lldb::ModuleSP new_module_sp, + std::function<lldb::addr_t(lldb::addr_t file_addr)> const + &link_address_callback); + + bool MatchesOperand(StackFrame &frame, + const Instruction::Operand &operand) const; + + /// Dump locations that contains file_addr if it's valid. Otherwise. dump all + /// locations. + bool DumpLocations(Stream *s, lldb::DescriptionLevel level, + lldb::addr_t func_load_addr, lldb::addr_t file_addr, + ABI *abi) const; + + /// Dump all locaitons with each seperated by new line. + void GetDescription(Stream *s, lldb::DescriptionLevel level, ABI *abi) const; + + /// Search for a load address in the dwarf location list + /// + /// \param[in] func_load_addr + /// The actual address of the function containing this location list. + /// + /// \param[in] addr + /// The address to resolve. + /// + /// \return + /// True if IsLocationList() is true and the address was found; + /// false otherwise. + // bool + // LocationListContainsLoadAddress (Process* process, const Address &addr) + // const; + // + bool ContainsAddress(lldb::addr_t func_load_addr, lldb::addr_t addr) const; + + void SetModule(const lldb::ModuleSP &module) { m_module_wp = module; } + + bool Evaluate(ExecutionContext *exe_ctx, RegisterContext *reg_ctx, + lldb::addr_t func_load_addr, const Value *initial_value_ptr, + const Value *object_address_ptr, Value &result, + Status *error_ptr) const; + +private: + // RangeDataVector requires a comparator for DWARFExpression, but it doesn't + // make sense to do so. + struct DWARFExpressionCompare { + public: + bool operator()(const DWARFExpression &lhs, + const DWARFExpression &rhs) const { + return false; + } + }; + using ExprVec = RangeDataVector<lldb::addr_t, lldb::addr_t, DWARFExpression, + 0, DWARFExpressionCompare>; + using Entry = ExprVec::Entry; + + // File address range mapping to single dwarf expression. + ExprVec m_exprs; + + /// Module which defined this expression. + lldb::ModuleWP m_module_wp; + + /// The DWARF compile unit this expression belongs to. It is used to evaluate + /// values indexing into the .debug_addr section (e.g. DW_OP_GNU_addr_index, + /// DW_OP_GNU_const_index) + const DWARFUnit *m_dwarf_cu = nullptr; + + // Function base file address. + lldb::addr_t m_func_file_addr = LLDB_INVALID_ADDRESS; + + using const_iterator = ExprVec::Collection::const_iterator; + const_iterator begin() const { return m_exprs.begin(); } + const_iterator end() const { return m_exprs.end(); } +}; +} // namespace lldb_private + +#endif // LLDB_EXPRESSION_DWARFEXPRESSIONLIST_H diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h index 45fc47b02c04..0fc1c61bdb92 100644 --- a/lldb/include/lldb/Interpreter/CommandObject.h +++ b/lldb/include/lldb/Interpreter/CommandObject.h @@ -104,9 +104,6 @@ public: typedef std::vector<CommandArgumentData> CommandArgumentEntry; // Used to build individual command argument lists - static ArgumentTableEntry g_arguments_data - [lldb::eArgTypeLastArg]; // Main argument information table - typedef std::map<std::string, lldb::CommandObjectSP> CommandMap; CommandObject(CommandInterpreter &interpreter, llvm::StringRef name, diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h index 83f9979c3c52..3eb4f5d7dedf 100644 --- a/lldb/include/lldb/Symbol/Function.h +++ b/lldb/include/lldb/Symbol/Function.h @@ -12,7 +12,7 @@ #include "lldb/Core/AddressRange.h" #include "lldb/Core/Declaration.h" #include "lldb/Core/Mangled.h" -#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Expression/DWARFExpressionList.h" #include "lldb/Symbol/Block.h" #include "lldb/Utility/UserID.h" #include "llvm/ADT/ArrayRef.h" @@ -253,8 +253,8 @@ class Function; /// Represent the locations of a parameter at a call site, both in the caller /// and in the callee. struct CallSiteParameter { - DWARFExpression LocationInCallee; - DWARFExpression LocationInCaller; + DWARFExpressionList LocationInCallee; + DWARFExpressionList LocationInCaller; }; /// A vector of \c CallSiteParameter. @@ -370,7 +370,7 @@ class IndirectCallEdge : public CallEdge { public: /// Construct a call edge using a DWARFExpression to identify the callee, and /// a return PC within the calling function to identify a specific call site. - IndirectCallEdge(DWARFExpression call_target, AddrType caller_address_type, + IndirectCallEdge(DWARFExpressionList call_target, AddrType caller_address_type, lldb::addr_t caller_address, bool is_tail_call, CallSiteParameterArray &¶meters) : CallEdge(caller_address_type, caller_address, is_tail_call, @@ -383,7 +383,7 @@ private: // Used to describe an indirect call. // // Specifies the location of the callee address in the calling frame. - DWARFExpression call_target; + DWARFExpressionList call_target; }; /// \class Function Function.h "lldb/Symbol/Function.h" @@ -521,13 +521,13 @@ public: /// \return /// A location expression that describes the function frame /// base. - DWARFExpression &GetFrameBaseExpression() { return m_frame_base; } + DWARFExpressionList &GetFrameBaseExpression() { return m_frame_base; } /// Get const accessor for the frame base location. /// /// \return /// A const compile unit object pointer. - const DWARFExpression &GetFrameBaseExpression() const { return m_frame_base; } + const DWARFExpressionList &GetFrameBaseExpression() const { return m_frame_base; } ConstString GetName() const; @@ -659,7 +659,7 @@ protected: /// The frame base expression for variables that are relative to the frame /// pointer. - DWARFExpression m_frame_base; + DWARFExpressionList m_frame_base; Flags m_flags; diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index c61e3c138944..e51d50592c90 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -673,6 +673,7 @@ public: virtual size_t ReadSectionData(Section *section, DataExtractor §ion_data); + /// Returns true if the object file exists only in memory. bool IsInMemory() const { return m_memory_addr != LLDB_INVALID_ADDRESS; } // Strip linker annotations (such as @@VERSION) from symbol names. @@ -736,6 +737,7 @@ protected: DataExtractor m_data; ///< The data for this object file so things can be parsed lazily. lldb::ProcessWP m_process_wp; + /// Set if the object file only exists in memory. const lldb::addr_t m_memory_addr; std::unique_ptr<lldb_private::SectionList> m_sections_up; std::unique_ptr<lldb_private::Symtab> m_symtab_up; diff --git a/lldb/include/lldb/Symbol/TypeList.h b/lldb/include/lldb/Symbol/TypeList.h index 03390858025b..403469c989f5 100644 --- a/lldb/include/lldb/Symbol/TypeList.h +++ b/lldb/include/lldb/Symbol/TypeList.h @@ -49,10 +49,11 @@ public: void ForEach(std::function<bool(lldb::TypeSP &type_sp)> const &callback); - void RemoveMismatchedTypes(const char *qualified_typename, bool exact_match); + void RemoveMismatchedTypes(llvm::StringRef qualified_typename, + bool exact_match); - void RemoveMismatchedTypes(const std::string &type_scope, - const std::string &type_basename, + void RemoveMismatchedTypes(llvm::StringRef type_scope, + llvm::StringRef type_basename, lldb::TypeClass type_class, bool exact_match); void RemoveMismatchedTypes(lldb::TypeClass type_class); diff --git a/lldb/include/lldb/Symbol/TypeMap.h b/lldb/include/lldb/Symbol/TypeMap.h index ede54c1a09d4..c200ccb9844f 100644 --- a/lldb/include/lldb/Symbol/TypeMap.h +++ b/lldb/include/lldb/Symbol/TypeMap.h @@ -53,14 +53,10 @@ public: bool Remove(const lldb::TypeSP &type_sp); - void RemoveMismatchedTypes(const char *qualified_typename, bool exact_match); - - void RemoveMismatchedTypes(const std::string &type_scope, - const std::string &type_basename, + void RemoveMismatchedTypes(llvm::StringRef type_scope, + llvm::StringRef type_basename, lldb::TypeClass type_class, bool exact_match); - void RemoveMismatchedTypes(lldb::TypeClass type_class); - private: typedef collection::iterator iterator; typedef collection::const_iterator const_iterator; diff --git a/lldb/include/lldb/Symbol/Variable.h b/lldb/include/lldb/Symbol/Variable.h index 88a975df3992..c437624d1ea6 100644 --- a/lldb/include/lldb/Symbol/Variable.h +++ b/lldb/include/lldb/Symbol/Variable.h @@ -11,7 +11,7 @@ #include "lldb/Core/Declaration.h" #include "lldb/Core/Mangled.h" -#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Expression/DWARFExpressionList.h" #include "lldb/Utility/CompletionRequest.h" #include "lldb/Utility/RangeMap.h" #include "lldb/Utility/UserID.h" @@ -32,8 +32,8 @@ public: Variable(lldb::user_id_t uid, const char *name, const char *mangled, const lldb::SymbolFileTypeSP &symfile_type_sp, lldb::ValueType scope, SymbolContextScope *owner_scope, const RangeList &scope_range, - Declaration *decl, const DWARFExpression &location, bool external, - bool artificial, bool location_is_constant_data, + Declaration *decl, const DWARFExpressionList &location, + bool external, bool artificial, bool location_is_constant_data, bool static_member = false); virtual ~Variable(); @@ -73,9 +73,11 @@ public: bool IsStaticMember() const { return m_static_member; } - DWARFExpression &LocationExpression() { return m_location; } + DWARFExpressionList &LocationExpressionList() { return m_location_list; } - const DWARFExpression &LocationExpression() const { return m_location; } + const DWARFExpressionList &LocationExpressionList() const { + return m_location_list; + } // When given invalid address, it dumps all locations. Otherwise it only dumps // the location that contains this address. @@ -128,7 +130,7 @@ protected: Declaration m_declaration; /// The location of this variable that can be fed to /// DWARFExpression::Evaluate(). - DWARFExpression m_location; + DWARFExpressionList m_location_list; /// Visible outside the containing compile unit? unsigned m_external : 1; /// Non-zero if the variable is not explicitly declared in source. diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index 1b0485b22cac..7c4340de4de0 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -202,7 +202,7 @@ public: /// frames may be unable to provide this value; they will return false. bool GetFrameBaseValue(Scalar &value, Status *error_ptr); - /// Get the DWARFExpression corresponding to the Canonical Frame Address. + /// Get the DWARFExpressionList corresponding to the Canonical Frame Address. /// /// Often a register (bp), but sometimes a register + offset. /// @@ -212,7 +212,7 @@ public: /// /// \return /// Returns the corresponding DWARF expression, or NULL. - DWARFExpression *GetFrameBaseExpression(Status *error_ptr); + DWARFExpressionList *GetFrameBaseExpression(Status *error_ptr); /// Get the current lexical scope block for this StackFrame, if possible. /// diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h index f4d7dee684c3..beae9e28417d 100644 --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -56,21 +56,24 @@ public: /// A stream object to dump the information to. virtual void Dump(Stream *s) const = 0; - /// Save the trace of a live process to the specified directory, which - /// will be created if needed. - /// This will also create a a file \a <directory>/trace.json with the main - /// properties of the trace session, along with others files which contain - /// the actual trace data. The trace.json file can be used later as input - /// for the "trace load" command to load the trace in LLDB. - /// The process being trace is not a live process, return an error. + /// Save the trace to the specified directory, which will be created if + /// needed. This will also create a a file \a <directory>/trace.json with the + /// main properties of the trace session, along with others files which + /// contain the actual trace data. The trace.json file can be used later as + /// input for the "trace load" command to load the trace in LLDB. /// /// \param[in] directory /// The directory where the trace files will be saved. /// + /// \param[in] compact + /// Try not to save to disk information irrelevant to the traced processes. + /// Each trace plug-in implements this in a different fashion. + /// /// \return - /// \a llvm::success if the operation was successful, or an \a llvm::Error - /// otherwise. - virtual llvm::Error SaveLiveTraceToDisk(FileSpec directory) = 0; + /// A \a FileSpec pointing to the bundle description file, or an \a + /// llvm::Error otherwise. + virtual llvm::Expected<FileSpec> SaveToDisk(FileSpec directory, + bool compact) = 0; /// Find a trace plug-in using JSON data. /// @@ -183,7 +186,8 @@ public: /// \param[in] verbose /// If \b true, print detailed info /// If \b false, print compact info - virtual void DumpTraceInfo(Thread &thread, Stream &s, bool verbose) = 0; + virtual void DumpTraceInfo(Thread &thread, Stream &s, bool verbose, + bool json) = 0; /// Check if a thread is currently traced by this object. /// diff --git a/lldb/include/lldb/Target/TraceCursor.h b/lldb/include/lldb/Target/TraceCursor.h index a4cf6433c19a..f6337e3d3d3f 100644 --- a/lldb/include/lldb/Target/TraceCursor.h +++ b/lldb/include/lldb/Target/TraceCursor.h @@ -266,6 +266,16 @@ public: /// The value of the counter or \b llvm::None if not available. virtual llvm::Optional<uint64_t> GetCounter(lldb::TraceCounter counter_type) const = 0; + + /// Get the CPU associated with the current trace item. + /// + /// This call might not be O(1), so it's suggested to invoke this method + /// whenever a cpu change event is fired. + /// + /// \return + /// The requested CPU id, or \a llvm::None if this information is + /// not available for the current item. + virtual llvm::Optional<lldb::cpu_id_t> GetCPU() const = 0; /// \} protected: diff --git a/lldb/include/lldb/Target/TraceDumper.h b/lldb/include/lldb/Target/TraceDumper.h index e78836e45b01..bbc1a55873d7 100644 --- a/lldb/include/lldb/Target/TraceDumper.h +++ b/lldb/include/lldb/Target/TraceDumper.h @@ -34,6 +34,8 @@ struct TraceDumperOptions { bool show_tsc = false; /// Dump the events that happened between instructions. bool show_events = false; + /// For each instruction, print the instruction kind. + bool show_control_flow_kind = false; /// Optional custom id to start traversing from. llvm::Optional<uint64_t> id = llvm::None; /// Optional number of instructions to skip from the starting position @@ -64,6 +66,7 @@ public: llvm::Optional<lldb::TraceEvent> event; llvm::Optional<SymbolInfo> symbol_info; llvm::Optional<SymbolInfo> prev_symbol_info; + llvm::Optional<lldb::cpu_id_t> cpu_id; }; /// Interface used to abstract away the format in which the instruction diff --git a/lldb/include/lldb/Utility/Environment.h b/lldb/include/lldb/Utility/Environment.h index c1549a3d60a6..27d740402c30 100644 --- a/lldb/include/lldb/Utility/Environment.h +++ b/lldb/include/lldb/Utility/Environment.h @@ -57,7 +57,7 @@ public: using Base::operator[]; Environment() {} - Environment(const Environment &RHS) : Base(RHS) {} + Environment(const Environment &RHS) : Base(static_cast<const Base&>(RHS)) {} Environment(Environment &&RHS) : Base(std::move(RHS)) {} Environment(char *const *Env) : Environment(const_cast<const char *const *>(Env)) {} diff --git a/lldb/include/lldb/Utility/RangeMap.h b/lldb/include/lldb/Utility/RangeMap.h index 7eb0cab8084c..257b177c7092 100644 --- a/lldb/include/lldb/Utility/RangeMap.h +++ b/lldb/include/lldb/Utility/RangeMap.h @@ -627,6 +627,10 @@ public: return (m_entries.empty() ? nullptr : &m_entries.back()); } + using const_iterator = typename Collection::const_iterator; + const_iterator begin() const { return m_entries.begin(); } + const_iterator end() const { return m_entries.end(); } + protected: Collection m_entries; Compare m_compare; diff --git a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h index 36b594613a91..bf9409743a6d 100644 --- a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h +++ b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h @@ -50,6 +50,10 @@ struct TraceIntelPTStartRequest : TraceStartRequest { /// Whether to have a trace buffer per thread or per cpu cpu. llvm::Optional<bool> per_cpu_tracing; + /// Disable the cgroup filtering that is automatically applied in per cpu + /// mode. + llvm::Optional<bool> disable_cgroup_filtering; + bool IsPerCpuTracing() const; }; @@ -107,6 +111,7 @@ struct LinuxPerfZeroTscConversion { struct TraceIntelPTGetStateResponse : TraceGetStateResponse { /// The TSC to wall time conversion if it exists, otherwise \b nullptr. llvm::Optional<LinuxPerfZeroTscConversion> tsc_perf_zero_conversion; + bool using_cgroup_filtering = false; }; bool fromJSON(const llvm::json::Value &value, diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index eba2667727f2..ad03f7e43056 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -970,20 +970,30 @@ enum ExpressionEvaluationPhase { /// control flow of a trace. /// /// A single instruction can match one or more of these categories. -FLAGS_ENUM(TraceInstructionControlFlowType){ - /// Any instruction. - eTraceInstructionControlFlowTypeInstruction = (1u << 1), - /// A conditional or unconditional branch/jump. - eTraceInstructionControlFlowTypeBranch = (1u << 2), - /// A conditional or unconditional branch/jump that changed - /// the control flow of the program. - eTraceInstructionControlFlowTypeTakenBranch = (1u << 3), - /// A call to a function. - eTraceInstructionControlFlowTypeCall = (1u << 4), - /// A return from a function. - eTraceInstructionControlFlowTypeReturn = (1u << 5)}; - -LLDB_MARK_AS_BITMASK_ENUM(TraceInstructionControlFlowType) +enum InstructionControlFlowKind { + /// The instruction could not be classified. + eInstructionControlFlowKindUnknown = 0, + /// The instruction is something not listed below, i.e. it's a sequential + /// instruction that doesn't affect the control flow of the program. + eInstructionControlFlowKindOther, + /// The instruction is a near (function) call. + eInstructionControlFlowKindCall, + /// The instruction is a near (function) return. + eInstructionControlFlowKindReturn, + /// The instruction is a near unconditional jump. + eInstructionControlFlowKindJump, + /// The instruction is a near conditional jump. + eInstructionControlFlowKindCondJump, + /// The instruction is a call-like far transfer. + /// E.g. SYSCALL, SYSENTER, or FAR CALL. + eInstructionControlFlowKindFarCall, + /// The instruction is a return-like far transfer. + /// E.g. SYSRET, SYSEXIT, IRET, or FAR RET. + eInstructionControlFlowKindFarReturn, + /// The instruction is a jump-like far transfer. + /// E.g. FAR JMP. + eInstructionControlFlowKindFarJump +}; /// Watchpoint Kind. /// @@ -1153,12 +1163,15 @@ enum TraceCounter { eTraceCounterTSC = 0, }; -// Events that might happen during a trace session. +/// Events that might happen during a trace session. enum TraceEvent { - // Tracing was disabled for some time due to a software trigger + /// Tracing was disabled for some time due to a software trigger eTraceEventDisabledSW, - // Tracing was disable for some time due to a hardware trigger + /// Tracing was disable for some time due to a hardware trigger eTraceEventDisabledHW, + /// Event due to CPU change for a thread. This event is also fired when + /// suddenly it's not possible to identify the cpu of a given thread. + eTraceEventCPUChanged, }; // Enum used to identify which kind of item a \a TraceCursor is pointing at diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 487b2f20792b..c51e1850338f 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -66,6 +66,7 @@ class ConstStringTable; class DWARFCallFrameInfo; class DWARFDataExtractor; class DWARFExpression; +class DWARFExpressionList; class DataBuffer; class WritableDataBuffer; class DataBufferHeap; diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 6cb9e5dbc1af..ced22628a297 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -241,7 +241,8 @@ bool SBInstruction::GetDescription(lldb::SBStream &s) { // didn't have a stream already created, one will get created... FormatEntity::Entry format; FormatEntity::Parse("${addr}: ", format); - inst_sp->Dump(&s.ref(), 0, true, false, nullptr, &sc, nullptr, &format, 0); + inst_sp->Dump(&s.ref(), 0, true, false, /*show_control_flow_kind=*/false, + nullptr, &sc, nullptr, &format, 0); return true; } return false; @@ -275,8 +276,8 @@ void SBInstruction::Print(FileSP out_sp) { StreamFile out_stream(out_sp); FormatEntity::Entry format; FormatEntity::Parse("${addr}: ", format); - inst_sp->Dump(&out_stream, 0, true, false, nullptr, &sc, nullptr, &format, - 0); + inst_sp->Dump(&out_stream, 0, true, false, /*show_control_flow_kind=*/false, + nullptr, &sc, nullptr, &format, 0); } } diff --git a/lldb/source/API/SBInstructionList.cpp b/lldb/source/API/SBInstructionList.cpp index e289e8e9343d..ae87d7965766 100644 --- a/lldb/source/API/SBInstructionList.cpp +++ b/lldb/source/API/SBInstructionList.cpp @@ -165,8 +165,9 @@ bool SBInstructionList::GetDescription(Stream &sref) { addr, eSymbolContextEverything, sc); } - inst->Dump(&sref, max_opcode_byte_size, true, false, nullptr, &sc, - &prev_sc, &format, 0); + inst->Dump(&sref, max_opcode_byte_size, true, false, + /*show_control_flow_kind=*/false, nullptr, &sc, &prev_sc, + &format, 0); sref.EOL(); } return true; diff --git a/lldb/source/API/SBSection.cpp b/lldb/source/API/SBSection.cpp index 733e0db0b5ba..3a9cf20e484a 100644 --- a/lldb/source/API/SBSection.cpp +++ b/lldb/source/API/SBSection.cpp @@ -242,6 +242,15 @@ uint32_t SBSection::GetTargetByteSize() { return 0; } +uint32_t SBSection::GetAlignment() { + LLDB_INSTRUMENT_VA(this); + + SectionSP section_sp(GetSP()); + if (section_sp.get()) + return (1 << section_sp->GetLog2Align()); + return 0; +} + bool SBSection::operator==(const SBSection &rhs) { LLDB_INSTRUMENT_VA(this, rhs); diff --git a/lldb/source/API/SBTrace.cpp b/lldb/source/API/SBTrace.cpp index fe9003237073..2b1f140161b6 100644 --- a/lldb/source/API/SBTrace.cpp +++ b/lldb/source/API/SBTrace.cpp @@ -43,6 +43,24 @@ SBTrace SBTrace::LoadTraceFromFile(SBError &error, SBDebugger &debugger, return SBTrace(trace_or_err.get()); } +SBFileSpec SBTrace::SaveToDisk(SBError &error, const SBFileSpec &bundle_dir, + bool compact) { + LLDB_INSTRUMENT_VA(this, error, bundle_dir, compact); + + error.Clear(); + SBFileSpec file_spec; + + if (!m_opaque_sp) + error.SetErrorString("error: invalid trace"); + else if (Expected<FileSpec> desc_file = + m_opaque_sp->SaveToDisk(bundle_dir.ref(), compact)) + file_spec.SetFileSpec(*desc_file); + else + error.SetErrorString(llvm::toString(desc_file.takeError()).c_str()); + + return file_spec; +} + const char *SBTrace::GetStartConfigurationHelp() { LLDB_INSTRUMENT_VA(this); return m_opaque_sp ? m_opaque_sp->GetStartConfigurationHelp() : nullptr; diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp index 9d081c83c0fb..6c33edc8a3a8 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.cpp +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -65,6 +65,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue( show_bytes = true; break; + case 'k': + show_control_flow_kind = true; + break; + case 's': { start_addr = OptionArgParser::ToAddress(execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); @@ -154,6 +158,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting( ExecutionContext *execution_context) { show_mixed = false; show_bytes = false; + show_control_flow_kind = false; num_lines_context = 0; num_instructions = 0; func_name.clear(); @@ -493,6 +498,9 @@ bool CommandObjectDisassemble::DoExecute(Args &command, if (m_options.show_bytes) options |= Disassembler::eOptionShowBytes; + if (m_options.show_control_flow_kind) + options |= Disassembler::eOptionShowControlFlowKind; + if (m_options.raw) options |= Disassembler::eOptionRawOuput; diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h index a4b3df8724da..b5146863628d 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.h +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -46,6 +46,7 @@ public: bool show_mixed; // Show mixed source/assembly bool show_bytes; + bool show_control_flow_kind; uint32_t num_lines_context = 0; uint32_t num_instructions = 0; bool raw; diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index c76ae99057f2..d36a574aba7d 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -579,14 +579,14 @@ protected: } } } - + Target *target = m_exe_ctx.GetTargetPtr(); BreakpointIDList run_to_bkpt_ids; // Don't pass an empty run_to_breakpoint list, as Verify will look for the // default breakpoint. if (m_options.m_run_to_bkpt_args.GetArgumentCount() > 0) CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids, + m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids, BreakpointName::Permissions::disablePerm); if (!result.Succeeded()) { return false; @@ -604,7 +604,7 @@ protected: std::vector<break_id_t> bkpts_disabled; std::vector<BreakpointID> locs_disabled; if (num_run_to_bkpt_ids != 0) { - // Go through the ID's specified, and separate the breakpoints from are + // Go through the ID's specified, and separate the breakpoints from are // the breakpoint.location specifications since the latter require // special handling. We also figure out whether there's at least one // specifier in the set that is enabled. @@ -613,22 +613,22 @@ protected: std::unordered_set<break_id_t> bkpts_with_locs_seen; BreakpointIDList with_locs; bool any_enabled = false; - + for (size_t idx = 0; idx < num_run_to_bkpt_ids; idx++) { BreakpointID bkpt_id = run_to_bkpt_ids.GetBreakpointIDAtIndex(idx); break_id_t bp_id = bkpt_id.GetBreakpointID(); break_id_t loc_id = bkpt_id.GetLocationID(); - BreakpointSP bp_sp + BreakpointSP bp_sp = bkpt_list.FindBreakpointByID(bp_id); - // Note, VerifyBreakpointOrLocationIDs checks for existence, so we + // Note, VerifyBreakpointOrLocationIDs checks for existence, so we // don't need to do it again here. if (bp_sp->IsEnabled()) { if (loc_id == LLDB_INVALID_BREAK_ID) { - // A breakpoint (without location) was specified. Make sure that + // A breakpoint (without location) was specified. Make sure that // at least one of the locations is enabled. size_t num_locations = bp_sp->GetNumLocations(); for (size_t loc_idx = 0; loc_idx < num_locations; loc_idx++) { - BreakpointLocationSP loc_sp + BreakpointLocationSP loc_sp = bp_sp->GetLocationAtIndex(loc_idx); if (loc_sp->IsEnabled()) { any_enabled = true; @@ -641,7 +641,7 @@ protected: if (loc_sp->IsEnabled()) any_enabled = true; } - + // Then sort the bp & bp.loc entries for later use: if (bkpt_id.GetLocationID() == LLDB_INVALID_BREAK_ID) bkpts_seen.insert(bkpt_id.GetBreakpointID()); @@ -653,14 +653,14 @@ protected: } // Do all the error checking here so once we start disabling we don't // have to back out half-way through. - + // Make sure at least one of the specified breakpoints is enabled. if (!any_enabled) { result.AppendError("at least one of the continue-to breakpoints must " "be enabled."); return false; } - + // Also, if you specify BOTH a breakpoint and one of it's locations, // we flag that as an error, since it won't do what you expect, the // breakpoint directive will mean "run to all locations", which is not @@ -671,7 +671,7 @@ protected: "one of its locations: {0}", bp_id); } } - + // Now go through the breakpoints in the target, disabling all the ones // that the user didn't mention: for (BreakpointSP bp_sp : bkpt_list.Breakpoints()) { @@ -695,7 +695,7 @@ protected: BreakpointLocationSP loc_sp = bp_sp->GetLocationAtIndex(loc_idx); tmp_id.SetBreakpointLocationID(loc_idx); size_t position = 0; - if (!with_locs.FindBreakpointID(tmp_id, &position) + if (!with_locs.FindBreakpointID(tmp_id, &position) && loc_sp->IsEnabled()) { locs_disabled.push_back(tmp_id); loc_sp->SetEnabled(false); @@ -723,20 +723,20 @@ protected: Status error; // For now we can only do -b with synchronous: bool old_sync = GetDebugger().GetAsyncExecution(); - + if (run_to_bkpt_ids.GetSize() != 0) { GetDebugger().SetAsyncExecution(false); synchronous_execution = true; - } + } if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); - + if (run_to_bkpt_ids.GetSize() != 0) { GetDebugger().SetAsyncExecution(old_sync); - } - + } + // Now re-enable the breakpoints we disabled: BreakpointList &bkpt_list = target->GetBreakpointList(); for (break_id_t bp_id : bkpts_disabled) { @@ -745,10 +745,10 @@ protected: bp_sp->SetEnabled(true); } for (const BreakpointID &bkpt_id : locs_disabled) { - BreakpointSP bp_sp + BreakpointSP bp_sp = bkpt_list.FindBreakpointByID(bkpt_id.GetBreakpointID()); if (bp_sp) { - BreakpointLocationSP loc_sp + BreakpointLocationSP loc_sp = bp_sp->FindLocationByID(bkpt_id.GetLocationID()); if (loc_sp) loc_sp->SetEnabled(true); @@ -1731,7 +1731,7 @@ protected: bool DoExecute(Args &signal_args, CommandReturnObject &result) override { Target &target = GetSelectedOrDummyTarget(); - // Any signals that are being set should be added to the Target's + // Any signals that are being set should be added to the Target's // DummySignals so they will get applied on rerun, etc. // If we have a process, however, we can do a more accurate job of vetting // the user's options. @@ -1761,8 +1761,8 @@ protected: "true or false.\n"); return false; } - - bool no_actions = (stop_action == -1 && pass_action == -1 + + bool no_actions = (stop_action == -1 && pass_action == -1 && notify_action == -1); if (m_options.only_target_values && !no_actions) { result.AppendError("-t is for reporting, not setting, target values."); @@ -1832,9 +1832,9 @@ protected: } auto set_lazy_bool = [] (int action) -> LazyBool { LazyBool lazy; - if (action == -1) + if (action == -1) lazy = eLazyBoolCalculate; - else if (action) + else if (action) lazy = eLazyBoolYes; else lazy = eLazyBoolNo; @@ -1876,7 +1876,7 @@ protected: PrintSignalInformation(result.GetOutputStream(), signal_args, num_signals_set, signals_sp); else - target.PrintDummySignals(result.GetOutputStream(), + target.PrintDummySignals(result.GetOutputStream(), signal_args); if (num_signals_set > 0) @@ -1909,80 +1909,6 @@ protected: } }; -// CommandObjectProcessTraceSave -#define LLDB_OPTIONS_process_trace_save -#include "CommandOptions.inc" - -#pragma mark CommandObjectProcessTraceSave - -class CommandObjectProcessTraceSave : public CommandObjectParsed { -public: - class CommandOptions : public Options { - public: - CommandOptions() { OptionParsingStarting(nullptr); } - - Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, - ExecutionContext *execution_context) override { - Status error; - const int short_option = m_getopt_table[option_idx].val; - - switch (short_option) { - - case 'd': { - m_directory.SetFile(option_arg, FileSpec::Style::native); - FileSystem::Instance().Resolve(m_directory); - break; - } - default: - llvm_unreachable("Unimplemented option"); - } - return error; - } - - void OptionParsingStarting(ExecutionContext *execution_context) override{}; - - llvm::ArrayRef<OptionDefinition> GetDefinitions() override { - return llvm::makeArrayRef(g_process_trace_save_options); - }; - - FileSpec m_directory; - }; - - Options *GetOptions() override { return &m_options; } - CommandObjectProcessTraceSave(CommandInterpreter &interpreter) - : CommandObjectParsed( - interpreter, "process trace save", - "Save the trace of the current process in the specified directory. " - "The directory will be created if needed. " - "This will also create a file <directory>/trace.json with the main " - "properties of the trace session, along with others files which " - "contain the actual trace data. The trace.json file can be used " - "later as input for the \"trace load\" command to load the trace " - "in LLDB", - "process trace save [<cmd-options>]", - eCommandRequiresProcess | eCommandTryTargetAPILock | - eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | - eCommandProcessMustBeTraced) {} - - ~CommandObjectProcessTraceSave() override = default; - -protected: - bool DoExecute(Args &command, CommandReturnObject &result) override { - ProcessSP process_sp = m_exe_ctx.GetProcessSP(); - - TraceSP trace_sp = process_sp->GetTarget().GetTrace(); - - if (llvm::Error err = trace_sp->SaveLiveTraceToDisk(m_options.m_directory)) - result.AppendError(toString(std::move(err))); - else - result.SetStatus(eReturnStatusSuccessFinishResult); - - return result.Succeeded(); - } - - CommandOptions m_options; -}; - // CommandObjectProcessTraceStop class CommandObjectProcessTraceStop : public CommandObjectParsed { public: @@ -2020,8 +1946,6 @@ public: : CommandObjectMultiword( interpreter, "trace", "Commands for tracing the current process.", "process trace <subcommand> [<subcommand objects>]") { - LoadSubCommand("save", CommandObjectSP( - new CommandObjectProcessTraceSave(interpreter))); LoadSubCommand("start", CommandObjectSP(new CommandObjectProcessTraceStart( interpreter))); LoadSubCommand("stop", CommandObjectSP( diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 2b71f1bc7bc8..51978878c8b9 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -47,12 +47,18 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/Args.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-private-enumerations.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatAdapters.h" @@ -2155,6 +2161,59 @@ protected: } }; +class CommandObjectTargetModulesDumpClangPCMInfo : public CommandObjectParsed { +public: + CommandObjectTargetModulesDumpClangPCMInfo(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target modules dump pcm-info", + "Dump information about the given clang module (pcm).") { + // Take a single file argument. + CommandArgumentData arg{eArgTypeFilename, eArgRepeatPlain}; + m_arguments.push_back({arg}); + } + + ~CommandObjectTargetModulesDumpClangPCMInfo() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + if (command.GetArgumentCount() != 1) { + result.AppendErrorWithFormat("'%s' takes exactly one pcm path argument.", + m_cmd_name.c_str()); + return false; + } + + const char *pcm_path = command.GetArgumentAtIndex(0); + FileSpec pcm_file{pcm_path}; + + if (pcm_file.GetFileNameExtension().GetStringRef() != ".pcm") { + result.AppendError("file must have a .pcm extension"); + return false; + } + + if (!FileSystem::Instance().Exists(pcm_file)) { + result.AppendError("pcm file does not exist"); + return false; + } + + clang::CompilerInstance compiler; + compiler.createDiagnostics(); + + const char *clang_args[] = {"clang", pcm_path}; + compiler.setInvocation(clang::createInvocation(clang_args)); + + clang::DumpModuleInfoAction dump_module_info; + dump_module_info.OutputStream = &result.GetOutputStream().AsRawOstream(); + // DumpModuleInfoAction requires ObjectFilePCHContainerReader. + compiler.getPCHContainerOperations()->registerReader( + std::make_unique<clang::ObjectFilePCHContainerReader>()); + + if (compiler.ExecuteAction(dump_module_info)) + result.SetStatus(eReturnStatusSuccessFinishResult); + + return result.Succeeded(); + } +}; + #pragma mark CommandObjectTargetModulesDumpClangAST // Clang AST dumping command @@ -2406,10 +2465,10 @@ public: CommandObjectTargetModulesDump(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "target modules dump", - "Commands for dumping information about one or " - "more target modules.", + "Commands for dumping information about one or more target " + "modules.", "target modules dump " - "[headers|symtab|sections|ast|symfile|line-table] " + "[objfile|symtab|sections|ast|symfile|line-table|pcm-info] " "[<file1> <file2> ...]") { LoadSubCommand("objfile", CommandObjectSP( @@ -2429,6 +2488,10 @@ public: LoadSubCommand("line-table", CommandObjectSP(new CommandObjectTargetModulesDumpLineTable( interpreter))); + LoadSubCommand( + "pcm-info", + CommandObjectSP( + new CommandObjectTargetModulesDumpClangPCMInfo(interpreter))); } ~CommandObjectTargetModulesDump() override = default; diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 993523e06736..ad49d27bb9a7 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1033,11 +1033,21 @@ protected: line_table->FindLineEntryByAddress(fun_end_addr, function_start, &end_ptr); + // Since not all source lines will contribute code, check if we are + // setting the breakpoint on the exact line number or the nearest + // subsequent line number and set breakpoints at all the line table + // entries of the chosen line number (exact or nearest subsequent). for (uint32_t line_number : line_numbers) { + LineEntry line_entry; + bool exact = false; uint32_t start_idx_ptr = index_ptr; + start_idx_ptr = sc.comp_unit->FindLineEntry( + index_ptr, line_number, nullptr, exact, &line_entry); + if (start_idx_ptr != UINT32_MAX) + line_number = line_entry.line; + exact = true; + start_idx_ptr = index_ptr; while (start_idx_ptr <= end_ptr) { - LineEntry line_entry; - const bool exact = false; start_idx_ptr = sc.comp_unit->FindLineEntry( start_idx_ptr, line_number, nullptr, exact, &line_entry); if (start_idx_ptr == UINT32_MAX) @@ -2164,6 +2174,10 @@ public: m_dumper_options.forwards = true; break; } + case 'k': { + m_dumper_options.show_control_flow_kind = true; + break; + } case 't': { m_dumper_options.show_tsc = true; break; @@ -2337,6 +2351,10 @@ public: m_verbose = true; break; } + case 'j': { + m_json = true; + break; + } default: llvm_unreachable("Unimplemented option"); } @@ -2345,6 +2363,7 @@ public: void OptionParsingStarting(ExecutionContext *execution_context) override { m_verbose = false; + m_json = false; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -2353,15 +2372,9 @@ public: // Instance variables to hold the values for command options. bool m_verbose; + bool m_json; }; - bool DoExecute(Args &command, CommandReturnObject &result) override { - Target &target = m_exe_ctx.GetTargetRef(); - result.GetOutputStream().Format("Trace technology: {0}\n", - target.GetTrace()->GetPluginName()); - return CommandObjectIterateOverThreads::DoExecute(command, result); - } - CommandObjectTraceDumpInfo(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread trace dump info", @@ -2383,7 +2396,7 @@ protected: ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); trace_sp->DumpTraceInfo(*thread_sp, result.GetOutputStream(), - m_options.m_verbose); + m_options.m_verbose, m_options.m_json); return true; } diff --git a/lldb/source/Commands/CommandObjectTrace.cpp b/lldb/source/Commands/CommandObjectTrace.cpp index 17aded9ed2a0..227de2de7065 100644 --- a/lldb/source/Commands/CommandObjectTrace.cpp +++ b/lldb/source/Commands/CommandObjectTrace.cpp @@ -30,6 +30,108 @@ using namespace lldb; using namespace lldb_private; using namespace llvm; +// CommandObjectTraceSave +#define LLDB_OPTIONS_trace_save +#include "CommandOptions.inc" + +#pragma mark CommandObjectTraceSave + +class CommandObjectTraceSave : public CommandObjectParsed { +public: + class CommandOptions : public Options { + public: + CommandOptions() { OptionParsingStarting(nullptr); } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'c': { + m_compact = true; + break; + } + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_compact = false; + }; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_trace_save_options); + }; + + bool m_compact; + }; + + Options *GetOptions() override { return &m_options; } + + CommandObjectTraceSave(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "trace save", + "Save the trace of the current target in the specified directory, " + "which will be created if needed. " + "This directory will contain a trace bundle, with all the " + "necessary files the reconstruct the trace session even on a " + "different computer. " + "Part of this bundle is the bundle description file with the name " + "trace.json. This file can be used by the \"trace load\" command " + "to load this trace in LLDB." + "Note: if the current target contains information of multiple " + "processes or targets, they all will be included in the bundle.", + "trace save [<cmd-options>] <bundle_directory>", + eCommandRequiresProcess | eCommandTryTargetAPILock | + eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | + eCommandProcessMustBeTraced) { + CommandArgumentData bundle_dir{eArgTypeDirectoryName, eArgRepeatPlain}; + m_arguments.push_back({bundle_dir}); + } + + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, + request, nullptr); + } + + ~CommandObjectTraceSave() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + if (command.size() != 1) { + result.AppendError("a single path to a directory where the trace bundle " + "will be created is required"); + return false; + } + + FileSpec bundle_dir(command[0].ref()); + FileSystem::Instance().Resolve(bundle_dir); + + ProcessSP process_sp = m_exe_ctx.GetProcessSP(); + + TraceSP trace_sp = process_sp->GetTarget().GetTrace(); + + if (llvm::Expected<FileSpec> desc_file = + trace_sp->SaveToDisk(bundle_dir, m_options.m_compact)) { + result.AppendMessageWithFormatv( + "Trace bundle description file written to: {0}", *desc_file); + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError(toString(desc_file.takeError())); + } + + return result.Succeeded(); + } + + CommandOptions m_options; +}; + // CommandObjectTraceLoad #define LLDB_OPTIONS_trace_load #include "CommandOptions.inc" @@ -75,11 +177,19 @@ public: : CommandObjectParsed( interpreter, "trace load", "Load a post-mortem processor trace session from a trace bundle.", - "trace load") { - CommandArgumentData session_file_arg{eArgTypePath, eArgRepeatPlain}; + "trace load <trace_description_file>") { + CommandArgumentData session_file_arg{eArgTypeFilename, eArgRepeatPlain}; m_arguments.push_back({session_file_arg}); } + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, + request, nullptr); + } + ~CommandObjectTraceLoad() override = default; Options *GetOptions() override { return &m_options; } @@ -284,6 +394,8 @@ CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter) CommandObjectSP(new CommandObjectTraceLoad(interpreter))); LoadSubCommand("dump", CommandObjectSP(new CommandObjectTraceDump(interpreter))); + LoadSubCommand("save", + CommandObjectSP(new CommandObjectTraceSave(interpreter))); LoadSubCommand("schema", CommandObjectSP(new CommandObjectTraceSchema(interpreter))); } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 7755daa878be..7981917fd8b5 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -300,6 +300,11 @@ let Command = "breakpoint command delete" in { let Command = "disassemble" in { def disassemble_options_bytes : Option<"bytes", "b">, Desc<"Show opcode bytes when disassembling.">; + def disassemble_options_kind : Option<"kind", "k">, + Desc<"Show instruction control flow kind. Refer to the enum " + "`InstructionControlFlowKind` for a list of control flow kind. " + "As an important note, far jumps, far calls and far returns often indicate " + "calls to and from kernel.">; def disassemble_options_context : Option<"context", "C">, Arg<"NumLines">, Desc<"Number of context lines of source to show.">; def disassemble_options_mixed : Option<"mixed", "m">, @@ -783,14 +788,6 @@ let Command = "process save_core" in { "This allows core files to be saved in different formats.">; } -let Command = "process trace save" in { - def process_trace_save_directory: Option<"directory", "d">, - Group<1>, - Arg<"Value">, Required, - Desc<"The directory where the trace will be saved." - "It will be created if it does not exist.">; -} - let Command = "script import" in { def script_import_allow_reload : Option<"allow-reload", "r">, Group<1>, Desc<"Allow the script to be loaded even if it was already loaded before. " @@ -1150,6 +1147,11 @@ let Command = "thread trace dump instructions" in { def thread_trace_dump_instructions_pretty_print: Option<"pretty-json", "J">, Group<1>, Desc<"Dump in JSON format but pretty printing the output for easier readability.">; + def thread_trace_dump_instructions_show_kind : Option<"kind", "k">, Group<1>, + Desc<"Show instruction control flow kind. Refer to the enum " + "`InstructionControlFlowKind` for a list of control flow kind. " + "As an important note, far jumps, far calls and far returns often indicate " + "calls to and from kernel.">; def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">, Group<1>, Desc<"For each instruction, print the corresponding timestamp counter if " "available.">; @@ -1167,6 +1169,8 @@ let Command = "thread trace dump instructions" in { let Command = "thread trace dump info" in { def thread_trace_dump_info_verbose : Option<"verbose", "v">, Group<1>, Desc<"show verbose thread trace dump info">; + def thread_trace_dump_info_json: Option<"json", "j">, Group<1>, + Desc<"Dump in JSON format.">; } let Command = "type summary add" in { @@ -1349,6 +1353,14 @@ let Command = "trace load" in { "implementation.">; } +let Command = "trace save" in { + def trace_save_compact: Option<"compact", "c">, + Group<1>, + Desc<"Try not to save to disk information irrelevant to the traced " + "processes. Each trace plug-in implements this in a different " + "fashion.">; +} + let Command = "trace dump" in { def trace_dump_verbose : Option<"verbose", "v">, Group<1>, Desc<"Show verbose trace information.">; diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index f17cd8856a6d..62857c181af8 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1835,9 +1835,20 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { // going to show the progress. const uint64_t id = data->GetID(); if (m_current_event_id) { + Log *log = GetLog(LLDBLog::Events); + if (log && log->GetVerbose()) { + StreamString log_stream; + log_stream.AsRawOstream() + << static_cast<void *>(this) << " Debugger(" << GetID() + << ")::HandleProgressEvent( m_current_event_id = " + << *m_current_event_id << ", data = { "; + data->Dump(&log_stream); + log_stream << " } )"; + log->PutString(log_stream.GetString()); + } if (id != *m_current_event_id) return; - if (data->GetCompleted()) + if (data->GetCompleted() == data->GetTotal()) m_current_event_id.reset(); } else { m_current_event_id = id; @@ -1860,7 +1871,7 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { // Print over previous line, if any. output->Printf("\r"); - if (data->GetCompleted()) { + if (data->GetCompleted() == data->GetTotal()) { // Clear the current line. output->Printf("\x1B[2K"); output->Flush(); diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 00d92053bc4f..7a9e214748a7 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -527,8 +527,11 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, } const bool show_bytes = (options & eOptionShowBytes) != 0; - inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, - &prev_sc, nullptr, address_text_size); + const bool show_control_flow_kind = + (options & eOptionShowControlFlowKind) != 0; + inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, + show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr, + address_text_size); strm.EOL(); } else { break; @@ -568,6 +571,334 @@ Instruction::Instruction(const Address &address, AddressClass addr_class) Instruction::~Instruction() = default; +namespace x86 { + +/// These are the three values deciding instruction control flow kind. +/// InstructionLengthDecode function decodes an instruction and get this struct. +/// +/// primary_opcode +/// Primary opcode of the instruction. +/// For one-byte opcode instruction, it's the first byte after prefix. +/// For two- and three-byte opcodes, it's the second byte. +/// +/// opcode_len +/// The length of opcode in bytes. Valid opcode lengths are 1, 2, or 3. +/// +/// modrm +/// ModR/M byte of the instruction. +/// Bits[7:6] indicate MOD. Bits[5:3] specify a register and R/M bits[2:0] +/// may contain a register or specify an addressing mode, depending on MOD. +struct InstructionOpcodeAndModrm { + uint8_t primary_opcode; + uint8_t opcode_len; + uint8_t modrm; +}; + +/// Determine the InstructionControlFlowKind based on opcode and modrm bytes. +/// Refer to http://ref.x86asm.net/coder.html for the full list of opcode and +/// instruction set. +/// +/// \param[in] opcode_and_modrm +/// Contains primary_opcode byte, its length, and ModR/M byte. +/// Refer to the struct InstructionOpcodeAndModrm for details. +/// +/// \return +/// The control flow kind of the instruction or +/// eInstructionControlFlowKindOther if the instruction doesn't affect +/// the control flow of the program. +lldb::InstructionControlFlowKind +MapOpcodeIntoControlFlowKind(InstructionOpcodeAndModrm opcode_and_modrm) { + uint8_t opcode = opcode_and_modrm.primary_opcode; + uint8_t opcode_len = opcode_and_modrm.opcode_len; + uint8_t modrm = opcode_and_modrm.modrm; + + if (opcode_len > 2) + return lldb::eInstructionControlFlowKindOther; + + if (opcode >= 0x70 && opcode <= 0x7F) { + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindCondJump; + else + return lldb::eInstructionControlFlowKindOther; + } + + if (opcode >= 0x80 && opcode <= 0x8F) { + if (opcode_len == 2) + return lldb::eInstructionControlFlowKindCondJump; + else + return lldb::eInstructionControlFlowKindOther; + } + + switch (opcode) { + case 0x9A: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindFarCall; + break; + case 0xFF: + if (opcode_len == 1) { + uint8_t modrm_reg = (modrm >> 3) & 7; + if (modrm_reg == 2) + return lldb::eInstructionControlFlowKindCall; + else if (modrm_reg == 3) + return lldb::eInstructionControlFlowKindFarCall; + else if (modrm_reg == 4) + return lldb::eInstructionControlFlowKindJump; + else if (modrm_reg == 5) + return lldb::eInstructionControlFlowKindFarJump; + } + break; + case 0xE8: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindCall; + break; + case 0xCD: + case 0xCC: + case 0xCE: + case 0xF1: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindFarCall; + break; + case 0xCF: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindFarReturn; + break; + case 0xE9: + case 0xEB: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindJump; + break; + case 0xEA: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindFarJump; + break; + case 0xE3: + case 0xE0: + case 0xE1: + case 0xE2: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindCondJump; + break; + case 0xC3: + case 0xC2: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindReturn; + break; + case 0xCB: + case 0xCA: + if (opcode_len == 1) + return lldb::eInstructionControlFlowKindFarReturn; + break; + case 0x05: + case 0x34: + if (opcode_len == 2) + return lldb::eInstructionControlFlowKindFarCall; + break; + case 0x35: + case 0x07: + if (opcode_len == 2) + return lldb::eInstructionControlFlowKindFarReturn; + break; + case 0x01: + if (opcode_len == 2) { + switch (modrm) { + case 0xc1: + return lldb::eInstructionControlFlowKindFarCall; + case 0xc2: + case 0xc3: + return lldb::eInstructionControlFlowKindFarReturn; + default: + break; + } + } + break; + default: + break; + } + + return lldb::eInstructionControlFlowKindOther; +} + +/// Decode an instruction into opcode, modrm and opcode_len. +/// Refer to http://ref.x86asm.net/coder.html for the instruction bytes layout. +/// Opcodes in x86 are generally the first byte of instruction, though two-byte +/// instructions and prefixes exist. ModR/M is the byte following the opcode +/// and adds additional information for how the instruction is executed. +/// +/// \param[in] inst_bytes +/// Raw bytes of the instruction +/// +/// +/// \param[in] bytes_len +/// The length of the inst_bytes array. +/// +/// \param[in] is_exec_mode_64b +/// If true, the execution mode is 64 bit. +/// +/// \return +/// Returns decoded instruction as struct InstructionOpcodeAndModrm, holding +/// primary_opcode, opcode_len and modrm byte. Refer to the struct definition +/// for more details. +/// Otherwise if the given instruction is invalid, returns None. +llvm::Optional<InstructionOpcodeAndModrm> +InstructionLengthDecode(const uint8_t *inst_bytes, int bytes_len, + bool is_exec_mode_64b) { + int op_idx = 0; + bool prefix_done = false; + InstructionOpcodeAndModrm ret = {0, 0, 0}; + + // In most cases, the primary_opcode is the first byte of the instruction + // but some instructions have a prefix to be skipped for these calculations. + // The following mapping is inspired from libipt's instruction decoding logic + // in `src/pt_ild.c` + while (!prefix_done) { + if (op_idx >= bytes_len) + return llvm::None; + + ret.primary_opcode = inst_bytes[op_idx]; + switch (ret.primary_opcode) { + // prefix_ignore + case 0x26: + case 0x2e: + case 0x36: + case 0x3e: + case 0x64: + case 0x65: + // prefix_osz, prefix_asz + case 0x66: + case 0x67: + // prefix_lock, prefix_f2, prefix_f3 + case 0xf0: + case 0xf2: + case 0xf3: + op_idx++; + break; + + // prefix_rex + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + if (is_exec_mode_64b) + op_idx++; + else + prefix_done = true; + break; + + // prefix_vex_c4, c5 + case 0xc5: + if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) { + prefix_done = true; + break; + } + + ret.opcode_len = 2; + ret.primary_opcode = inst_bytes[op_idx + 2]; + ret.modrm = inst_bytes[op_idx + 3]; + return ret; + + case 0xc4: + if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) { + prefix_done = true; + break; + } + ret.opcode_len = inst_bytes[op_idx + 1] & 0x1f; + ret.primary_opcode = inst_bytes[op_idx + 3]; + ret.modrm = inst_bytes[op_idx + 4]; + return ret; + + // prefix_evex + case 0x62: + if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) { + prefix_done = true; + break; + } + ret.opcode_len = inst_bytes[op_idx + 1] & 0x03; + ret.primary_opcode = inst_bytes[op_idx + 4]; + ret.modrm = inst_bytes[op_idx + 5]; + return ret; + + default: + prefix_done = true; + break; + } + } // prefix done + + ret.primary_opcode = inst_bytes[op_idx]; + ret.modrm = inst_bytes[op_idx + 1]; + ret.opcode_len = 1; + + // If the first opcode is 0F, it's two- or three- byte opcodes. + if (ret.primary_opcode == 0x0F) { + ret.primary_opcode = inst_bytes[++op_idx]; // get the next byte + + if (ret.primary_opcode == 0x38) { + ret.opcode_len = 3; + ret.primary_opcode = inst_bytes[++op_idx]; // get the next byte + ret.modrm = inst_bytes[op_idx + 1]; + } else if (ret.primary_opcode == 0x3A) { + ret.opcode_len = 3; + ret.primary_opcode = inst_bytes[++op_idx]; + ret.modrm = inst_bytes[op_idx + 1]; + } else if ((ret.primary_opcode & 0xf8) == 0x38) { + ret.opcode_len = 0; + ret.primary_opcode = inst_bytes[++op_idx]; + ret.modrm = inst_bytes[op_idx + 1]; + } else if (ret.primary_opcode == 0x0F) { + ret.opcode_len = 3; + // opcode is 0x0F, no needs to update + ret.modrm = inst_bytes[op_idx + 1]; + } else { + ret.opcode_len = 2; + ret.modrm = inst_bytes[op_idx + 1]; + } + } + + return ret; +} + +lldb::InstructionControlFlowKind GetControlFlowKind(bool is_exec_mode_64b, + Opcode m_opcode) { + llvm::Optional<InstructionOpcodeAndModrm> ret = llvm::None; + + if (m_opcode.GetOpcodeBytes() == nullptr || m_opcode.GetByteSize() <= 0) { + // x86_64 and i386 instructions are categorized as Opcode::Type::eTypeBytes + return lldb::eInstructionControlFlowKindUnknown; + } + + // Opcode bytes will be decoded into primary_opcode, modrm and opcode length. + // These are the three values deciding instruction control flow kind. + ret = InstructionLengthDecode((const uint8_t *)m_opcode.GetOpcodeBytes(), + m_opcode.GetByteSize(), is_exec_mode_64b); + if (!ret) + return lldb::eInstructionControlFlowKindUnknown; + else + return MapOpcodeIntoControlFlowKind(ret.value()); +} + +} // namespace x86 + +lldb::InstructionControlFlowKind +Instruction::GetControlFlowKind(const ArchSpec &arch) { + if (arch.GetTriple().getArch() == llvm::Triple::x86) + return x86::GetControlFlowKind(/*is_exec_mode_64b=*/false, m_opcode); + else if (arch.GetTriple().getArch() == llvm::Triple::x86_64) + return x86::GetControlFlowKind(/*is_exec_mode_64b=*/true, m_opcode); + else + return eInstructionControlFlowKindUnknown; // not implemented +} + AddressClass Instruction::GetAddressClass() { if (m_address_class == AddressClass::eInvalid) m_address_class = m_address.GetAddressClass(); @@ -576,6 +907,7 @@ AddressClass Instruction::GetAddressClass() { void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, bool show_address, bool show_bytes, + bool show_control_flow_kind, const ExecutionContext *exe_ctx, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, @@ -613,6 +945,38 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, } } + if (show_control_flow_kind) { + switch (GetControlFlowKind(exe_ctx->GetTargetRef().GetArchitecture())) { + case eInstructionControlFlowKindUnknown: + ss.Printf("%-12s", "unknown"); + break; + case eInstructionControlFlowKindOther: + ss.Printf("%-12s", "other"); + break; + case eInstructionControlFlowKindCall: + ss.Printf("%-12s", "call"); + break; + case eInstructionControlFlowKindReturn: + ss.Printf("%-12s", "return"); + break; + case eInstructionControlFlowKindJump: + ss.Printf("%-12s", "jump"); + break; + case eInstructionControlFlowKindCondJump: + ss.Printf("%-12s", "cond jump"); + break; + case eInstructionControlFlowKindFarCall: + ss.Printf("%-12s", "far call"); + break; + case eInstructionControlFlowKindFarReturn: + ss.Printf("%-12s", "far return"); + break; + case eInstructionControlFlowKindFarJump: + ss.Printf("%-12s", "far jump"); + break; + } + } + const size_t opcode_pos = ss.GetSizeOfLastLine(); // The default opcode size of 7 characters is plenty for most architectures @@ -957,6 +1321,7 @@ InstructionSP InstructionList::GetInstructionAtAddress(const Address &address) { } void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes, + bool show_control_flow_kind, const ExecutionContext *exe_ctx) { const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); collection::const_iterator pos, begin, end; @@ -975,8 +1340,9 @@ void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes, pos != end; ++pos) { if (pos != begin) s->EOL(); - (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, - nullptr, nullptr, disassembly_format, 0); + (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, + show_control_flow_kind, exe_ctx, nullptr, nullptr, + disassembly_format, 0); } } @@ -994,7 +1360,7 @@ InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; - + if (found_calls) *found_calls = false; for (size_t i = start; i < num_instructions; i++) { diff --git a/lldb/source/Core/DumpDataExtractor.cpp b/lldb/source/Core/DumpDataExtractor.cpp index 4ef1df1aeb0f..dc96a3454b72 100644 --- a/lldb/source/Core/DumpDataExtractor.cpp +++ b/lldb/source/Core/DumpDataExtractor.cpp @@ -170,10 +170,11 @@ static lldb::offset_t DumpInstructions(const DataExtractor &DE, Stream *s, offset += bytes_consumed; const bool show_address = base_addr != LLDB_INVALID_ADDRESS; const bool show_bytes = true; + const bool show_control_flow_kind = true; ExecutionContext exe_ctx; exe_scope->CalculateExecutionContext(exe_ctx); - disassembler_sp->GetInstructionList().Dump(s, show_address, show_bytes, - &exe_ctx); + disassembler_sp->GetInstructionList().Dump( + s, show_address, show_bytes, show_control_flow_kind, &exe_ctx); } } } else diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 41c21e1dc326..893e20837124 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -144,9 +144,7 @@ Module::Module(const ModuleSpec &module_spec) module_spec.GetArchitecture().GetArchitectureName(), module_spec.GetFileSpec().GetPath().c_str(), module_spec.GetObjectName().IsEmpty() ? "" : "(", - module_spec.GetObjectName().IsEmpty() - ? "" - : module_spec.GetObjectName().AsCString(""), + module_spec.GetObjectName().AsCString(""), module_spec.GetObjectName().IsEmpty() ? "" : ")"); auto data_sp = module_spec.GetData(); @@ -254,8 +252,7 @@ Module::Module(const FileSpec &file_spec, const ArchSpec &arch, LLDB_LOGF(log, "%p Module::Module((%s) '%s%s%s%s')", static_cast<void *>(this), m_arch.GetArchitectureName(), m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(", - m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), - m_object_name.IsEmpty() ? "" : ")"); + m_object_name.AsCString(""), m_object_name.IsEmpty() ? "" : ")"); } Module::Module() : m_file_has_changed(false), m_first_file_changed_log(false) { @@ -283,8 +280,7 @@ Module::~Module() { LLDB_LOGF(log, "%p Module::~Module((%s) '%s%s%s%s')", static_cast<void *>(this), m_arch.GetArchitectureName(), m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(", - m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), - m_object_name.IsEmpty() ? "" : ")"); + m_object_name.AsCString(""), m_object_name.IsEmpty() ? "" : ")"); // Release any auto pointers before we start tearing down our member // variables since the object file and symbol files might need to make // function calls back into this module object. The ordering is important @@ -1000,8 +996,7 @@ void Module::FindTypes( FindTypes_Impl(type_basename_const_str, CompilerDeclContext(), max_matches, searched_symbol_files, typesmap); if (typesmap.GetSize()) - typesmap.RemoveMismatchedTypes(std::string(type_scope), - std::string(type_basename), type_class, + typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class, exact_match); } else { // The type is not in a namespace/class scope, just search for it by @@ -1011,15 +1006,13 @@ void Module::FindTypes( // class prefix (like "struct", "class", "union", "typedef" etc). FindTypes_Impl(ConstString(type_basename), CompilerDeclContext(), UINT_MAX, searched_symbol_files, typesmap); - typesmap.RemoveMismatchedTypes(std::string(type_scope), - std::string(type_basename), type_class, + typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class, exact_match); } else { FindTypes_Impl(name, CompilerDeclContext(), UINT_MAX, searched_symbol_files, typesmap); if (exact_match) { - std::string name_str(name.AsCString("")); - typesmap.RemoveMismatchedTypes(std::string(type_scope), name_str, + typesmap.RemoveMismatchedTypes(type_scope, name.GetStringRef(), type_class, exact_match); } } diff --git a/lldb/source/Core/ValueObjectVariable.cpp b/lldb/source/Core/ValueObjectVariable.cpp index 8e89503a8a76..4e2bd12c1053 100644 --- a/lldb/source/Core/ValueObjectVariable.cpp +++ b/lldb/source/Core/ValueObjectVariable.cpp @@ -13,7 +13,7 @@ #include "lldb/Core/Declaration.h" #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" -#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Expression/DWARFExpressionList.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" @@ -127,17 +127,16 @@ bool ValueObjectVariable::UpdateValue() { m_error.Clear(); Variable *variable = m_variable_sp.get(); - DWARFExpression &expr = variable->LocationExpression(); + DWARFExpressionList &expr_list = variable->LocationExpressionList(); if (variable->GetLocationIsConstantValueData()) { // expr doesn't contain DWARF bytes, it contains the constant variable // value bytes themselves... - if (expr.GetExpressionData(m_data)) { - if (m_data.GetDataStart() && m_data.GetByteSize()) + if (expr_list.GetExpressionData(m_data)) { + if (m_data.GetDataStart() && m_data.GetByteSize()) m_value.SetBytes(m_data.GetDataStart(), m_data.GetByteSize()); m_value.SetContext(Value::ContextType::Variable, variable); - } - else + } else m_error.SetErrorString("empty constant data"); // constant bytes can't be edited - sorry m_resolved_value.SetContext(Value::ContextType::Invalid, nullptr); @@ -151,7 +150,7 @@ bool ValueObjectVariable::UpdateValue() { m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); } - if (expr.IsLocationList()) { + if (!expr_list.IsAlwaysValidSingleExpr()) { SymbolContext sc; variable->CalculateSymbolContext(&sc); if (sc.function) @@ -160,8 +159,8 @@ bool ValueObjectVariable::UpdateValue() { target); } Value old_value(m_value); - if (expr.Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, nullptr, - nullptr, m_value, &m_error)) { + if (expr_list.Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, nullptr, + nullptr, m_value, &m_error)) { m_resolved_value = m_value; m_value.SetContext(Value::ContextType::Variable, variable); @@ -246,7 +245,7 @@ bool ValueObjectVariable::UpdateValue() { m_resolved_value.SetContext(Value::ContextType::Invalid, nullptr); } } - + return m_error.Success(); } diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 1f11907dc64c..9e6b21fc25ea 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -45,29 +45,10 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::dwarf; -static lldb::addr_t -ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, - uint32_t index) { - uint32_t index_size = dwarf_cu->GetAddressByteSize(); - dw_offset_t addr_base = dwarf_cu->GetAddrBase(); - lldb::offset_t offset = addr_base + index * index_size; - const DWARFDataExtractor &data = - dwarf_cu->GetSymbolFileDWARF().GetDWARFContext().getOrLoadAddrData(); - if (data.ValidOffsetForDataOfSize(offset, index_size)) - return data.GetMaxU64_unchecked(&offset, index_size); - return LLDB_INVALID_ADDRESS; -} - // DWARFExpression constructor -DWARFExpression::DWARFExpression() : m_module_wp(), m_data() {} - -DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp, - const DataExtractor &data, - const DWARFUnit *dwarf_cu) - : m_module_wp(), m_data(data), m_dwarf_cu(dwarf_cu) { - if (module_sp) - m_module_wp = module_sp; -} +DWARFExpression::DWARFExpression() : m_data() {} + +DWARFExpression::DWARFExpression(const DataExtractor &data) : m_data(data) {} // Destructor DWARFExpression::~DWARFExpression() = default; @@ -86,71 +67,19 @@ void DWARFExpression::UpdateValue(uint64_t const_value, m_data.SetAddressByteSize(addr_byte_size); } -void DWARFExpression::DumpLocation(Stream *s, const DataExtractor &data, - lldb::DescriptionLevel level, +void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const { - llvm::DWARFExpression(data.GetAsLLVM(), data.GetAddressByteSize()) + llvm::DWARFExpression(m_data.GetAsLLVM(), m_data.GetAddressByteSize()) .print(s->AsRawOstream(), llvm::DIDumpOptions(), abi ? &abi->GetMCRegisterInfo() : nullptr, nullptr); } -void DWARFExpression::SetLocationListAddresses(addr_t cu_file_addr, - addr_t func_file_addr) { - m_loclist_addresses = LoclistAddresses{cu_file_addr, func_file_addr}; -} - -int DWARFExpression::GetRegisterKind() { return m_reg_kind; } +RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) { m_reg_kind = reg_kind; } -bool DWARFExpression::IsLocationList() const { - return bool(m_loclist_addresses); -} - -namespace { -/// Implement enough of the DWARFObject interface in order to be able to call -/// DWARFLocationTable::dumpLocationList. We don't have access to a real -/// DWARFObject here because DWARFExpression is used in non-DWARF scenarios too. -class DummyDWARFObject final: public llvm::DWARFObject { -public: - DummyDWARFObject(bool IsLittleEndian) : IsLittleEndian(IsLittleEndian) {} - - bool isLittleEndian() const override { return IsLittleEndian; } - - llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec, - uint64_t Pos) const override { - return llvm::None; - } -private: - bool IsLittleEndian; -}; -} - -void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level, - ABI *abi) const { - if (IsLocationList()) { - // We have a location list - lldb::offset_t offset = 0; - std::unique_ptr<llvm::DWARFLocationTable> loctable_up = - m_dwarf_cu->GetLocationTable(m_data); - - llvm::MCRegisterInfo *MRI = abi ? &abi->GetMCRegisterInfo() : nullptr; - llvm::DIDumpOptions DumpOpts; - DumpOpts.RecoverableErrorHandler = [&](llvm::Error E) { - s->AsRawOstream() << "error: " << toString(std::move(E)); - }; - loctable_up->dumpLocationList( - &offset, s->AsRawOstream(), - llvm::object::SectionedAddress{m_loclist_addresses->cu_file_addr}, MRI, - DummyDWARFObject(m_data.GetByteOrder() == eByteOrderLittle), nullptr, - DumpOpts, s->GetIndentLevel() + 2); - } else { - // We have a normal location that contains DW_OP location opcodes - DumpLocation(s, m_data, level, abi); - } -} static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx, lldb::RegisterKind reg_kind, @@ -409,11 +338,10 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data, return LLDB_INVALID_OFFSET; } -lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx, +lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, + uint32_t op_addr_idx, bool &error) const { error = false; - if (IsLocationList()) - return LLDB_INVALID_ADDRESS; lldb::offset_t offset = 0; uint32_t curr_op_addr_idx = 0; while (m_data.ValidOffset(offset)) { @@ -423,19 +351,18 @@ lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx, const lldb::addr_t op_file_addr = m_data.GetAddress(&offset); if (curr_op_addr_idx == op_addr_idx) return op_file_addr; - else - ++curr_op_addr_idx; + ++curr_op_addr_idx; } else if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) { uint64_t index = m_data.GetULEB128(&offset); if (curr_op_addr_idx == op_addr_idx) { - if (!m_dwarf_cu) { + if (!dwarf_cu) { error = true; break; } - return ReadAddressFromDebugAddrSection(m_dwarf_cu, index); - } else - ++curr_op_addr_idx; + return dwarf_cu->ReadAddressFromDebugAddrSection(index); + } + ++curr_op_addr_idx; } else { const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); if (op_arg_size == LLDB_INVALID_OFFSET) { @@ -449,8 +376,6 @@ lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx, } bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) { - if (IsLocationList()) - return false; lldb::offset_t offset = 0; while (m_data.ValidOffset(offset)) { const uint8_t op = m_data.GetU8(&offset); @@ -487,11 +412,6 @@ bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) { } bool DWARFExpression::ContainsThreadLocalStorage() const { - // We are assuming for now that any thread local variable will not have a - // location list. This has been true for all thread local variables we have - // seen so far produced by any compiler. - if (IsLocationList()) - return false; lldb::offset_t offset = 0; while (m_data.ValidOffset(offset)) { const uint8_t op = m_data.GetU8(&offset); @@ -501,27 +421,18 @@ bool DWARFExpression::ContainsThreadLocalStorage() const { const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); if (op_arg_size == LLDB_INVALID_OFFSET) return false; - else - offset += op_arg_size; + offset += op_arg_size; } return false; } bool DWARFExpression::LinkThreadLocalStorage( - lldb::ModuleSP new_module_sp, std::function<lldb::addr_t(lldb::addr_t file_addr)> const &link_address_callback) { - // We are assuming for now that any thread local variable will not have a - // location list. This has been true for all thread local variables we have - // seen so far produced by any compiler. - if (IsLocationList()) - return false; - const uint32_t addr_byte_size = m_data.GetAddressByteSize(); // We have to make a copy of the data as we don't know if this data is from a // read only memory mapped buffer, so we duplicate all of the data first, // then modify it, and if all goes well, we then replace the data for this // expression. - // Make en encoder that contains a copy of the location expression data so we // can write the address into the buffer using the correct byte order. DataEncoder encoder(m_data.GetDataStart(), m_data.GetByteSize(), @@ -593,42 +504,10 @@ bool DWARFExpression::LinkThreadLocalStorage( } } - // If we linked the TLS address correctly, update the module so that when the - // expression is evaluated it can resolve the file address to a load address - // and read the - // TLS data - m_module_wp = new_module_sp; m_data.SetData(encoder.GetDataBuffer()); return true; } -bool DWARFExpression::LocationListContainsAddress(addr_t func_load_addr, - lldb::addr_t addr) const { - if (func_load_addr == LLDB_INVALID_ADDRESS || addr == LLDB_INVALID_ADDRESS) - return false; - - if (!IsLocationList()) - return false; - - return GetLocationExpression(func_load_addr, addr) != llvm::None; -} - -bool DWARFExpression::DumpLocationForAddress(Stream *s, - lldb::DescriptionLevel level, - addr_t func_load_addr, - addr_t address, ABI *abi) { - if (!IsLocationList()) { - DumpLocation(s, m_data, level, abi); - return true; - } - if (llvm::Optional<DataExtractor> expr = - GetLocationExpression(func_load_addr, address)) { - DumpLocation(s, *expr, level, abi); - return true; - } - return false; -} - static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack, ExecutionContext *exe_ctx, RegisterContext *reg_ctx, @@ -824,10 +703,10 @@ static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack, // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value // subexpresion whenever llvm does. Value result; - const DWARFExpression ¶m_expr = matched_param->LocationInCaller; + const DWARFExpressionList ¶m_expr = matched_param->LocationInCaller; if (!param_expr.Evaluate(&parent_exe_ctx, parent_frame->GetRegisterContext().get(), - /*loclist_base_load_addr=*/LLDB_INVALID_ADDRESS, + LLDB_INVALID_ADDRESS, /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr, result, error_ptr)) { LLDB_LOG(log, @@ -839,63 +718,6 @@ static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack, return true; } -bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope, - lldb::addr_t loclist_base_load_addr, - const Value *initial_value_ptr, - const Value *object_address_ptr, Value &result, - Status *error_ptr) const { - ExecutionContext exe_ctx(exe_scope); - return Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, initial_value_ptr, - object_address_ptr, result, error_ptr); -} - -bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx, - RegisterContext *reg_ctx, - lldb::addr_t func_load_addr, - const Value *initial_value_ptr, - const Value *object_address_ptr, Value &result, - Status *error_ptr) const { - ModuleSP module_sp = m_module_wp.lock(); - - if (IsLocationList()) { - Address pc; - StackFrame *frame = nullptr; - if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) { - frame = exe_ctx->GetFramePtr(); - if (!frame) - return false; - RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); - if (!reg_ctx_sp) - return false; - reg_ctx_sp->GetPCForSymbolication(pc); - } - - if (func_load_addr != LLDB_INVALID_ADDRESS) { - if (!pc.IsValid()) { - if (error_ptr) - error_ptr->SetErrorString("Invalid PC in frame."); - return false; - } - - Target *target = exe_ctx->GetTargetPtr(); - if (llvm::Optional<DataExtractor> expr = GetLocationExpression( - func_load_addr, pc.GetLoadAddress(target))) { - return DWARFExpression::Evaluate( - exe_ctx, reg_ctx, module_sp, *expr, m_dwarf_cu, m_reg_kind, - initial_value_ptr, object_address_ptr, result, error_ptr); - } - } - if (error_ptr) - error_ptr->SetErrorString("variable not available"); - return false; - } - - // Not a location list, just a single expression. - return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, m_data, - m_dwarf_cu, m_reg_kind, initial_value_ptr, - object_address_ptr, result, error_ptr); -} - namespace { /// The location description kinds described by the DWARF v5 /// specification. Composite locations are handled out-of-band and @@ -2670,7 +2492,7 @@ bool DWARFExpression::Evaluate( return false; } uint64_t index = opcodes.GetULEB128(&offset); - lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); + lldb::addr_t value = dwarf_cu->ReadAddressFromDebugAddrSection(index); stack.push_back(Scalar(value)); stack.back().SetValueType(Value::ValueType::FileAddress); } break; @@ -2690,7 +2512,7 @@ bool DWARFExpression::Evaluate( return false; } uint64_t index = opcodes.GetULEB128(&offset); - lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); + lldb::addr_t value = dwarf_cu->ReadAddressFromDebugAddrSection(index); stack.push_back(Scalar(value)); } break; @@ -2743,61 +2565,16 @@ bool DWARFExpression::Evaluate( return true; // Return true on success } -static DataExtractor ToDataExtractor(const llvm::DWARFLocationExpression &loc, - ByteOrder byte_order, uint32_t addr_size) { - auto buffer_sp = - std::make_shared<DataBufferHeap>(loc.Expr.data(), loc.Expr.size()); - return DataExtractor(buffer_sp, byte_order, addr_size); -} - -bool DWARFExpression::DumpLocations(Stream *s, lldb::DescriptionLevel level, - addr_t load_function_start, addr_t addr, - ABI *abi) { - if (!IsLocationList()) { - DumpLocation(s, m_data, level, abi); - return true; - } - bool dump_all = addr == LLDB_INVALID_ADDRESS; - llvm::ListSeparator separator; - auto callback = [&](llvm::DWARFLocationExpression loc) -> bool { - if (loc.Range && - (dump_all || (loc.Range->LowPC <= addr && addr < loc.Range->HighPC))) { - uint32_t addr_size = m_data.GetAddressByteSize(); - DataExtractor data = ToDataExtractor(loc, m_data.GetByteOrder(), - m_data.GetAddressByteSize()); - s->AsRawOstream() << separator; - s->PutCString("["); - s->AsRawOstream() << llvm::format_hex(loc.Range->LowPC, - 2 + 2 * addr_size); - s->PutCString(", "); - s->AsRawOstream() << llvm::format_hex(loc.Range->HighPC, - 2 + 2 * addr_size); - s->PutCString(") -> "); - DumpLocation(s, data, level, abi); - return dump_all; - } - return true; - }; - if (!GetLocationExpressions(load_function_start, callback)) - return false; - return true; -} - -bool DWARFExpression::GetLocationExpressions( - addr_t load_function_start, - llvm::function_ref<bool(llvm::DWARFLocationExpression)> callback) const { - if (load_function_start == LLDB_INVALID_ADDRESS) - return false; - - Log *log = GetLog(LLDBLog::Expressions); - +bool DWARFExpression::ParseDWARFLocationList( + const DWARFUnit *dwarf_cu, const DataExtractor &data, + DWARFExpressionList *location_list) { + location_list->Clear(); std::unique_ptr<llvm::DWARFLocationTable> loctable_up = - m_dwarf_cu->GetLocationTable(m_data); - - uint64_t offset = 0; + dwarf_cu->GetLocationTable(data); + Log *log = GetLog(LLDBLog::Expressions); auto lookup_addr = [&](uint32_t index) -> llvm::Optional<llvm::object::SectionedAddress> { - addr_t address = ReadAddressFromDebugAddrSection(m_dwarf_cu, index); + addr_t address = dwarf_cu->ReadAddressFromDebugAddrSection(index); if (address == LLDB_INVALID_ADDRESS) return llvm::None; return llvm::object::SectionedAddress{address}; @@ -2807,18 +2584,17 @@ bool DWARFExpression::GetLocationExpressions( LLDB_LOG_ERROR(log, loc.takeError(), "{0}"); return true; } - if (loc->Range) { - // This relocates low_pc and high_pc by adding the difference between the - // function file address, and the actual address it is loaded in memory. - addr_t slide = load_function_start - m_loclist_addresses->func_file_addr; - loc->Range->LowPC += slide; - loc->Range->HighPC += slide; - } - return callback(*loc); + auto buffer_sp = + std::make_shared<DataBufferHeap>(loc->Expr.data(), loc->Expr.size()); + DWARFExpression expr = DWARFExpression(DataExtractor( + buffer_sp, data.GetByteOrder(), data.GetAddressByteSize())); + location_list->AddExpression(loc->Range->LowPC, loc->Range->HighPC, expr); + return true; }; llvm::Error error = loctable_up->visitAbsoluteLocationList( - offset, llvm::object::SectionedAddress{m_loclist_addresses->cu_file_addr}, + 0, llvm::object::SectionedAddress{dwarf_cu->GetBaseAddress()}, lookup_addr, process_list); + location_list->Sort(); if (error) { LLDB_LOG_ERROR(log, std::move(error), "{0}"); return false; @@ -2826,23 +2602,8 @@ bool DWARFExpression::GetLocationExpressions( return true; } -llvm::Optional<DataExtractor> -DWARFExpression::GetLocationExpression(addr_t load_function_start, - addr_t addr) const { - llvm::Optional<DataExtractor> data; - auto callback = [&](llvm::DWARFLocationExpression loc) { - if (loc.Range && loc.Range->LowPC <= addr && addr < loc.Range->HighPC) { - data = ToDataExtractor(loc, m_data.GetByteOrder(), - m_data.GetAddressByteSize()); - } - return !data; - }; - GetLocationExpressions(load_function_start, callback); - return data; -} - -bool DWARFExpression::MatchesOperand(StackFrame &frame, - const Instruction::Operand &operand) { +bool DWARFExpression::MatchesOperand( + StackFrame &frame, const Instruction::Operand &operand) const { using namespace OperandMatchers; RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); @@ -2850,28 +2611,7 @@ bool DWARFExpression::MatchesOperand(StackFrame &frame, return false; } - DataExtractor opcodes; - if (IsLocationList()) { - SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction); - if (!sc.function) - return false; - - addr_t load_function_start = - sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); - if (load_function_start == LLDB_INVALID_ADDRESS) - return false; - - addr_t pc = frame.GetFrameCodeAddressForSymbolication().GetLoadAddress( - frame.CalculateTarget().get()); - - if (llvm::Optional<DataExtractor> expr = - GetLocationExpression(load_function_start, pc)) - opcodes = std::move(*expr); - else - return false; - } else - opcodes = m_data; - + DataExtractor opcodes(m_data); lldb::offset_t op_offset = 0; uint8_t opcode = opcodes.GetU8(&op_offset); @@ -2879,7 +2619,7 @@ bool DWARFExpression::MatchesOperand(StackFrame &frame, if (opcode == DW_OP_fbreg) { int64_t offset = opcodes.GetSLEB128(&op_offset); - DWARFExpression *fb_expr = frame.GetFrameBaseExpression(nullptr); + DWARFExpressionList *fb_expr = frame.GetFrameBaseExpression(nullptr); if (!fb_expr) { return false; } diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp new file mode 100644 index 000000000000..5cf722c42fa9 --- /dev/null +++ b/lldb/source/Expression/DWARFExpressionList.cpp @@ -0,0 +1,248 @@ +//===-- DWARFExpressionList.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DWARFExpressionList.h" +#include "Plugins/SymbolFile/DWARF/DWARFUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" + +using namespace lldb; +using namespace lldb_private; + +bool DWARFExpressionList::IsAlwaysValidSingleExpr() const { + return GetAlwaysValidExpr() != nullptr; +} + +const DWARFExpression * DWARFExpressionList::GetAlwaysValidExpr() const { + if (m_exprs.GetSize() != 1) + return nullptr; + const auto *expr = m_exprs.GetEntryAtIndex(0); + if (expr->base == 0 && expr->size == LLDB_INVALID_ADDRESS) + return &expr->data; + return nullptr; +} + +bool DWARFExpressionList::AddExpression(addr_t base, addr_t end, + DWARFExpression expr) { + if (IsAlwaysValidSingleExpr() || base >= end) + return false; + m_exprs.Append({base, end - base, expr}); + return true; +} + +bool DWARFExpressionList::GetExpressionData(DataExtractor &data, + lldb::addr_t func_load_addr, + lldb::addr_t file_addr) const { + if (const DWARFExpression *expr = + GetExpressionAtAddress(func_load_addr, file_addr)) + return expr->GetExpressionData(data); + return false; +} + +bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr, + lldb::addr_t addr) const { + if (IsAlwaysValidSingleExpr()) + return true; + return GetExpressionAtAddress(func_load_addr, addr) != nullptr; +} + +const DWARFExpression * +DWARFExpressionList::GetExpressionAtAddress(lldb::addr_t func_load_addr, + lldb::addr_t load_addr) const { + if (const DWARFExpression *expr = GetAlwaysValidExpr()) + return expr; + if (func_load_addr == LLDB_INVALID_ADDRESS) + func_load_addr = m_func_file_addr; + addr_t addr = load_addr - func_load_addr + m_func_file_addr; + uint32_t index = m_exprs.FindEntryIndexThatContains(addr); + if (index == UINT32_MAX) + return nullptr; + return &m_exprs.GetEntryAtIndex(index)->data; +} + +DWARFExpression * +DWARFExpressionList::GetMutableExpressionAtAddress(lldb::addr_t func_load_addr, + lldb::addr_t load_addr) { + if (IsAlwaysValidSingleExpr()) + return &m_exprs.GetMutableEntryAtIndex(0)->data; + if (func_load_addr == LLDB_INVALID_ADDRESS) + func_load_addr = m_func_file_addr; + addr_t addr = load_addr - func_load_addr + m_func_file_addr; + uint32_t index = m_exprs.FindEntryIndexThatContains(addr); + if (index == UINT32_MAX) + return nullptr; + return &m_exprs.GetMutableEntryAtIndex(index)->data; +} + +bool DWARFExpressionList::ContainsThreadLocalStorage() const { + // We are assuming for now that any thread local variable will not have a + // location list. This has been true for all thread local variables we have + // seen so far produced by any compiler. + if (!IsAlwaysValidSingleExpr()) + return false; + + const DWARFExpression &expr = m_exprs.GetEntryRef(0).data; + return expr.ContainsThreadLocalStorage(); +} + +bool DWARFExpressionList::LinkThreadLocalStorage( + lldb::ModuleSP new_module_sp, + std::function<lldb::addr_t(lldb::addr_t file_addr)> const + &link_address_callback) { + // We are assuming for now that any thread local variable will not have a + // location list. This has been true for all thread local variables we have + // seen so far produced by any compiler. + if (!IsAlwaysValidSingleExpr()) + return false; + + DWARFExpression &expr = m_exprs.GetEntryRef(0).data; + // If we linked the TLS address correctly, update the module so that when the + // expression is evaluated it can resolve the file address to a load address + // and read the TLS data + if (expr.LinkThreadLocalStorage(link_address_callback)) + m_module_wp = new_module_sp; + return true; +} + +bool DWARFExpressionList::MatchesOperand( + StackFrame &frame, const Instruction::Operand &operand) const { + RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); + if (!reg_ctx_sp) { + return false; + } + const DWARFExpression *expr = nullptr; + if (IsAlwaysValidSingleExpr()) + expr = &m_exprs.GetEntryAtIndex(0)->data; + else { + SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction); + if (!sc.function) + return false; + + addr_t load_function_start = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + if (load_function_start == LLDB_INVALID_ADDRESS) + return false; + + addr_t pc = frame.GetFrameCodeAddressForSymbolication().GetFileAddress(); + expr = GetExpressionAtAddress(LLDB_INVALID_ADDRESS, pc); + } + if (!expr) + return false; + return expr->MatchesOperand(frame, operand); +} + +bool DWARFExpressionList::DumpLocations(Stream *s, lldb::DescriptionLevel level, + lldb::addr_t func_load_addr, + lldb::addr_t file_addr, + ABI *abi) const { + llvm::raw_ostream &os = s->AsRawOstream(); + llvm::ListSeparator separator; + if (const DWARFExpression *expr = GetAlwaysValidExpr()) { + expr->DumpLocation(s, level, abi); + return true; + } + for (const Entry &entry : *this) { + addr_t load_base = entry.GetRangeBase() + func_load_addr - m_func_file_addr; + addr_t load_end = entry.GetRangeEnd() + func_load_addr - m_func_file_addr; + if (file_addr != LLDB_INVALID_ADDRESS && + (file_addr < load_base || file_addr >= load_end)) + continue; + const auto &expr = entry.data; + DataExtractor data; + expr.GetExpressionData(data); + uint32_t addr_size = data.GetAddressByteSize(); + + os << separator; + os << "["; + os << llvm::format_hex(load_base, 2 + 2 * addr_size); + os << ", "; + os << llvm::format_hex(load_end, 2 + 2 * addr_size); + os << ") -> "; + expr.DumpLocation(s, level, abi); + if (file_addr != LLDB_INVALID_ADDRESS) + break; + } + return true; +} + +void DWARFExpressionList::GetDescription(Stream *s, + lldb::DescriptionLevel level, + ABI *abi) const { + llvm::raw_ostream &os = s->AsRawOstream(); + if (IsAlwaysValidSingleExpr()) { + m_exprs.Back()->data.DumpLocation(s, level, abi); + return; + } + os << llvm::format("0x%8.8" PRIx64 ": ", 0); + for (const Entry &entry : *this) { + const auto &expr = entry.data; + DataExtractor data; + expr.GetExpressionData(data); + uint32_t addr_size = data.GetAddressByteSize(); + os << "\n"; + os.indent(s->GetIndentLevel() + 2); + os << "["; + llvm::DWARFFormValue::dumpAddress(os, addr_size, entry.GetRangeBase()); + os << ", "; + llvm::DWARFFormValue::dumpAddress(os, addr_size, entry.GetRangeEnd()); + os << "): "; + expr.DumpLocation(s, level, abi); + } +} + +bool DWARFExpressionList::Evaluate(ExecutionContext *exe_ctx, + RegisterContext *reg_ctx, + lldb::addr_t func_load_addr, + const Value *initial_value_ptr, + const Value *object_address_ptr, + Value &result, Status *error_ptr) const { + ModuleSP module_sp = m_module_wp.lock(); + DataExtractor data; + RegisterKind reg_kind; + DWARFExpression expr; + if (IsAlwaysValidSingleExpr()) { + expr = m_exprs.Back()->data; + } else { + Address pc; + StackFrame *frame = nullptr; + if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) { + if (exe_ctx) + frame = exe_ctx->GetFramePtr(); + if (!frame) + return false; + RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); + if (!reg_ctx_sp) + return false; + reg_ctx_sp->GetPCForSymbolication(pc); + } + + if (!pc.IsValid()) { + if (error_ptr) + error_ptr->SetErrorString("Invalid PC in frame."); + return false; + } + addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr()); + const DWARFExpression *entry = + GetExpressionAtAddress(func_load_addr, pc_load_addr); + if (!entry) { + if (error_ptr) { + error_ptr->SetErrorString("variable not available"); + } + return false; + } + expr = *entry; + } + expr.GetExpressionData(data); + reg_kind = expr.GetRegisterKind(); + return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, data, + m_dwarf_cu, reg_kind, initial_value_ptr, + object_address_ptr, result, error_ptr); +} diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 49fa72f7112d..6b710084faf7 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -200,7 +200,9 @@ Status IRExecutionUnit::DisassembleFunction(Stream &stream, UINT32_MAX, false, false); InstructionList &instruction_list = disassembler_sp->GetInstructionList(); - instruction_list.Dump(&stream, true, true, &exe_ctx); + instruction_list.Dump(&stream, true, true, /*show_control_flow_kind=*/true, + &exe_ctx); + return ret; } diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 9ee2d983ddfc..965a96b7f909 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -520,7 +520,7 @@ public: if (data.GetByteSize() < m_variable_sp->GetType()->GetByteSize(scope)) { if (data.GetByteSize() == 0 && - !m_variable_sp->LocationExpression().IsValid()) { + !m_variable_sp->LocationExpressionList().IsValid()) { err.SetErrorStringWithFormat("the variable '%s' has no location, " "it may have been optimized out", m_variable_sp->GetName().AsCString()); diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index c92fec53a55e..910d740625e9 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -547,7 +547,7 @@ CommandObject::LookupArgumentName(llvm::StringRef arg_name) { const ArgumentTableEntry *table = GetArgumentTable(); for (int i = 0; i < eArgTypeLastArg; ++i) if (arg_name == table[i].arg_name) - return_type = g_arguments_data[i].arg_type; + return_type = GetArgumentTable()[i].arg_type; return return_type; } @@ -924,14 +924,14 @@ const char *CommandObject::GetArgumentTypeAsCString( const lldb::CommandArgumentType arg_type) { assert(arg_type < eArgTypeLastArg && "Invalid argument type passed to GetArgumentTypeAsCString"); - return g_arguments_data[arg_type].arg_name; + return GetArgumentTable()[arg_type].arg_name; } const char *CommandObject::GetArgumentDescriptionAsCString( const lldb::CommandArgumentType arg_type) { assert(arg_type < eArgTypeLastArg && "Invalid argument type passed to GetArgumentDescriptionAsCString"); - return g_arguments_data[arg_type].help_text; + return GetArgumentTable()[arg_type].help_text; } Target &CommandObject::GetDummyTarget() { @@ -1041,7 +1041,7 @@ static llvm::StringRef arch_helper() { return g_archs_help.GetString(); } -CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = { +static constexpr CommandObject::ArgumentTableEntry g_arguments_data[] = { // clang-format off { eArgTypeAddress, "address", CommandCompletions::eNoCompletion, { nullptr, false }, "A valid address in the target program's execution space." }, { eArgTypeAddressOrExpression, "address-expression", CommandCompletions::eNoCompletion, { nullptr, false }, "An expression that resolves to an address." }, @@ -1134,17 +1134,18 @@ CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = { { eArgTypeSaveCoreStyle, "corefile-style", CommandCompletions::eNoCompletion, { nullptr, false }, "The type of corefile that lldb will try to create, dependant on this target's capabilities." }, { eArgTypeLogHandler, "log-handler", CommandCompletions::eNoCompletion, { nullptr, false }, "The log handle that will be used to write out log messages." }, { eArgTypeSEDStylePair, "substitution-pair", CommandCompletions::eNoCompletion, { nullptr, false }, "A sed-style pattern and target pair." }, + { eArgTypeRecognizerID, "frame-recognizer-id", CommandCompletions::eNoCompletion, { nullptr, false }, "The ID for a stack frame recognizer." }, { eArgTypeConnectURL, "process-connect-url", CommandCompletions::eNoCompletion, { nullptr, false }, "A URL-style specification for a remote connection." }, { eArgTypeTargetID, "target-id", CommandCompletions::eNoCompletion, { nullptr, false }, "The index ID for an lldb Target." }, { eArgTypeStopHookID, "stop-hook-id", CommandCompletions::eNoCompletion, { nullptr, false }, "The ID you receive when you create a stop-hook." } // clang-format on }; +static_assert( + (sizeof(g_arguments_data) / sizeof(CommandObject::ArgumentTableEntry)) == + eArgTypeLastArg, + "g_arguments_data out of sync with CommandArgumentType enumeration"); + const CommandObject::ArgumentTableEntry *CommandObject::GetArgumentTable() { - // If this assertion fires, then the table above is out of date with the - // CommandArgumentType enumeration - static_assert((sizeof(CommandObject::g_arguments_data) / - sizeof(CommandObject::ArgumentTableEntry)) == eArgTypeLastArg, - ""); - return CommandObject::g_arguments_data; + return g_arguments_data; } diff --git a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h index e74b9126404e..a9c2ed9c2f14 100644 --- a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h +++ b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h @@ -40,10 +40,15 @@ public: bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override; - // In Windows_x86_64 ABI, stack will always be maintained 16-byte aligned + // In Windows_x86_64 ABI requires that the stack will be maintained 16-byte + // aligned. + // When ntdll invokes callbacks such as KiUserExceptionDispatcher or + // KiUserCallbackDispatcher, those functions won't have a properly 16-byte + // aligned stack - but tolerate unwinding through them by relaxing the + // requirement to 8 bytes. bool CallFrameAddressIsValid(lldb::addr_t cfa) override { - if (cfa & (16ull - 1ull)) - return false; // Not 16 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned if (cfa == 0) return false; // Zero is not a valid stack address return true; diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp index c85c66442510..a774d5b61cfe 100644 --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -1181,11 +1181,7 @@ DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch, // If any AArch64 variant, enable latest ISA with all extensions. if (triple.isAArch64()) { - features_str += "+v9.3a,"; - std::vector<llvm::StringRef> features; - // Get all possible features - llvm::AArch64::getExtensionFeatures(-1, features); - features_str += llvm::join(features, ","); + features_str += "+all,"; if (triple.getVendor() == llvm::Triple::Apple) cpu = "apple-latest"; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index cc45871bcd71..4305a9982343 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -772,10 +772,6 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) { return; AddContextClassType(context, TypeFromUser(m_ctx_obj->GetCompilerType())); - - m_struct_vars->m_object_pointer_type = - TypeFromUser(ctx_obj_ptr->GetCompilerType()); - return; } @@ -810,18 +806,6 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) { class_qual_type.getAsString()); AddContextClassType(context, class_user_type); - - 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(), - function_decl_ctx.GetTypeSystem()); - - m_struct_vars->m_object_pointer_type = self_user_type; - } return; } @@ -852,8 +836,6 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) { ClangUtil::GetQualType(pointee_type).getAsString()); AddContextClassType(context, pointee_type); - TypeFromUser this_user_type(this_type->GetFullCompilerType()); - m_struct_vars->m_object_pointer_type = this_user_type; } } @@ -869,10 +851,6 @@ void ClangExpressionDeclMap::LookUpLldbObjCClass(NameSearchContext &context) { return; AddOneType(context, TypeFromUser(m_ctx_obj->GetCompilerType())); - - m_struct_vars->m_object_pointer_type = - TypeFromUser(ctx_obj_ptr->GetCompilerType()); - return; } @@ -917,28 +895,6 @@ void ClangExpressionDeclMap::LookUpLldbObjCClass(NameSearchContext &context) { ClangUtil::ToString(interface_type)); AddOneType(context, class_user_type); - - 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(), - function_decl_ctx.GetTypeSystem()); - - 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(), - function_decl_ctx.GetTypeSystem()); - - m_struct_vars->m_object_pointer_type = self_user_type; - } - return; } // This branch will get hit if we are executing code in the context of @@ -981,10 +937,6 @@ void ClangExpressionDeclMap::LookUpLldbObjCClass(NameSearchContext &context) { TypeFromUser class_user_type(self_clang_type); AddOneType(context, class_user_type); - - TypeFromUser self_user_type(self_type->GetFullCompilerType()); - - m_struct_vars->m_object_pointer_type = self_user_type; } void ClangExpressionDeclMap::LookupLocalVarNamespace( @@ -1485,15 +1437,14 @@ bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var, return false; } - DWARFExpression &var_location_expr = var->LocationExpression(); + DWARFExpressionList &var_location_list = var->LocationExpressionList(); 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)) { + if (var_location_list.GetExpressionData(const_value_extractor)) { var_location = Value(const_value_extractor.GetDataStart(), const_value_extractor.GetByteSize()); var_location.SetValueType(Value::ValueType::HostAddress); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h index e39dc587bc43..f968f859cc72 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -353,7 +353,7 @@ private: /// The following values contain layout information for the materialized /// struct, but are not specific to a single materialization struct StructVars { - StructVars() : m_result_name(), m_object_pointer_type(nullptr, nullptr) {} + StructVars() = default; lldb::offset_t m_struct_alignment = 0; ///< The alignment of the struct in bytes. @@ -364,8 +364,6 @@ private: /// 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<StructVars> m_struct_vars; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 82f825871593..89bee3e000c0 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -911,6 +911,14 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "std::map iterator synthetic children", ConstString("^std::__[[:alnum:]]+::__map_iterator<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEndCreator, + "std::unordered_map iterator synthetic children", + ConstString("^std::__[[:alnum:]]+::__hash_map_(const_)?iterator<.+>$"), + stl_synth_flags, true); } static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index eaaa16413b1e..3b04b3a1b2ac 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -27,6 +27,7 @@ #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/lldb-enumerations.h" #include <tuple> using namespace lldb; @@ -283,6 +284,22 @@ bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { llvm::dyn_cast_or_null<TypeSystemClang>(pair_type.GetTypeSystem()); if (!ast_ctx) return false; + + // Mimick layout of std::__tree_iterator::__ptr_ and read it in + // from process memory. + // + // The following shows the contiguous block of memory: + // + // +-----------------------------+ class __tree_end_node + // __ptr_ | pointer __left_; | + // +-----------------------------+ class __tree_node_base + // | pointer __right_; | + // | __parent_pointer __parent_; | + // | bool __is_black_; | + // +-----------------------------+ class __tree_node + // | __node_value_type __value_; | <<< our key/value pair + // +-----------------------------+ + // CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( ConstString(), {{"ptr0", @@ -359,6 +376,156 @@ lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( : nullptr); } +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + Update() { + m_pair_sp.reset(); + m_iter_ptr = nullptr; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return false; + + if (!valobj_sp) + return false; + + auto exprPathOptions = ValueObject::GetValueForExpressionPathOptions() + .DontCheckDotVsArrowSyntax() + .SetSyntheticChildrenTraversal( + ValueObject::GetValueForExpressionPathOptions:: + SyntheticChildrenTraversal::None); + + // This must be a ValueObject* because it is a child of the ValueObject we + // are producing children for it if were a ValueObjectSP, we would end up + // with a loop (iterator -> synthetic -> child -> parent == iterator) and + // that would in turn leak memory by never allowing the ValueObjects to die + // and free their memory. + m_iter_ptr = + valobj_sp + ->GetValueForExpressionPath(".__i_.__node_", nullptr, nullptr, + exprPathOptions, nullptr) + .get(); + + if (m_iter_ptr) { + auto iter_child( + valobj_sp->GetChildMemberWithName(ConstString("__i_"), true)); + if (!iter_child) { + m_iter_ptr = nullptr; + return false; + } + + CompilerType node_type(iter_child->GetCompilerType() + .GetTypeTemplateArgument(0) + .GetPointeeType()); + + CompilerType pair_type(node_type.GetTypeTemplateArgument(0)); + + std::string name; + uint64_t bit_offset_ptr; + uint32_t bitfield_bit_size_ptr; + bool is_bitfield_ptr; + + pair_type = pair_type.GetFieldAtIndex( + 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); + if (!pair_type) { + m_iter_ptr = nullptr; + return false; + } + + uint64_t addr = m_iter_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + m_iter_ptr = nullptr; + + if (addr == 0 || addr == LLDB_INVALID_ADDRESS) + return false; + + TypeSystemClang *ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(pair_type.GetTypeSystem()); + if (!ast_ctx) + return false; + + // Mimick layout of std::__hash_iterator::__node_ and read it in + // from process memory. + // + // The following shows the contiguous block of memory: + // + // +-----------------------------+ class __hash_node_base + // __node_ | __next_pointer __next_; | + // +-----------------------------+ class __hash_node + // | size_t __hash_; | + // | __node_value_type __value_; | <<< our key/value pair + // +-----------------------------+ + // + CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( + ConstString(), + {{"__next_", + ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"__hash_", ast_ctx->GetBasicType(lldb::eBasicTypeUnsignedLongLong)}, + {"__value_", pair_type}}); + llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr); + if (!size) + return false; + WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); + ProcessSP process_sp(target_sp->GetProcessSP()); + Status error; + process_sp->ReadMemory(addr, buffer_sp->GetBytes(), + buffer_sp->GetByteSize(), error); + if (error.Fail()) + return false; + DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + auto pair_sp = CreateValueObjectFromData( + "pair", extractor, valobj_sp->GetExecutionContextRef(), tree_node_type); + if (pair_sp) + m_pair_sp = pair_sp->GetChildAtIndex(2, true); + } + + return false; +} + +size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx, true); + return lldb::ValueObjectSP(); +} + +bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} + /* (lldb) fr var ibeg --raw --ptr-depth 1 -T (std::__1::__wrap_iter<int *>) ibeg = { @@ -547,101 +714,77 @@ bool lldb_private::formatters::LibcxxContainerSummaryProvider( } /// The field layout in a libc++ string (cap, side, data or data, size, cap). -enum LibcxxStringLayoutMode { - eLibcxxStringLayoutModeCSD = 0, - eLibcxxStringLayoutModeDSC = 1, - eLibcxxStringLayoutModeInvalid = 0xffff -}; +namespace { +enum class StringLayout { CSD, DSC }; +} /// Determine the size in bytes of \p valobj (a libc++ std::string object) and /// extract its data payload. Return the size + payload pair. // TODO: Support big-endian architectures. static llvm::Optional<std::pair<uint64_t, ValueObjectSP>> ExtractLibcxxStringInfo(ValueObject &valobj) { - ValueObjectSP dataval_sp(valobj.GetChildAtIndexPath({0, 0, 0, 0})); - if (!dataval_sp) + ValueObjectSP valobj_r_sp = + valobj.GetChildMemberWithName(ConstString("__r_"), /*can_create=*/true); + if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) return {}; - if (!dataval_sp->GetError().Success()) + + // __r_ is a compressed_pair of the actual data and the allocator. The data we + // want is in the first base class. + ValueObjectSP valobj_r_base_sp = + valobj_r_sp->GetChildAtIndex(0, /*can_create=*/true); + if (!valobj_r_base_sp) return {}; - ValueObjectSP layout_decider( - dataval_sp->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0}))); + ValueObjectSP valobj_rep_sp = valobj_r_base_sp->GetChildMemberWithName( + ConstString("__value_"), /*can_create=*/true); + if (!valobj_rep_sp) + return {}; - // this child should exist - if (!layout_decider) + ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(ConstString("__l"), + /*can_create=*/true); + if (!l) return {}; - ConstString g_data_name("__data_"); - ConstString g_size_name("__size_"); + StringLayout layout = l->GetIndexOfChildWithName(ConstString("__data_")) == 0 + ? StringLayout::DSC + : StringLayout::CSD; + bool short_mode = false; // this means the string is in short-mode and the // data is stored inline bool using_bitmasks = true; // Whether the class uses bitmasks for the mode // flag (pre-D123580). uint64_t size; - LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) - ? eLibcxxStringLayoutModeDSC - : eLibcxxStringLayoutModeCSD; uint64_t size_mode_value = 0; - ValueObjectSP short_sp(dataval_sp->GetChildAtIndex(1, true)); + ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName( + ConstString("__s"), /*can_create=*/true); if (!short_sp) return {}; - ValueObjectSP short_fields_sp; ValueObjectSP is_long = short_sp->GetChildMemberWithName(ConstString("__is_long_"), true); - if (is_long) { - short_fields_sp = short_sp; - } else { - // After D128285, we need to access the `__is_long_` and `__size_` fields - // from a packed anonymous struct - short_fields_sp = short_sp->GetChildAtIndex(0, true); - is_long = short_sp->GetChildMemberWithName(ConstString("__is_long_"), true); - } + ValueObjectSP size_sp = + short_sp->GetChildAtNamePath({ConstString("__size_")}); + if (!size_sp) + return {}; if (is_long) { using_bitmasks = false; short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); - if (ValueObjectSP size_member = - dataval_sp->GetChildAtNamePath({ConstString("__s"), ConstString("__size_")})) - size = size_member->GetValueAsUnsigned(/*fail_value=*/0); - else - return {}; - } else if (layout == eLibcxxStringLayoutModeDSC) { - llvm::SmallVector<size_t, 3> size_mode_locations[] = { - {1, 2}, // Post-c3d0205ee771 layout. This was in use for only a brief - // period, so we can delete it if it becomes a burden. - {1, 1, 0}, - {1, 1, 1}, - }; - ValueObjectSP size_mode; - for (llvm::ArrayRef<size_t> loc : size_mode_locations) { - size_mode = dataval_sp->GetChildAtIndexPath(loc); - if (size_mode && size_mode->GetName() == g_size_name) - break; - } - - if (!size_mode) - return {}; - - size_mode_value = (size_mode->GetValueAsUnsigned(0)); - short_mode = ((size_mode_value & 0x80) == 0); + size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); } else { - ValueObjectSP size_mode(dataval_sp->GetChildAtIndexPath({1, 0, 0})); - if (!size_mode) - return {}; - - size_mode_value = (size_mode->GetValueAsUnsigned(0)); - short_mode = ((size_mode_value & 1) == 0); + // The string mode is encoded in the size field. + size_mode_value = size_sp->GetValueAsUnsigned(0); + uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; + short_mode = (size_mode_value & mode_mask) == 0; } if (short_mode) { ValueObjectSP location_sp = - short_sp->GetChildMemberWithName(g_data_name, true); + short_sp->GetChildMemberWithName(ConstString("__data_"), true); if (using_bitmasks) - size = (layout == eLibcxxStringLayoutModeDSC) - ? size_mode_value - : ((size_mode_value >> 1) % 256); + size = (layout == StringLayout::DSC) ? size_mode_value + : ((size_mode_value >> 1) % 256); // When the small-string optimization takes place, the data must fit in the // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's @@ -656,10 +799,6 @@ ExtractLibcxxStringInfo(ValueObject &valobj) { return std::make_pair(size, location_sp); } - ValueObjectSP l(dataval_sp->GetChildAtIndex(0, true)); - if (!l) - return {}; - // we can use the layout_decider object as the data pointer ValueObjectSP location_sp = l->GetChildMemberWithName(ConstString("__data_"), /*can_create=*/true); @@ -667,19 +806,11 @@ ExtractLibcxxStringInfo(ValueObject &valobj) { l->GetChildMemberWithName(ConstString("__size_"), /*can_create=*/true); ValueObjectSP capacity_vo = l->GetChildMemberWithName(ConstString("__cap_"), /*can_create=*/true); - if (!capacity_vo) { - // After D128285, we need to access the `__cap_` field from a packed - // anonymous struct - if (ValueObjectSP packed_fields_sp = l->GetChildAtIndex(0, true)) { - ValueObjectSP capacity_vo = packed_fields_sp->GetChildMemberWithName( - ConstString("__cap_"), /*can_create=*/true); - } - } if (!size_vo || !location_sp || !capacity_vo) return {}; size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); - if (!using_bitmasks && layout == eLibcxxStringLayoutModeCSD) + if (!using_bitmasks && layout == StringLayout::CSD) capacity *= 2; if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || capacity < size) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index b4e789e65b51..b5ade4af8574 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -103,6 +103,56 @@ SyntheticChildrenFrontEnd * LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +/// Formats libcxx's std::unordered_map iterators +/// +/// In raw form a std::unordered_map::iterator is represented as follows: +/// +/// (lldb) var it --raw --ptr-depth 1 +/// (std::__1::__hash_map_iterator< +/// std::__1::__hash_iterator< +/// std::__1::__hash_node< +/// std::__1::__hash_value_type< +/// std::__1::basic_string<char, std::__1::char_traits<char>, +/// std::__1::allocator<char> >, std::__1::basic_string<char, +/// std::__1::char_traits<char>, std::__1::allocator<char> > >, +/// void *> *> >) +/// it = { +/// __i_ = { +/// __node_ = 0x0000600001700040 { +/// __next_ = 0x0000600001704000 +/// } +/// } +/// } +class LibCxxUnorderedMapIteratorSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_iter_ptr = nullptr; ///< Held, not owned. Child of iterator + ///< ValueObject supplied at construction. + + lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair + ///< that the iterator currently points + ///< to. +}; + +SyntheticChildrenFrontEnd * +LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + SyntheticChildrenFrontEnd * LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index a2522372f5af..9fe222eceedc 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -40,6 +40,8 @@ typedef struct ar_hdr { using namespace lldb; using namespace lldb_private; +using namespace llvm::object; + LLDB_PLUGIN_DEFINE(ObjectContainerBSDArchive) ObjectContainerBSDArchive::Object::Object() : ar_name() {} @@ -55,6 +57,74 @@ void ObjectContainerBSDArchive::Object::Clear() { file_size = 0; } +lldb::offset_t ObjectContainerBSDArchive::Object::ExtractFromThin( + const DataExtractor &data, lldb::offset_t offset, + llvm::StringRef stringTable) { + size_t ar_name_len = 0; + std::string str; + char *err; + + // File header + // + // The common format is as follows. + // + // Offset Length Name Format + // 0 16 File name ASCII right padded with spaces (no spaces + // allowed in file name) + // 16 12 File mod Decimal as cstring right padded with + // spaces + // 28 6 Owner ID Decimal as cstring right padded with + // spaces + // 34 6 Group ID Decimal as cstring right padded with + // spaces + // 40 8 File mode Octal as cstring right padded with + // spaces + // 48 10 File byte size Decimal as cstring right padded with + // spaces + // 58 2 File magic 0x60 0x0A + + // Make sure there is enough data for the file header and bail if not + if (!data.ValidOffsetForDataOfSize(offset, 60)) + return LLDB_INVALID_OFFSET; + + str.assign((const char *)data.GetData(&offset, 16), 16); + if (!(llvm::StringRef(str).startswith("//") || stringTable.empty())) { + // Strip off any trailing spaces. + const size_t last_pos = str.find_last_not_of(' '); + if (last_pos != std::string::npos) { + if (last_pos + 1 < 16) + str.erase(last_pos + 1); + } + int start = strtoul(str.c_str() + 1, &err, 10); + int end = stringTable.find('\n', start); + str.assign(stringTable.data() + start, end - start - 1); + ar_name.SetCString(str.c_str()); + } + + str.assign((const char *)data.GetData(&offset, 12), 12); + modification_time = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + uid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + gid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 8), 8); + mode = strtoul(str.c_str(), &err, 8); + + str.assign((const char *)data.GetData(&offset, 10), 10); + size = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) { + file_offset = offset; + file_size = size - ar_name_len; + return offset; + } + return LLDB_INVALID_OFFSET; +} + lldb::offset_t ObjectContainerBSDArchive::Object::Extract(const DataExtractor &data, lldb::offset_t offset) { @@ -136,9 +206,10 @@ ObjectContainerBSDArchive::Object::Extract(const DataExtractor &data, ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec &arch, const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, - lldb_private::DataExtractor &data) + lldb_private::DataExtractor &data, + ArchiveType archive_type) : m_arch(arch), m_modification_time(time), m_file_offset(file_offset), - m_objects(), m_data(data) {} + m_objects(), m_data(data), m_archive_type(archive_type) {} ObjectContainerBSDArchive::Archive::~Archive() = default; @@ -163,6 +234,48 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { // Now sort all of the object name pointers m_object_name_to_index_map.Sort(); + } else if (str == ThinArchiveMagic) { + Object obj; + size_t obj_idx; + + // Retrieve symbol table + offset = obj.ExtractFromThin(data, offset, ""); + if (offset == LLDB_INVALID_OFFSET) + return m_objects.size(); + obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + offset += obj.file_size; + obj.Clear(); + + // Retrieve string table + offset = obj.ExtractFromThin(data, offset, ""); + if (offset == LLDB_INVALID_OFFSET) + return m_objects.size(); + obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + // Extract string table + llvm::StringRef strtab((const char *)data.GetData(&offset, obj.size), + obj.size); + obj.Clear(); + + // Retrieve object files + do { + offset = obj.ExtractFromThin(data, offset, strtab); + if (offset == LLDB_INVALID_OFFSET) + break; + obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + obj.Clear(); + } while (data.ValidOffset(offset)); + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort(); } return m_objects.size(); } @@ -237,8 +350,9 @@ ObjectContainerBSDArchive::Archive::shared_ptr ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile( const FileSpec &file, const ArchSpec &arch, const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, - DataExtractor &data) { - shared_ptr archive_sp(new Archive(arch, time, file_offset, data)); + DataExtractor &data, ArchiveType archive_type) { + shared_ptr archive_sp( + new Archive(arch, time, file_offset, data, archive_type)); if (archive_sp) { const size_t num_objects = archive_sp->ParseObjects(); if (num_objects > 0) { @@ -288,7 +402,8 @@ ObjectContainer *ObjectContainerBSDArchive::CreateInstance( // contents for the archive and cache it DataExtractor data; data.SetData(data_sp, data_offset, length); - if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) { + ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data); + if (file && data_sp && archive_type != ArchiveType::Invalid) { LLDB_SCOPED_TIMERF( "ObjectContainerBSDArchive::CreateInstance (module = %s, file = " "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", @@ -312,7 +427,7 @@ ObjectContainer *ObjectContainerBSDArchive::CreateInstance( std::unique_ptr<ObjectContainerBSDArchive> container_up( new ObjectContainerBSDArchive(module_sp, archive_data_sp, archive_data_offset, file, file_offset, - length)); + length, archive_type)); if (container_up) { if (archive_sp) { @@ -331,7 +446,8 @@ ObjectContainer *ObjectContainerBSDArchive::CreateInstance( if (archive_sp) { std::unique_ptr<ObjectContainerBSDArchive> container_up( new ObjectContainerBSDArchive(module_sp, data_sp, data_offset, file, - file_offset, length)); + file_offset, length, + archive_sp->GetArchiveType())); if (container_up) { // We already have this archive in our cache, use it @@ -343,23 +459,35 @@ ObjectContainer *ObjectContainerBSDArchive::CreateInstance( return nullptr; } -bool ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) { +ArchiveType +ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) { uint32_t offset = 0; const char *armag = (const char *)data.PeekData(offset, sizeof(ar_hdr)); - if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) { + if (armag == nullptr) + return ArchiveType::Invalid; + if (::strncmp(armag, ARMAG, SARMAG) == 0) { armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; if (strncmp(armag, ARFMAG, 2) == 0) - return true; + return ArchiveType::Archive; + } else if (::strncmp(armag, ThinArchiveMagic, strlen(ThinArchiveMagic)) == + 0) { + armag += offsetof(struct ar_hdr, ar_fmag) + strlen(ThinArchiveMagic); + if (strncmp(armag, ARFMAG, 2) == 0) { + return ArchiveType::ThinArchive; + } } - return false; + return ArchiveType::Invalid; } ObjectContainerBSDArchive::ObjectContainerBSDArchive( const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, - lldb::offset_t file_offset, lldb::offset_t size) + lldb::offset_t file_offset, lldb::offset_t size, ArchiveType archive_type) : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset), - m_archive_sp() {} + m_archive_sp() { + m_archive_type = archive_type; +} + void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr &archive_sp) { m_archive_sp = archive_sp; } @@ -373,7 +501,7 @@ bool ObjectContainerBSDArchive::ParseHeader() { if (module_sp) { m_archive_sp = Archive::ParseAndCacheArchiveForFile( m_file, module_sp->GetArchitecture(), - module_sp->GetModificationTime(), m_offset, m_data); + module_sp->GetModificationTime(), m_offset, m_data, m_archive_type); } // Clear the m_data that contains the entire archive data and let our // m_archive_sp hold onto the data. @@ -407,6 +535,19 @@ void ObjectContainerBSDArchive::Dump(Stream *s) const { s->EOL(); } +FileSpec GetChildFileSpecificationsFromThin(llvm::StringRef childPath, + const FileSpec &parentFileSpec) { + llvm::SmallString<128> FullPath; + if (llvm::sys::path::is_absolute(childPath)) { + FullPath = childPath; + } else { + FullPath = parentFileSpec.GetDirectory().GetStringRef(); + llvm::sys::path::append(FullPath, childPath); + } + FileSpec child = FileSpec(FullPath.str(), llvm::sys::path::Style::posix); + return child; +} + ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) { ModuleSP module_sp(GetModule()); if (module_sp) { @@ -414,6 +555,22 @@ ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) { Object *object = m_archive_sp->FindObject( module_sp->GetObjectName(), module_sp->GetObjectModificationTime()); if (object) { + if (m_archive_type == ArchiveType::ThinArchive) { + // Set file to child object file + FileSpec child = GetChildFileSpecificationsFromThin( + object->ar_name.GetStringRef(), m_file); + lldb::offset_t file_offset = 0; + lldb::offset_t file_size = object->size; + std::shared_ptr<DataBuffer> child_data_sp = + FileSystem::Instance().CreateDataBuffer(child, file_size, + file_offset); + if (child_data_sp->GetByteSize() != object->file_size) + return ObjectFileSP(); + lldb::offset_t data_offset = 0; + return ObjectFile::FindPlugin( + module_sp, &child, m_offset + object->file_offset, + object->file_size, child_data_sp, data_offset); + } lldb::offset_t data_offset = object->file_offset; return ObjectFile::FindPlugin( module_sp, file, m_offset + object->file_offset, object->file_size, @@ -434,7 +591,8 @@ size_t ObjectContainerBSDArchive::GetModuleSpecifications( // contents for the archive and cache it DataExtractor data; data.SetData(data_sp, data_offset, data_sp->GetByteSize()); - if (!file || !data_sp || !ObjectContainerBSDArchive::MagicBytesMatch(data)) + ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data); + if (!file || !data_sp || archive_type == ArchiveType::Invalid) return 0; const size_t initial_count = specs.GetSize(); @@ -449,7 +607,7 @@ size_t ObjectContainerBSDArchive::GetModuleSpecifications( if (data_sp) { data.SetData(data_sp, 0, data_sp->GetByteSize()); archive_sp = Archive::ParseAndCacheArchiveForFile( - file, ArchSpec(), file_mod_time, file_offset, data); + file, ArchSpec(), file_mod_time, file_offset, data, archive_type); } } @@ -458,6 +616,24 @@ size_t ObjectContainerBSDArchive::GetModuleSpecifications( for (size_t idx = 0; idx < num_objects; ++idx) { const Object *object = archive_sp->GetObjectAtIndex(idx); if (object) { + if (archive_sp->GetArchiveType() == ArchiveType::ThinArchive) { + if (object->ar_name.IsEmpty()) + continue; + FileSpec child = GetChildFileSpecificationsFromThin( + object->ar_name.GetStringRef(), file); + if (ObjectFile::GetModuleSpecifications(child, 0, object->file_size, + specs)) { + ModuleSpec &spec = + specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); + llvm::sys::TimePoint<> object_mod_time( + std::chrono::seconds(object->modification_time)); + spec.GetObjectName() = object->ar_name; + spec.SetObjectOffset(0); + spec.SetObjectSize(object->file_size); + spec.GetObjectModificationTime() = object_mod_time; + } + continue; + } const lldb::offset_t object_file_offset = file_offset + object->file_offset; if (object->file_offset < file_size && file_size > object_file_offset) { diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h index 21106d7b8590..ace072cbe149 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h @@ -15,19 +15,24 @@ #include "lldb/Utility/ConstString.h" #include "lldb/Utility/FileSpec.h" +#include "llvm/Object/Archive.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Path.h" #include <map> #include <memory> #include <mutex> +enum class ArchiveType { Invalid, Archive, ThinArchive }; + class ObjectContainerBSDArchive : public lldb_private::ObjectContainer { public: ObjectContainerBSDArchive(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, - lldb::offset_t offset, lldb::offset_t length); + lldb::offset_t offset, lldb::offset_t length, + ArchiveType archive_type); ~ObjectContainerBSDArchive() override; @@ -54,7 +59,7 @@ public: lldb::offset_t length, lldb_private::ModuleSpecList &specs); - static bool MagicBytesMatch(const lldb_private::DataExtractor &data); + static ArchiveType MagicBytesMatch(const lldb_private::DataExtractor &data); // Member Functions bool ParseHeader() override; @@ -78,6 +83,10 @@ protected: void Clear(); + lldb::offset_t ExtractFromThin(const lldb_private::DataExtractor &data, + lldb::offset_t offset, + llvm::StringRef stringTable); + lldb::offset_t Extract(const lldb_private::DataExtractor &data, lldb::offset_t offset); /// Object name in the archive. @@ -112,7 +121,7 @@ protected: Archive(const lldb_private::ArchSpec &arch, const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, - lldb_private::DataExtractor &data); + lldb_private::DataExtractor &data, ArchiveType archive_type); ~Archive(); @@ -127,7 +136,7 @@ protected: static Archive::shared_ptr ParseAndCacheArchiveForFile( const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, - lldb_private::DataExtractor &data); + lldb_private::DataExtractor &data, ArchiveType archive_type); size_t GetNumObjects() const { return m_objects.size(); } @@ -156,6 +165,8 @@ protected: lldb_private::DataExtractor &GetData() { return m_data; } + ArchiveType GetArchiveType() { return m_archive_type; } + protected: typedef lldb_private::UniqueCStringMap<uint32_t> ObjectNameToIndexMap; // Member Variables @@ -167,11 +178,14 @@ protected: lldb_private::DataExtractor m_data; ///< The data for this object container ///so we don't lose data if the .a files ///gets modified + ArchiveType m_archive_type; }; void SetArchive(Archive::shared_ptr &archive_sp); Archive::shared_ptr m_archive_sp; + + ArchiveType m_archive_type; }; #endif // LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BSD_ARCHIVE_OBJECTCONTAINERBSDARCHIVE_H diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 28ccfbe3d6e6..f9fb36890d5a 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -3386,8 +3386,7 @@ size_t ObjectFileELF::ReadSectionData(Section *section, auto buffer_sp = std::make_shared<DataBufferHeap>(Decompressor->getDecompressedSize(), 0); if (auto error = Decompressor->decompress( - {reinterpret_cast<char *>(buffer_sp->GetBytes()), - size_t(buffer_sp->GetByteSize())})) { + {buffer_sp->GetBytes(), size_t(buffer_sp->GetByteSize())})) { GetModule()->ReportWarning( "Decompression of section '%s' failed: %s", section->GetName().GetCString(), diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index 1c10efed9564..44c708676e52 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -137,8 +137,6 @@ size_t ObjectFilePDB::GetModuleSpecifications( case PDB_Machine::x86: module_arch.SetTriple("i386-pc-windows"); specs.Append(module_spec); - module_arch.SetTriple("i686-pc-windows"); - specs.Append(module_spec); break; case PDB_Machine::ArmNT: module_arch.SetTriple("armv7-pc-windows"); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 700e6ebdf84c..c44ace96dd55 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2727,7 +2727,7 @@ bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid, m_curr_pid = ret->pid; m_curr_tid = ret->tid; } - return ret.hasValue(); + return ret.has_value(); } bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid, @@ -2742,7 +2742,7 @@ bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid, m_curr_pid_run = ret->pid; m_curr_tid_run = ret->tid; } - return ret.hasValue(); + return ret.has_value(); } bool GDBRemoteCommunicationClient::GetStopReply( diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 58ed22187747..d367f75cee0e 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -254,8 +254,6 @@ public: lldb::addr_t GetShlibInfoAddr(); - bool GetSupportsThreadSuffix(); - bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info); uint32_t FindProcesses(const ProcessInstanceInfoMatch &process_match_info, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 2aacac3692be..4b9354371bda 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2292,7 +2292,7 @@ DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit, int call_file = 0; int call_line = 0; int call_column = 0; - DWARFExpression frame_base; + DWARFExpressionList frame_base; const dw_tag_t tag = die.Tag(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp index ec074be581b5..06cdd877f7dc 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp @@ -441,7 +441,7 @@ bool DWARFDIE::GetDIENamesAndRanges( const char *&name, const char *&mangled, DWARFRangeList &ranges, int &decl_file, int &decl_line, int &decl_column, int &call_file, int &call_line, int &call_column, - lldb_private::DWARFExpression *frame_base) const { + lldb_private::DWARFExpressionList *frame_base) const { if (IsValid()) { return m_die->GetDIENamesAndRanges( GetCU(), name, mangled, ranges, decl_file, decl_line, decl_column, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h index 5ee44a763204..7ce9550a081e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h @@ -85,11 +85,12 @@ public: DWARFDIE GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const; - bool GetDIENamesAndRanges(const char *&name, const char *&mangled, - DWARFRangeList &ranges, int &decl_file, - int &decl_line, int &decl_column, int &call_file, - int &call_line, int &call_column, - lldb_private::DWARFExpression *frame_base) const; + bool + GetDIENamesAndRanges(const char *&name, const char *&mangled, + DWARFRangeList &ranges, int &decl_file, int &decl_line, + int &decl_column, int &call_file, int &call_line, + int &call_column, + lldb_private::DWARFExpressionList *frame_base) const; /// The range of all the children of this DIE. llvm::iterator_range<child_iterator> children() const; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp index 95c0cb6472c5..c98953640a58 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -231,7 +231,7 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges( DWARFUnit *cu, const char *&name, const char *&mangled, DWARFRangeList &ranges, int &decl_file, int &decl_line, int &decl_column, int &call_file, int &call_line, int &call_column, - DWARFExpression *frame_base) const { + DWARFExpressionList *frame_base) const { dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; std::vector<DWARFDIE> dies; @@ -345,21 +345,22 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges( uint32_t block_offset = form_value.BlockData() - data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); - *frame_base = DWARFExpression( - module, DataExtractor(data, block_offset, block_length), cu); + *frame_base = + DWARFExpressionList(module, + DWARFExpression(DataExtractor( + data, block_offset, block_length)), + cu); } else { DataExtractor data = cu->GetLocationData(); const dw_offset_t offset = form_value.Unsigned(); if (data.ValidOffset(offset)) { data = DataExtractor(data, offset, data.GetByteSize() - offset); - *frame_base = DWARFExpression(module, data, cu); if (lo_pc != LLDB_INVALID_ADDRESS) { assert(lo_pc >= cu->GetBaseAddress()); - frame_base->SetLocationListAddresses(cu->GetBaseAddress(), - lo_pc); - } else { + DWARFExpression::ParseDWARFLocationList(cu, data, frame_base); + frame_base->SetFuncFileAddress(lo_pc); + } else set_frame_base_loclist_addr = true; - } } } } @@ -384,7 +385,7 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges( if (set_frame_base_loclist_addr) { dw_addr_t lowest_range_pc = ranges.GetMinRangeBase(0); assert(lowest_range_pc >= cu->GetBaseAddress()); - frame_base->SetLocationListAddresses(cu->GetBaseAddress(), lowest_range_pc); + frame_base->SetFuncFileAddress(lowest_range_pc); } if (ranges.IsEmpty() || name == nullptr || mangled == nullptr) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h index 64e86c71ac09..32f653e99a70 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -104,7 +104,7 @@ public: DWARFUnit *cu, const char *&name, const char *&mangled, DWARFRangeList &rangeList, int &decl_file, int &decl_line, int &decl_column, int &call_file, int &call_line, int &call_column, - lldb_private::DWARFExpression *frame_base = nullptr) const; + lldb_private::DWARFExpressionList *frame_base = nullptr) const; const DWARFAbbreviationDeclaration * GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 903cd2e38f76..7b4a5d8eca3e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -579,6 +579,17 @@ void DWARFUnit::SetStrOffsetsBase(dw_offset_t str_offsets_base) { m_str_offsets_base = str_offsets_base; } +dw_addr_t DWARFUnit::ReadAddressFromDebugAddrSection(uint32_t index) const { + uint32_t index_size = GetAddressByteSize(); + dw_offset_t addr_base = GetAddrBase(); + dw_addr_t offset = addr_base + index * index_size; + const DWARFDataExtractor &data = + m_dwarf.GetDWARFContext().getOrLoadAddrData(); + if (data.ValidOffsetForDataOfSize(offset, index_size)) + return data.GetMaxU64_unchecked(&offset, index_size); + return LLDB_INVALID_ADDRESS; +} + // It may be called only with m_die_array_mutex held R/W. void DWARFUnit::ClearDIEsRWLocked() { m_die_array.clear(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index 265e28b51c99..40a1943b847a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -166,6 +166,8 @@ public: void SetStrOffsetsBase(dw_offset_t str_offsets_base); virtual void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) = 0; + dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const; + lldb::ByteOrder GetByteOrder() const; const DWARFDebugAranges &GetFunctionAranges(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index c0bf13e0281d..cbc24b1550c7 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -9,6 +9,7 @@ #include "SymbolFileDWARF.h" #include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Threading.h" @@ -1885,11 +1886,14 @@ SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { for (size_t g = 0; g < num_globals; ++g) { VariableSP var_sp = globals_sp->GetVariableAtIndex(g); if (var_sp && !var_sp->GetLocationIsConstantValueData()) { - const DWARFExpression &location = var_sp->LocationExpression(); + const DWARFExpressionList &location = + var_sp->LocationExpressionList(); Value location_result; Status error; - if (location.Evaluate(nullptr, LLDB_INVALID_ADDRESS, nullptr, - nullptr, location_result, &error)) { + ExecutionContext exe_ctx; + if (location.Evaluate(&exe_ctx, nullptr, LLDB_INVALID_ADDRESS, + nullptr, nullptr, location_result, + &error)) { if (location_result.GetValueType() == Value::ValueType::FileAddress) { lldb::addr_t file_addr = @@ -3163,7 +3167,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, const char *mangled = nullptr; Declaration decl; DWARFFormValue type_die_form; - DWARFExpression location; + DWARFExpressionList location_list(module, DWARFExpression(), die.GetCU()); bool is_external = false; bool is_artificial = false; DWARFFormValue const_value_form, location_form; @@ -3229,16 +3233,15 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, // present in the class declaration and DW_AT_location in the DIE defining // the member. bool location_is_const_value_data = false; - bool has_explicit_location = false; + bool has_explicit_location = location_form.IsValid(); bool use_type_size_for_value = false; if (location_form.IsValid()) { - has_explicit_location = true; if (DWARFFormValue::IsBlockForm(location_form.Form())) { const DWARFDataExtractor &data = die.GetData(); uint32_t block_offset = location_form.BlockData() - data.GetDataStart(); uint32_t block_length = location_form.Unsigned(); - location = DWARFExpression( + location_list = DWARFExpressionList( module, DataExtractor(data, block_offset, block_length), die.GetCU()); } else { DataExtractor data = die.GetCU()->GetLocationData(); @@ -3247,10 +3250,10 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, offset = die.GetCU()->GetLoclistOffset(offset).value_or(-1); if (data.ValidOffset(offset)) { data = DataExtractor(data, offset, data.GetByteSize() - offset); - location = DWARFExpression(module, data, die.GetCU()); - assert(func_low_pc != LLDB_INVALID_ADDRESS); - location.SetLocationListAddresses( - location_form.GetUnit()->GetBaseAddress(), func_low_pc); + const DWARFUnit *dwarf_cu = location_form.GetUnit(); + if (DWARFExpression::ParseDWARFLocationList(dwarf_cu, data, + &location_list)) + location_list.SetFuncFileAddress(func_low_pc); } } } else if (const_value_form.IsValid()) { @@ -3263,7 +3266,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, uint32_t block_offset = const_value_form.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = const_value_form.Unsigned(); - location = DWARFExpression( + location_list = DWARFExpressionList( module, DataExtractor(debug_info_data, block_offset, block_length), die.GetCU()); } else if (DWARFFormValue::IsDataForm(const_value_form.Form())) { @@ -3273,7 +3276,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, use_type_size_for_value = true; } else if (const char *str = const_value_form.AsCString()) { uint32_t string_length = strlen(str) + 1; - location = DWARFExpression( + location_list = DWARFExpressionList( module, DataExtractor(str, string_length, die.GetCU()->GetByteOrder(), die.GetCU()->GetAddressByteSize()), @@ -3323,16 +3326,19 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus // so we need to look through the whole expression. bool is_static_lifetime = - has_explicit_mangled || (has_explicit_location && !location.IsValid()); + has_explicit_mangled || + (has_explicit_location && !location_list.IsValid()); // Check if the location has a DW_OP_addr with any address value... lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; if (!location_is_const_value_data) { bool op_error = false; - location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error); + const DWARFExpression* location = location_list.GetAlwaysValidExpr(); + if (location) + location_DW_OP_addr = location->GetLocation_DW_OP_addr( + location_form.GetUnit(), 0, op_error); if (op_error) { StreamString strm; - location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0, - nullptr); + location->DumpLocation(&strm, eDescriptionLevelFull, nullptr); GetObjectFile()->GetModule()->ReportError( "0x%8.8x: %s has an invalid location: %s", die.GetOffset(), die.GetTagAsCString(), strm.GetData()); @@ -3345,7 +3351,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, // Set the module of the expression to the linked module // instead of the object file so the relocated address can be // found there. - location.SetModule(debug_map_symfile->GetObjectFile()->GetModule()); + location_list.SetModule(debug_map_symfile->GetObjectFile()->GetModule()); if (is_static_lifetime) { if (is_external) @@ -3386,7 +3392,9 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, const addr_t exe_file_addr = exe_symbol->GetAddressRef().GetFileAddress(); if (exe_file_addr != LLDB_INVALID_ADDRESS) { - if (location.Update_DW_OP_addr(exe_file_addr)) { + DWARFExpression *location = + location_list.GetMutableExpressionAtAddress(); + if (location->Update_DW_OP_addr(exe_file_addr)) { linked_oso_file_addr = true; symbol_context_scope = exe_symbol; } @@ -3404,7 +3412,9 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr); if (exe_file_addr != LLDB_INVALID_ADDRESS) { // Update the file address for this variable - location.Update_DW_OP_addr(exe_file_addr); + DWARFExpression *location = + location_list.GetMutableExpressionAtAddress(); + location->Update_DW_OP_addr(exe_file_addr); } else { // Variable didn't make it into the final executable return nullptr; @@ -3419,8 +3429,8 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, scope = eValueTypeVariableLocal; if (debug_map_symfile) { // We need to check for TLS addresses that we need to fixup - if (location.ContainsThreadLocalStorage()) { - location.LinkThreadLocalStorage( + if (location_list.ContainsThreadLocalStorage()) { + location_list.LinkThreadLocalStorage( debug_map_symfile->GetObjectFile()->GetModule(), [this, debug_map_symfile]( lldb::addr_t unlinked_file_addr) -> lldb::addr_t { @@ -3463,14 +3473,17 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, auto type_sp = std::make_shared<SymbolFileType>( *this, GetUID(type_die_form.Reference())); - if (use_type_size_for_value && type_sp->GetType()) - location.UpdateValue(const_value_form.Unsigned(), - type_sp->GetType()->GetByteSize(nullptr).value_or(0), - die.GetCU()->GetAddressByteSize()); + if (use_type_size_for_value && type_sp->GetType()) { + DWARFExpression *location = location_list.GetMutableExpressionAtAddress(); + location->UpdateValue( + const_value_form.Unsigned(), + type_sp->GetType()->GetByteSize(nullptr).getValueOr(0), + die.GetCU()->GetAddressByteSize()); + } return std::make_shared<Variable>( die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, - scope_ranges, &decl, location, is_external, is_artificial, + scope_ranges, &decl, location_list, is_external, is_artificial, location_is_const_value_data, is_static_member); } @@ -3755,8 +3768,8 @@ CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) { child.Tag() != DW_TAG_GNU_call_site_parameter) continue; - llvm::Optional<DWARFExpression> LocationInCallee; - llvm::Optional<DWARFExpression> LocationInCaller; + llvm::Optional<DWARFExpressionList> LocationInCallee; + llvm::Optional<DWARFExpressionList> LocationInCaller; DWARFAttributes attributes; const size_t num_attributes = child.GetAttributes(attributes); @@ -3764,7 +3777,7 @@ CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) { // Parse the location at index \p attr_index within this call site parameter // DIE, or return None on failure. auto parse_simple_location = - [&](int attr_index) -> llvm::Optional<DWARFExpression> { + [&](int attr_index) -> llvm::Optional<DWARFExpressionList> { DWARFFormValue form_value; if (!attributes.ExtractFormValueAtIndex(attr_index, form_value)) return {}; @@ -3773,9 +3786,9 @@ CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) { auto data = child.GetData(); uint32_t block_offset = form_value.BlockData() - data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); - return DWARFExpression(module, - DataExtractor(data, block_offset, block_length), - child.GetCU()); + return DWARFExpressionList( + module, DataExtractor(data, block_offset, block_length), + child.GetCU()); }; for (size_t i = 0; i < num_attributes; ++i) { @@ -3820,7 +3833,7 @@ SymbolFileDWARF::CollectCallEdges(ModuleSP module, DWARFDIE function_die) { continue; llvm::Optional<DWARFDIE> call_origin; - llvm::Optional<DWARFExpression> call_target; + llvm::Optional<DWARFExpressionList> call_target; addr_t return_pc = LLDB_INVALID_ADDRESS; addr_t call_inst_pc = LLDB_INVALID_ADDRESS; addr_t low_pc = LLDB_INVALID_ADDRESS; @@ -3881,7 +3894,7 @@ SymbolFileDWARF::CollectCallEdges(ModuleSP module, DWARFDIE function_die) { auto data = child.GetData(); uint32_t block_offset = form_value.BlockData() - data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); - call_target = DWARFExpression( + call_target = DWARFExpressionList( module, DataExtractor(data, block_offset, block_length), child.GetCU()); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 2403ee2624ea..cfd18f02053b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -21,6 +21,7 @@ #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Core/dwarf.h" +#include "lldb/Expression/DWARFExpressionList.h" #include "lldb/Symbol/DebugMacros.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/SymbolFile.h" diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp index 3ba0079c96e6..3166c8ae65c6 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp @@ -122,7 +122,7 @@ static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module, DataBufferSP buffer = std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); DataExtractor extractor(buffer, byte_order, address_size, byte_size); - DWARFExpression result(module, extractor, nullptr); + DWARFExpression result(extractor); result.SetRegisterKind(register_kind); return result; @@ -247,7 +247,7 @@ DWARFExpression lldb_private::npdb::MakeConstantLocationExpression( .take_front(size); buffer->CopyData(bytes.data(), size); DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size); - DWARFExpression result(nullptr, extractor, nullptr); + DWARFExpression result(extractor); return result; } diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp index 7bb7c69eece7..6317b140f7e8 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp @@ -602,7 +602,7 @@ static RegisterId GetBaseFrameRegister(PdbIndex &index, } VariableInfo lldb_private::npdb::GetVariableLocationInfo( - PdbIndex &index, PdbCompilandSymId var_id, Block &block, + PdbIndex &index, PdbCompilandSymId var_id, Block &func_block, lldb::ModuleSP module) { CVSymbol sym = index.ReadSymbolRecord(var_id); @@ -642,14 +642,8 @@ VariableInfo lldb_private::npdb::GetVariableLocationInfo( Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps); - // TODO: may be better to pass function scope and not lookup it every - // time? find nearest parent function block - Block *cur = █ - while (cur->GetParent()) { - cur = cur->GetParent(); - } PdbCompilandSymId func_scope_id = - PdbSymUid(cur->GetID()).asCompilandSym(); + PdbSymUid(func_block.GetID()).asCompilandSym(); CVSymbol func_block_cvs = index.ReadSymbolRecord(func_scope_id); lldbassert(func_block_cvs.kind() == S_GPROC32 || func_block_cvs.kind() == S_LPROC32); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h index 138c11aaeb43..066bcc89fd3b 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -142,8 +142,8 @@ LookThroughModifierRecord(llvm::codeview::CVType modifier); llvm::StringRef DropNameScope(llvm::StringRef name); VariableInfo GetVariableNameInfo(llvm::codeview::CVSymbol symbol); -VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, Block& block, - lldb::ModuleSP module); +VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, + Block &func_block, lldb::ModuleSP module); size_t GetTypeSizeForSimpleKind(llvm::codeview::SimpleTypeKind kind); lldb::BasicType diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 7dc99818c244..7e10e315be20 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -823,8 +823,10 @@ VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) { m_ast->GetOrCreateVariableDecl(var_id); - DWARFExpression location = MakeGlobalLocationExpression( - section, offset, GetObjectFile()->GetModule()); + ModuleSP module_sp = GetObjectFile()->GetModule(); + DWARFExpressionList location( + module_sp, MakeGlobalLocationExpression(section, offset, module_sp), + nullptr); std::string global_name("::"); global_name += name; @@ -855,8 +857,10 @@ SymbolFileNativePDB::CreateConstantSymbol(PdbGlobalSymId var_id, Declaration decl; Variable::RangeList ranges; ModuleSP module = GetObjectFile()->GetModule(); - DWARFExpression location = MakeConstantLocationExpression( - constant.Type, tpi, constant.Value, module); + DWARFExpressionList location(module, + MakeConstantLocationExpression( + constant.Type, tpi, constant.Value, module), + nullptr); bool external = false; bool artificial = false; @@ -1689,8 +1693,15 @@ VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id, bool is_param) { ModuleSP module = GetObjectFile()->GetModule(); Block &block = GetOrCreateBlock(scope_id); + // Get function block. + Block *func_block = █ + while (func_block->GetParent()) { + func_block = func_block->GetParent(); + } + Address addr; + func_block->GetStartAddress(addr); VariableInfo var_info = - GetVariableLocationInfo(*m_index, var_id, block, module); + GetVariableLocationInfo(*m_index, var_id, *func_block, module); if (!var_info.location || !var_info.ranges) return nullptr; @@ -1709,11 +1720,12 @@ VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id, bool artificial = false; bool location_is_constant_data = false; bool static_member = false; + DWARFExpressionList locaiton_list = DWARFExpressionList( + module, *var_info.location, nullptr); VariableSP var_sp = std::make_shared<Variable>( toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope, - &block, *var_info.ranges, &decl, *var_info.location, external, - artificial, location_is_constant_data, static_member); - + &block, *var_info.ranges, &decl, locaiton_list, external, artificial, + location_is_constant_data, static_member); if (!is_param) m_ast->GetOrCreateVariableDecl(scope_id, var_id); diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp index 96e9de704e41..94023737b2a2 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp @@ -175,7 +175,7 @@ DWARFExpression ConvertPDBLocationToDWARFExpression( DataBufferSP buffer = std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); DataExtractor extractor(buffer, byte_order, address_size, byte_size); - DWARFExpression result(module, extractor, nullptr); + DWARFExpression result(extractor); result.SetRegisterKind(register_kind); return result; diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp index bd3d16aad6c2..baa48532864b 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -1023,8 +1023,11 @@ VariableSP SymbolFilePDB::ParseVariableForPDBData( auto mangled_cstr = mangled.empty() ? nullptr : mangled.c_str(); bool is_constant; - DWARFExpression location = ConvertPDBLocationToDWARFExpression( - GetObjectFile()->GetModule(), pdb_data, ranges, is_constant); + ModuleSP module_sp = GetObjectFile()->GetModule(); + DWARFExpressionList location(module_sp, + ConvertPDBLocationToDWARFExpression( + module_sp, pdb_data, ranges, is_constant), + nullptr); var_sp = std::make_shared<Variable>( var_uid, var_name.c_str(), mangled_cstr, type_sp, scope, context_scope, diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp index 6c386f6a83fa..91eafdaa11bc 100644 --- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp @@ -32,13 +32,12 @@ Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue( switch (short_option) { case 's': { - int64_t ipt_trace_size; - if (option_arg.empty() || option_arg.getAsInteger(0, ipt_trace_size) || - ipt_trace_size < 0) - error.SetErrorStringWithFormat("invalid integer value for option '%s'", - option_arg.str().c_str()); + if (Optional<uint64_t> bytes = + ParsingUtils::ParseUserFriendlySizeExpression(option_arg)) + m_ipt_trace_size = *bytes; else - m_ipt_trace_size = ipt_trace_size; + error.SetErrorStringWithFormat("invalid bytes expression for '%s'", + option_arg.str().c_str()); break; } case 't': { @@ -98,24 +97,21 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue( switch (short_option) { case 's': { - int64_t ipt_trace_size; - if (option_arg.empty() || option_arg.getAsInteger(0, ipt_trace_size) || - ipt_trace_size < 0) - error.SetErrorStringWithFormat("invalid integer value for option '%s'", - option_arg.str().c_str()); + if (Optional<uint64_t> bytes = + ParsingUtils::ParseUserFriendlySizeExpression(option_arg)) + m_ipt_trace_size = *bytes; else - m_ipt_trace_size = ipt_trace_size; + error.SetErrorStringWithFormat("invalid bytes expression for '%s'", + option_arg.str().c_str()); break; } case 'l': { - int64_t process_buffer_size_limit; - if (option_arg.empty() || - option_arg.getAsInteger(0, process_buffer_size_limit) || - process_buffer_size_limit < 0) - error.SetErrorStringWithFormat("invalid integer value for option '%s'", - option_arg.str().c_str()); + if (Optional<uint64_t> bytes = + ParsingUtils::ParseUserFriendlySizeExpression(option_arg)) + m_process_buffer_size_limit = *bytes; else - m_process_buffer_size_limit = process_buffer_size_limit; + error.SetErrorStringWithFormat("invalid bytes expression for '%s'", + option_arg.str().c_str()); break; } case 't': { @@ -126,6 +122,10 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue( m_per_cpu_tracing = true; break; } + case 'd': { + m_disable_cgroup_filtering = true; + break; + } case 'p': { int64_t psb_period; if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) || @@ -149,6 +149,7 @@ void CommandObjectProcessTraceStartIntelPT::CommandOptions:: m_enable_tsc = kDefaultEnableTscValue; m_psb_period = kDefaultPsbPeriod; m_per_cpu_tracing = kDefaultPerCpuTracing; + m_disable_cgroup_filtering = kDefaultDisableCgroupFiltering; } llvm::ArrayRef<OptionDefinition> @@ -158,13 +159,55 @@ CommandObjectProcessTraceStartIntelPT::CommandOptions::GetDefinitions() { bool CommandObjectProcessTraceStartIntelPT::DoExecute( Args &command, CommandReturnObject &result) { - if (Error err = m_trace.Start(m_options.m_ipt_trace_size, - m_options.m_process_buffer_size_limit, - m_options.m_enable_tsc, m_options.m_psb_period, - m_options.m_per_cpu_tracing)) + if (Error err = m_trace.Start( + m_options.m_ipt_trace_size, m_options.m_process_buffer_size_limit, + m_options.m_enable_tsc, m_options.m_psb_period, + m_options.m_per_cpu_tracing, m_options.m_disable_cgroup_filtering)) result.SetError(Status(std::move(err))); else result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } + +Optional<uint64_t> +ParsingUtils::ParseUserFriendlySizeExpression(llvm::StringRef size_expression) { + if (size_expression.empty()) { + return llvm::None; + } + const uint64_t kBytesMultiplier = 1; + const uint64_t kKibiBytesMultiplier = 1024; + const uint64_t kMebiBytesMultiplier = 1024 * 1024; + + DenseMap<StringRef, uint64_t> multipliers = { + {"mib", kMebiBytesMultiplier}, {"mb", kMebiBytesMultiplier}, + {"m", kMebiBytesMultiplier}, {"kib", kKibiBytesMultiplier}, + {"kb", kKibiBytesMultiplier}, {"k", kKibiBytesMultiplier}, + {"b", kBytesMultiplier}, {"", kBytesMultiplier}}; + + const auto non_digit_index = size_expression.find_first_not_of("0123456789"); + if (non_digit_index == 0) { // expression starts from from non-digit char. + return llvm::None; + } + + const llvm::StringRef number_part = + non_digit_index == llvm::StringRef::npos + ? size_expression + : size_expression.substr(0, non_digit_index); + uint64_t parsed_number; + if (number_part.getAsInteger(10, parsed_number)) { + return llvm::None; + } + + if (non_digit_index != llvm::StringRef::npos) { // if expression has units. + const auto multiplier = size_expression.substr(non_digit_index).lower(); + + auto it = multipliers.find(multiplier); + if (it == multipliers.end()) + return llvm::None; + + return parsed_number * it->second; + } else { + return parsed_number; + } +} diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h index b5d6a0f24043..083184e4817c 100644 --- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h @@ -79,6 +79,7 @@ public: bool m_enable_tsc; llvm::Optional<uint64_t> m_psb_period; bool m_per_cpu_tracing; + bool m_disable_cgroup_filtering; }; CommandObjectProcessTraceStartIntelPT(TraceIntelPT &trace, @@ -109,6 +110,23 @@ protected: CommandOptions m_options; }; +namespace ParsingUtils { +/// Convert an integral size expression like 12KiB or 4MB into bytes. The units +/// are taken loosely to help users input sizes into LLDB, e.g. KiB and KB are +/// considered the same (2^20 bytes) for simplicity. +/// +/// \param[in] size_expression +/// String expression which is an integral number plus a unit that can be +/// lower or upper case. Supported units: K, KB and KiB for 2^10 bytes; M, +/// MB and MiB for 2^20 bytes; and B for bytes. A single integral number is +/// considered bytes. +/// \return +/// The converted number of bytes or \a llvm::None if the expression is +/// invalid. +llvm::Optional<uint64_t> +ParseUserFriendlySizeExpression(llvm::StringRef size_expression); +} // namespace ParsingUtils + } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp index 578828ff1633..0859c5a20b7e 100644 --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp @@ -63,11 +63,28 @@ DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind) { void DecodedThread::NotifyTsc(uint64_t tsc) { if (!m_last_tsc || *m_last_tsc != tsc) { - m_instruction_timestamps.emplace(m_item_kinds.size(), tsc); + m_timestamps.emplace(m_item_kinds.size(), tsc); m_last_tsc = tsc; } } +void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) { + if (!m_last_cpu || *m_last_cpu != cpu_id) { + m_cpus.emplace(m_item_kinds.size(), cpu_id); + m_last_cpu = cpu_id; + AppendEvent(lldb::eTraceEventCPUChanged); + } +} + +Optional<lldb::cpu_id_t> +DecodedThread::GetCPUByIndex(uint64_t insn_index) const { + // Could possibly optimize the search + auto it = m_cpus.upper_bound(insn_index); + if (it == m_cpus.begin()) + return None; + return prev(it)->second; +} + void DecodedThread::AppendEvent(lldb::TraceEvent event) { CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event; m_events_stats.RecordEvent(event); @@ -136,8 +153,8 @@ Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange( return candidate_range; } // Now we do a more expensive lookup - auto it = m_instruction_timestamps.upper_bound(insn_index); - if (it == m_instruction_timestamps.begin()) + auto it = m_timestamps.upper_bound(insn_index); + if (it == m_timestamps.begin()) return None; return TscRange(--it, *this); @@ -160,7 +177,8 @@ lldb::TraceCursorUP DecodedThread::CreateNewCursor() { size_t DecodedThread::CalculateApproximateMemoryUsage() const { return sizeof(TraceItemStorage) * m_item_data.size() + sizeof(uint8_t) * m_item_kinds.size() + - (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size(); + (sizeof(size_t) + sizeof(uint64_t)) * m_timestamps.size() + + (sizeof(size_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size(); } DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it, @@ -168,7 +186,7 @@ DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it, : m_it(it), m_decoded_thread(&decoded_thread) { auto next_it = m_it; ++next_it; - m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end()) + m_end_index = (next_it == m_decoded_thread->m_timestamps.end()) ? std::numeric_limits<uint64_t>::max() : next_it->first - 1; } @@ -191,13 +209,13 @@ bool DecodedThread::TscRange::InRange(size_t insn_index) const { Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const { auto next_it = m_it; ++next_it; - if (next_it == m_decoded_thread->m_instruction_timestamps.end()) + if (next_it == m_decoded_thread->m_timestamps.end()) return None; return TscRange(next_it, *m_decoded_thread); } Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const { - if (m_it == m_decoded_thread->m_instruction_timestamps.begin()) + if (m_it == m_decoded_thread->m_timestamps.begin()) return None; auto prev_it = m_it; --prev_it; diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h index b17e927fafe4..bd1a90aaf250 100644 --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h @@ -168,6 +168,15 @@ public: /// The underlying event type for the given trace item index. lldb::TraceEvent GetEventByIndex(int item_index) const; + /// Get the most recent CPU id before or at the given trace item index. + /// + /// \param[in] item_index + /// The trace item index to compare with. + /// + /// \return + /// The requested cpu id, or \a llvm::None if not available. + llvm::Optional<lldb::cpu_id_t> GetCPUByIndex(uint64_t item_index) const; + /// \return /// The load address of the instruction at the given index. lldb::addr_t GetInstructionLoadAddress(size_t item_index) const; @@ -204,8 +213,13 @@ public: lldb::ThreadSP GetThread(); /// Notify this object that a new tsc has been seen. + /// If this a new TSC, an event will be created. void NotifyTsc(uint64_t tsc); + /// Notify this object that a CPU has been seen. + /// If this a new CPU, an event will be created. + void NotifyCPU(lldb::cpu_id_t cpu_id); + /// Append a decoding error. void AppendError(const IntelPTError &error); @@ -254,10 +268,17 @@ private: /// are sporadic and we can think of them as ranges. If TSCs are present in /// the trace, all instructions will have an associated TSC, including the /// first one. Otherwise, this map will be empty. - std::map<uint64_t, uint64_t> m_instruction_timestamps; + std::map<uint64_t, uint64_t> m_timestamps; /// This is the chronologically last TSC that has been added. llvm::Optional<uint64_t> m_last_tsc = llvm::None; + // The cpu information is stored as a map. It maps `instruction index -> CPU` + // A CPU is associated with the next instructions that follow until the next + // cpu is seen. + std::map<uint64_t, lldb::cpu_id_t> m_cpus; + /// This is the chronologically last CPU ID. + llvm::Optional<uint64_t> m_last_cpu = llvm::None; + /// Statistics of all tracing events. EventsStats m_events_stats; /// Statistics of libipt errors when decoding TSCs. diff --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp index dd34467e38b5..a98337a4e058 100644 --- a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp @@ -285,6 +285,8 @@ Error lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread( for (size_t i = 0; i < executions.size(); i++) { const IntelPTThreadContinousExecution &execution = executions[i]; + decoded_thread.NotifyCPU(execution.thread_execution.cpu_id); + auto variant = execution.thread_execution.variant; // If we haven't seen a PSB yet, then it's fine not to show errors if (has_seen_psbs) { diff --git a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp index 6b4251a0fcd9..0c468cf7852f 100644 --- a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp @@ -16,8 +16,13 @@ using namespace llvm; /// non-linux platforms. /// \{ #define PERF_RECORD_MISC_SWITCH_OUT (1 << 13) -#define PERF_RECORD_MAX 19 + +#define PERF_RECORD_LOST 2 +#define PERF_RECORD_THROTTLE 5 +#define PERF_RECORD_UNTHROTTLE 6 +#define PERF_RECORD_LOST_SAMPLES 13 #define PERF_RECORD_SWITCH_CPU_WIDE 15 +#define PERF_RECORD_MAX 19 struct perf_event_header { uint32_t type; @@ -54,6 +59,11 @@ struct perf_event_header { bool IsContextSwitchRecord() const { return type == PERF_RECORD_SWITCH_CPU_WIDE; } + + bool IsErrorRecord() const { + return type == PERF_RECORD_LOST || type == PERF_RECORD_THROTTLE || + type == PERF_RECORD_UNTHROTTLE || type == PERF_RECORD_LOST_SAMPLES; + } }; /// \} @@ -286,3 +296,36 @@ lldb_private::trace_intel_pt::DecodePerfContextSwitchTrace( return executions; } + +Expected<std::vector<uint8_t>> +lldb_private::trace_intel_pt::FilterProcessesFromContextSwitchTrace( + llvm::ArrayRef<uint8_t> data, const std::set<lldb::pid_t> &pids) { + size_t offset = 0; + std::vector<uint8_t> out_data; + + while (offset < data.size()) { + const perf_event_header &perf_record = + *reinterpret_cast<const perf_event_header *>(data.data() + offset); + if (Error err = perf_record.SanityCheck()) + return std::move(err); + bool should_copy = false; + if (perf_record.IsContextSwitchRecord()) { + const PerfContextSwitchRecord &context_switch_record = + *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() + + offset); + if (pids.count(context_switch_record.pid)) + should_copy = true; + } else if (perf_record.IsErrorRecord()) { + should_copy = true; + } + + if (should_copy) { + for (size_t i = 0; i < perf_record.size; i++) { + out_data.push_back(data[offset + i]); + } + } + + offset += perf_record.size; + } + return out_data; +} diff --git a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h index 721aa1d77481..a16a437e1888 100644 --- a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h @@ -14,6 +14,7 @@ #include "llvm/Support/Error.h" +#include <set> #include <vector> namespace lldb_private { @@ -139,6 +140,10 @@ DecodePerfContextSwitchTrace(llvm::ArrayRef<uint8_t> data, lldb::cpu_id_t cpu_id, const LinuxPerfZeroTscConversion &tsc_conversion); +llvm::Expected<std::vector<uint8_t>> +FilterProcessesFromContextSwitchTrace(llvm::ArrayRef<uint8_t> data, + const std::set<lldb::pid_t> &pids); + } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h b/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h index 92b563257437..2b85ed30334f 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h +++ b/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h @@ -35,9 +35,9 @@ public: /// /// \return /// The return value of the task. - template <class R> R TimeTask(llvm::StringRef name, std::function<R()> task) { + template <typename C> auto TimeTask(llvm::StringRef name, C task) { auto start = std::chrono::steady_clock::now(); - R result = task(); + auto result = task(); auto end = std::chrono::steady_clock::now(); std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp index 8b90afb219af..d3ac61f7e658 100644 --- a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp @@ -35,9 +35,8 @@ Expected<DecodedThreadSP> ThreadDecoder::Decode() { } llvm::Expected<DecodedThreadSP> ThreadDecoder::DoDecode() { - return m_trace.GetTimer() - .ForThread(m_thread_sp->GetID()) - .TimeTask<Expected<DecodedThreadSP>>( + return m_trace.GetThreadTimer(m_thread_sp->GetID()) + .TimeTask( "Decoding instructions", [&]() -> Expected<DecodedThreadSP> { DecodedThreadSP decoded_thread_sp = std::make_shared<DecodedThread>(m_thread_sp); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp index da91ba7c13d8..185c02b6bcd9 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp @@ -84,6 +84,10 @@ TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) const { } } +Optional<lldb::cpu_id_t> TraceCursorIntelPT::GetCPU() const { + return m_decoded_thread_sp->GetCPUByIndex(m_pos); +} + lldb::TraceEvent TraceCursorIntelPT::GetEventType() const { return m_decoded_thread_sp->GetEventByIndex(m_pos); } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h index c90431de3bbc..2e0f67e67dfc 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h @@ -34,6 +34,8 @@ public: lldb::TraceEvent GetEventType() const override; + llvm::Optional<lldb::cpu_id_t> GetCPU() const override; + lldb::TraceItemKind GetItemKind() const override; bool GoToId(lldb::user_id_t id) override; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp index fc7a103fbe15..57433ffb14cb 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -55,9 +55,9 @@ StringRef TraceIntelPT::GetSchema() { void TraceIntelPT::Dump(Stream *s) const {} -llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) { +Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) { RefreshLiveProcessState(); - return TraceIntelPTBundleSaver().SaveToDisk(*this, directory); + return TraceIntelPTBundleSaver().SaveToDisk(*this, directory, compact); } Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle( @@ -146,10 +146,16 @@ TraceIntelPT::CreateNewCursor(Thread &thread) { return decoded_thread.takeError(); } -void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { +void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose, + bool json) { Storage &storage = GetUpdatedStorage(); lldb::tid_t tid = thread.GetID(); + if (json) { + DumpTraceInfoAsJson(thread, s, verbose); + return; + } + s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); if (!IsTraced(tid)) { s << ", not traced\n"; @@ -172,12 +178,14 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { } Optional<uint64_t> raw_size = *raw_size_or_error; + s.Format("\n Trace technology: {0}\n", GetPluginName()); + /// Instruction stats { uint64_t items_count = decoded_thread_sp->GetItemsCount(); uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); - s.Format(" Total number of trace items: {0}\n", items_count); + s.Format("\n Total number of trace items: {0}\n", items_count); s << "\n Memory usage:\n"; if (raw_size) @@ -199,10 +207,10 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { std::chrono::milliseconds duration) { s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0); }; - GetTimer().ForThread(tid).ForEachTimedTask(print_duration); + GetThreadTimer(tid).ForEachTimedTask(print_duration); s << "\n Timing for global tasks:\n"; - GetTimer().ForGlobal().ForEachTimedTask(print_duration); + GetGlobalTimer().ForEachTimedTask(print_duration); } // Instruction events stats @@ -226,6 +234,12 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { s.Format( " Number of continuous executions for this thread: {0}\n", storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); + s.Format(" Total number of PSB blocks found: {0}\n", + storage.multicpu_decoder->GetTotalPSBBlocksCount()); + s.Format(" Number of PSB blocks for this thread: {0}\n", + storage.multicpu_decoder->GePSBBlocksCountForThread(tid)); + s.Format(" Total number of unattributed PSB blocks found: {0}\n", + storage.multicpu_decoder->GetUnattributedPSBBlocksCount()); } // Errors @@ -243,6 +257,117 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { } } +void TraceIntelPT::DumpTraceInfoAsJson(Thread &thread, Stream &s, + bool verbose) { + Storage &storage = GetUpdatedStorage(); + + lldb::tid_t tid = thread.GetID(); + json::OStream json_str(s.AsRawOstream(), 2); + if (!IsTraced(tid)) { + s << "error: thread not traced\n"; + return; + } + + Expected<Optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread); + if (!raw_size_or_error) { + s << "error: " << toString(raw_size_or_error.takeError()) << "\n"; + return; + } + + Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread); + if (!decoded_thread_sp_or_err) { + s << "error: " << toString(decoded_thread_sp_or_err.takeError()) << "\n"; + return; + } + DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err; + + json_str.object([&] { + json_str.attribute("traceTechnology", "intel-pt"); + json_str.attributeObject("threadStats", [&] { + json_str.attribute("tid", tid); + + uint64_t insn_len = decoded_thread_sp->GetItemsCount(); + json_str.attribute("traceItemsCount", insn_len); + + // Instruction stats + uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); + json_str.attributeObject("memoryUsage", [&] { + json_str.attribute("totalInBytes", std::to_string(mem_used)); + Optional<double> avg; + if (insn_len != 0) + avg = double(mem_used) / insn_len; + json_str.attribute("avgPerItemInBytes", avg); + }); + + // Timing + json_str.attributeObject("timingInSeconds", [&] { + GetTimer().ForThread(tid).ForEachTimedTask( + [&](const std::string &name, std::chrono::milliseconds duration) { + json_str.attribute(name, duration.count() / 1000.0); + }); + }); + + // Instruction events stats + const DecodedThread::EventsStats &events_stats = + decoded_thread_sp->GetEventsStats(); + json_str.attributeObject("events", [&] { + json_str.attribute("totalCount", events_stats.total_count); + json_str.attributeObject("individualCounts", [&] { + for (const auto &event_to_count : events_stats.events_counts) { + json_str.attribute( + TraceCursor::EventKindToString(event_to_count.first), + event_to_count.second); + } + }); + }); + + if (storage.multicpu_decoder) { + json_str.attribute( + "continuousExecutions", + storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); + json_str.attribute( + "PSBBlocks", + storage.multicpu_decoder->GePSBBlocksCountForThread(tid)); + } + + // Errors + const DecodedThread::LibiptErrorsStats &tsc_errors_stats = + decoded_thread_sp->GetTscErrorsStats(); + json_str.attributeObject("errorItems", [&] { + json_str.attribute("total", tsc_errors_stats.total_count); + json_str.attributeObject("individualErrors", [&] { + for (const auto &error_message_to_count : + tsc_errors_stats.libipt_errors_counts) { + json_str.attribute(error_message_to_count.first, + error_message_to_count.second); + } + }); + }); + }); + json_str.attributeObject("globalStats", [&] { + json_str.attributeObject("timingInSeconds", [&] { + GetTimer().ForGlobal().ForEachTimedTask( + [&](const std::string &name, std::chrono::milliseconds duration) { + json_str.attribute(name, duration.count() / 1000.0); + }); + }); + if (storage.multicpu_decoder) { + json_str.attribute( + "totalUnattributedPSBBlocks", + storage.multicpu_decoder->GetUnattributedPSBBlocksCount()); + json_str.attribute( + "totalCountinuosExecutions", + storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); + json_str.attribute("totalPSBBlocks", + storage.multicpu_decoder->GetTotalPSBBlocksCount()); + json_str.attribute( + "totalContinuousExecutions", + storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); + } + }); + }); +} + llvm::Expected<Optional<uint64_t>> TraceIntelPT::GetRawTraceSize(Thread &thread) { if (GetUpdatedStorage().multicpu_decoder) @@ -408,17 +533,22 @@ const char *TraceIntelPT::GetStartConfigurationHelp() { [process tracing only] - int processBufferSizeLimit (defaults to {4} MiB): + [process tracing only] + + - boolean disableCgroupFiltering (default to {5}): [process tracing only])", kDefaultIptTraceSize, kDefaultEnableTscValue, kDefaultPsbPeriod, kDefaultPerCpuTracing, - kDefaultProcessBufferSizeLimit / 1024 / 1024)); + kDefaultProcessBufferSizeLimit / 1024 / 1024, + kDefaultDisableCgroupFiltering)); } return message->c_str(); } Error TraceIntelPT::Start(uint64_t ipt_trace_size, uint64_t total_buffer_size_limit, bool enable_tsc, - Optional<uint64_t> psb_period, bool per_cpu_tracing) { + Optional<uint64_t> psb_period, bool per_cpu_tracing, + bool disable_cgroup_filtering) { TraceIntelPTStartRequest request; request.ipt_trace_size = ipt_trace_size; request.process_buffer_size_limit = total_buffer_size_limit; @@ -426,6 +556,7 @@ Error TraceIntelPT::Start(uint64_t ipt_trace_size, request.psb_period = psb_period; request.type = GetPluginName().str(); request.per_cpu_tracing = per_cpu_tracing; + request.disable_cgroup_filtering = disable_cgroup_filtering; return Trace::Start(toJSON(request)); } @@ -435,6 +566,7 @@ Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { bool enable_tsc = kDefaultEnableTscValue; Optional<uint64_t> psb_period = kDefaultPsbPeriod; bool per_cpu_tracing = kDefaultPerCpuTracing; + bool disable_cgroup_filtering = kDefaultDisableCgroupFiltering; if (configuration) { if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { @@ -444,6 +576,8 @@ Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); dict->GetValueForKeyAsInteger("psbPeriod", psb_period); dict->GetValueForKeyAsBoolean("perCpuTracing", per_cpu_tracing); + dict->GetValueForKeyAsBoolean("disableCgroupFiltering", + disable_cgroup_filtering); } else { return createStringError(inconvertibleErrorCode(), "configuration object is not a dictionary"); @@ -451,7 +585,7 @@ Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { } return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc, - psb_period, per_cpu_tracing); + psb_period, per_cpu_tracing, disable_cgroup_filtering); } llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, @@ -476,7 +610,20 @@ Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, if (configuration) { if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { - dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); + llvm::StringRef ipt_trace_size_not_parsed; + if (dict->GetValueForKeyAsString("iptTraceSize", + ipt_trace_size_not_parsed)) { + if (Optional<uint64_t> bytes = + ParsingUtils::ParseUserFriendlySizeExpression( + ipt_trace_size_not_parsed)) + ipt_trace_size = *bytes; + else + return createStringError(inconvertibleErrorCode(), + "iptTraceSize is wrong bytes expression"); + } else { + dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); + } + dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); dict->GetValueForKeyAsInteger("psbPeriod", psb_period); } else { @@ -494,3 +641,11 @@ Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, } TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; } + +ScopedTaskTimer &TraceIntelPT::GetThreadTimer(lldb::tid_t tid) { + return GetTimer().ForThread(tid); +} + +ScopedTaskTimer &TraceIntelPT::GetGlobalTimer() { + return GetTimer().ForGlobal(); +} diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h index 09ecbe7da61a..d3e58374867d 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -25,7 +25,8 @@ class TraceIntelPT : public Trace { public: void Dump(Stream *s) const override; - llvm::Error SaveLiveTraceToDisk(FileSpec directory) override; + llvm::Expected<FileSpec> SaveToDisk(FileSpec directory, + bool compact) override; ~TraceIntelPT() override = default; @@ -72,7 +73,8 @@ public: llvm::Expected<lldb::TraceCursorUP> CreateNewCursor(Thread &thread) override; - void DumpTraceInfo(Thread &thread, Stream &s, bool verbose) override; + void DumpTraceInfo(Thread &thread, Stream &s, bool verbose, + bool json) override; llvm::Expected<llvm::Optional<uint64_t>> GetRawTraceSize(Thread &thread); @@ -104,12 +106,16 @@ public: /// This value defines whether to have an intel pt trace buffer per thread /// or per cpu core. /// + /// \param[in] disable_cgroup_filtering + /// Disable the cgroup filtering that is automatically applied when doing + /// per cpu tracing. + /// /// \return /// \a llvm::Error::success if the operation was successful, or /// \a llvm::Error otherwise. llvm::Error Start(uint64_t ipt_trace_size, uint64_t total_buffer_size_limit, bool enable_tsc, llvm::Optional<uint64_t> psb_period, - bool m_per_cpu_tracing); + bool m_per_cpu_tracing, bool disable_cgroup_filtering); /// \copydoc Trace::Start llvm::Error Start(StructuredData::ObjectSP configuration = @@ -157,6 +163,14 @@ public: /// The timer object for this trace. TaskTimer &GetTimer(); + /// \return + /// The ScopedTaskTimer object for the given thread in this trace. + ScopedTaskTimer &GetThreadTimer(lldb::tid_t tid); + + /// \return + /// The global copedTaskTimer object for this trace. + ScopedTaskTimer &GetGlobalTimer(); + TraceIntelPTSP GetSharedPtr(); private: @@ -206,6 +220,9 @@ private: /// returned if the decoder couldn't be properly set up. llvm::Expected<DecodedThreadSP> Decode(Thread &thread); + // Dump out trace info in JSON format + void DumpTraceInfoAsJson(Thread &thread, Stream &s, bool verbose); + /// We package all the data that can change upon process stops to make sure /// this contract is very visible. /// This variable should only be accessed directly by constructores or live diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp index b2ebaee732b8..8be70dc2139b 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp @@ -7,8 +7,11 @@ //===----------------------------------------------------------------------===// #include "TraceIntelPTBundleSaver.h" + +#include "PerfContextSwitchDecoder.h" #include "TraceIntelPT.h" #include "TraceIntelPTJSONStructs.h" + #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Target/Process.h" @@ -30,6 +33,13 @@ using namespace lldb_private; using namespace lldb_private::trace_intel_pt; using namespace llvm; +/// Strip the \p directory component from the given \p path. It assumes that \p +/// directory is a prefix of \p path. +static std::string GetRelativePath(const FileSpec &directory, + const FileSpec &path) { + return path.GetPath().substr(directory.GetPath().size() + 1); +} + /// Write a stream of bytes from \p data to the given output file. /// It creates or overwrites the output file, but not append. static llvm::Error WriteBytesToDisk(FileSpec &output_file, @@ -57,11 +67,11 @@ static llvm::Error WriteBytesToDisk(FileSpec &output_file, /// The directory where the JSON file will be saved. /// /// \return -/// \a llvm::Success if the operation was successful, or an \a llvm::Error -/// otherwise. -static llvm::Error +/// A \a FileSpec pointing to the bundle description file, or an \a +/// llvm::Error otherwise. +static Expected<FileSpec> SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description, - const FileSpec &directory) { + const FileSpec &directory) { FileSpec trace_path = directory; trace_path.AppendPathComponent("trace.json"); std::ofstream os(trace_path.GetPath()); @@ -71,7 +81,7 @@ SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description, return createStringError(inconvertibleErrorCode(), formatv("couldn't write to the file {0}", trace_path.GetPath().c_str())); - return Error::success(); + return trace_path; } /// Build the threads sub-section of the trace bundle description file. @@ -106,7 +116,7 @@ BuildThreadsSection(Process &process, FileSpec directory) { if (trace_sp->GetTracedCpus().empty()) { FileSpec output_file = threads_dir; output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace"); - json_thread.ipt_trace = output_file.GetPath(); + json_thread.ipt_trace = GetRelativePath(directory, output_file); llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead( tid, IntelPTDataKinds::kIptTrace, @@ -122,8 +132,68 @@ BuildThreadsSection(Process &process, FileSpec directory) { return json_threads; } +/// \return +/// an \a llvm::Error in case of failures, \a None if the trace is not written +/// to disk because the trace is empty and the \p compact flag is present, or +/// the FileSpec of the trace file on disk. +static Expected<Optional<FileSpec>> +WriteContextSwitchTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id, + const FileSpec &cpus_dir, bool compact) { + FileSpec output_context_switch_trace = cpus_dir; + output_context_switch_trace.AppendPathComponent(std::to_string(cpu_id) + + ".perf_context_switch_trace"); + + bool should_skip = false; + + Error err = trace_ipt.OnCpuBinaryDataRead( + cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace, + [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error { + if (!compact) + return WriteBytesToDisk(output_context_switch_trace, data); + + std::set<lldb::pid_t> pids; + for (Process *process : trace_ipt.GetAllProcesses()) + pids.insert(process->GetID()); + + Expected<std::vector<uint8_t>> compact_context_switch_trace = + FilterProcessesFromContextSwitchTrace(data, pids); + if (!compact_context_switch_trace) + return compact_context_switch_trace.takeError(); + + if (compact_context_switch_trace->empty()) { + should_skip = true; + return Error::success(); + } + + return WriteBytesToDisk(output_context_switch_trace, + *compact_context_switch_trace); + }); + if (err) + return std::move(err); + + if (should_skip) + return None; + return output_context_switch_trace; +} + +static Expected<FileSpec> WriteIntelPTTrace(TraceIntelPT &trace_ipt, + lldb::cpu_id_t cpu_id, + const FileSpec &cpus_dir) { + FileSpec output_trace = cpus_dir; + output_trace.AppendPathComponent(std::to_string(cpu_id) + ".intelpt_trace"); + + Error err = trace_ipt.OnCpuBinaryDataRead( + cpu_id, IntelPTDataKinds::kIptTrace, + [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error { + return WriteBytesToDisk(output_trace, data); + }); + if (err) + return std::move(err); + return output_trace; +} + static llvm::Expected<llvm::Optional<std::vector<JSONCpu>>> -BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory) { +BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) { if (trace_ipt.GetTracedCpus().empty()) return None; @@ -135,36 +205,21 @@ BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory) { for (lldb::cpu_id_t cpu_id : trace_ipt.GetTracedCpus()) { JSONCpu json_cpu; json_cpu.id = cpu_id; + Expected<Optional<FileSpec>> context_switch_trace_path = + WriteContextSwitchTrace(trace_ipt, cpu_id, cpus_dir, compact); + if (!context_switch_trace_path) + return context_switch_trace_path.takeError(); + if (!*context_switch_trace_path) + continue; + json_cpu.context_switch_trace = + GetRelativePath(directory, **context_switch_trace_path); - { - FileSpec output_trace = cpus_dir; - output_trace.AppendPathComponent(std::to_string(cpu_id) + - ".intelpt_trace"); - json_cpu.ipt_trace = output_trace.GetPath(); - - llvm::Error err = trace_ipt.OnCpuBinaryDataRead( - cpu_id, IntelPTDataKinds::kIptTrace, - [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error { - return WriteBytesToDisk(output_trace, data); - }); - if (err) - return std::move(err); - } - - { - FileSpec output_context_switch_trace = cpus_dir; - output_context_switch_trace.AppendPathComponent( - std::to_string(cpu_id) + ".perf_context_switch_trace"); - json_cpu.context_switch_trace = output_context_switch_trace.GetPath(); + if (Expected<FileSpec> ipt_trace_path = + WriteIntelPTTrace(trace_ipt, cpu_id, cpus_dir)) + json_cpu.ipt_trace = GetRelativePath(directory, *ipt_trace_path); + else + return ipt_trace_path.takeError(); - llvm::Error err = trace_ipt.OnCpuBinaryDataRead( - cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace, - [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error { - return WriteBytesToDisk(output_context_switch_trace, data); - }); - if (err) - return std::move(err); - } json_cpus.push_back(std::move(json_cpu)); } return json_cpus; @@ -222,14 +277,14 @@ BuildModulesSection(Process &process, FileSpec directory) { path_to_copy_module.AppendPathComponent(system_path); sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString()); - if (std::error_code ec = llvm::sys::fs::copy_file( - system_path, path_to_copy_module.GetPath())) + if (std::error_code ec = + llvm::sys::fs::copy_file(file, path_to_copy_module.GetPath())) return createStringError( inconvertibleErrorCode(), formatv("couldn't write to the file. {0}", ec.message())); json_modules.push_back( - JSONModule{system_path, path_to_copy_module.GetPath(), + JSONModule{system_path, GetRelativePath(directory, path_to_copy_module), JSONUINT64{load_addr}, module_sp->GetUUID().GetAsString()}); } return json_modules; @@ -280,8 +335,9 @@ BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) { return processes; } -Error TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt, - FileSpec directory) { +Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt, + FileSpec directory, + bool compact) { if (std::error_code ec = sys::fs::create_directories(directory.GetPath().c_str())) return llvm::errorCodeToError(ec); @@ -299,7 +355,7 @@ Error TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt, return json_processes.takeError(); Expected<Optional<std::vector<JSONCpu>>> json_cpus = - BuildCpusSection(trace_ipt, directory); + BuildCpusSection(trace_ipt, directory, compact); if (!json_cpus) return json_cpus.takeError(); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h index c36677e1c00d..7224636f0c74 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h @@ -31,10 +31,16 @@ public: /// \param[in] directory /// The directory where the trace bundle will be created. /// + /// \param[in] compact + /// Filter out information irrelevant to the traced processes in the + /// context switch and intel pt traces when using per-cpu mode. This + /// effectively reduces the size of those traces. + /// /// \return - /// \a llvm::success if the operation was successful, or an \a llvm::Error - /// otherwise. - llvm::Error SaveToDisk(TraceIntelPT &trace_ipt, FileSpec directory); + /// A \a FileSpec pointing to the bundle description file, or an \a + /// llvm::Error otherwise. + llvm::Expected<FileSpec> SaveToDisk(TraceIntelPT &trace_ipt, + FileSpec directory, bool compact); }; } // namespace trace_intel_pt diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h index 61fdb4574d54..43c86fca3425 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h @@ -21,6 +21,7 @@ const size_t kDefaultProcessBufferSizeLimit = 5 * 1024 * 1024; // 500MB const bool kDefaultEnableTscValue = false; const llvm::Optional<size_t> kDefaultPsbPeriod = llvm::None; const bool kDefaultPerCpuTracing = false; +const bool kDefaultDisableCgroupFiltering = false; } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp index d2dbc049672c..e547032f739d 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp @@ -1,4 +1,4 @@ -//===-- TraceIntelPTMultiCpuDecoder.cpp ----0------------------------------===// +//===-- TraceIntelPTMultiCpuDecoder.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -39,30 +39,35 @@ Expected<DecodedThreadSP> TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) { if (Error err = CorrelateContextSwitchesAndIntelPtTraces()) return std::move(err); - auto it = m_decoded_threads.find(thread.GetID()); - if (it != m_decoded_threads.end()) - return it->second; - - DecodedThreadSP decoded_thread_sp = - std::make_shared<DecodedThread>(thread.shared_from_this()); - TraceIntelPTSP trace_sp = GetTrace(); - Error err = trace_sp->OnAllCpusBinaryDataRead( - IntelPTDataKinds::kIptTrace, - [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error { - auto it = m_continuous_executions_per_thread->find(thread.GetID()); - if (it != m_continuous_executions_per_thread->end()) - return DecodeSystemWideTraceForThread(*decoded_thread_sp, *trace_sp, - buffers, it->second); - - return Error::success(); + return trace_sp + ->GetThreadTimer(thread.GetID()) + .TimeTask("Decoding instructions", [&]() -> Expected<DecodedThreadSP> { + auto it = m_decoded_threads.find(thread.GetID()); + if (it != m_decoded_threads.end()) + return it->second; + + DecodedThreadSP decoded_thread_sp = + std::make_shared<DecodedThread>(thread.shared_from_this()); + + Error err = trace_sp->OnAllCpusBinaryDataRead( + IntelPTDataKinds::kIptTrace, + [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error { + auto it = + m_continuous_executions_per_thread->find(thread.GetID()); + if (it != m_continuous_executions_per_thread->end()) + return DecodeSystemWideTraceForThread( + *decoded_thread_sp, *trace_sp, buffers, it->second); + + return Error::success(); + }); + if (err) + return std::move(err); + + m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp); + return decoded_thread_sp; }); - if (err) - return std::move(err); - - m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp); - return decoded_thread_sp; } static Expected<std::vector<IntelPTThreadSubtrace>> @@ -105,6 +110,7 @@ TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() { if (!intel_pt_subtraces) return intel_pt_subtraces.takeError(); + m_total_psb_blocks += intel_pt_subtraces->size(); // We'll be iterating through the thread continuous executions and the intel // pt subtraces sorted by time. auto it = intel_pt_subtraces->begin(); @@ -118,7 +124,7 @@ TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() { if (it->tsc > thread_execution.GetStartTSC()) { execution.intelpt_subtraces.push_back(*it); } else { - m_unattributed_intelpt_subtraces++; + m_unattributed_psb_blocks++; } } continuous_executions_per_thread[thread_execution.tid].push_back( @@ -137,6 +143,8 @@ TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() { }); if (err) return std::move(err); + + m_unattributed_psb_blocks += intel_pt_subtraces->end() - it; } // We now sort the executions of each thread to have them ready for // instruction decoding @@ -153,7 +161,7 @@ Error TraceIntelPTMultiCpuDecoder::CorrelateContextSwitchesAndIntelPtTraces() { if (m_continuous_executions_per_thread) return Error::success(); - Error err = GetTrace()->GetTimer().ForGlobal().TimeTask<Error>( + Error err = GetTrace()->GetGlobalTimer().TimeTask( "Context switch and Intel PT traces correlation", [&]() -> Error { if (auto correlation = DoCorrelateContextSwitchesAndIntelPtTraces()) { m_continuous_executions_per_thread.emplace(std::move(*correlation)); @@ -187,3 +195,24 @@ size_t TraceIntelPTMultiCpuDecoder::GetTotalContinuousExecutionsCount() const { count += kv.second.size(); return count; } + +size_t +TraceIntelPTMultiCpuDecoder::GePSBBlocksCountForThread(lldb::tid_t tid) const { + if (!m_continuous_executions_per_thread) + return 0; + size_t count = 0; + auto it = m_continuous_executions_per_thread->find(tid); + if (it == m_continuous_executions_per_thread->end()) + return 0; + for (const IntelPTThreadContinousExecution &execution : it->second) + count += execution.intelpt_subtraces.size(); + return count; +} + +size_t TraceIntelPTMultiCpuDecoder::GetUnattributedPSBBlocksCount() const { + return m_unattributed_psb_blocks; +} + +size_t TraceIntelPTMultiCpuDecoder::GetTotalPSBBlocksCount() const { + return m_total_psb_blocks; +} diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h index 11771e018f7b..3b7926760f3c 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h @@ -50,9 +50,22 @@ public: size_t GetNumContinuousExecutionsForThread(lldb::tid_t tid) const; /// \return + /// The number of PSB blocks for a given thread in all cores. + size_t GePSBBlocksCountForThread(lldb::tid_t tid) const; + + /// \return /// The total number of continuous executions found across CPUs. size_t GetTotalContinuousExecutionsCount() const; + /// \return + /// The number of psb blocks in all cores that couldn't be matched with a + /// thread execution coming from context switch traces. + size_t GetUnattributedPSBBlocksCount() const; + + /// \return + /// The total number of PSB blocks in all cores. + size_t GetTotalPSBBlocksCount() const; + private: /// Traverse the context switch traces and the basic intel pt continuous /// subtraces and produce a list of continuous executions for each process and @@ -80,7 +93,8 @@ private: /// This variable will be non-None if a severe error happened during the setup /// of the decoder and we don't want decoding to be reattempted. llvm::Optional<std::string> m_setup_error; - uint64_t m_unattributed_intelpt_subtraces; + uint64_t m_unattributed_psb_blocks = 0; + uint64_t m_total_psb_blocks = 0; }; } // namespace trace_intel_pt diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td index 29aa1459306a..95bd5c3d1cce 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td @@ -11,7 +11,9 @@ let Command = "thread trace start intel pt" in { Arg<"Value">, Desc<"Trace size in bytes per thread. It must be a power of 2 greater " "than or equal to 4096 (2^12). The trace is circular keeping " - "the most recent data. Defaults to 4096 bytes.">; + "the most recent data. Defaults to 4096 bytes. It's possible to " + "specify size using multiples of unit bytes, e.g., 4KB, 1MB, 1MiB, " + "where 1K is 1024 bytes and 1M is 1048576 bytes.">; def thread_trace_start_intel_pt_tsc: Option<"tsc", "t">, Group<1>, Desc<"Enable the use of TSC timestamps. This is supported on all devices " @@ -40,7 +42,8 @@ let Command = "process trace start intel pt" in { Arg<"Value">, Desc<"Size in bytes used by each individual per-thread or per-cpu trace " "buffer. It must be a power of 2 greater than or equal to 4096 (2^12) " - "bytes.">; + "bytes. It's possible to specify a unit for these bytes, like 4KB, " + "16KiB or 1MB. Lower case units are allowed for convenience.">; def process_trace_start_intel_pt_per_cpu_tracing: Option<"per-cpu-tracing", "c">, Group<1>, @@ -53,7 +56,8 @@ let Command = "process trace start intel pt" in { "option forces the capture of TSC timestamps (see --tsc). Also, this " "option can't be used simulatenously with any other trace sessions " "because of its system-wide nature.">; - def process_trace_start_intel_pt_process_size_limit: Option<"total-size-limit", "l">, + def process_trace_start_intel_pt_process_size_limit: + Option<"total-size-limit", "l">, Group<1>, Arg<"Value">, Desc<"Maximum total trace size per process in bytes. This limit applies to " @@ -62,7 +66,9 @@ let Command = "process trace start intel pt" in { "Whenever a thread is attempted to be traced due to this command and " "the limit would be reached, the process is stopped with a " "\"processor trace\" reason, so that the user can retrace the process " - "if needed. Defaults to 500MB.">; + "if needed. Defaults to 500MB. It's possible to specify a unit for " + "these bytes, like 4KB, 16KiB or 1MB. Lower case units are allowed " + "for convenience.">; def process_trace_start_intel_pt_tsc: Option<"tsc", "t">, Group<1>, Desc<"Enable the use of TSC timestamps. This is supported on all devices " @@ -83,14 +89,9 @@ let Command = "process trace start intel pt" in { "converted to the approximate number of raw trace bytes between PSB " "packets as: 2 ^ (value + 11), e.g. value 3 means 16KiB between PSB " "packets. Defaults to 0 if supported.">; -} - -let Command = "process trace save intel pt" in { - def process_trace_save_intel_directory: Option<"directory", "d">, - Group<1>, - Arg<"Value">, Required, - Desc<"This value defines the directory where the trace will be saved." - "It will be created if it does not exist. It will also create a " - "trace files with the trace data and a trace.json with the main " - "properties of the trace session.">; + def process_trace_start_intel_pt_disable_cgroup_filtering: + Option<"disable-cgroup-filtering", "d">, + Desc<"Disable the automatic cgroup filtering that is applied if --per-cpu " + "is provided. Cgroup filtering allows collecting intel pt data " + "exclusively of processes of the same cgroup as the target.">; } diff --git a/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp b/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp index 7deeaf2bf10f..d8e44ee66de8 100644 --- a/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp +++ b/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp @@ -160,15 +160,15 @@ TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor) cursor.Next(); } else { lldb::addr_t current_instruction_load_address = cursor.GetLoadAddress(); - lldb::TraceInstructionControlFlowType current_instruction_type = - cursor.GetInstructionControlFlowType(); + lldb::InstructionControlFlowKind current_instruction_type = + cursor.GetInstructionControlFlowKind(); m_instruction_layer_up->AppendInstruction( current_instruction_load_address); cursor.Next(); bool more_data_in_trace = cursor.HasValue(); if (current_instruction_type & - lldb::eTraceInstructionControlFlowTypeCall) { + lldb::eInstructionControlFlowKindCall) { if (more_data_in_trace && !cursor.IsError()) { m_instruction_layer_up->AddCallInstructionMetadata( current_instruction_load_address, diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp index ac5e316eecb0..03515a32ff86 100644 --- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -83,6 +83,7 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const bool show_address = true; const bool show_bytes = true; + const bool show_control_flow_kind = true; m_inst_emulator_up->GetRegisterInfo(unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); @@ -244,7 +245,8 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( lldb_private::FormatEntity::Entry format; FormatEntity::Parse("${frame.pc}: ", format); inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address, - show_bytes, nullptr, nullptr, nullptr, &format, 0); + show_bytes, show_control_flow_kind, nullptr, nullptr, + nullptr, &format, 0); log->PutString(strm.GetString()); } diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp index 648a12524aed..8ec4bc90cd13 100644 --- a/lldb/source/Symbol/Function.cpp +++ b/lldb/source/Symbol/Function.cpp @@ -196,11 +196,10 @@ Function *IndirectCallEdge::GetCallee(ModuleList &images, Log *log = GetLog(LLDBLog::Step); Status error; Value callee_addr_val; - if (!call_target.Evaluate(&exe_ctx, exe_ctx.GetRegisterContext(), - /*loclist_base_load_addr=*/LLDB_INVALID_ADDRESS, - /*initial_value_ptr=*/nullptr, - /*object_address_ptr=*/nullptr, callee_addr_val, - &error)) { + if (!call_target.Evaluate( + &exe_ctx, exe_ctx.GetRegisterContext(), LLDB_INVALID_ADDRESS, + /*initial_value_ptr=*/nullptr, + /*object_address_ptr=*/nullptr, callee_addr_val, &error)) { LLDB_LOGF(log, "IndirectCallEdge: Could not evaluate expression: %s", error.AsCString()); return nullptr; @@ -440,8 +439,9 @@ bool Function::GetDisassembly(const ExecutionContext &exe_ctx, if (disassembler_sp) { const bool show_address = true; const bool show_bytes = false; - disassembler_sp->GetInstructionList().Dump(&strm, show_address, show_bytes, - &exe_ctx); + const bool show_control_flow_kind = false; + disassembler_sp->GetInstructionList().Dump( + &strm, show_address, show_bytes, show_control_flow_kind, &exe_ctx); return true; } return false; diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp index 9ec7f2638f71..668276aa2500 100644 --- a/lldb/source/Symbol/Symbol.cpp +++ b/lldb/source/Symbol/Symbol.cpp @@ -558,8 +558,9 @@ bool Symbol::GetDisassembly(const ExecutionContext &exe_ctx, const char *flavor, if (disassembler_sp) { const bool show_address = true; const bool show_bytes = false; - disassembler_sp->GetInstructionList().Dump(&strm, show_address, show_bytes, - &exe_ctx); + const bool show_control_flow_kind = false; + disassembler_sp->GetInstructionList().Dump( + &strm, show_address, show_bytes, show_control_flow_kind, &exe_ctx); return true; } return false; diff --git a/lldb/source/Symbol/TypeList.cpp b/lldb/source/Symbol/TypeList.cpp index ace715d933ea..494e59e3a0fc 100644 --- a/lldb/source/Symbol/TypeList.cpp +++ b/lldb/source/Symbol/TypeList.cpp @@ -97,7 +97,7 @@ void TypeList::Dump(Stream *s, bool show_context) { } } -void TypeList::RemoveMismatchedTypes(const char *qualified_typename, +void TypeList::RemoveMismatchedTypes(llvm::StringRef qualified_typename, bool exact_match) { llvm::StringRef type_scope; llvm::StringRef type_basename; @@ -107,13 +107,12 @@ void TypeList::RemoveMismatchedTypes(const char *qualified_typename, type_basename = qualified_typename; type_scope = ""; } - return RemoveMismatchedTypes(std::string(type_scope), - std::string(type_basename), type_class, + return RemoveMismatchedTypes(type_scope, type_basename, type_class, exact_match); } -void TypeList::RemoveMismatchedTypes(const std::string &type_scope, - const std::string &type_basename, +void TypeList::RemoveMismatchedTypes(llvm::StringRef type_scope, + llvm::StringRef type_basename, TypeClass type_class, bool exact_match) { // Our "collection" type currently is a std::map which doesn't have any good // way to iterate and remove items from the map so we currently just make a diff --git a/lldb/source/Symbol/TypeMap.cpp b/lldb/source/Symbol/TypeMap.cpp index 2cda9b6c27d1..0d5f6d53e5a0 100644 --- a/lldb/source/Symbol/TypeMap.cpp +++ b/lldb/source/Symbol/TypeMap.cpp @@ -127,23 +127,8 @@ void TypeMap::Dump(Stream *s, bool show_context, lldb::DescriptionLevel level) { } } -void TypeMap::RemoveMismatchedTypes(const char *qualified_typename, - bool exact_match) { - llvm::StringRef type_scope; - llvm::StringRef type_basename; - TypeClass type_class = eTypeClassAny; - if (!Type::GetTypeScopeAndBasename(qualified_typename, type_scope, - type_basename, type_class)) { - type_basename = qualified_typename; - type_scope = ""; - } - return RemoveMismatchedTypes(std::string(type_scope), - std::string(type_basename), type_class, - exact_match); -} - -void TypeMap::RemoveMismatchedTypes(const std::string &type_scope, - const std::string &type_basename, +void TypeMap::RemoveMismatchedTypes(llvm::StringRef type_scope, + llvm::StringRef type_basename, TypeClass type_class, bool exact_match) { // Our "collection" type currently is a std::map which doesn't have any good // way to iterate and remove items from the map so we currently just make a @@ -214,25 +199,3 @@ void TypeMap::RemoveMismatchedTypes(const std::string &type_scope, } m_types.swap(matching_types); } - -void TypeMap::RemoveMismatchedTypes(TypeClass type_class) { - if (type_class == eTypeClassAny) - return; - - // Our "collection" type currently is a std::map which doesn't have any good - // way to iterate and remove items from the map so we currently just make a - // new list and add all of the matching types to it, and then swap it into - // m_types at the end - collection matching_types; - - iterator pos, end = m_types.end(); - - for (pos = m_types.begin(); pos != end; ++pos) { - Type *the_type = pos->second.get(); - TypeClass match_type_class = - the_type->GetForwardCompilerType().GetTypeClass(); - if (match_type_class & type_class) - matching_types.insert(*pos); - } - m_types.swap(matching_types); -} diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp index b92c86654496..f65e73e5d049 100644 --- a/lldb/source/Symbol/Variable.cpp +++ b/lldb/source/Symbol/Variable.cpp @@ -39,13 +39,13 @@ Variable::Variable(lldb::user_id_t uid, const char *name, const char *mangled, const lldb::SymbolFileTypeSP &symfile_type_sp, ValueType scope, SymbolContextScope *context, const RangeList &scope_range, Declaration *decl_ptr, - const DWARFExpression &location, bool external, + const DWARFExpressionList &location_list, bool external, bool artificial, bool location_is_constant_data, bool static_member) : UserID(uid), m_name(name), m_mangled(ConstString(mangled)), m_symfile_type_sp(symfile_type_sp), m_scope(scope), m_owner_scope(context), m_scope_range(scope_range), - m_declaration(decl_ptr), m_location(location), m_external(external), + m_declaration(decl_ptr), m_location_list(location_list), m_external(external), m_artificial(artificial), m_loc_is_const_data(location_is_constant_data), m_static_member(static_member) {} @@ -145,7 +145,7 @@ void Variable::Dump(Stream *s, bool show_context) const { bool show_fullpaths = false; m_declaration.Dump(s, show_fullpaths); - if (m_location.IsValid()) { + if (m_location_list.IsValid()) { s->PutCString(", location = "); ABISP abi; if (m_owner_scope) { @@ -153,7 +153,7 @@ void Variable::Dump(Stream *s, bool show_context) const { if (module_sp) abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture()); } - m_location.GetDescription(s, lldb::eDescriptionLevelBrief, abi.get()); + m_location_list.GetDescription(s, lldb::eDescriptionLevelBrief, abi.get()); } if (m_external) @@ -212,12 +212,6 @@ void Variable::CalculateSymbolContext(SymbolContext *sc) { } bool Variable::LocationIsValidForFrame(StackFrame *frame) { - // Is the variable is described by a single location? - if (!m_location.IsLocationList()) { - // Yes it is, the location is valid. - return true; - } - if (frame) { Function *function = frame->GetSymbolContext(eSymbolContextFunction).function; @@ -231,7 +225,7 @@ bool Variable::LocationIsValidForFrame(StackFrame *frame) { return false; // It is a location list. We just need to tell if the location list // contains the current address when converted to a load address - return m_location.LocationListContainsAddress( + return m_location_list.ContainsAddress( loclist_base_load_addr, frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get())); } @@ -244,7 +238,7 @@ bool Variable::LocationIsValidForAddress(const Address &address) { // function. if (address.IsSectionOffset()) { // We need to check if the address is valid for both scope range and value - // range. + // range. // Empty scope range means block range. bool valid_in_scope_range = GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains( @@ -255,7 +249,7 @@ bool Variable::LocationIsValidForAddress(const Address &address) { CalculateSymbolContext(&sc); if (sc.module_sp == address.GetModule()) { // Is the variable is described by a single location? - if (!m_location.IsLocationList()) { + if (m_location_list.IsAlwaysValidSingleExpr()) { // Yes it is, the location is valid. return true; } @@ -267,8 +261,8 @@ bool Variable::LocationIsValidForAddress(const Address &address) { return false; // It is a location list. We just need to tell if the location list // contains the current address when converted to a load address - return m_location.LocationListContainsAddress(loclist_base_file_addr, - address.GetFileAddress()); + return m_location_list.ContainsAddress(loclist_base_file_addr, + address.GetFileAddress()); } } } @@ -459,9 +453,9 @@ bool Variable::DumpLocations(Stream *s, const Address &address) { sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) return false; - return m_location.DumpLocations(s, eDescriptionLevelBrief, - loclist_base_file_addr, file_addr, - abi.get()); + return m_location_list.DumpLocations(s, eDescriptionLevelBrief, + loclist_base_file_addr, file_addr, + abi.get()); } return false; } diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index e98aed7e1555..a0f97d7e7cff 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -11,7 +11,7 @@ #include "lldb/Core/AddressRange.h" #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" -#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Expression/DWARFExpressionList.h" #include "lldb/Symbol/ArmUnwindInfo.h" #include "lldb/Symbol/CallFrameInfo.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" @@ -381,7 +381,7 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { // symbol/function information - just stick in some reasonable defaults and // hope we can unwind past this frame. If we're above a trap handler, // we may be at a bogus address because we jumped through a bogus function - // pointer and trapped, so don't force the arch default unwind plan in that + // pointer and trapped, so don't force the arch default unwind plan in that // case. ModuleSP pc_module_sp(m_current_pc.GetModule()); if ((!m_current_pc.IsValid() || !pc_module_sp) && @@ -1286,7 +1286,7 @@ RegisterContextUnwind::SavedLocationForRegister( // arch default unwind plan is used as the Fast Unwind Plan, we // need to recognize this & switch over to the Full Unwind Plan // to see what unwind rule that (more knoweldgeable, probably) - // UnwindPlan has. If the full UnwindPlan says the register + // UnwindPlan has. If the full UnwindPlan says the register // location is Undefined, then it really is. if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc) && @@ -1335,13 +1335,13 @@ RegisterContextUnwind::SavedLocationForRegister( m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) { // If this is a trap handler frame, we should have access to - // the complete register context when the interrupt/async + // the complete register context when the interrupt/async // signal was received, we should fetch the actual saved $pc // value instead of the Return Address register. // If $pc is not available, fall back to the RA reg. UnwindPlan::Row::RegisterLocation scratch; if (m_frame_type == eTrapHandlerFrame && - active_row->GetRegisterInfo + active_row->GetRegisterInfo (pc_regnum.GetAsKind (unwindplan_registerkind), scratch)) { UnwindLogMsg("Providing pc register instead of rewriting to " "RA reg because this is a trap handler and there is " @@ -1642,8 +1642,9 @@ RegisterContextUnwind::SavedLocationForRegister( process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; - DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr); - dwarfexpr.SetRegisterKind(unwindplan_registerkind); + DWARFExpressionList dwarfexpr(opcode_ctx, dwarfdata, nullptr); + dwarfexpr.GetMutableExpressionAtAddress()->SetRegisterKind( + unwindplan_registerkind); Value cfa_val = Scalar(m_cfa); cfa_val.SetValueType(Value::ValueType::LoadAddress); Value result; @@ -2006,8 +2007,9 @@ bool RegisterContextUnwind::ReadFrameAddress( process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; - DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr); - dwarfexpr.SetRegisterKind(row_register_kind); + DWARFExpressionList dwarfexpr(opcode_ctx, dwarfdata, nullptr); + dwarfexpr.GetMutableExpressionAtAddress()->SetRegisterKind( + row_register_kind); Value result; Status error; if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, result, diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 1e3dbc73a04e..e87cf5af3e39 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -1087,7 +1087,7 @@ bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { ExecutionContext exe_ctx(shared_from_this()); Value expr_value; addr_t loclist_base_addr = LLDB_INVALID_ADDRESS; - if (m_sc.function->GetFrameBaseExpression().IsLocationList()) + if (!m_sc.function->GetFrameBaseExpression().IsAlwaysValidSingleExpr()) loclist_base_addr = m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress( exe_ctx.GetTargetPtr()); @@ -1116,7 +1116,7 @@ bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { return m_frame_base_error.Success(); } -DWARFExpression *StackFrame::GetFrameBaseExpression(Status *error_ptr) { +DWARFExpressionList *StackFrame::GetFrameBaseExpression(Status *error_ptr) { if (!m_sc.function) { if (error_ptr) { error_ptr->SetErrorString("No function in symbol context."); @@ -1200,7 +1200,7 @@ lldb::LanguageType StackFrame::GuessLanguage() { LanguageType lang_type = GetLanguage(); if (lang_type == eLanguageTypeUnknown) { - SymbolContext sc = GetSymbolContext(eSymbolContextFunction + SymbolContext sc = GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol); if (sc.function) { lang_type = sc.function->GetMangled().GuessLanguage(); @@ -1417,7 +1417,7 @@ ValueObjectSP GetValueForDereferincingOffset(StackFrame &frame, Status error; ValueObjectSP pointee = base->Dereference(error); - + if (!pointee) { return ValueObjectSP(); } @@ -1505,7 +1505,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, Instruction::Operand::BuildRegister(reg)); for (VariableSP var_sp : variables) { - if (var_sp->LocationExpression().MatchesOperand(frame, op)) + if (var_sp->LocationExpressionList().MatchesOperand(frame, op)) return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); } diff --git a/lldb/source/Target/ThreadPlanTracer.cpp b/lldb/source/Target/ThreadPlanTracer.cpp index f2346fc237ce..f5331428038b 100644 --- a/lldb/source/Target/ThreadPlanTracer.cpp +++ b/lldb/source/Target/ThreadPlanTracer.cpp @@ -170,13 +170,14 @@ void ThreadPlanAssemblyTracer::Log() { if (instruction_list.GetSize()) { const bool show_bytes = true; const bool show_address = true; + const bool show_control_flow_kind = true; Instruction *instruction = instruction_list.GetInstructionAtIndex(0).get(); const FormatEntity::Entry *disassemble_format = m_process.GetTarget().GetDebugger().GetDisassemblyFormat(); instruction->Dump(stream, max_opcode_byte_size, show_address, - show_bytes, nullptr, nullptr, nullptr, - disassemble_format, 0); + show_bytes, show_control_flow_kind, nullptr, nullptr, + nullptr, disassemble_format, 0); } } } diff --git a/lldb/source/Target/TraceCursor.cpp b/lldb/source/Target/TraceCursor.cpp index 1c3fabc4dec0..f99b0d28c154 100644 --- a/lldb/source/Target/TraceCursor.cpp +++ b/lldb/source/Target/TraceCursor.cpp @@ -48,5 +48,8 @@ const char *TraceCursor::EventKindToString(lldb::TraceEvent event_kind) { return "hardware disabled tracing"; case lldb::eTraceEventDisabledSW: return "software disabled tracing"; + case lldb::eTraceEventCPUChanged: + return "CPU core changed"; } + llvm_unreachable("Fully covered switch above"); } diff --git a/lldb/source/Target/TraceDumper.cpp b/lldb/source/Target/TraceDumper.cpp index 6a5fd0268e02..739105e9e9fb 100644 --- a/lldb/source/Target/TraceDumper.cpp +++ b/lldb/source/Target/TraceDumper.cpp @@ -129,32 +129,30 @@ public: m_s.Format(" {0}: ", item.id); if (m_options.show_tsc) { - m_s << "[tsc="; - - if (item.tsc) - m_s.Format("{0}", *item.tsc); - else - m_s << "unavailable"; - - m_s << "] "; + m_s.Format("[tsc={0}] ", + item.tsc ? std::to_string(*item.tsc) : "unavailable"); } if (item.event) { m_s << "(event) " << TraceCursor::EventKindToString(*item.event); + if (*item.event == eTraceEventCPUChanged) { + m_s.Format(" [new CPU={0}]", + item.cpu_id ? std::to_string(*item.cpu_id) : "unavailable"); + } } else if (item.error) { m_s << "(error) " << *item.error; } else { m_s.Format("{0:x+16}", item.load_address); - if (item.symbol_info) { + if (item.symbol_info && item.symbol_info->instruction) { m_s << " "; - item.symbol_info->instruction->Dump(&m_s, /*max_opcode_byte_size=*/0, - /*show_address=*/false, - /*show_bytes=*/false, - &item.symbol_info->exe_ctx, - &item.symbol_info->sc, - /*prev_sym_ctx=*/nullptr, - /*disassembly_addr_format=*/nullptr, - /*max_address_text_size=*/0); + item.symbol_info->instruction->Dump( + &m_s, /*max_opcode_byte_size=*/0, + /*show_address=*/false, + /*show_bytes=*/false, m_options.show_control_flow_kind, + &item.symbol_info->exe_ctx, &item.symbol_info->sc, + /*prev_sym_ctx=*/nullptr, + /*disassembly_addr_format=*/nullptr, + /*max_address_text_size=*/0); } } @@ -172,14 +170,16 @@ class OutputWriterJSON : public TraceDumper::OutputWriter { /* schema: error_message: string | { + "event": string, "id": decimal, "tsc"?: string decimal, - "event": string + "cpuId"? decimal, } | { + "error": string, "id": decimal, "tsc"?: string decimal, - "error": string, | { + "loadAddress": string decimal, "id": decimal, "tsc"?: string decimal, "module"?: string, @@ -200,6 +200,37 @@ public: ~OutputWriterJSON() { m_j.arrayEnd(); } + void DumpEvent(const TraceDumper::TraceItem &item) { + m_j.attribute("event", TraceCursor::EventKindToString(*item.event)); + if (item.event == eTraceEventCPUChanged) + m_j.attribute("cpuId", item.cpu_id); + } + + void DumpInstruction(const TraceDumper::TraceItem &item) { + m_j.attribute("loadAddress", formatv("{0:x}", item.load_address)); + if (item.symbol_info) { + m_j.attribute("module", ToOptionalString(GetModuleName(item))); + m_j.attribute( + "symbol", + ToOptionalString(item.symbol_info->sc.GetFunctionName().AsCString())); + + if (item.symbol_info->instruction) { + m_j.attribute("mnemonic", + ToOptionalString(item.symbol_info->instruction->GetMnemonic( + &item.symbol_info->exe_ctx))); + } + + if (IsLineEntryValid(item.symbol_info->sc.line_entry)) { + m_j.attribute( + "source", + ToOptionalString( + item.symbol_info->sc.line_entry.file.GetPath().c_str())); + m_j.attribute("line", item.symbol_info->sc.line_entry.line); + m_j.attribute("column", item.symbol_info->sc.line_entry.column); + } + } + } + void TraceItem(const TraceDumper::TraceItem &item) override { m_j.object([&] { m_j.attribute("id", item.id); @@ -209,37 +240,11 @@ public: item.tsc ? Optional<std::string>(std::to_string(*item.tsc)) : None); if (item.event) { - m_j.object([&] { - m_j.attribute("event", TraceCursor::EventKindToString(*item.event)); - }); - return; - } - - if (item.error) { + DumpEvent(item); + } else if (item.error) { m_j.attribute("error", *item.error); - return; - } - - // we know we are seeing an actual instruction - m_j.attribute("loadAddress", formatv("{0:x}", item.load_address)); - if (item.symbol_info) { - m_j.attribute("module", ToOptionalString(GetModuleName(item))); - m_j.attribute("symbol", - ToOptionalString( - item.symbol_info->sc.GetFunctionName().AsCString())); - m_j.attribute( - "mnemonic", - ToOptionalString(item.symbol_info->instruction->GetMnemonic( - &item.symbol_info->exe_ctx))); - - if (IsLineEntryValid(item.symbol_info->sc.line_entry)) { - m_j.attribute( - "source", - ToOptionalString( - item.symbol_info->sc.line_entry.file.GetPath().c_str())); - m_j.attribute("line", item.symbol_info->sc.line_entry.line); - m_j.attribute("column", item.symbol_info->sc.line_entry.column); - } + } else { + DumpInstruction(item); } }); } @@ -361,6 +366,8 @@ Optional<lldb::user_id_t> TraceDumper::DumpInstructions(size_t count) { if (!m_options.show_events) continue; item.event = m_cursor_up->GetEventType(); + if (*item.event == eTraceEventCPUChanged) + item.cpu_id = m_cursor_up->GetCPU(); } else if (m_cursor_up->IsError()) { item.error = m_cursor_up->GetError(); } else { diff --git a/lldb/source/Utility/Args.cpp b/lldb/source/Utility/Args.cpp index 3978f9422653..daccb91d8436 100644 --- a/lldb/source/Utility/Args.cpp +++ b/lldb/source/Utility/Args.cpp @@ -385,6 +385,7 @@ std::string Args::GetShellSafeArgument(const FileSpec &shell, }; static ShellDescriptor g_Shells[] = {{ConstString("bash"), " '\"<>()&;"}, + {ConstString("fish"), " '\"<>()&\\|;"}, {ConstString("tcsh"), " '\"<>()&;"}, {ConstString("zsh"), " '\"<>()&;\\|"}, {ConstString("sh"), " '\"<>()&;"}}; diff --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp index 1ad74cacc4c3..7a0ed9c53c65 100644 --- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp +++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -53,7 +53,8 @@ bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet, if (packet.IsProcessTracing()) { if (!o.map("processBufferSizeLimit", packet.process_buffer_size_limit) || - !o.map("perCpuTracing", packet.per_cpu_tracing)) + !o.map("perCpuTracing", packet.per_cpu_tracing) || + !o.map("disableCgroupTracing", packet.disable_cgroup_filtering)) return false; } return true; @@ -67,6 +68,7 @@ json::Value toJSON(const TraceIntelPTStartRequest &packet) { obj.try_emplace("psbPeriod", packet.psb_period); obj.try_emplace("enableTsc", packet.enable_tsc); obj.try_emplace("perCpuTracing", packet.per_cpu_tracing); + obj.try_emplace("disableCgroupTracing", packet.disable_cgroup_filtering); return base; } @@ -108,13 +110,15 @@ bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, json::Path path) { ObjectMapper o(value, path); return o && fromJSON(value, (TraceGetStateResponse &)packet, path) && - o.map("tscPerfZeroConversion", packet.tsc_perf_zero_conversion); + o.map("tscPerfZeroConversion", packet.tsc_perf_zero_conversion) && + o.map("usingCgroupFiltering", packet.using_cgroup_filtering); } json::Value toJSON(const TraceIntelPTGetStateResponse &packet) { json::Value base = toJSON((const TraceGetStateResponse &)packet); - base.getAsObject()->insert( - {"tscPerfZeroConversion", packet.tsc_perf_zero_conversion}); + json::Object &obj = *base.getAsObject(); + obj.insert({"tscPerfZeroConversion", packet.tsc_perf_zero_conversion}); + obj.insert({"usingCgroupFiltering", packet.using_cgroup_filtering}); return base; } |