Index: vendor/lldb/dist/include/lldb/API/SBAddress.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBAddress.h (revision 317958) +++ vendor/lldb/dist/include/lldb/API/SBAddress.h (revision 317959) @@ -1,122 +1,126 @@ //===-- SBAddress.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBAddress_h_ #define LLDB_SBAddress_h_ #include "lldb/API/SBDefines.h" #include "lldb/API/SBModule.h" namespace lldb { class LLDB_API SBAddress { public: SBAddress(); SBAddress(const lldb::SBAddress &rhs); SBAddress(lldb::SBSection section, lldb::addr_t offset); // Create an address by resolving a load address using the supplied target SBAddress(lldb::addr_t load_addr, lldb::SBTarget &target); ~SBAddress(); const lldb::SBAddress &operator=(const lldb::SBAddress &rhs); bool IsValid() const; void Clear(); addr_t GetFileAddress() const; addr_t GetLoadAddress(const lldb::SBTarget &target) const; void SetAddress(lldb::SBSection section, lldb::addr_t offset); void SetLoadAddress(lldb::addr_t load_addr, lldb::SBTarget &target); bool OffsetAddress(addr_t offset); bool GetDescription(lldb::SBStream &description); // The following queries can lookup symbol information for a given address. // An address might refer to code or data from an existing module, or it // might refer to something on the stack or heap. The following functions // will only return valid values if the address has been resolved to a code // or data address using "void SBAddress::SetLoadAddress(...)" or // "lldb::SBAddress SBTarget::ResolveLoadAddress (...)". lldb::SBSymbolContext GetSymbolContext(uint32_t resolve_scope); // The following functions grab individual objects for a given address and // are less efficient if you want more than one symbol related objects. // Use one of the following when you want multiple debug symbol related // objects for an address: // lldb::SBSymbolContext SBAddress::GetSymbolContext (uint32_t // resolve_scope); // lldb::SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const // SBAddress &addr, uint32_t resolve_scope); // One or more bits from the SymbolContextItem enumerations can be logically // OR'ed together to more efficiently retrieve multiple symbol objects. lldb::SBSection GetSection(); lldb::addr_t GetOffset(); lldb::SBModule GetModule(); lldb::SBCompileUnit GetCompileUnit(); lldb::SBFunction GetFunction(); lldb::SBBlock GetBlock(); lldb::SBSymbol GetSymbol(); lldb::SBLineEntry GetLineEntry(); lldb::AddressClass GetAddressClass(); protected: friend class SBBlock; friend class SBBreakpointLocation; friend class SBFrame; friend class SBFunction; friend class SBLineEntry; friend class SBInstruction; friend class SBModule; friend class SBSection; friend class SBSymbol; friend class SBSymbolContext; friend class SBTarget; friend class SBThread; friend class SBThreadPlan; friend class SBValue; friend class SBQueueItem; lldb_private::Address *operator->(); const lldb_private::Address *operator->() const; + friend bool operator==(const SBAddress &lhs, const SBAddress &rhs); + lldb_private::Address *get(); lldb_private::Address &ref(); const lldb_private::Address &ref() const; SBAddress(const lldb_private::Address *lldb_object_ptr); void SetAddress(const lldb_private::Address *lldb_object_ptr); private: std::unique_ptr m_opaque_ap; }; + +bool operator==(const SBAddress &lhs, const SBAddress &rhs); } // namespace lldb #endif // LLDB_SBAddress_h_ Index: vendor/lldb/dist/include/lldb/API/SBInstruction.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBInstruction.h (revision 317958) +++ vendor/lldb/dist/include/lldb/API/SBInstruction.h (revision 317959) @@ -1,85 +1,87 @@ //===-- SBInstruction.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBInstruction_h_ #define LLDB_SBInstruction_h_ #include "lldb/API/SBData.h" #include "lldb/API/SBDefines.h" #include // There's a lot to be fixed here, but need to wait for underlying insn // implementation // to be revised & settle down first. class InstructionImpl; namespace lldb { class LLDB_API SBInstruction { public: SBInstruction(); SBInstruction(const SBInstruction &rhs); const SBInstruction &operator=(const SBInstruction &rhs); ~SBInstruction(); bool IsValid(); SBAddress GetAddress(); lldb::AddressClass GetAddressClass(); const char *GetMnemonic(lldb::SBTarget target); const char *GetOperands(lldb::SBTarget target); const char *GetComment(lldb::SBTarget target); lldb::SBData GetData(lldb::SBTarget target); size_t GetByteSize(); bool DoesBranch(); bool HasDelaySlot(); + bool CanSetBreakpoint(); + void Print(FILE *out); bool GetDescription(lldb::SBStream &description); bool EmulateWithFrame(lldb::SBFrame &frame, uint32_t evaluate_options); bool DumpEmulation(const char *triple); // triple is to specify the // architecture, e.g. 'armv6' or // 'armv7-apple-ios' bool TestEmulation(lldb::SBStream &output_stream, const char *test_file); protected: friend class SBInstructionList; SBInstruction(const lldb::DisassemblerSP &disasm_sp, const lldb::InstructionSP &inst_sp); void SetOpaque(const lldb::DisassemblerSP &disasm_sp, const lldb::InstructionSP &inst_sp); lldb::InstructionSP GetOpaque(); private: std::shared_ptr m_opaque_sp; }; } // namespace lldb #endif // LLDB_SBInstruction_h_ Index: vendor/lldb/dist/include/lldb/API/SBInstructionList.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBInstructionList.h (revision 317958) +++ vendor/lldb/dist/include/lldb/API/SBInstructionList.h (revision 317959) @@ -1,58 +1,67 @@ //===-- SBInstructionList.h -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBInstructionList_h_ #define LLDB_SBInstructionList_h_ #include "lldb/API/SBDefines.h" #include namespace lldb { class LLDB_API SBInstructionList { public: SBInstructionList(); SBInstructionList(const SBInstructionList &rhs); const SBInstructionList &operator=(const SBInstructionList &rhs); ~SBInstructionList(); bool IsValid() const; size_t GetSize(); lldb::SBInstruction GetInstructionAtIndex(uint32_t idx); + // ---------------------------------------------------------------------- + // Returns the number of instructions between the start and end address. + // If canSetBreakpoint is true then the count will be the number of + // instructions on which a breakpoint can be set. + // ---------------------------------------------------------------------- + size_t GetInstructionsCount(const SBAddress &start, + const SBAddress &end, + bool canSetBreakpoint = false); + void Clear(); void AppendInstruction(lldb::SBInstruction inst); void Print(FILE *out); bool GetDescription(lldb::SBStream &description); bool DumpEmulationForAllInstructions(const char *triple); protected: friend class SBFunction; friend class SBSymbol; friend class SBTarget; void SetDisassembler(const lldb::DisassemblerSP &opaque_sp); private: lldb::DisassemblerSP m_opaque_sp; }; } // namespace lldb #endif // LLDB_SBInstructionList_h_ Index: vendor/lldb/dist/include/lldb/Core/Disassembler.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/Disassembler.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Core/Disassembler.h (revision 317959) @@ -1,557 +1,559 @@ //===-- Disassembler.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Disassembler_h_ #define liblldb_Disassembler_h_ #include "lldb/Core/Address.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/FormatEntity.h" // for FormatEntity #include "lldb/Core/Opcode.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Interpreter/OptionValue.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Target/ExecutionContext.h" // for ExecutionContext #include "lldb/Utility/ConstString.h" // for ConstString #include "lldb/Utility/FileSpec.h" #include "lldb/lldb-defines.h" // for DISALLOW_COPY_AND_ASSIGN #include "lldb/lldb-enumerations.h" // for AddressClass, AddressClass... #include "lldb/lldb-forward.h" // for InstructionSP, DisassemblerSP #include "lldb/lldb-types.h" // for addr_t, offset_t #include "llvm/ADT/StringRef.h" // for StringRef #include // for function #include #include // for enable_shared_from_this #include #include #include #include // for size_t #include // for uint32_t, int64_t #include // for FILE namespace lldb_private { class AddressRange; } namespace lldb_private { class DataExtractor; } namespace lldb_private { class Debugger; } namespace lldb_private { class Disassembler; } namespace lldb_private { class Module; } namespace lldb_private { class Stream; } namespace lldb_private { class SymbolContext; } namespace lldb_private { class SymbolContextList; } namespace lldb_private { class Target; } namespace lldb_private { struct RegisterInfo; } namespace llvm { template class SmallVectorImpl; } namespace lldb_private { class Instruction { public: Instruction(const Address &address, lldb::AddressClass addr_class = lldb::eAddressClassInvalid); virtual ~Instruction(); const Address &GetAddress() const { return m_address; } const char *GetMnemonic(const ExecutionContext *exe_ctx) { CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx); return m_opcode_name.c_str(); } const char *GetOperands(const ExecutionContext *exe_ctx) { CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx); return m_mnemonics.c_str(); } const char *GetComment(const ExecutionContext *exe_ctx) { CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx); return m_comment.c_str(); } virtual void CalculateMnemonicOperandsAndComment(const ExecutionContext *exe_ctx) = 0; lldb::AddressClass GetAddressClass(); void SetAddress(const Address &addr) { // Invalidate the address class to lazily discover // it if we need to. m_address_class = lldb::eAddressClassInvalid; m_address = addr; } //------------------------------------------------------------------ /// Dump the text representation of this Instruction to a Stream /// /// Print the (optional) address, (optional) bytes, opcode, /// operands, and instruction comments to a stream. /// /// @param[in] s /// The Stream to add the text to. /// /// @param[in] show_address /// Whether the address (using disassembly_addr_format_spec formatting) /// should be printed. /// /// @param[in] show_bytes /// Whether the bytes of the assembly 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) /// Only needed if show_bytes is true. /// /// @param[in] exe_ctx /// The current execution context, if available. May be used in /// the assembling of the operands+comments for this instruction. /// Pass NULL if not applicable. /// /// @param[in] sym_ctx /// The SymbolContext for this instruction. /// Pass NULL if not available/computed. /// Only needed if show_address is true. /// /// @param[in] prev_sym_ctx /// The SymbolContext for the previous instruction. Depending on /// the disassembly address format specification, a change in /// Symbol / Function may mean that a line is printed with the new /// symbol/function name. /// Pass NULL if unavailable, or if this is the first instruction of /// the InstructionList. /// Only needed if show_address is true. /// /// @param[in] disassembly_addr_format /// The format specification for how addresses are printed. /// Only needed if show_address is true. /// /// @param[in] max_address_text_size /// The length of the longest address string at the start of the /// disassembly line that will be printed (the /// Debugger::FormatDisassemblerAddress() string) /// 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, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, const FormatEntity::Entry *disassembly_addr_format, size_t max_address_text_size); virtual bool DoesBranch() = 0; virtual bool HasDelaySlot(); + bool CanSetBreakpoint (); + virtual size_t Decode(const Disassembler &disassembler, const DataExtractor &data, lldb::offset_t data_offset) = 0; virtual void SetDescription(llvm::StringRef) { } // May be overridden in sub-classes that have descriptions. lldb::OptionValueSP ReadArray(FILE *in_file, Stream *out_stream, OptionValue::Type data_type); lldb::OptionValueSP ReadDictionary(FILE *in_file, Stream *out_stream); bool DumpEmulation(const ArchSpec &arch); virtual bool TestEmulation(Stream *stream, const char *test_file_name); bool Emulate(const ArchSpec &arch, uint32_t evaluate_options, void *baton, EmulateInstruction::ReadMemoryCallback read_mem_callback, EmulateInstruction::WriteMemoryCallback write_mem_calback, EmulateInstruction::ReadRegisterCallback read_reg_callback, EmulateInstruction::WriteRegisterCallback write_reg_callback); const Opcode &GetOpcode() const { return m_opcode; } uint32_t GetData(DataExtractor &data); struct Operand { enum class Type { Invalid = 0, Register, Immediate, Dereference, Sum, Product } m_type = Type::Invalid; std::vector m_children; lldb::addr_t m_immediate = 0; ConstString m_register; bool m_negative = false; bool m_clobbered = false; bool IsValid() { return m_type != Type::Invalid; } static Operand BuildRegister(ConstString &r); static Operand BuildImmediate(lldb::addr_t imm, bool neg); static Operand BuildImmediate(int64_t imm); static Operand BuildDereference(const Operand &ref); static Operand BuildSum(const Operand &lhs, const Operand &rhs); static Operand BuildProduct(const Operand &lhs, const Operand &rhs); }; virtual bool ParseOperands(llvm::SmallVectorImpl &operands) { return false; } virtual bool IsCall() { return false; } protected: Address m_address; // The section offset address of this instruction // We include an address class in the Instruction class to // allow the instruction specify the eAddressClassCodeAlternateISA // (currently used for thumb), and also to specify data (eAddressClassData). // The usual value will be eAddressClassCode, but often when // disassembling memory, you might run into data. This can // help us to disassemble appropriately. private: lldb::AddressClass m_address_class; // Use GetAddressClass () accessor function! protected: Opcode m_opcode; // The opcode for this instruction std::string m_opcode_name; std::string m_mnemonics; std::string m_comment; bool m_calculated_strings; void CalculateMnemonicOperandsAndCommentIfNeeded(const ExecutionContext *exe_ctx) { if (!m_calculated_strings) { m_calculated_strings = true; CalculateMnemonicOperandsAndComment(exe_ctx); } } }; namespace OperandMatchers { std::function MatchBinaryOp(std::function base, std::function left, std::function right); std::function MatchUnaryOp(std::function base, std::function child); std::function MatchRegOp(const RegisterInfo &info); std::function FetchRegOp(ConstString ®); std::function MatchImmOp(int64_t imm); std::function FetchImmOp(int64_t &imm); std::function MatchOpType(Instruction::Operand::Type type); } class InstructionList { public: InstructionList(); ~InstructionList(); size_t GetSize() const; uint32_t GetMaxOpcocdeByteSize() const; lldb::InstructionSP GetInstructionAtIndex(size_t idx) const; uint32_t GetIndexOfNextBranchInstruction(uint32_t start, Target &target) const; uint32_t GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, Target &target); uint32_t GetIndexOfInstructionAtAddress(const Address &addr); void Clear(); void Append(lldb::InstructionSP &inst_sp); void Dump(Stream *s, bool show_address, bool show_bytes, const ExecutionContext *exe_ctx); private: typedef std::vector collection; typedef collection::iterator iterator; typedef collection::const_iterator const_iterator; collection m_instructions; }; class PseudoInstruction : public Instruction { public: PseudoInstruction(); ~PseudoInstruction() override; bool DoesBranch() override; bool HasDelaySlot() override; void CalculateMnemonicOperandsAndComment( const ExecutionContext *exe_ctx) override { // TODO: fill this in and put opcode name into Instruction::m_opcode_name, // mnemonic into Instruction::m_mnemonics, and any comment into // Instruction::m_comment } size_t Decode(const Disassembler &disassembler, const DataExtractor &data, lldb::offset_t data_offset) override; void SetOpcode(size_t opcode_size, void *opcode_data); void SetDescription(llvm::StringRef description) override; protected: std::string m_description; DISALLOW_COPY_AND_ASSIGN(PseudoInstruction); }; class Disassembler : public std::enable_shared_from_this, public PluginInterface { public: enum { eOptionNone = 0u, eOptionShowBytes = (1u << 0), eOptionRawOuput = (1u << 1), 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 }; enum HexImmediateStyle { eHexStyleC, eHexStyleAsm, }; // FindPlugin should be lax about the flavor string (it is too annoying to // have various internal uses of the // disassembler fail because the global flavor string gets set wrong. // Instead, if you get a flavor string you // don't understand, use the default. Folks who care to check can use the // FlavorValidForArchSpec method on the // disassembler they got back. static lldb::DisassemblerSP FindPlugin(const ArchSpec &arch, const char *flavor, const char *plugin_name); // This version will use the value in the Target settings if flavor is NULL; static lldb::DisassemblerSP FindPluginForTarget(const lldb::TargetSP target_sp, const ArchSpec &arch, const char *flavor, const char *plugin_name); static lldb::DisassemblerSP DisassembleRange(const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const AddressRange &disasm_range, bool prefer_file_cache); static lldb::DisassemblerSP DisassembleBytes(const ArchSpec &arch, const char *plugin_name, const char *flavor, const Address &start, const void *bytes, size_t length, uint32_t max_num_instructions, bool data_from_file); static bool Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const AddressRange &range, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm); static bool Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const Address &start, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm); static size_t Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, SymbolContextList &sc_list, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm); static bool Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const ConstString &name, Module *module, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm); static bool Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm); //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ Disassembler(const ArchSpec &arch, const char *flavor); ~Disassembler() override; typedef const char *(*SummaryCallback)(const Instruction &inst, ExecutionContext *exe_context, void *user_data); static bool PrintInstructions(Disassembler *disasm_ptr, Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm); size_t ParseInstructions(const ExecutionContext *exe_ctx, const AddressRange &range, Stream *error_strm_ptr, bool prefer_file_cache); size_t ParseInstructions(const ExecutionContext *exe_ctx, const Address &range, uint32_t num_instructions, bool prefer_file_cache); virtual size_t DecodeInstructions(const Address &base_addr, const DataExtractor &data, lldb::offset_t data_offset, size_t num_instructions, bool append, bool data_from_file) = 0; InstructionList &GetInstructionList(); const InstructionList &GetInstructionList() const; const ArchSpec &GetArchitecture() const { return m_arch; } const char *GetFlavor() const { return m_flavor.c_str(); } virtual bool FlavorValidForArchSpec(const lldb_private::ArchSpec &arch, const char *flavor) = 0; protected: // SourceLine and SourceLinesToDisplay structures are only used in // the mixed source and assembly display methods internal to this class. struct SourceLine { FileSpec file; uint32_t line; uint32_t column; SourceLine() : file(), line(LLDB_INVALID_LINE_NUMBER), column(0) {} bool operator==(const SourceLine &rhs) const { return file == rhs.file && line == rhs.line && rhs.column == column; } bool operator!=(const SourceLine &rhs) const { return file != rhs.file || line != rhs.line || column != rhs.column; } bool IsValid() const { return line != LLDB_INVALID_LINE_NUMBER; } }; struct SourceLinesToDisplay { std::vector lines; // index of the "current" source line, if we want to highlight that // when displaying the source lines. (as opposed to the surrounding // source lines provided to give context) size_t current_source_line; // Whether to print a blank line at the end of the source lines. bool print_source_context_end_eol; SourceLinesToDisplay() : lines(), current_source_line(-1), print_source_context_end_eol(true) { } }; // Get the function's declaration line number, hopefully a line number earlier // than the opening curly brace at the start of the function body. static SourceLine GetFunctionDeclLineEntry(const SymbolContext &sc); // Add the provided SourceLine to the map of filenames-to-source-lines-seen. static void AddLineToSourceLineTables( SourceLine &line, std::map> &source_lines_seen); // Given a source line, determine if we should print it when we're doing // mixed source & assembly output. // We're currently using the target.process.thread.step-avoid-regexp setting // (which is used for stepping over inlined STL functions by default) to // determine what source lines to avoid showing. // // Returns true if this source line should be elided (if the source line // should // not be displayed). static bool ElideMixedSourceAndDisassemblyLine(const ExecutionContext &exe_ctx, const SymbolContext &sc, SourceLine &line); static bool ElideMixedSourceAndDisassemblyLine(const ExecutionContext &exe_ctx, const SymbolContext &sc, LineEntry &line) { SourceLine sl; sl.file = line.file; sl.line = line.line; sl.column = line.column; return ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, sl); }; //------------------------------------------------------------------ // Classes that inherit from Disassembler can see and modify these //------------------------------------------------------------------ ArchSpec m_arch; InstructionList m_instruction_list; lldb::addr_t m_base_addr; std::string m_flavor; private: //------------------------------------------------------------------ // For Disassembler only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(Disassembler); }; } // namespace lldb_private #endif // liblldb_Disassembler_h_ Index: vendor/lldb/dist/include/lldb/Expression/Expression.h =================================================================== --- vendor/lldb/dist/include/lldb/Expression/Expression.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Expression/Expression.h (revision 317959) @@ -1,119 +1,129 @@ //===-- Expression.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Expression_h_ #define liblldb_Expression_h_ // C Includes // C++ Includes #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Expression/ExpressionTypeSystemHelper.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private.h" namespace lldb_private { class RecordingMemoryManager; //---------------------------------------------------------------------- /// @class Expression Expression.h "lldb/Expression/Expression.h" /// @brief Encapsulates a single expression for use in lldb /// /// LLDB uses expressions for various purposes, notably to call functions /// and as a backend for the expr command. Expression encapsulates /// the objects needed to parse and interpret or JIT an expression. It /// uses the expression parser appropriate to the language of the expression /// to produce LLVM IR from the expression. //---------------------------------------------------------------------- class Expression { public: enum ResultType { eResultTypeAny, eResultTypeId }; Expression(Target &target); Expression(ExecutionContextScope &exe_scope); //------------------------------------------------------------------ /// Destructor //------------------------------------------------------------------ virtual ~Expression() {} //------------------------------------------------------------------ /// Return the string that the parser should parse. Must be a full /// translation unit. //------------------------------------------------------------------ virtual const char *Text() = 0; //------------------------------------------------------------------ /// Return the function name that should be used for executing the /// expression. Text() should contain the definition of this /// function. //------------------------------------------------------------------ virtual const char *FunctionName() = 0; //------------------------------------------------------------------ /// Return the language that should be used when parsing. To use /// the default, return eLanguageTypeUnknown. //------------------------------------------------------------------ virtual lldb::LanguageType Language() { return lldb::eLanguageTypeUnknown; } //------------------------------------------------------------------ /// Return the desired result type of the function, or /// eResultTypeAny if indifferent. //------------------------------------------------------------------ virtual ResultType DesiredResultType() { return eResultTypeAny; } //------------------------------------------------------------------ /// Flags //------------------------------------------------------------------ //------------------------------------------------------------------ /// Return true if validation code should be inserted into the /// expression. //------------------------------------------------------------------ virtual bool NeedsValidation() = 0; //------------------------------------------------------------------ /// Return true if external variables in the expression should be /// resolved. //------------------------------------------------------------------ virtual bool NeedsVariableResolution() = 0; virtual EvaluateExpressionOptions *GetOptions() { return nullptr; }; //------------------------------------------------------------------ /// Return the address of the function's JIT-compiled code, or /// LLDB_INVALID_ADDRESS if the function is not JIT compiled //------------------------------------------------------------------ lldb::addr_t StartAddress() { return m_jit_start_addr; } + //------------------------------------------------------------------ + /// Called to notify the expression that it is about to be executed. + //------------------------------------------------------------------ + virtual void WillStartExecuting() {} + + //------------------------------------------------------------------ + /// Called to notify the expression that its execution has finished. + //------------------------------------------------------------------ + virtual void DidFinishExecuting() {} + virtual ExpressionTypeSystemHelper *GetTypeSystemHelper() { return nullptr; } protected: lldb::TargetWP m_target_wp; /// Expression's always have to have a target... lldb::ProcessWP m_jit_process_wp; /// An expression might have a process, but /// it doesn't need to (e.g. calculator /// mode.) lldb::addr_t m_jit_start_addr; ///< The address of the JITted function within ///the JIT allocation. LLDB_INVALID_ADDRESS if ///invalid. lldb::addr_t m_jit_end_addr; ///< The address of the JITted function within ///the JIT allocation. LLDB_INVALID_ADDRESS if ///invalid. }; } // namespace lldb_private #endif // liblldb_Expression_h_ Index: vendor/lldb/dist/include/lldb/Host/MainLoop.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/MainLoop.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Host/MainLoop.h (revision 317959) @@ -1,105 +1,112 @@ //===-- MainLoop.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef lldb_Host_MainLoop_h_ #define lldb_Host_MainLoop_h_ #include "lldb/Host/Config.h" #include "lldb/Host/MainLoopBase.h" #include "llvm/ADT/DenseMap.h" #if !HAVE_PPOLL && !HAVE_SYS_EVENT_H #define SIGNAL_POLLING_UNSUPPORTED 1 #endif namespace lldb_private { // Implementation of the MainLoopBase class. It can monitor file descriptors for // readability using ppoll, kqueue, poll or WSAPoll. On Windows it only supports // polling sockets, and will not work on generic file handles or pipes. On // systems without kqueue or ppoll handling singnals is not supported. In // addition to the common base, this class provides the ability to invoke a // given handler when a signal is received. // // Since this class is primarily intended to be used for single-threaded // processing, it does not attempt to perform any internal synchronisation and // any concurrent accesses must be protected externally. However, it is // perfectly legitimate to have more than one instance of this class running on // separate threads, or even a single thread (with some limitations on signal // monitoring). // TODO: Add locking if this class is to be used in a multi-threaded context. class MainLoop : public MainLoopBase { private: class SignalHandle; public: typedef std::unique_ptr SignalHandleUP; + MainLoop(); ~MainLoop() override; ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, Error &error) override; // Listening for signals from multiple MainLoop instances is perfectly safe as // long as they don't try to listen for the same signal. The callback function // is invoked when the control returns to the Run() function, not when the // hander is executed. This mean that you can treat the callback as a normal // function and perform things which would not be safe in a signal handler. // However, since the callback is not invoked synchronously, you cannot use // this mechanism to handle SIGSEGV and the like. SignalHandleUP RegisterSignal(int signo, const Callback &callback, Error &error); Error Run() override; // This should only be performed from a callback. Do not attempt to terminate // the processing from another thread. // TODO: Add synchronization if we want to be terminated from another thread. void RequestTermination() override { m_terminate_request = true; } protected: void UnregisterReadObject(IOObject::WaitableHandle handle) override; void UnregisterSignal(int signo); private: + void ProcessReadObject(IOObject::WaitableHandle handle); + void ProcessSignal(int signo); + class SignalHandle { public: ~SignalHandle() { m_mainloop.UnregisterSignal(m_signo); } private: SignalHandle(MainLoop &mainloop, int signo) : m_mainloop(mainloop), m_signo(signo) {} MainLoop &m_mainloop; int m_signo; friend class MainLoop; DISALLOW_COPY_AND_ASSIGN(SignalHandle); }; struct SignalInfo { Callback callback; #if HAVE_SIGACTION struct sigaction old_action; #endif bool was_blocked : 1; }; class RunImpl; llvm::DenseMap m_read_fds; llvm::DenseMap m_signals; +#if HAVE_SYS_EVENT_H + int m_kqueue; +#endif bool m_terminate_request : 1; }; } // namespace lldb_private #endif // lldb_Host_MainLoop_h_ Index: vendor/lldb/dist/include/lldb/Host/common/UDPSocket.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/common/UDPSocket.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Host/common/UDPSocket.h (revision 317959) @@ -1,37 +1,35 @@ //===-- UDPSocket.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_UDPSocket_h_ #define liblldb_UDPSocket_h_ #include "lldb/Host/Socket.h" namespace lldb_private { class UDPSocket : public Socket { public: UDPSocket(bool should_close, bool child_processes_inherit); static Error Connect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket); private: - UDPSocket(NativeSocket socket, const UDPSocket &listen_socket); + UDPSocket(NativeSocket socket); size_t Send(const void *buf, const size_t num_bytes) override; Error Connect(llvm::StringRef name) override; Error Listen(llvm::StringRef name, int backlog) override; Error Accept(Socket *&socket) override; - - Error CreateSocket(); SocketAddress m_sockaddr; }; } #endif // ifndef liblldb_UDPSocket_h_ Index: vendor/lldb/dist/include/lldb/Target/ThreadPlanCallFunction.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/ThreadPlanCallFunction.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Target/ThreadPlanCallFunction.h (revision 317959) @@ -1,164 +1,164 @@ //===-- ThreadPlanCallFunction.h --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ThreadPlanCallFunction_h_ #define liblldb_ThreadPlanCallFunction_h_ // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/lldb-private.h" #include "llvm/ADT/ArrayRef.h" namespace lldb_private { class ThreadPlanCallFunction : public ThreadPlan { // Create a thread plan to call a function at the address passed in the // "function" // argument. If you plan to call GetReturnValueObject, then pass in the // return type, otherwise just pass in an invalid CompilerType. public: ThreadPlanCallFunction(Thread &thread, const Address &function, const CompilerType &return_type, llvm::ArrayRef args, const EvaluateExpressionOptions &options); ThreadPlanCallFunction(Thread &thread, const Address &function, const EvaluateExpressionOptions &options); ~ThreadPlanCallFunction() override; void GetDescription(Stream *s, lldb::DescriptionLevel level) override; bool ValidatePlan(Stream *error) override; bool ShouldStop(Event *event_ptr) override; Vote ShouldReportStop(Event *event_ptr) override; bool StopOthers() override; lldb::StateType GetPlanRunState() override; void DidPush() override; bool WillStop() override; bool MischiefManaged() override; // To get the return value from a function call you must create a // lldb::ValueSP that contains a valid clang type in its context and call // RequestReturnValue. The ValueSP will be stored and when the function is // done executing, the object will check if there is a requested return // value. If there is, the return value will be retrieved using the // ABI::GetReturnValue() for the ABI in the process. Then after the thread // plan is complete, you can call "GetReturnValue()" to retrieve the value // that was extracted. lldb::ValueObjectSP GetReturnValueObject() override { return m_return_valobj_sp; } // Return the stack pointer that the function received // on entry. Any stack address below this should be // considered invalid after the function has been // cleaned up. lldb::addr_t GetFunctionStackPointer() { return m_function_sp; } // Classes that derive from FunctionCaller, and implement // their own WillPop methods should call this so that the // thread state gets restored if the plan gets discarded. void WillPop() override; // If the thread plan stops mid-course, this will be the stop reason that // interrupted us. // Once DoTakedown is called, this will be the real stop reason at the end of // the function call. // If it hasn't been set for one or the other of these reasons, we'll return // the PrivateStopReason. // This is needed because we want the CallFunction thread plans not to show up // as the stop reason. // But if something bad goes wrong, it is nice to be able to tell the user // what really happened. lldb::StopInfoSP GetRealStopInfo() override { if (m_real_stop_info_sp) return m_real_stop_info_sp; else return GetPrivateStopInfo(); } lldb::addr_t GetStopAddress() { return m_stop_address; } bool RestoreThreadState() override; void ThreadDestroyed() override { m_takedown_done = true; } void SetStopOthers(bool new_value) override; protected: void ReportRegisterState(const char *message); bool DoPlanExplainsStop(Event *event_ptr) override; virtual void SetReturnValue(); bool ConstructorSetup(Thread &thread, ABI *&abi, lldb::addr_t &start_load_addr, lldb::addr_t &function_load_addr); - void DoTakedown(bool success); + virtual void DoTakedown(bool success); void SetBreakpoints(); void ClearBreakpoints(); bool BreakpointsExplainStop(); bool m_valid; bool m_stop_other_threads; bool m_unwind_on_error; bool m_ignore_breakpoints; bool m_debug_execution; bool m_trap_exceptions; Address m_function_addr; Address m_start_addr; lldb::addr_t m_function_sp; lldb::ThreadPlanSP m_subplan_sp; LanguageRuntime *m_cxx_language_runtime; LanguageRuntime *m_objc_language_runtime; Thread::ThreadStateCheckpoint m_stored_thread_state; lldb::StopInfoSP m_real_stop_info_sp; // In general we want to hide call function // thread plans, but for reporting purposes, // it's nice to know the real stop reason. // This gets set in DoTakedown. StreamString m_constructor_errors; lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, // use the ABI to extract values when // complete bool m_takedown_done; // We want to ensure we only do the takedown once. This // ensures that. bool m_should_clear_objc_exception_bp; bool m_should_clear_cxx_exception_bp; lldb::addr_t m_stop_address; // This is the address we stopped at. Also set // in DoTakedown; private: CompilerType m_return_type; DISALLOW_COPY_AND_ASSIGN(ThreadPlanCallFunction); }; } // namespace lldb_private #endif // liblldb_ThreadPlanCallFunction_h_ Index: vendor/lldb/dist/include/lldb/Target/ThreadPlanCallUserExpression.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/ThreadPlanCallUserExpression.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Target/ThreadPlanCallUserExpression.h (revision 317959) @@ -1,66 +1,69 @@ //===-- ThreadPlanCallUserExpression.h --------------------------------*- C++ //-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ThreadPlanCallUserExpression_h_ #define liblldb_ThreadPlanCallUserExpression_h_ // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/lldb-private.h" #include "llvm/ADT/ArrayRef.h" namespace lldb_private { class ThreadPlanCallUserExpression : public ThreadPlanCallFunction { public: ThreadPlanCallUserExpression(Thread &thread, Address &function, llvm::ArrayRef args, const EvaluateExpressionOptions &options, lldb::UserExpressionSP &user_expression_sp); ~ThreadPlanCallUserExpression() override; void GetDescription(Stream *s, lldb::DescriptionLevel level) override; + void DidPush() override; + void WillPop() override; lldb::StopInfoSP GetRealStopInfo() override; bool MischiefManaged() override; void TransferExpressionOwnership() { m_manage_materialization = true; } lldb::ExpressionVariableSP GetExpressionVariable() override { return m_result_var_sp; } protected: + void DoTakedown(bool success) override; private: lldb::UserExpressionSP m_user_expression_sp; // This is currently just used to ensure the // User expression the initiated this ThreadPlan // lives as long as the thread plan does. bool m_manage_materialization = false; lldb::ExpressionVariableSP m_result_var_sp; // If we are left to manage the materialization, // then stuff the result expression variable here. DISALLOW_COPY_AND_ASSIGN(ThreadPlanCallUserExpression); }; } // namespace lldb_private #endif // liblldb_ThreadPlanCallUserExpression_h_ Index: vendor/lldb/dist/include/lldb/Utility/TaskPool.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/TaskPool.h (revision 317958) +++ vendor/lldb/dist/include/lldb/Utility/TaskPool.h (revision 317959) @@ -1,189 +1,91 @@ //===--------------------- TaskPool.h ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef utility_TaskPool_h_ #define utility_TaskPool_h_ #include // for bind, function #include #include #include // for make_shared #include // for mutex, unique_lock, condition_variable #include // for forward, result_of, move // Global TaskPool class for running tasks in parallel on a set of worker thread // created the first // time the task pool is used. The TaskPool provide no guarantee about the order // the task will be run // and about what tasks will run in parallel. None of the task added to the task // pool should block // on something (mutex, future, condition variable) what will be set only by the // completion of an // other task on the task pool as they may run on the same thread sequentally. class TaskPool { public: // Add a new task to the task pool and return a std::future belonging to the // newly created task. // The caller of this function has to wait on the future for this task to // complete. template static std::future::type> AddTask(F &&f, Args &&... args); // Run all of the specified tasks on the task pool and wait until all of them // are finished // before returning. This method is intended to be used for small number tasks // where listing // them as function arguments is acceptable. For running large number of tasks // you should use // AddTask for each task and then call wait() on each returned future. template static void RunTasks(T &&... tasks); private: TaskPool() = delete; template struct RunTaskImpl; static void AddTaskImpl(std::function &&task_fn); }; -// Wrapper class around the global TaskPool implementation to make it possible -// to create a set of -// tasks and then wait for the tasks to be completed by the -// WaitForNextCompletedTask call. This -// class should be used when WaitForNextCompletedTask is needed because this -// class add no other -// extra functionality to the TaskPool class and it have a very minor -// performance overhead. -template // The return type of the tasks what will be added to this - // task runner - class TaskRunner { -public: - // Add a task to the task runner what will also add the task to the global - // TaskPool. The - // function doesn't return the std::future for the task because it will be - // supplied by the - // WaitForNextCompletedTask after the task is completed. - template void AddTask(F &&f, Args &&... args); - - // Wait for the next task in this task runner to finish and then return the - // std::future what - // belongs to the finished task. If there is no task in this task runner - // (neither pending nor - // comleted) then this function will return an invalid future. Usually this - // function should be - // called in a loop processing the results of the tasks until it returns an - // invalid std::future - // what means that all task in this task runner is completed. - std::future WaitForNextCompletedTask(); - - // Convenience method to wait for all task in this TaskRunner to finish. Do - // NOT use this class - // just because of this method. Use TaskPool instead and wait for each - // std::future returned by - // AddTask in a loop. - void WaitForAllTasks(); - -private: - std::list> m_ready; - std::list> m_pending; - std::mutex m_mutex; - std::condition_variable m_cv; -}; - template std::future::type> TaskPool::AddTask(F &&f, Args &&... args) { auto task_sp = std::make_shared< std::packaged_task::type()>>( std::bind(std::forward(f), std::forward(args)...)); AddTaskImpl([task_sp]() { (*task_sp)(); }); return task_sp->get_future(); } template void TaskPool::RunTasks(T &&... tasks) { RunTaskImpl::Run(std::forward(tasks)...); } template struct TaskPool::RunTaskImpl { static void Run(Head &&h, Tail &&... t) { auto f = AddTask(std::forward(h)); RunTaskImpl::Run(std::forward(t)...); f.wait(); } }; template <> struct TaskPool::RunTaskImpl<> { static void Run() {} }; -template -template -void TaskRunner::AddTask(F &&f, Args &&... args) { - std::unique_lock lock(m_mutex); - auto it = m_pending.emplace(m_pending.end()); - *it = std::move(TaskPool::AddTask( - [this, it](F f, Args... args) { - T &&r = f(std::forward(args)...); - - std::unique_lock lock(this->m_mutex); - this->m_ready.splice(this->m_ready.end(), this->m_pending, it); - lock.unlock(); - - this->m_cv.notify_one(); - return r; - }, - std::forward(f), std::forward(args)...)); -} - -template <> -template -void TaskRunner::AddTask(F &&f, Args &&... args) { - std::unique_lock lock(m_mutex); - auto it = m_pending.emplace(m_pending.end()); - *it = std::move(TaskPool::AddTask( - [this, it](F f, Args... args) { - f(std::forward(args)...); - - std::unique_lock lock(this->m_mutex); - this->m_ready.emplace_back(std::move(*it)); - this->m_pending.erase(it); - lock.unlock(); - - this->m_cv.notify_one(); - }, - std::forward(f), std::forward(args)...)); -} - -template std::future TaskRunner::WaitForNextCompletedTask() { - std::unique_lock lock(m_mutex); - if (m_ready.empty() && m_pending.empty()) - return std::future(); // No more tasks - - if (m_ready.empty()) - m_cv.wait(lock, [this]() { return !this->m_ready.empty(); }); - - std::future res = std::move(m_ready.front()); - m_ready.pop_front(); - - lock.unlock(); - res.wait(); - - return std::move(res); -} - -template void TaskRunner::WaitForAllTasks() { - while (WaitForNextCompletedTask().valid()) - ; -} +// Run 'func' on every value from begin .. end-1. Each worker will grab +// 'batch_size' numbers at a time to work on, so for very fast functions, batch +// should be large enough to avoid too much cache line contention. +void TaskMapOverInt(size_t begin, size_t end, + std::function const &func); #endif // #ifndef utility_TaskPool_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/multiline/TestMultilineExpressions.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/multiline/TestMultilineExpressions.py (revision 317958) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/multiline/TestMultilineExpressions.py (revision 317959) @@ -1,62 +1,90 @@ """Test multiline expressions.""" from __future__ import print_function import os import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class MultilineExpressionsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break on inside main.cpp. self.line = line_number('main.c', 'break') @skipIfRemote @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") def test_with_run_commands(self): """Test that multiline expressions work correctly""" self.build() import pexpect exe = os.path.join(os.getcwd(), "a.out") prompt = "(lldb) " # So that the child gets torn down after the test. self.child = pexpect.spawn( '%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) child = self.child # Turn on logging for what the child sends back. if self.TraceOn(): child.logfile_read = sys.stdout # Set the breakpoint, run the inferior, when it breaks, issue print on # the various convenience variables. child.expect_exact(prompt) child.sendline('breakpoint set -f main.c -l %d' % self.line) child.expect_exact(prompt) child.sendline('run') child.expect_exact("stop reason = breakpoint 1.1") child.expect_exact(prompt) child.sendline('expr') child.expect_exact('1:') child.sendline('2+') child.expect_exact('2:') child.sendline('3') child.expect_exact('3:') child.sendline('') child.expect_exact(prompt) self.expect(child.before, exe=False, patterns=['= 5']) + + @skipIfRemote + @expectedFailureAll( + oslist=["windows"], + bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") + def test_empty_list(self): + """Test printing an empty list of expressions""" + import pexpect + prompt = "(lldb) " + + # So that the child gets torn down after the test + self.child = pexpect.spawn( + "%s %s" % + (lldbtest_config.lldbExec, self.lldbOption)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + # We expect a prompt, then send "print" to start a list of expressions, + # then an empty line. We expect a prompt back. + child.expect_exact(prompt) + child.sendline("print") + child.expect_exact('1:') + child.sendline("") + child.expect_exact(prompt) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/step_over_breakpoint/TestStepOverBreakpoint.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/step_over_breakpoint/TestStepOverBreakpoint.py (revision 317958) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/step_over_breakpoint/TestStepOverBreakpoint.py (revision 317959) @@ -1,120 +1,119 @@ """ Test that breakpoints do not affect stepping. Check for correct StopReason when stepping to the line with breakpoint which chould be eStopReasonBreakpoint in general, and eStopReasonPlanComplete when breakpoint's condition fails. """ from __future__ import print_function import unittest2 import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class StepOverBreakpointsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): TestBase.setUp(self) self.build() exe = os.path.join(os.getcwd(), "a.out") src = lldb.SBFileSpec("main.cpp") # Create a target by the debugger. self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) # Setup four breakpoints, two of them with false condition self.line1 = line_number('main.cpp', "breakpoint_1") self.line4 = line_number('main.cpp', "breakpoint_4") self.breakpoint1 = self.target.BreakpointCreateByLocation(src, self.line1) self.assertTrue( self.breakpoint1 and self.breakpoint1.GetNumLocations() == 1, VALID_BREAKPOINT) self.breakpoint2 = self.target.BreakpointCreateBySourceRegex("breakpoint_2", src) self.breakpoint2.GetLocationAtIndex(0).SetCondition('false') self.breakpoint3 = self.target.BreakpointCreateBySourceRegex("breakpoint_3", src) self.breakpoint3.GetLocationAtIndex(0).SetCondition('false') self.breakpoint4 = self.target.BreakpointCreateByLocation(src, self.line4) # Start debugging self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertIsNotNone(self.process, PROCESS_IS_VALID) self.thread = lldbutil.get_one_thread_stopped_at_breakpoint(self.process, self.breakpoint1) self.assertIsNotNone(self.thread, "Didn't stop at breakpoint 1.") def test_step_instruction(self): # Count instructions between breakpoint_1 and breakpoint_4 contextList = self.target.FindFunctions('main', lldb.eFunctionNameTypeAuto) self.assertEquals(contextList.GetSize(), 1) symbolContext = contextList.GetContextAtIndex(0) function = symbolContext.GetFunction() self.assertTrue(function) instructions = function.GetInstructions(self.target) addr_1 = self.breakpoint1.GetLocationAtIndex(0).GetAddress() addr_4 = self.breakpoint4.GetLocationAtIndex(0).GetAddress() - for i in range(instructions.GetSize()) : - addr = instructions.GetInstructionAtIndex(i).GetAddress() - if (addr == addr_1) : index_1 = i - if (addr == addr_4) : index_4 = i - steps_expected = index_4 - index_1 + # if third argument is true then the count will be the number of + # instructions on which a breakpoint can be set. + # start = addr_1, end = addr_4, canSetBreakpoint = True + steps_expected = instructions.GetInstructionsCount(addr_1, addr_4, True) step_count = 0 # Step from breakpoint_1 to breakpoint_4 while True: self.thread.StepInstruction(True) step_count = step_count + 1 self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertTrue(self.thread.GetStopReason() == lldb.eStopReasonPlanComplete or self.thread.GetStopReason() == lldb.eStopReasonBreakpoint) if (self.thread.GetStopReason() == lldb.eStopReasonBreakpoint) : # we should not stop on breakpoint_2 and _3 because they have false condition self.assertEquals(self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.line4) # breakpoint_2 and _3 should not affect step count self.assertTrue(step_count >= steps_expected) break # Run the process until termination self.process.Continue() self.assertEquals(self.process.GetState(), lldb.eStateExited) @skipIf(bugnumber="llvm.org/pr31972", hostoslist=["windows"]) def test_step_over(self): #lldb.DBG.EnableLog("lldb", ["step","breakpoint"]) self.thread.StepOver() # We should be stopped at the breakpoint_2 line with stop plan complete reason self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete) self.thread.StepOver() # We should be stopped at the breakpoint_3 line with stop plan complete reason self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete) self.thread.StepOver() # We should be stopped at the breakpoint_4 self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonBreakpoint) thread1 = lldbutil.get_one_thread_stopped_at_breakpoint(self.process, self.breakpoint4) self.assertEquals(self.thread, thread1, "Didn't stop at breakpoint 4.") # Check that stepping does not affect breakpoint's hit count self.assertEquals(self.breakpoint1.GetHitCount(), 1) self.assertEquals(self.breakpoint2.GetHitCount(), 0) self.assertEquals(self.breakpoint3.GetHitCount(), 0) self.assertEquals(self.breakpoint4.GetHitCount(), 1) # Run the process until termination self.process.Continue() self.assertEquals(self.process.GetState(), lldb.eStateExited) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py (revision 317958) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py (revision 317959) @@ -1,239 +1,267 @@ """ Test getting return-values correctly when stepping out """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ReturnValueTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(oslist=["freebsd"], archs=["i386"]) @expectedFailureAll(oslist=["macosx"], archs=["i386"], bugnumber="") @expectedFailureAll( oslist=["linux"], compiler="clang", compiler_version=[ "<=", "3.6"], archs=["i386"]) @expectedFailureAll( bugnumber="llvm.org/pr25785", hostoslist=["windows"], compiler="gcc", archs=["i386"], triple='.*-android') @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") @add_test_categories(['pyapi']) def test_with_python(self): """Test getting return values from stepping out.""" self.build() exe = os.path.join(os.getcwd(), "a.out") error = lldb.SBError() self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) inner_sint_bkpt = self.target.BreakpointCreateByName("inner_sint", exe) self.assertTrue(inner_sint_bkpt, VALID_BREAKPOINT) # Now launch the process, and do not stop at entry point. self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertTrue(self.process, PROCESS_IS_VALID) # The stop reason of the thread should be breakpoint. self.assertTrue(self.process.GetState() == lldb.eStateStopped, STOPPED_DUE_TO_BREAKPOINT) # Now finish, and make sure the return value is correct. thread = lldbutil.get_stopped_thread( self.process, lldb.eStopReasonBreakpoint) # inner_sint returns the variable value, so capture that here: in_int = thread.GetFrameAtIndex(0).FindVariable( "value").GetValueAsSigned(error) self.assertTrue(error.Success()) thread.StepOut() self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "outer_sint") return_value = thread.GetStopReturnValue() self.assertTrue(return_value.IsValid()) ret_int = return_value.GetValueAsSigned(error) self.assertTrue(error.Success()) self.assertTrue(in_int == ret_int) # Run again and we will stop in inner_sint the second time outer_sint is called. # Then test stepping out two frames at once: self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint( self.process, inner_sint_bkpt) self.assertTrue(len(thread_list) == 1) thread = thread_list[0] # We are done with the inner_sint breakpoint: self.target.BreakpointDelete(inner_sint_bkpt.GetID()) frame = thread.GetFrameAtIndex(1) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "outer_sint") in_int = frame.FindVariable("value").GetValueAsSigned(error) self.assertTrue(error.Success()) thread.StepOutOfFrame(frame) self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "main") ret_value = thread.GetStopReturnValue() self.assertTrue(return_value.IsValid()) ret_int = ret_value.GetValueAsSigned(error) self.assertTrue(error.Success()) self.assertTrue(2 * in_int == ret_int) # Now try some simple returns that have different types: inner_float_bkpt = self.target.BreakpointCreateByName( "inner_float", exe) self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT) self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint( self.process, inner_float_bkpt) self.assertTrue(len(thread_list) == 1) thread = thread_list[0] self.target.BreakpointDelete(inner_float_bkpt.GetID()) frame = thread.GetFrameAtIndex(0) in_value = frame.FindVariable("value") in_float = float(in_value.GetValue()) thread.StepOut() self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "outer_float") #return_value = thread.GetStopReturnValue() #self.assertTrue(return_value.IsValid()) #return_float = float(return_value.GetValue()) #self.assertTrue(in_float == return_float) self.return_and_test_struct_value("return_one_int") self.return_and_test_struct_value("return_two_int") self.return_and_test_struct_value("return_three_int") self.return_and_test_struct_value("return_four_int") self.return_and_test_struct_value("return_five_int") self.return_and_test_struct_value("return_two_double") self.return_and_test_struct_value("return_one_double_two_float") self.return_and_test_struct_value("return_one_int_one_float_one_int") self.return_and_test_struct_value("return_one_pointer") self.return_and_test_struct_value("return_two_pointer") self.return_and_test_struct_value("return_one_float_one_pointer") self.return_and_test_struct_value("return_one_int_one_pointer") self.return_and_test_struct_value("return_three_short_one_float") self.return_and_test_struct_value("return_one_int_one_double") self.return_and_test_struct_value("return_one_int_one_double_one_int") self.return_and_test_struct_value( "return_one_short_one_double_one_short") self.return_and_test_struct_value("return_one_float_one_int_one_float") self.return_and_test_struct_value("return_two_float") # I am leaving out the packed test until we have a way to tell CLANG # about alignment when reading DWARF for packed types. #self.return_and_test_struct_value ("return_one_int_one_double_packed") self.return_and_test_struct_value("return_one_int_one_long") - # icc and gcc don't support this extension. - if self.getCompiler().endswith('clang'): - self.return_and_test_struct_value("return_vector_size_float32_8") - self.return_and_test_struct_value("return_vector_size_float32_16") - self.return_and_test_struct_value("return_vector_size_float32_32") - self.return_and_test_struct_value( - "return_ext_vector_size_float32_2") - self.return_and_test_struct_value( - "return_ext_vector_size_float32_4") - self.return_and_test_struct_value( - "return_ext_vector_size_float32_8") + @expectedFailureAll(oslist=["freebsd"], archs=["i386"]) + @expectedFailureAll(oslist=["macosx"], archs=["i386"], bugnumber="") + @expectedFailureAll( + oslist=["linux"], + compiler="clang", + compiler_version=[ + "<=", + "3.6"], + archs=["i386"]) + @expectedFailureAll( + bugnumber="llvm.org/pr25785", + hostoslist=["windows"], + compiler="gcc", + archs=["i386"], + triple='.*-android') + @expectedFailureAll(compiler=["gcc"], archs=["x86_64", "i386"]) + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") + def test_vector_values(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + main_bktp = self.target.BreakpointCreateByName("main", exe) + self.assertTrue(main_bktp, VALID_BREAKPOINT) + + self.process = self.target.LaunchSimple( + None, None, self.get_process_working_directory()) + self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint( + self.process, main_bktp)), 1) + + self.return_and_test_struct_value("return_vector_size_float32_8") + self.return_and_test_struct_value("return_vector_size_float32_16") + self.return_and_test_struct_value("return_vector_size_float32_32") + self.return_and_test_struct_value("return_ext_vector_size_float32_2") + self.return_and_test_struct_value("return_ext_vector_size_float32_4") + self.return_and_test_struct_value("return_ext_vector_size_float32_8") def return_and_test_struct_value(self, func_name): """Pass in the name of the function to return from - takes in value, returns value.""" # Set the breakpoint, run to it, finish out. bkpt = self.target.BreakpointCreateByName(func_name) self.assertTrue(bkpt.GetNumResolvedLocations() > 0) self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint( self.process, bkpt) self.assertTrue(len(thread_list) == 1) thread = thread_list[0] self.target.BreakpointDelete(bkpt.GetID()) in_value = thread.GetFrameAtIndex(0).FindVariable("value") self.assertTrue(in_value.IsValid()) num_in_children = in_value.GetNumChildren() # This is a little hokey, but if we don't get all the children now, then # once we've stepped we won't be able to get them? for idx in range(0, num_in_children): in_child = in_value.GetChildAtIndex(idx) in_child_str = in_child.GetValue() thread.StepOut() self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) # Assuming all these functions step out to main. Could figure out the caller dynamically # if that would add something to the test. frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "main") frame = thread.GetFrameAtIndex(0) ret_value = thread.GetStopReturnValue() self.assertTrue(ret_value.IsValid()) num_ret_children = ret_value.GetNumChildren() self.assertTrue(num_in_children == num_ret_children) for idx in range(0, num_ret_children): in_child = in_value.GetChildAtIndex(idx) ret_child = ret_value.GetChildAtIndex(idx) in_child_str = in_child.GetValue() ret_child_str = ret_child.GetValue() self.assertEqual(in_child_str, ret_child_str) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteHostInfo.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteHostInfo.py (revision 317958) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteHostInfo.py (revision 317959) @@ -1,126 +1,127 @@ from __future__ import print_function # lldb test suite imports from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import TestBase # gdb-remote-specific imports import lldbgdbserverutils from gdbremote_testcase import GdbRemoteTestCaseBase class TestGdbRemoteHostInfo(GdbRemoteTestCaseBase): mydir = TestBase.compute_mydir(__file__) KNOWN_HOST_INFO_KEYS = set([ + "arch", "cputype", "cpusubtype", "distribution_id", "endian", "hostname", "ostype", "os_build", "os_kernel", "os_version", "ptrsize", "triple", "vendor", "watchpoint_exceptions_received", "default_packet_timeout", ]) DARWIN_REQUIRED_HOST_INFO_KEYS = set([ "cputype", "cpusubtype", "endian", "ostype", "ptrsize", "vendor", "watchpoint_exceptions_received" ]) def add_host_info_collection_packets(self): self.test_sequence.add_log_lines( ["read packet: $qHostInfo#9b", {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", "capture": {1: "host_info_raw"}}], True) def parse_host_info_response(self, context): # Ensure we have a host info response. self.assertIsNotNone(context) host_info_raw = context.get("host_info_raw") self.assertIsNotNone(host_info_raw) # Pull out key:value; pairs. host_info_dict = {match.group(1): match.group(2) for match in re.finditer(r"([^:]+):([^;]+);", host_info_raw)} import pprint print("\nqHostInfo response:") pprint.pprint(host_info_dict) # Validate keys are known. for (key, val) in list(host_info_dict.items()): self.assertTrue(key in self.KNOWN_HOST_INFO_KEYS, "unknown qHostInfo key: " + key) self.assertIsNotNone(val) # Return the key:val pairs. return host_info_dict def get_qHostInfo_response(self): # Launch the debug monitor stub, attaching to the inferior. server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() # Request qHostInfo and get response self.add_host_info_collection_packets() context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Parse qHostInfo response. host_info = self.parse_host_info_response(context) self.assertIsNotNone(host_info) self.assertGreater(len(host_info), 0, "qHostInfo should have returned " "at least one key:val pair.") return host_info def validate_darwin_minimum_host_info_keys(self, host_info_dict): self.assertIsNotNone(host_info_dict) missing_keys = [key for key in self.DARWIN_REQUIRED_HOST_INFO_KEYS if key not in host_info_dict] self.assertEquals(0, len(missing_keys), "qHostInfo is missing the following required " "keys: " + str(missing_keys)) @debugserver_test def test_qHostInfo_returns_at_least_one_key_val_pair_debugserver(self): self.init_debugserver_test() self.build() self.get_qHostInfo_response() @llgs_test def test_qHostInfo_returns_at_least_one_key_val_pair_llgs(self): self.init_llgs_test() self.build() self.get_qHostInfo_response() @skipUnlessDarwin @debugserver_test def test_qHostInfo_contains_darwin_required_keys_debugserver(self): self.init_debugserver_test() self.build() host_info_dict = self.get_qHostInfo_response() self.validate_darwin_minimum_host_info_keys(host_info_dict) @skipUnlessDarwin @llgs_test def test_qHostInfo_contains_darwin_required_keys_llgs(self): self.init_llgs_test() self.build() host_info_dict = self.get_qHostInfo_response() self.validate_darwin_minimum_host_info_keys(host_info_dict) Index: vendor/lldb/dist/scripts/interface/SBInstruction.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBInstruction.i (revision 317958) +++ vendor/lldb/dist/scripts/interface/SBInstruction.i (revision 317959) @@ -1,106 +1,109 @@ //===-- SWIG Interface for SBInstruction ------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include // There's a lot to be fixed here, but need to wait for underlying insn implementation // to be revised & settle down first. namespace lldb { class SBInstruction { public: SBInstruction (); SBInstruction (const SBInstruction &rhs); ~SBInstruction (); bool IsValid(); lldb::SBAddress GetAddress(); lldb::AddressClass GetAddressClass (); const char * GetMnemonic (lldb::SBTarget target); const char * GetOperands (lldb::SBTarget target); const char * GetComment (lldb::SBTarget target); lldb::SBData GetData (lldb::SBTarget target); size_t GetByteSize (); bool DoesBranch (); bool HasDelaySlot (); + bool + CanSetBreakpoint (); + void Print (FILE *out); bool GetDescription (lldb::SBStream &description); bool EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options); bool DumpEmulation (const char * triple); // triple is to specify the architecture, e.g. 'armv6' or 'armv7-apple-ios' bool TestEmulation (lldb::SBStream &output_stream, const char *test_file); %pythoncode %{ def __mnemonic_property__ (self): return self.GetMnemonic (target) def __operands_property__ (self): return self.GetOperands (target) def __comment_property__ (self): return self.GetComment (target) def __file_addr_property__ (self): return self.GetAddress ().GetFileAddress() def __load_adrr_property__ (self): return self.GetComment (target) __swig_getmethods__["mnemonic"] = __mnemonic_property__ if _newclass: mnemonic = property(__mnemonic_property__, None, doc='''A read only property that returns the mnemonic for this instruction as a string.''') __swig_getmethods__["operands"] = __operands_property__ if _newclass: operands = property(__operands_property__, None, doc='''A read only property that returns the operands for this instruction as a string.''') __swig_getmethods__["comment"] = __comment_property__ if _newclass: comment = property(__comment_property__, None, doc='''A read only property that returns the comment for this instruction as a string.''') __swig_getmethods__["addr"] = GetAddress if _newclass: addr = property(GetAddress, None, doc='''A read only property that returns an lldb object that represents the address (lldb.SBAddress) for this instruction.''') __swig_getmethods__["size"] = GetByteSize if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns the size in bytes for this instruction as an integer.''') __swig_getmethods__["is_branch"] = DoesBranch if _newclass: is_branch = property(DoesBranch, None, doc='''A read only property that returns a boolean value that indicates if this instruction is a branch instruction.''') %} }; } // namespace lldb Index: vendor/lldb/dist/scripts/interface/SBInstructionList.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBInstructionList.i (revision 317958) +++ vendor/lldb/dist/scripts/interface/SBInstructionList.i (revision 317959) @@ -1,91 +1,94 @@ //===-- SWIG Interface for SBInstructionList --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include namespace lldb { %feature("docstring", "Represents a list of machine instructions. SBFunction and SBSymbol have GetInstructions() methods which return SBInstructionList instances. SBInstructionList supports instruction (SBInstruction instance) iteration. For example (see also SBDebugger for a more complete example), def disassemble_instructions (insts): for i in insts: print i defines a function which takes an SBInstructionList instance and prints out the machine instructions in assembly format." ) SBInstructionList; class SBInstructionList { public: SBInstructionList (); SBInstructionList (const SBInstructionList &rhs); ~SBInstructionList (); bool IsValid () const; size_t GetSize (); lldb::SBInstruction GetInstructionAtIndex (uint32_t idx); + size_t GetInstructionsCount(const SBAddress &start, const SBAddress &end, + bool canSetBreakpoint); + void Clear (); void AppendInstruction (lldb::SBInstruction inst); void Print (FILE *out); bool GetDescription (lldb::SBStream &description); bool DumpEmulationForAllInstructions (const char *triple); %pythoncode %{ def __len__(self): '''Access len of the instruction list.''' return int(self.GetSize()) def __getitem__(self, key): '''Access instructions by integer index for array access or by lldb.SBAddress to find an instruction that matches a section offset address object.''' if type(key) is int: # Find an instruction by index if key < len(self): return self.GetInstructionAtIndex(key) elif type(key) is SBAddress: # Find an instruction using a lldb.SBAddress object lookup_file_addr = key.file_addr closest_inst = None for idx in range(self.GetSize()): inst = self.GetInstructionAtIndex(idx) inst_file_addr = inst.addr.file_addr if inst_file_addr == lookup_file_addr: return inst elif inst_file_addr > lookup_file_addr: return closest_inst else: closest_inst = inst return None %} }; } // namespace lldb Index: vendor/lldb/dist/source/API/SBAddress.cpp =================================================================== --- vendor/lldb/dist/source/API/SBAddress.cpp (revision 317958) +++ vendor/lldb/dist/source/API/SBAddress.cpp (revision 317959) @@ -1,244 +1,250 @@ //===-- SBAddress.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBAddress.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBStream.h" #include "lldb/Core/Address.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" using namespace lldb; using namespace lldb_private; SBAddress::SBAddress() : m_opaque_ap(new Address()) {} SBAddress::SBAddress(const Address *lldb_object_ptr) : m_opaque_ap(new Address()) { if (lldb_object_ptr) ref() = *lldb_object_ptr; } SBAddress::SBAddress(const SBAddress &rhs) : m_opaque_ap(new Address()) { if (rhs.IsValid()) ref() = rhs.ref(); } SBAddress::SBAddress(lldb::SBSection section, lldb::addr_t offset) : m_opaque_ap(new Address(section.GetSP(), offset)) {} // Create an address by resolving a load address using the supplied target SBAddress::SBAddress(lldb::addr_t load_addr, lldb::SBTarget &target) : m_opaque_ap(new Address()) { SetLoadAddress(load_addr, target); } SBAddress::~SBAddress() {} const SBAddress &SBAddress::operator=(const SBAddress &rhs) { if (this != &rhs) { if (rhs.IsValid()) ref() = rhs.ref(); else m_opaque_ap.reset(new Address()); } return *this; } +bool lldb::operator==(const SBAddress &lhs, const SBAddress &rhs) { + if (lhs.IsValid() && rhs.IsValid()) + return lhs.ref() == rhs.ref(); + return false; +} + bool SBAddress::IsValid() const { return m_opaque_ap.get() != NULL && m_opaque_ap->IsValid(); } void SBAddress::Clear() { m_opaque_ap.reset(new Address()); } void SBAddress::SetAddress(lldb::SBSection section, lldb::addr_t offset) { Address &addr = ref(); addr.SetSection(section.GetSP()); addr.SetOffset(offset); } void SBAddress::SetAddress(const Address *lldb_object_ptr) { if (lldb_object_ptr) ref() = *lldb_object_ptr; else m_opaque_ap.reset(new Address()); } lldb::addr_t SBAddress::GetFileAddress() const { if (m_opaque_ap->IsValid()) return m_opaque_ap->GetFileAddress(); else return LLDB_INVALID_ADDRESS; } lldb::addr_t SBAddress::GetLoadAddress(const SBTarget &target) const { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); lldb::addr_t addr = LLDB_INVALID_ADDRESS; TargetSP target_sp(target.GetSP()); if (target_sp) { if (m_opaque_ap->IsValid()) { std::lock_guard guard(target_sp->GetAPIMutex()); addr = m_opaque_ap->GetLoadAddress(target_sp.get()); } } if (log) { if (addr == LLDB_INVALID_ADDRESS) log->Printf( "SBAddress::GetLoadAddress (SBTarget(%p)) => LLDB_INVALID_ADDRESS", static_cast(target_sp.get())); else log->Printf("SBAddress::GetLoadAddress (SBTarget(%p)) => 0x%" PRIx64, static_cast(target_sp.get()), addr); } return addr; } void SBAddress::SetLoadAddress(lldb::addr_t load_addr, lldb::SBTarget &target) { // Create the address object if we don't already have one ref(); if (target.IsValid()) *this = target.ResolveLoadAddress(load_addr); else m_opaque_ap->Clear(); // Check if we weren't were able to resolve a section offset address. // If we weren't it is ok, the load address might be a location on the // stack or heap, so we should just have an address with no section and // a valid offset if (!m_opaque_ap->IsValid()) m_opaque_ap->SetOffset(load_addr); } bool SBAddress::OffsetAddress(addr_t offset) { if (m_opaque_ap->IsValid()) { addr_t addr_offset = m_opaque_ap->GetOffset(); if (addr_offset != LLDB_INVALID_ADDRESS) { m_opaque_ap->SetOffset(addr_offset + offset); return true; } } return false; } lldb::SBSection SBAddress::GetSection() { lldb::SBSection sb_section; if (m_opaque_ap->IsValid()) sb_section.SetSP(m_opaque_ap->GetSection()); return sb_section; } lldb::addr_t SBAddress::GetOffset() { if (m_opaque_ap->IsValid()) return m_opaque_ap->GetOffset(); return 0; } Address *SBAddress::operator->() { return m_opaque_ap.get(); } const Address *SBAddress::operator->() const { return m_opaque_ap.get(); } Address &SBAddress::ref() { if (m_opaque_ap.get() == NULL) m_opaque_ap.reset(new Address()); return *m_opaque_ap; } const Address &SBAddress::ref() const { // This object should already have checked with "IsValid()" // prior to calling this function. In case you didn't we will assert // and die to let you know. assert(m_opaque_ap.get()); return *m_opaque_ap; } Address *SBAddress::get() { return m_opaque_ap.get(); } bool SBAddress::GetDescription(SBStream &description) { // Call "ref()" on the stream to make sure it creates a backing stream in // case there isn't one already... Stream &strm = description.ref(); if (m_opaque_ap->IsValid()) { m_opaque_ap->Dump(&strm, NULL, Address::DumpStyleResolvedDescription, Address::DumpStyleModuleWithFileAddress, 4); StreamString sstrm; // m_opaque_ap->Dump (&sstrm, NULL, // Address::DumpStyleResolvedDescription, Address::DumpStyleInvalid, // 4); // if (sstrm.GetData()) // strm.Printf (" (%s)", sstrm.GetData()); } else strm.PutCString("No value"); return true; } SBModule SBAddress::GetModule() { SBModule sb_module; if (m_opaque_ap->IsValid()) sb_module.SetSP(m_opaque_ap->GetModule()); return sb_module; } SBSymbolContext SBAddress::GetSymbolContext(uint32_t resolve_scope) { SBSymbolContext sb_sc; if (m_opaque_ap->IsValid()) m_opaque_ap->CalculateSymbolContext(&sb_sc.ref(), resolve_scope); return sb_sc; } SBCompileUnit SBAddress::GetCompileUnit() { SBCompileUnit sb_comp_unit; if (m_opaque_ap->IsValid()) sb_comp_unit.reset(m_opaque_ap->CalculateSymbolContextCompileUnit()); return sb_comp_unit; } SBFunction SBAddress::GetFunction() { SBFunction sb_function; if (m_opaque_ap->IsValid()) sb_function.reset(m_opaque_ap->CalculateSymbolContextFunction()); return sb_function; } SBBlock SBAddress::GetBlock() { SBBlock sb_block; if (m_opaque_ap->IsValid()) sb_block.SetPtr(m_opaque_ap->CalculateSymbolContextBlock()); return sb_block; } SBSymbol SBAddress::GetSymbol() { SBSymbol sb_symbol; if (m_opaque_ap->IsValid()) sb_symbol.reset(m_opaque_ap->CalculateSymbolContextSymbol()); return sb_symbol; } SBLineEntry SBAddress::GetLineEntry() { SBLineEntry sb_line_entry; if (m_opaque_ap->IsValid()) { LineEntry line_entry; if (m_opaque_ap->CalculateSymbolContextLineEntry(line_entry)) sb_line_entry.SetLineEntry(line_entry); } return sb_line_entry; } AddressClass SBAddress::GetAddressClass() { if (m_opaque_ap->IsValid()) return m_opaque_ap->GetAddressClass(); return eAddressClassInvalid; } Index: vendor/lldb/dist/source/API/SBInstruction.cpp =================================================================== --- vendor/lldb/dist/source/API/SBInstruction.cpp (revision 317958) +++ vendor/lldb/dist/source/API/SBInstruction.cpp (revision 317959) @@ -1,278 +1,285 @@ //===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBInstruction.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" //---------------------------------------------------------------------- // We recently fixed a leak in one of the Instruction subclasses where // the instruction will only hold a weak reference to the disassembler // to avoid a cycle that was keeping both objects alive (leak) and we // need the InstructionImpl class to make sure our public API behaves // as users would expect. Calls in our public API allow clients to do // things like: // // 1 lldb::SBInstruction inst; // 2 inst = target.ReadInstructions(pc, 1).GetInstructionAtIndex(0) // 3 if (inst.DoesBranch()) // 4 ... // // There was a temporary lldb::DisassemblerSP object created in the // SBInstructionList that was returned by lldb.target.ReadInstructions() // that will go away after line 2 but the "inst" object should be able // to still answer questions about itself. So we make sure that any // SBInstruction objects that are given out have a strong reference to // the disassembler and the instruction so that the object can live and // successfully respond to all queries. //---------------------------------------------------------------------- class InstructionImpl { public: InstructionImpl(const lldb::DisassemblerSP &disasm_sp, const lldb::InstructionSP &inst_sp) : m_disasm_sp(disasm_sp), m_inst_sp(inst_sp) {} lldb::InstructionSP GetSP() const { return m_inst_sp; } bool IsValid() const { return (bool)m_inst_sp; } protected: lldb::DisassemblerSP m_disasm_sp; // Can be empty/invalid lldb::InstructionSP m_inst_sp; }; using namespace lldb; using namespace lldb_private; SBInstruction::SBInstruction() : m_opaque_sp() {} SBInstruction::SBInstruction(const lldb::DisassemblerSP &disasm_sp, const lldb::InstructionSP &inst_sp) : m_opaque_sp(new InstructionImpl(disasm_sp, inst_sp)) {} SBInstruction::SBInstruction(const SBInstruction &rhs) : m_opaque_sp(rhs.m_opaque_sp) {} const SBInstruction &SBInstruction::operator=(const SBInstruction &rhs) { if (this != &rhs) m_opaque_sp = rhs.m_opaque_sp; return *this; } SBInstruction::~SBInstruction() {} bool SBInstruction::IsValid() { return m_opaque_sp && m_opaque_sp->IsValid(); } SBAddress SBInstruction::GetAddress() { SBAddress sb_addr; lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp && inst_sp->GetAddress().IsValid()) sb_addr.SetAddress(&inst_sp->GetAddress()); return sb_addr; } const char *SBInstruction::GetMnemonic(SBTarget target) { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { ExecutionContext exe_ctx; TargetSP target_sp(target.GetSP()); std::unique_lock lock; if (target_sp) { lock = std::unique_lock(target_sp->GetAPIMutex()); target_sp->CalculateExecutionContext(exe_ctx); exe_ctx.SetProcessSP(target_sp->GetProcessSP()); } return inst_sp->GetMnemonic(&exe_ctx); } return NULL; } const char *SBInstruction::GetOperands(SBTarget target) { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { ExecutionContext exe_ctx; TargetSP target_sp(target.GetSP()); std::unique_lock lock; if (target_sp) { lock = std::unique_lock(target_sp->GetAPIMutex()); target_sp->CalculateExecutionContext(exe_ctx); exe_ctx.SetProcessSP(target_sp->GetProcessSP()); } return inst_sp->GetOperands(&exe_ctx); } return NULL; } const char *SBInstruction::GetComment(SBTarget target) { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { ExecutionContext exe_ctx; TargetSP target_sp(target.GetSP()); std::unique_lock lock; if (target_sp) { lock = std::unique_lock(target_sp->GetAPIMutex()); target_sp->CalculateExecutionContext(exe_ctx); exe_ctx.SetProcessSP(target_sp->GetProcessSP()); } return inst_sp->GetComment(&exe_ctx); } return NULL; } size_t SBInstruction::GetByteSize() { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) return inst_sp->GetOpcode().GetByteSize(); return 0; } SBData SBInstruction::GetData(SBTarget target) { lldb::SBData sb_data; lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { DataExtractorSP data_extractor_sp(new DataExtractor()); if (inst_sp->GetData(*data_extractor_sp)) { sb_data.SetOpaque(data_extractor_sp); } } return sb_data; } bool SBInstruction::DoesBranch() { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) return inst_sp->DoesBranch(); return false; } bool SBInstruction::HasDelaySlot() { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) return inst_sp->HasDelaySlot(); return false; } +bool SBInstruction::CanSetBreakpoint () { + lldb::InstructionSP inst_sp(GetOpaque()); + if (inst_sp) + return inst_sp->CanSetBreakpoint(); + return false; +} + lldb::InstructionSP SBInstruction::GetOpaque() { if (m_opaque_sp) return m_opaque_sp->GetSP(); else return lldb::InstructionSP(); } void SBInstruction::SetOpaque(const lldb::DisassemblerSP &disasm_sp, const lldb::InstructionSP &inst_sp) { m_opaque_sp.reset(new InstructionImpl(disasm_sp, inst_sp)); } bool SBInstruction::GetDescription(lldb::SBStream &s) { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { SymbolContext sc; const Address &addr = inst_sp->GetAddress(); ModuleSP module_sp(addr.GetModule()); if (module_sp) module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); // Use the "ref()" instead of the "get()" accessor in case the SBStream // 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, NULL, &sc, NULL, &format, 0); return true; } return false; } void SBInstruction::Print(FILE *out) { if (out == NULL) return; lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { SymbolContext sc; const Address &addr = inst_sp->GetAddress(); ModuleSP module_sp(addr.GetModule()); if (module_sp) module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); StreamFile out_stream(out, false); FormatEntity::Entry format; FormatEntity::Parse("${addr}: ", format); inst_sp->Dump(&out_stream, 0, true, false, NULL, &sc, NULL, &format, 0); } } bool SBInstruction::EmulateWithFrame(lldb::SBFrame &frame, uint32_t evaluate_options) { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) { lldb::StackFrameSP frame_sp(frame.GetFrameSP()); if (frame_sp) { lldb_private::ExecutionContext exe_ctx; frame_sp->CalculateExecutionContext(exe_ctx); lldb_private::Target *target = exe_ctx.GetTargetPtr(); lldb_private::ArchSpec arch = target->GetArchitecture(); return inst_sp->Emulate( arch, evaluate_options, (void *)frame_sp.get(), &lldb_private::EmulateInstruction::ReadMemoryFrame, &lldb_private::EmulateInstruction::WriteMemoryFrame, &lldb_private::EmulateInstruction::ReadRegisterFrame, &lldb_private::EmulateInstruction::WriteRegisterFrame); } } return false; } bool SBInstruction::DumpEmulation(const char *triple) { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp && triple) { lldb_private::ArchSpec arch(triple, NULL); return inst_sp->DumpEmulation(arch); } return false; } bool SBInstruction::TestEmulation(lldb::SBStream &output_stream, const char *test_file) { if (!m_opaque_sp) SetOpaque(lldb::DisassemblerSP(), lldb::InstructionSP(new PseudoInstruction())); lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) return inst_sp->TestEmulation(output_stream.get(), test_file); return false; } lldb::AddressClass SBInstruction::GetAddressClass() { lldb::InstructionSP inst_sp(GetOpaque()); if (inst_sp) return inst_sp->GetAddressClass(); return eAddressClassInvalid; } Index: vendor/lldb/dist/source/API/SBInstructionList.cpp =================================================================== --- vendor/lldb/dist/source/API/SBInstructionList.cpp (revision 317958) +++ vendor/lldb/dist/source/API/SBInstructionList.cpp (revision 317959) @@ -1,111 +1,137 @@ //===-- SBInstructionList.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBInstruction.h" +#include "lldb/API/SBAddress.h" #include "lldb/API/SBStream.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; SBInstructionList::SBInstructionList() : m_opaque_sp() {} SBInstructionList::SBInstructionList(const SBInstructionList &rhs) : m_opaque_sp(rhs.m_opaque_sp) {} const SBInstructionList &SBInstructionList:: operator=(const SBInstructionList &rhs) { if (this != &rhs) m_opaque_sp = rhs.m_opaque_sp; return *this; } SBInstructionList::~SBInstructionList() {} bool SBInstructionList::IsValid() const { return m_opaque_sp.get() != NULL; } size_t SBInstructionList::GetSize() { if (m_opaque_sp) return m_opaque_sp->GetInstructionList().GetSize(); return 0; } SBInstruction SBInstructionList::GetInstructionAtIndex(uint32_t idx) { SBInstruction inst; if (m_opaque_sp && idx < m_opaque_sp->GetInstructionList().GetSize()) inst.SetOpaque( m_opaque_sp, m_opaque_sp->GetInstructionList().GetInstructionAtIndex(idx)); return inst; +} + +size_t SBInstructionList::GetInstructionsCount(const SBAddress &start, + const SBAddress &end, + bool canSetBreakpoint) { + size_t num_instructions = GetSize(); + size_t i = 0; + SBAddress addr; + size_t lower_index = 0; + size_t upper_index = 0; + size_t instructions_to_skip = 0; + for (i = 0; i < num_instructions; ++i) { + addr = GetInstructionAtIndex(i).GetAddress(); + if (start == addr) + lower_index = i; + if (end == addr) + upper_index = i; + } + if (canSetBreakpoint) + for (i = lower_index; i <= upper_index; ++i) { + SBInstruction insn = GetInstructionAtIndex(i); + if (!insn.CanSetBreakpoint()) + ++instructions_to_skip; + } + return upper_index - lower_index - instructions_to_skip; } void SBInstructionList::Clear() { m_opaque_sp.reset(); } void SBInstructionList::AppendInstruction(SBInstruction insn) {} void SBInstructionList::SetDisassembler(const lldb::DisassemblerSP &opaque_sp) { m_opaque_sp = opaque_sp; } void SBInstructionList::Print(FILE *out) { if (out == NULL) return; } bool SBInstructionList::GetDescription(lldb::SBStream &description) { if (m_opaque_sp) { size_t num_instructions = GetSize(); if (num_instructions) { // Call the ref() to make sure a stream is created if one deesn't // exist already inside description... Stream &sref = description.ref(); const uint32_t max_opcode_byte_size = m_opaque_sp->GetInstructionList().GetMaxOpcocdeByteSize(); FormatEntity::Entry format; FormatEntity::Parse("${addr}: ", format); SymbolContext sc; SymbolContext prev_sc; for (size_t i = 0; i < num_instructions; ++i) { Instruction *inst = m_opaque_sp->GetInstructionList().GetInstructionAtIndex(i).get(); if (inst == NULL) break; const Address &addr = inst->GetAddress(); prev_sc = sc; ModuleSP module_sp(addr.GetModule()); if (module_sp) { module_sp->ResolveSymbolContextForAddress( addr, eSymbolContextEverything, sc); } inst->Dump(&sref, max_opcode_byte_size, true, false, NULL, &sc, &prev_sc, &format, 0); sref.EOL(); } return true; } } return false; } bool SBInstructionList::DumpEmulationForAllInstructions(const char *triple) { if (m_opaque_sp) { size_t len = GetSize(); for (size_t i = 0; i < len; ++i) { if (!GetInstructionAtIndex((uint32_t)i).DumpEmulation(triple)) return false; } } return true; } Index: vendor/lldb/dist/source/API/SBProcess.cpp =================================================================== --- vendor/lldb/dist/source/API/SBProcess.cpp (revision 317958) +++ vendor/lldb/dist/source/API/SBProcess.cpp (revision 317959) @@ -1,1360 +1,1372 @@ //===-- SBProcess.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBProcess.h" // C Includes #include #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Interpreter/Args.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" // Project includes #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBTrace.h" #include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBUnixSignals.h" using namespace lldb; using namespace lldb_private; SBProcess::SBProcess() : m_opaque_wp() {} //---------------------------------------------------------------------- // SBProcess constructor //---------------------------------------------------------------------- SBProcess::SBProcess(const SBProcess &rhs) : m_opaque_wp(rhs.m_opaque_wp) {} SBProcess::SBProcess(const lldb::ProcessSP &process_sp) : m_opaque_wp(process_sp) {} const SBProcess &SBProcess::operator=(const SBProcess &rhs) { if (this != &rhs) m_opaque_wp = rhs.m_opaque_wp; return *this; } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- SBProcess::~SBProcess() {} const char *SBProcess::GetBroadcasterClassName() { return Process::GetStaticBroadcasterClass().AsCString(); } const char *SBProcess::GetPluginName() { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } const char *SBProcess::GetShortPluginName() { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } lldb::ProcessSP SBProcess::GetSP() const { return m_opaque_wp.lock(); } void SBProcess::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; } void SBProcess::Clear() { m_opaque_wp.reset(); } bool SBProcess::IsValid() const { ProcessSP process_sp(m_opaque_wp.lock()); return ((bool)process_sp && process_sp->IsValid()); } bool SBProcess::RemoteLaunch(char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, bool stop_at_entry, lldb::SBError &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::RemoteLaunch (argv=%p, envp=%p, stdin=%s, " "stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, " "stop_at_entry=%i, &error (%p))...", static_cast(m_opaque_wp.lock().get()), static_cast(argv), static_cast(envp), stdin_path ? stdin_path : "NULL", stdout_path ? stdout_path : "NULL", stderr_path ? stderr_path : "NULL", working_directory ? working_directory : "NULL", launch_flags, stop_at_entry, static_cast(error.get())); ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { if (stop_at_entry) launch_flags |= eLaunchFlagStopAtEntry; ProcessLaunchInfo launch_info( FileSpec{stdin_path, false}, FileSpec{stdout_path, false}, FileSpec{stderr_path, false}, FileSpec{working_directory, false}, launch_flags); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); if (exe_module) launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); if (argv) launch_info.GetArguments().AppendArguments(argv); if (envp) launch_info.GetEnvironmentEntries().SetArguments(envp); error.SetError(process_sp->Launch(launch_info)); } else { error.SetErrorString("must be in eStateConnected to call RemoteLaunch"); } } else { error.SetErrorString("unable to attach pid"); } if (log) { SBStream sstr; error.GetDescription(sstr); log->Printf("SBProcess(%p)::RemoteLaunch (...) => SBError (%p): %s", static_cast(process_sp.get()), static_cast(error.get()), sstr.GetData()); } return error.Success(); } bool SBProcess::RemoteAttachToProcessWithID(lldb::pid_t pid, lldb::SBError &error) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { ProcessAttachInfo attach_info; attach_info.SetProcessID(pid); error.SetError(process_sp->Attach(attach_info)); } else { error.SetErrorString( "must be in eStateConnected to call RemoteAttachToProcessWithID"); } } else { error.SetErrorString("unable to attach pid"); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; error.GetDescription(sstr); log->Printf("SBProcess(%p)::RemoteAttachToProcessWithID (%" PRIu64 ") => SBError (%p): %s", static_cast(process_sp.get()), pid, static_cast(error.get()), sstr.GetData()); } return error.Success(); } uint32_t SBProcess::GetNumThreads() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_threads = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); num_threads = process_sp->GetThreadList().GetSize(can_update); } if (log) log->Printf("SBProcess(%p)::GetNumThreads () => %d", static_cast(process_sp.get()), num_threads); return num_threads; } SBThread SBProcess::GetSelectedThread() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetSelectedThread(); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", static_cast(process_sp.get()), static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->CreateOSPluginThread(tid, context); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::CreateOSPluginThread (tid=0x%" PRIx64 ", context=0x%" PRIx64 ") => SBThread(%p)", static_cast(process_sp.get()), tid, context, static_cast(thread_sp.get())); return sb_thread; } SBTarget SBProcess::GetTarget() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBTarget sb_target; TargetSP target_sp; ProcessSP process_sp(GetSP()); if (process_sp) { target_sp = process_sp->GetTarget().shared_from_this(); sb_target.SetSP(target_sp); } if (log) log->Printf("SBProcess(%p)::GetTarget () => SBTarget(%p)", static_cast(process_sp.get()), static_cast(target_sp.get())); return sb_target; } size_t SBProcess::PutSTDIN(const char *src, size_t src_len) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); size_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; ret_val = process_sp->PutSTDIN(src, src_len, error); } if (log) log->Printf("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), src, static_cast(src_len), static_cast(ret_val)); return ret_val; } size_t SBProcess::GetSTDOUT(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetSTDOUT(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetSTDERR(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetSTDERR(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetAsyncProfileData(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetAsyncProfileData (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } lldb::SBTrace SBProcess::StartTrace(SBTraceOptions &options, lldb::SBError &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); error.Clear(); SBTrace trace_instance; trace_instance.SetSP(process_sp); lldb::user_id_t uid = LLDB_INVALID_UID; if (!process_sp) { error.SetErrorString("invalid process"); } else { uid = process_sp->StartTrace(options.m_traceoptions_sp, error.ref()); trace_instance.SetTraceUID(uid); LLDB_LOG(log, "SBProcess::returned uid - %" PRIx64, uid); } return trace_instance; } void SBProcess::ReportEventState(const SBEvent &event, FILE *out) const { if (out == NULL) return; ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent(event); char message[1024]; int message_len = ::snprintf( message, sizeof(message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString(event_state)); if (message_len > 0) ::fwrite(message, 1, message_len, out); } } void SBProcess::AppendEventStateReport(const SBEvent &event, SBCommandReturnObject &result) { ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent(event); char message[1024]; ::snprintf(message, sizeof(message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString(event_state)); result.AppendMessage(message); } } bool SBProcess::SetSelectedThread(const SBThread &thread) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); return process_sp->GetThreadList().SetSelectedThreadByID( thread.GetThreadID()); } return false; } bool SBProcess::SetSelectedThreadByID(lldb::tid_t tid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByID(tid); } if (log) log->Printf("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4" PRIx64 ") => %s", static_cast(process_sp.get()), tid, (ret_val ? "true" : "false")); return ret_val; } bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id); } if (log) log->Printf("SBProcess(%p)::SetSelectedThreadByID (tid=0x%x) => %s", static_cast(process_sp.get()), index_id, (ret_val ? "true" : "false")); return ret_val; } SBThread SBProcess::GetThreadAtIndex(size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(thread_sp.get())); return sb_thread; } uint32_t SBProcess::GetNumQueues() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_queues = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); num_queues = process_sp->GetQueueList().GetSize(); } } if (log) log->Printf("SBProcess(%p)::GetNumQueues () => %d", static_cast(process_sp.get()), num_queues); return num_queues; } SBQueue SBProcess::GetQueueAtIndex(size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBQueue sb_queue; QueueSP queue_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index); sb_queue.SetQueue(queue_sp); } } if (log) log->Printf("SBProcess(%p)::GetQueueAtIndex (index=%d) => SBQueue(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(queue_sp.get())); return sb_queue; } uint32_t SBProcess::GetStopID(bool include_expression_stops) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (include_expression_stops) return process_sp->GetStopID(); else return process_sp->GetLastNaturalStopID(); } return 0; } SBEvent SBProcess::GetStopEventForStopID(uint32_t stop_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBEvent sb_event; EventSP event_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); event_sp = process_sp->GetStopEventForStopID(stop_id); sb_event.reset(event_sp); } if (log) log->Printf("SBProcess(%p)::GetStopEventForStopID (stop_id=%" PRIu32 ") => SBEvent(%p)", static_cast(process_sp.get()), stop_id, static_cast(event_sp.get())); return sb_event; } StateType SBProcess::GetState() { StateType ret_val = eStateInvalid; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetState(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetState () => %s", static_cast(process_sp.get()), lldb_private::StateAsCString(ret_val)); return ret_val; } int SBProcess::GetExitStatus() { int exit_status = 0; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); exit_status = process_sp->GetExitStatus(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)", static_cast(process_sp.get()), exit_status, exit_status); return exit_status; } const char *SBProcess::GetExitDescription() { const char *exit_desc = NULL; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); exit_desc = process_sp->GetExitDescription(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExitDescription () => %s", static_cast(process_sp.get()), exit_desc); return exit_desc; } lldb::pid_t SBProcess::GetProcessID() { lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetID(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetProcessID () => %" PRIu64, static_cast(process_sp.get()), ret_val); return ret_val; } uint32_t SBProcess::GetUniqueID() { uint32_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetUniqueID(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetUniqueID () => %" PRIu32, static_cast(process_sp.get()), ret_val); return ret_val; } ByteOrder SBProcess::GetByteOrder() const { ByteOrder byteOrder = eByteOrderInvalid; ProcessSP process_sp(GetSP()); if (process_sp) byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetByteOrder () => %d", static_cast(process_sp.get()), byteOrder); return byteOrder; } uint32_t SBProcess::GetAddressByteSize() const { uint32_t size = 0; ProcessSP process_sp(GetSP()); if (process_sp) size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetAddressByteSize () => %d", static_cast(process_sp.get()), size); return size; } SBError SBProcess::Continue() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBError sb_error; ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::Continue ()...", static_cast(process_sp.get())); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetTarget().GetDebugger().GetAsyncExecution()) sb_error.ref() = process_sp->Resume(); else sb_error.ref() = process_sp->ResumeSynchronous(NULL); } else sb_error.SetErrorString("SBProcess is invalid"); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Continue () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Destroy() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(false)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Destroy () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Stop() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Halt()); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Stop () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Kill() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(true)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Kill () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Detach() { // FIXME: This should come from a process default. bool keep_stopped = false; return Detach(keep_stopped); } SBError SBProcess::Detach(bool keep_stopped) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Detach(keep_stopped)); } else sb_error.SetErrorString("SBProcess is invalid"); return sb_error; } SBError SBProcess::Signal(int signo) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Signal(signo)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s", static_cast(process_sp.get()), signo, static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBUnixSignals SBProcess::GetUnixSignals() { if (auto process_sp = GetSP()) return SBUnixSignals{process_sp}; return {}; } void SBProcess::SendAsyncInterrupt() { ProcessSP process_sp(GetSP()); if (process_sp) { process_sp->SendAsyncInterrupt(); } } SBThread SBProcess::GetThreadByID(tid_t tid) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByID(tid, can_update); sb_thread.SetThread(thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetThreadByID (tid=0x%4.4" PRIx64 ") => SBThread (%p)", static_cast(process_sp.get()), tid, static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::GetThreadByIndexID(uint32_t index_id) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByIndexID(index_id, can_update); sb_thread.SetThread(thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetThreadByID (tid=0x%x) => SBThread (%p)", static_cast(process_sp.get()), index_id, static_cast(thread_sp.get())); return sb_thread; } StateType SBProcess::GetStateFromEvent(const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); StateType ret_val = Process::ProcessEventData::GetStateFromEvent(event.get()); if (log) log->Printf("SBProcess::GetStateFromEvent (event.sp=%p) => %s", static_cast(event.get()), lldb_private::StateAsCString(ret_val)); return ret_val; } bool SBProcess::GetRestartedFromEvent(const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = Process::ProcessEventData::GetRestartedFromEvent(event.get()); if (log) log->Printf("SBProcess::%s (event.sp=%p) => %d", __FUNCTION__, static_cast(event.get()), ret_val); return ret_val; } size_t SBProcess::GetNumRestartedReasonsFromEvent(const lldb::SBEvent &event) { return Process::ProcessEventData::GetNumRestartedReasons(event.get()); } const char * SBProcess::GetRestartedReasonAtIndexFromEvent(const lldb::SBEvent &event, size_t idx) { return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx); } SBProcess SBProcess::GetProcessFromEvent(const SBEvent &event) { ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event.get()); if (!process_sp) { // StructuredData events also know the process they come from. // Try that. process_sp = EventDataStructuredData::GetProcessFromEvent(event.get()); } return SBProcess(process_sp); } bool SBProcess::GetInterruptedFromEvent(const SBEvent &event) { return Process::ProcessEventData::GetInterruptedFromEvent(event.get()); } lldb::SBStructuredData SBProcess::GetStructuredDataFromEvent(const lldb::SBEvent &event) { return SBStructuredData(event.GetSP()); } bool SBProcess::EventIsProcessEvent(const SBEvent &event) { return (event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass()) && !EventIsStructuredDataEvent(event); } bool SBProcess::EventIsStructuredDataEvent(const lldb::SBEvent &event) { EventSP event_sp = event.GetSP(); EventData *event_data = event_sp ? event_sp->GetData() : nullptr; return event_data && (event_data->GetFlavor() == EventDataStructuredData::GetFlavorString()); } SBBroadcaster SBProcess::GetBroadcaster() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); SBBroadcaster broadcaster(process_sp.get(), false); if (log) log->Printf("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)", static_cast(process_sp.get()), static_cast(broadcaster.get())); return broadcaster; } const char *SBProcess::GetBroadcasterClass() { return Process::GetStaticBroadcasterClass().AsCString(); } size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len, SBError &sb_error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadMemory(addr, dst, dst_len, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::ReadMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_read)); } return bytes_read; } size_t SBProcess::ReadCStringFromMemory(addr_t addr, void *buf, size_t size, lldb::SBError &sb_error) { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadCStringFromMemory(addr, (char *)buf, size, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadCStringFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return bytes_read; } uint64_t SBProcess::ReadUnsignedFromMemory(addr_t addr, uint32_t byte_size, lldb::SBError &sb_error) { uint64_t value = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); value = process_sp->ReadUnsignedIntegerFromMemory(addr, byte_size, 0, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadUnsignedFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return value; } lldb::addr_t SBProcess::ReadPointerFromMemory(addr_t addr, lldb::SBError &sb_error) { lldb::addr_t ptr = LLDB_INVALID_ADDRESS; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ptr = process_sp->ReadPointerFromMemory(addr, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadPointerFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return ptr; } size_t SBProcess::WriteMemory(addr_t addr, const void *src, size_t src_len, SBError &sb_error) { size_t bytes_written = 0; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_written = process_sp->WriteMemory(addr, src, src_len, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::WriteMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_written)); } return bytes_written; } bool SBProcess::GetDescription(SBStream &description) { Stream &strm = description.ref(); ProcessSP process_sp(GetSP()); if (process_sp) { char path[PATH_MAX]; GetTarget().GetExecutable().GetPath(path, sizeof(path)); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); const char *exe_name = NULL; if (exe_module) exe_name = exe_module->GetFileSpec().GetFilename().AsCString(); strm.Printf("SBProcess: pid = %" PRIu64 ", state = %s, threads = %d%s%s", process_sp->GetID(), lldb_private::StateAsCString(GetState()), GetNumThreads(), exe_name ? ", executable = " : "", exe_name ? exe_name : ""); } else strm.PutCString("No value"); return true; } uint32_t SBProcess::GetNumSupportedHardwareWatchpoints(lldb::SBError &sb_error) const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num = 0; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->GetWatchpointSupportInfo(num)); if (log) log->Printf("SBProcess(%p)::GetNumSupportedHardwareWatchpoints () => %u", static_cast(process_sp.get()), num); } else { sb_error.SetErrorString("SBProcess is invalid"); } return num; } uint32_t SBProcess::LoadImage(lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { return LoadImage(SBFileSpec(), sb_remote_image_spec, sb_error); } uint32_t SBProcess::LoadImage(const lldb::SBFileSpec &sb_local_image_spec, const lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { + if (log) + log->Printf("SBProcess(%p)::LoadImage() => calling Platform::LoadImage" + "for: %s", + static_cast(process_sp.get()), + sb_local_image_spec.GetFilename()); + std::lock_guard guard( - process_sp->GetTarget().GetAPIMutex()); + process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); return platform_sp->LoadImage(process_sp.get(), *sb_local_image_spec, *sb_remote_image_spec, sb_error.ref()); } else { - Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::LoadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } + } else { + if (log) + log->Printf("SBProcess(%p)::LoadImage() => error: called with invalid" + " process", + static_cast(process_sp.get())); + sb_error.SetErrorString("process is invalid"); } return LLDB_INVALID_IMAGE_TOKEN; } lldb::SBError SBProcess::UnloadImage(uint32_t image_token) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); sb_error.SetError( platform_sp->UnloadImage(process_sp.get(), image_token)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::UnloadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } lldb::SBError SBProcess::SendEventData(const char *event_data) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->SendEventData(event_data)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::SendEventData() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } uint32_t SBProcess::GetNumExtendedBacktraceTypes() { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); return runtime->GetExtendedBacktraceTypes().size(); } return 0; } const char *SBProcess::GetExtendedBacktraceTypeAtIndex(uint32_t idx) { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); const std::vector &names = runtime->GetExtendedBacktraceTypes(); if (idx < names.size()) { return names[idx].AsCString(); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExtendedBacktraceTypeAtIndex() => " "error: requested extended backtrace name out of bounds", static_cast(process_sp.get())); } } return NULL; } SBThreadCollection SBProcess::GetHistoryThreads(addr_t addr) { ProcessSP process_sp(GetSP()); SBThreadCollection threads; if (process_sp) { threads = SBThreadCollection(process_sp->GetHistoryThreads(addr)); } return threads; } bool SBProcess::IsInstrumentationRuntimePresent( InstrumentationRuntimeType type) { ProcessSP process_sp(GetSP()); if (!process_sp) return false; InstrumentationRuntimeSP runtime_sp = process_sp->GetInstrumentationRuntime(type); if (!runtime_sp.get()) return false; return runtime_sp->IsActive(); } lldb::SBError SBProcess::SaveCore(const char *file_name) { lldb::SBError error; ProcessSP process_sp(GetSP()); if (!process_sp) { error.SetErrorString("SBProcess is invalid"); return error; } std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() != eStateStopped) { error.SetErrorString("the process is not stopped"); return error; } FileSpec core_file(file_name, false); error.ref() = PluginManager::SaveCore(process_sp, core_file); return error; } lldb::SBError SBProcess::GetMemoryRegionInfo(lldb::addr_t load_addr, SBMemoryRegionInfo &sb_region_info) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); MemoryRegionInfoSP region_info_sp = std::make_shared(); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.ref() = process_sp->GetMemoryRegionInfo(load_addr, *region_info_sp); if (sb_error.Success()) { sb_region_info.ref() = *region_info_sp; } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetMemoryRegionInfo() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return sb_error; } lldb::SBMemoryRegionInfoList SBProcess::GetMemoryRegions() { lldb::SBError sb_error; lldb::SBMemoryRegionInfoList sb_region_list; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); std::vector region_list; sb_error.ref() = process_sp->GetMemoryRegions(region_list); if (sb_error.Success()) { std::vector::iterator end = region_list.end(); for (std::vector::iterator it = region_list.begin(); it != end; it++) { SBMemoryRegionInfo sb_region_info(it->get()); sb_region_list.Append(sb_region_info); } } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetMemoryRegionInfo() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return sb_region_list; } Index: vendor/lldb/dist/source/Core/Disassembler.cpp =================================================================== --- vendor/lldb/dist/source/Core/Disassembler.cpp (revision 317958) +++ vendor/lldb/dist/source/Core/Disassembler.cpp (revision 317959) @@ -1,1459 +1,1463 @@ //===-- Disassembler.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/Disassembler.h" #include "lldb/Core/AddressRange.h" // for AddressRange #include "lldb/Core/Debugger.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/Mangled.h" // for Mangled, Mangled... #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" // for ModuleList #include "lldb/Core/PluginManager.h" #include "lldb/Core/SourceManager.h" // for SourceManager #include "lldb/Core/Timer.h" #include "lldb/Host/FileSystem.h" #include "lldb/Interpreter/OptionValue.h" #include "lldb/Interpreter/OptionValueArray.h" #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/Interpreter/OptionValueRegex.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" // for Symbol #include "lldb/Symbol/SymbolContext.h" // for SymbolContext #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" // for Thread #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StreamString.h" // for StreamString #include "lldb/lldb-private-enumerations.h" // for InstructionType:... #include "lldb/lldb-private-interfaces.h" // for DisassemblerCrea... #include "lldb/lldb-private-types.h" // for RegisterInfo #include "llvm/ADT/Triple.h" // for Triple, Triple::... #include "llvm/Support/Compiler.h" // for LLVM_PRETTY_FUNC... #include // for uint32_t, UINT32... #include #include // for pair #include // for assert #define DEFAULT_DISASM_BYTE_SIZE 32 using namespace lldb; using namespace lldb_private; DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch, const char *flavor, const char *plugin_name) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "Disassembler::FindPlugin (arch = %s, plugin_name = %s)", arch.GetArchitectureName(), plugin_name); DisassemblerCreateInstance create_callback = nullptr; if (plugin_name) { ConstString const_plugin_name(plugin_name); create_callback = PluginManager::GetDisassemblerCreateCallbackForPluginName( const_plugin_name); if (create_callback) { DisassemblerSP disassembler_sp(create_callback(arch, flavor)); if (disassembler_sp) return disassembler_sp; } } else { for (uint32_t idx = 0; (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex( idx)) != nullptr; ++idx) { DisassemblerSP disassembler_sp(create_callback(arch, flavor)); if (disassembler_sp) return disassembler_sp; } } return DisassemblerSP(); } DisassemblerSP Disassembler::FindPluginForTarget(const TargetSP target_sp, const ArchSpec &arch, const char *flavor, const char *plugin_name) { if (target_sp && flavor == nullptr) { // FIXME - we don't have the mechanism in place to do per-architecture // settings. But since we know that for now // we only support flavors on x86 & x86_64, if (arch.GetTriple().getArch() == llvm::Triple::x86 || arch.GetTriple().getArch() == llvm::Triple::x86_64) flavor = target_sp->GetDisassemblyFlavor(); } return FindPlugin(arch, flavor, plugin_name); } static void ResolveAddress(const ExecutionContext &exe_ctx, const Address &addr, Address &resolved_addr) { if (!addr.IsSectionOffset()) { // If we weren't passed in a section offset address range, // try and resolve it to something Target *target = exe_ctx.GetTargetPtr(); if (target) { if (target->GetSectionLoadList().IsEmpty()) { target->GetImages().ResolveFileAddress(addr.GetOffset(), resolved_addr); } else { target->GetSectionLoadList().ResolveLoadAddress(addr.GetOffset(), resolved_addr); } // We weren't able to resolve the address, just treat it as a // raw address if (resolved_addr.IsValid()) return; } } resolved_addr = addr; } size_t Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, SymbolContextList &sc_list, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) { size_t success_count = 0; const size_t count = sc_list.GetSize(); SymbolContext sc; AddressRange range; const uint32_t scope = eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol; const bool use_inline_block_range = true; for (size_t i = 0; i < count; ++i) { if (!sc_list.GetContextAtIndex(i, sc)) break; for (uint32_t range_idx = 0; sc.GetAddressRange(scope, range_idx, use_inline_block_range, range); ++range_idx) { if (Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range, num_instructions, mixed_source_and_assembly, num_mixed_context_lines, options, strm)) { ++success_count; strm.EOL(); } } } return success_count; } bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const ConstString &name, Module *module, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) { SymbolContextList sc_list; if (name) { const bool include_symbols = true; const bool include_inlines = true; if (module) { module->FindFunctions(name, nullptr, eFunctionNameTypeAuto, include_symbols, include_inlines, true, sc_list); } else if (exe_ctx.GetTargetPtr()) { exe_ctx.GetTargetPtr()->GetImages().FindFunctions( name, eFunctionNameTypeAuto, include_symbols, include_inlines, false, sc_list); } } if (sc_list.GetSize()) { return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, sc_list, num_instructions, mixed_source_and_assembly, num_mixed_context_lines, options, strm); } return false; } lldb::DisassemblerSP Disassembler::DisassembleRange( const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const AddressRange &range, bool prefer_file_cache) { lldb::DisassemblerSP disasm_sp; if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) { disasm_sp = Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name); if (disasm_sp) { size_t bytes_disassembled = disasm_sp->ParseInstructions( &exe_ctx, range, nullptr, prefer_file_cache); if (bytes_disassembled == 0) disasm_sp.reset(); } } return disasm_sp; } lldb::DisassemblerSP Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name, const char *flavor, const Address &start, const void *src, size_t src_len, uint32_t num_instructions, bool data_from_file) { lldb::DisassemblerSP disasm_sp; if (src) { disasm_sp = Disassembler::FindPlugin(arch, flavor, plugin_name); if (disasm_sp) { DataExtractor data(src, src_len, arch.GetByteOrder(), arch.GetAddressByteSize()); (void)disasm_sp->DecodeInstructions(start, data, 0, num_instructions, false, data_from_file); } } return disasm_sp; } bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const AddressRange &disasm_range, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) { if (disasm_range.GetByteSize()) { lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget( exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); if (disasm_sp) { AddressRange range; ResolveAddress(exe_ctx, disasm_range.GetBaseAddress(), range.GetBaseAddress()); range.SetByteSize(disasm_range.GetByteSize()); const bool prefer_file_cache = false; size_t bytes_disassembled = disasm_sp->ParseInstructions( &exe_ctx, range, &strm, prefer_file_cache); if (bytes_disassembled == 0) return false; return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx, num_instructions, mixed_source_and_assembly, num_mixed_context_lines, options, strm); } } return false; } bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, const Address &start_address, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) { if (num_instructions > 0) { lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget( exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); if (disasm_sp) { Address addr; ResolveAddress(exe_ctx, start_address, addr); const bool prefer_file_cache = false; size_t bytes_disassembled = disasm_sp->ParseInstructions( &exe_ctx, addr, num_instructions, prefer_file_cache); if (bytes_disassembled == 0) return false; return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx, num_instructions, mixed_source_and_assembly, num_mixed_context_lines, options, strm); } } return false; } Disassembler::SourceLine Disassembler::GetFunctionDeclLineEntry(const SymbolContext &sc) { SourceLine decl_line; if (sc.function && sc.line_entry.IsValid()) { LineEntry prologue_end_line = sc.line_entry; FileSpec func_decl_file; uint32_t func_decl_line; sc.function->GetStartLineSourceInfo(func_decl_file, func_decl_line); if (func_decl_file == prologue_end_line.file || func_decl_file == prologue_end_line.original_file) { decl_line.file = func_decl_file; decl_line.line = func_decl_line; // TODO do we care about column on these entries? If so, we need to // plumb that through GetStartLineSourceInfo. decl_line.column = 0; } } return decl_line; } void Disassembler::AddLineToSourceLineTables( SourceLine &line, std::map> &source_lines_seen) { if (line.IsValid()) { auto source_lines_seen_pos = source_lines_seen.find(line.file); if (source_lines_seen_pos == source_lines_seen.end()) { std::set lines; lines.insert(line.line); source_lines_seen.emplace(line.file, lines); } else { source_lines_seen_pos->second.insert(line.line); } } } bool Disassembler::ElideMixedSourceAndDisassemblyLine( const ExecutionContext &exe_ctx, const SymbolContext &sc, SourceLine &line) { // TODO: should we also check target.process.thread.step-avoid-libraries ? const RegularExpression *avoid_regex = nullptr; // Skip any line #0 entries - they are implementation details if (line.line == 0) return false; ThreadSP thread_sp = exe_ctx.GetThreadSP(); if (thread_sp) { avoid_regex = thread_sp->GetSymbolsToAvoidRegexp(); } else { TargetSP target_sp = exe_ctx.GetTargetSP(); if (target_sp) { Error error; OptionValueSP value_sp = target_sp->GetDebugger().GetPropertyValue( &exe_ctx, "target.process.thread.step-avoid-regexp", false, error); if (value_sp && value_sp->GetType() == OptionValue::eTypeRegex) { OptionValueRegex *re = value_sp->GetAsRegex(); if (re) { avoid_regex = re->GetCurrentValue(); } } } } if (avoid_regex && sc.symbol != nullptr) { const char *function_name = sc.GetFunctionName(Mangled::ePreferDemangledWithoutArguments) .GetCString(); if (function_name) { RegularExpression::Match regex_match(1); if (avoid_regex->Execute(function_name, ®ex_match)) { // skip this source line return true; } } } // don't skip this source line return false; } bool Disassembler::PrintInstructions(Disassembler *disasm_ptr, Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) { // We got some things disassembled... size_t num_instructions_found = disasm_ptr->GetInstructionList().GetSize(); if (num_instructions > 0 && num_instructions < num_instructions_found) num_instructions_found = num_instructions; const uint32_t max_opcode_byte_size = disasm_ptr->GetInstructionList().GetMaxOpcocdeByteSize(); SymbolContext sc; SymbolContext prev_sc; AddressRange current_source_line_range; const Address *pc_addr_ptr = nullptr; StackFrame *frame = exe_ctx.GetFramePtr(); TargetSP target_sp(exe_ctx.GetTargetSP()); SourceManager &source_manager = target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager(); if (frame) { pc_addr_ptr = &frame->GetFrameCodeAddress(); } const uint32_t scope = eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol; const bool use_inline_block_range = false; const FormatEntity::Entry *disassembly_format = nullptr; FormatEntity::Entry format; if (exe_ctx.HasTargetScope()) { disassembly_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat(); } else { FormatEntity::Parse("${addr}: ", format); disassembly_format = &format; } // First pass: step through the list of instructions, // find how long the initial addresses strings are, insert padding // in the second pass so the opcodes all line up nicely. // Also build up the source line mapping if this is mixed source & assembly // mode. // Calculate the source line for each assembly instruction (eliding inlined // functions // which the user wants to skip). std::map> source_lines_seen; Symbol *previous_symbol = nullptr; size_t address_text_size = 0; for (size_t i = 0; i < num_instructions_found; ++i) { Instruction *inst = disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get(); if (inst) { const Address &addr = inst->GetAddress(); ModuleSP module_sp(addr.GetModule()); if (module_sp) { const uint32_t resolve_mask = eSymbolContextFunction | eSymbolContextSymbol | eSymbolContextLineEntry; uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress(addr, resolve_mask, sc); if (resolved_mask) { StreamString strmstr; Debugger::FormatDisassemblerAddress(disassembly_format, &sc, nullptr, &exe_ctx, &addr, strmstr); size_t cur_line = strmstr.GetSizeOfLastLine(); if (cur_line > address_text_size) address_text_size = cur_line; // Add entries to our "source_lines_seen" map+set which list which // sources lines occur in this disassembly session. We will print // lines of context around a source line, but we don't want to print // a source line that has a line table entry of its own - we'll leave // that source line to be printed when it actually occurs in the // disassembly. if (mixed_source_and_assembly && sc.line_entry.IsValid()) { if (sc.symbol != previous_symbol) { SourceLine decl_line = GetFunctionDeclLineEntry(sc); if (ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, decl_line) == false) AddLineToSourceLineTables(decl_line, source_lines_seen); } if (sc.line_entry.IsValid()) { SourceLine this_line; this_line.file = sc.line_entry.file; this_line.line = sc.line_entry.line; this_line.column = sc.line_entry.column; if (ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, this_line) == false) AddLineToSourceLineTables(this_line, source_lines_seen); } } } sc.Clear(false); } } } previous_symbol = nullptr; SourceLine previous_line; for (size_t i = 0; i < num_instructions_found; ++i) { Instruction *inst = disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get(); if (inst) { const Address &addr = inst->GetAddress(); const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr; SourceLinesToDisplay source_lines_to_display; prev_sc = sc; ModuleSP module_sp(addr.GetModule()); if (module_sp) { uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress( addr, eSymbolContextEverything, sc); if (resolved_mask) { if (mixed_source_and_assembly) { // If we've started a new function (non-inlined), print all of the // source lines from the // function declaration until the first line table entry - typically // the opening curly brace of // the function. if (previous_symbol != sc.symbol) { // The default disassembly format puts an extra blank line between // functions - so // when we're displaying the source context for a function, we // don't want to add // a blank line after the source context or we'll end up with two // of them. if (previous_symbol != nullptr) source_lines_to_display.print_source_context_end_eol = false; previous_symbol = sc.symbol; if (sc.function && sc.line_entry.IsValid()) { LineEntry prologue_end_line = sc.line_entry; if (ElideMixedSourceAndDisassemblyLine( exe_ctx, sc, prologue_end_line) == false) { FileSpec func_decl_file; uint32_t func_decl_line; sc.function->GetStartLineSourceInfo(func_decl_file, func_decl_line); if (func_decl_file == prologue_end_line.file || func_decl_file == prologue_end_line.original_file) { // Add all the lines between the function declaration // and the first non-prologue source line to the list // of lines to print. for (uint32_t lineno = func_decl_line; lineno <= prologue_end_line.line; lineno++) { SourceLine this_line; this_line.file = func_decl_file; this_line.line = lineno; source_lines_to_display.lines.push_back(this_line); } // Mark the last line as the "current" one. Usually // this is the open curly brace. if (source_lines_to_display.lines.size() > 0) source_lines_to_display.current_source_line = source_lines_to_display.lines.size() - 1; } } } sc.GetAddressRange(scope, 0, use_inline_block_range, current_source_line_range); } // If we've left a previous source line's address range, print a new // source line if (!current_source_line_range.ContainsFileAddress(addr)) { sc.GetAddressRange(scope, 0, use_inline_block_range, current_source_line_range); if (sc != prev_sc && sc.comp_unit && sc.line_entry.IsValid()) { SourceLine this_line; this_line.file = sc.line_entry.file; this_line.line = sc.line_entry.line; if (ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, this_line) == false) { // Only print this source line if it is different from the // last source line we printed. There may have been inlined // functions between these lines that we elided, resulting in // the same line being printed twice in a row for a contiguous // block of assembly instructions. if (this_line != previous_line) { std::vector previous_lines; for (uint32_t i = 0; i < num_mixed_context_lines && (this_line.line - num_mixed_context_lines) > 0; i++) { uint32_t line = this_line.line - num_mixed_context_lines + i; auto pos = source_lines_seen.find(this_line.file); if (pos != source_lines_seen.end()) { if (pos->second.count(line) == 1) { previous_lines.clear(); } else { previous_lines.push_back(line); } } } for (size_t i = 0; i < previous_lines.size(); i++) { SourceLine previous_line; previous_line.file = this_line.file; previous_line.line = previous_lines[i]; auto pos = source_lines_seen.find(previous_line.file); if (pos != source_lines_seen.end()) { pos->second.insert(previous_line.line); } source_lines_to_display.lines.push_back(previous_line); } source_lines_to_display.lines.push_back(this_line); source_lines_to_display.current_source_line = source_lines_to_display.lines.size() - 1; for (uint32_t i = 0; i < num_mixed_context_lines; i++) { SourceLine next_line; next_line.file = this_line.file; next_line.line = this_line.line + i + 1; auto pos = source_lines_seen.find(next_line.file); if (pos != source_lines_seen.end()) { if (pos->second.count(next_line.line) == 1) break; pos->second.insert(next_line.line); } source_lines_to_display.lines.push_back(next_line); } } previous_line = this_line; } } } } } else { sc.Clear(true); } } if (source_lines_to_display.lines.size() > 0) { strm.EOL(); for (size_t idx = 0; idx < source_lines_to_display.lines.size(); idx++) { SourceLine ln = source_lines_to_display.lines[idx]; const char *line_highlight = ""; if (inst_is_at_pc && (options & eOptionMarkPCSourceLine)) { line_highlight = "->"; } else if (idx == source_lines_to_display.current_source_line) { line_highlight = "**"; } source_manager.DisplaySourceLinesWithLineNumbers( ln.file, ln.line, ln.column, 0, 0, line_highlight, &strm); } if (source_lines_to_display.print_source_context_end_eol) strm.EOL(); } 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); strm.EOL(); } else { break; } } return true; } bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, uint32_t num_instructions, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) { AddressRange range; StackFrame *frame = exe_ctx.GetFramePtr(); if (frame) { SymbolContext sc( frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); if (sc.function) { range = sc.function->GetAddressRange(); } else if (sc.symbol && sc.symbol->ValueIsAddress()) { range.GetBaseAddress() = sc.symbol->GetAddressRef(); range.SetByteSize(sc.symbol->GetByteSize()); } else { range.GetBaseAddress() = frame->GetFrameCodeAddress(); } if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0) range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); } return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range, num_instructions, mixed_source_and_assembly, num_mixed_context_lines, options, strm); } Instruction::Instruction(const Address &address, AddressClass addr_class) : m_address(address), m_address_class(addr_class), m_opcode(), m_calculated_strings(false) {} Instruction::~Instruction() = default; AddressClass Instruction::GetAddressClass() { if (m_address_class == eAddressClassInvalid) m_address_class = m_address.GetAddressClass(); return m_address_class; } void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, bool show_address, bool show_bytes, const ExecutionContext *exe_ctx, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, const FormatEntity::Entry *disassembly_addr_format, size_t max_address_text_size) { size_t opcode_column_width = 7; const size_t operand_column_width = 25; CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx); StreamString ss; if (show_address) { Debugger::FormatDisassemblerAddress(disassembly_addr_format, sym_ctx, prev_sym_ctx, exe_ctx, &m_address, ss); ss.FillLastLineToColumn(max_address_text_size, ' '); } if (show_bytes) { if (m_opcode.GetType() == Opcode::eTypeBytes) { // x86_64 and i386 are the only ones that use bytes right now so // pad out the byte dump to be able to always show 15 bytes (3 chars each) // plus a space if (max_opcode_byte_size > 0) m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1); else m_opcode.Dump(&ss, 15 * 3 + 1); } else { // Else, we have ARM or MIPS which can show up to a uint32_t // 0x00000000 (10 spaces) plus two for padding... if (max_opcode_byte_size > 0) m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1); else m_opcode.Dump(&ss, 12); } } const size_t opcode_pos = ss.GetSizeOfLastLine(); // The default opcode size of 7 characters is plenty for most architectures // but some like arm can pull out the occasional vqrshrun.s16. We won't get // consistent column spacing in these cases, unfortunately. if (m_opcode_name.length() >= opcode_column_width) { opcode_column_width = m_opcode_name.length() + 1; } ss.PutCString(m_opcode_name); ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' '); ss.PutCString(m_mnemonics); if (!m_comment.empty()) { ss.FillLastLineToColumn( opcode_pos + opcode_column_width + operand_column_width, ' '); ss.PutCString(" ; "); ss.PutCString(m_comment); } s->PutCString(ss.GetString()); } bool Instruction::DumpEmulation(const ArchSpec &arch) { std::unique_ptr insn_emulator_ap( EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr)); if (insn_emulator_ap) { insn_emulator_ap->SetInstruction(GetOpcode(), GetAddress(), nullptr); return insn_emulator_ap->EvaluateInstruction(0); } return false; } +bool Instruction::CanSetBreakpoint () { + return !HasDelaySlot(); +} + bool Instruction::HasDelaySlot() { // Default is false. return false; } OptionValueSP Instruction::ReadArray(FILE *in_file, Stream *out_stream, OptionValue::Type data_type) { bool done = false; char buffer[1024]; auto option_value_sp = std::make_shared(1u << data_type); int idx = 0; while (!done) { if (!fgets(buffer, 1023, in_file)) { out_stream->Printf( "Instruction::ReadArray: Error reading file (fgets).\n"); option_value_sp.reset(); return option_value_sp; } std::string line(buffer); size_t len = line.size(); if (line[len - 1] == '\n') { line[len - 1] = '\0'; line.resize(len - 1); } if ((line.size() == 1) && line[0] == ']') { done = true; line.clear(); } if (!line.empty()) { std::string value; static RegularExpression g_reg_exp( llvm::StringRef("^[ \t]*([^ \t]+)[ \t]*$")); RegularExpression::Match regex_match(1); bool reg_exp_success = g_reg_exp.Execute(line, ®ex_match); if (reg_exp_success) regex_match.GetMatchAtIndex(line.c_str(), 1, value); else value = line; OptionValueSP data_value_sp; switch (data_type) { case OptionValue::eTypeUInt64: data_value_sp = std::make_shared(0, 0); data_value_sp->SetValueFromString(value); break; // Other types can be added later as needed. default: data_value_sp = std::make_shared(value.c_str(), ""); break; } option_value_sp->GetAsArray()->InsertValue(idx, data_value_sp); ++idx; } } return option_value_sp; } OptionValueSP Instruction::ReadDictionary(FILE *in_file, Stream *out_stream) { bool done = false; char buffer[1024]; auto option_value_sp = std::make_shared(); static ConstString encoding_key("data_encoding"); OptionValue::Type data_type = OptionValue::eTypeInvalid; while (!done) { // Read the next line in the file if (!fgets(buffer, 1023, in_file)) { out_stream->Printf( "Instruction::ReadDictionary: Error reading file (fgets).\n"); option_value_sp.reset(); return option_value_sp; } // Check to see if the line contains the end-of-dictionary marker ("}") std::string line(buffer); size_t len = line.size(); if (line[len - 1] == '\n') { line[len - 1] = '\0'; line.resize(len - 1); } if ((line.size() == 1) && (line[0] == '}')) { done = true; line.clear(); } // Try to find a key-value pair in the current line and add it to the // dictionary. if (!line.empty()) { static RegularExpression g_reg_exp(llvm::StringRef( "^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$")); RegularExpression::Match regex_match(2); bool reg_exp_success = g_reg_exp.Execute(line, ®ex_match); std::string key; std::string value; if (reg_exp_success) { regex_match.GetMatchAtIndex(line.c_str(), 1, key); regex_match.GetMatchAtIndex(line.c_str(), 2, value); } else { out_stream->Printf("Instruction::ReadDictionary: Failure executing " "regular expression.\n"); option_value_sp.reset(); return option_value_sp; } ConstString const_key(key.c_str()); // Check value to see if it's the start of an array or dictionary. lldb::OptionValueSP value_sp; assert(value.empty() == false); assert(key.empty() == false); if (value[0] == '{') { assert(value.size() == 1); // value is a dictionary value_sp = ReadDictionary(in_file, out_stream); if (!value_sp) { option_value_sp.reset(); return option_value_sp; } } else if (value[0] == '[') { assert(value.size() == 1); // value is an array value_sp = ReadArray(in_file, out_stream, data_type); if (!value_sp) { option_value_sp.reset(); return option_value_sp; } // We've used the data_type to read an array; re-set the type to Invalid data_type = OptionValue::eTypeInvalid; } else if ((value[0] == '0') && (value[1] == 'x')) { value_sp = std::make_shared(0, 0); value_sp->SetValueFromString(value); } else { size_t len = value.size(); if ((value[0] == '"') && (value[len - 1] == '"')) value = value.substr(1, len - 2); value_sp = std::make_shared(value.c_str(), ""); } if (const_key == encoding_key) { // A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data // indicating the // data type of an upcoming array (usually the next bit of data to be // read in). if (strcmp(value.c_str(), "uint32_t") == 0) data_type = OptionValue::eTypeUInt64; } else option_value_sp->GetAsDictionary()->SetValueForKey(const_key, value_sp, false); } } return option_value_sp; } bool Instruction::TestEmulation(Stream *out_stream, const char *file_name) { if (!out_stream) return false; if (!file_name) { out_stream->Printf("Instruction::TestEmulation: Missing file_name."); return false; } FILE *test_file = FileSystem::Fopen(file_name, "r"); if (!test_file) { out_stream->Printf( "Instruction::TestEmulation: Attempt to open test file failed."); return false; } char buffer[256]; if (!fgets(buffer, 255, test_file)) { out_stream->Printf( "Instruction::TestEmulation: Error reading first line of test file.\n"); fclose(test_file); return false; } if (strncmp(buffer, "InstructionEmulationState={", 27) != 0) { out_stream->Printf("Instructin::TestEmulation: Test file does not contain " "emulation state dictionary\n"); fclose(test_file); return false; } // Read all the test information from the test file into an // OptionValueDictionary. OptionValueSP data_dictionary_sp(ReadDictionary(test_file, out_stream)); if (!data_dictionary_sp) { out_stream->Printf( "Instruction::TestEmulation: Error reading Dictionary Object.\n"); fclose(test_file); return false; } fclose(test_file); OptionValueDictionary *data_dictionary = data_dictionary_sp->GetAsDictionary(); static ConstString description_key("assembly_string"); static ConstString triple_key("triple"); OptionValueSP value_sp = data_dictionary->GetValueForKey(description_key); if (!value_sp) { out_stream->Printf("Instruction::TestEmulation: Test file does not " "contain description string.\n"); return false; } SetDescription(value_sp->GetStringValue()); value_sp = data_dictionary->GetValueForKey(triple_key); if (!value_sp) { out_stream->Printf( "Instruction::TestEmulation: Test file does not contain triple.\n"); return false; } ArchSpec arch; arch.SetTriple(llvm::Triple(value_sp->GetStringValue())); bool success = false; std::unique_ptr insn_emulator_ap( EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr)); if (insn_emulator_ap) success = insn_emulator_ap->TestEmulation(out_stream, arch, data_dictionary); if (success) out_stream->Printf("Emulation test succeeded."); else out_stream->Printf("Emulation test failed."); return success; } bool Instruction::Emulate( const ArchSpec &arch, uint32_t evaluate_options, void *baton, EmulateInstruction::ReadMemoryCallback read_mem_callback, EmulateInstruction::WriteMemoryCallback write_mem_callback, EmulateInstruction::ReadRegisterCallback read_reg_callback, EmulateInstruction::WriteRegisterCallback write_reg_callback) { std::unique_ptr insn_emulator_ap( EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr)); if (insn_emulator_ap) { insn_emulator_ap->SetBaton(baton); insn_emulator_ap->SetCallbacks(read_mem_callback, write_mem_callback, read_reg_callback, write_reg_callback); insn_emulator_ap->SetInstruction(GetOpcode(), GetAddress(), nullptr); return insn_emulator_ap->EvaluateInstruction(evaluate_options); } return false; } uint32_t Instruction::GetData(DataExtractor &data) { return m_opcode.GetData(data); } InstructionList::InstructionList() : m_instructions() {} InstructionList::~InstructionList() = default; size_t InstructionList::GetSize() const { return m_instructions.size(); } uint32_t InstructionList::GetMaxOpcocdeByteSize() const { uint32_t max_inst_size = 0; collection::const_iterator pos, end; for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end; ++pos) { uint32_t inst_size = (*pos)->GetOpcode().GetByteSize(); if (max_inst_size < inst_size) max_inst_size = inst_size; } return max_inst_size; } InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const { InstructionSP inst_sp; if (idx < m_instructions.size()) inst_sp = m_instructions[idx]; return inst_sp; } void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes, const ExecutionContext *exe_ctx) { const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); collection::const_iterator pos, begin, end; const FormatEntity::Entry *disassembly_format = nullptr; FormatEntity::Entry format; if (exe_ctx && exe_ctx->HasTargetScope()) { disassembly_format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); } else { FormatEntity::Parse("${addr}: ", format); disassembly_format = &format; } for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin; 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); } } void InstructionList::Clear() { m_instructions.clear(); } void InstructionList::Append(lldb::InstructionSP &inst_sp) { if (inst_sp) m_instructions.push_back(inst_sp); } uint32_t InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, Target &target) const { size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; size_t i; for (i = start; i < num_instructions; i++) { if (m_instructions[i]->DoesBranch()) { next_branch = i; break; } } // Hexagon needs the first instruction of the packet with the branch. // Go backwards until we find an instruction marked end-of-packet, or // until we hit start. if (target.GetArchitecture().GetTriple().getArch() == llvm::Triple::hexagon) { // If we didn't find a branch, find the last packet start. if (next_branch == UINT32_MAX) { i = num_instructions - 1; } while (i > start) { --i; Error error; uint32_t inst_bytes; bool prefer_file_cache = false; // Read from process if process is running lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; target.ReadMemory(m_instructions[i]->GetAddress(), prefer_file_cache, &inst_bytes, sizeof(inst_bytes), error, &load_addr); // If we have an error reading memory, return start if (!error.Success()) return start; // check if this is the last instruction in a packet // bits 15:14 will be 11b or 00b for a duplex if (((inst_bytes & 0xC000) == 0xC000) || ((inst_bytes & 0xC000) == 0x0000)) { // instruction after this should be the start of next packet next_branch = i + 1; break; } } if (next_branch == UINT32_MAX) { // We couldn't find the previous packet, so return start next_branch = start; } } return next_branch; } uint32_t InstructionList::GetIndexOfInstructionAtAddress(const Address &address) { size_t num_instructions = m_instructions.size(); uint32_t index = UINT32_MAX; for (size_t i = 0; i < num_instructions; i++) { if (m_instructions[i]->GetAddress() == address) { index = i; break; } } return index; } uint32_t InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, Target &target) { Address address; address.SetLoadAddress(load_addr, &target); return GetIndexOfInstructionAtAddress(address); } size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx, const AddressRange &range, Stream *error_strm_ptr, bool prefer_file_cache) { if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); const addr_t byte_size = range.GetByteSize(); if (target == nullptr || byte_size == 0 || !range.GetBaseAddress().IsValid()) return 0; auto data_sp = std::make_shared(byte_size, '\0'); Error error; lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; const size_t bytes_read = target->ReadMemory( range.GetBaseAddress(), prefer_file_cache, data_sp->GetBytes(), data_sp->GetByteSize(), error, &load_addr); if (bytes_read > 0) { if (bytes_read != data_sp->GetByteSize()) data_sp->SetByteSize(bytes_read); DataExtractor data(data_sp, m_arch.GetByteOrder(), m_arch.GetAddressByteSize()); const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; return DecodeInstructions(range.GetBaseAddress(), data, 0, UINT32_MAX, false, data_from_file); } else if (error_strm_ptr) { const char *error_cstr = error.AsCString(); if (error_cstr) { error_strm_ptr->Printf("error: %s\n", error_cstr); } } } else if (error_strm_ptr) { error_strm_ptr->PutCString("error: invalid execution context\n"); } return 0; } size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx, const Address &start, uint32_t num_instructions, bool prefer_file_cache) { m_instruction_list.Clear(); if (exe_ctx == nullptr || num_instructions == 0 || !start.IsValid()) return 0; Target *target = exe_ctx->GetTargetPtr(); // Calculate the max buffer size we will need in order to disassemble const addr_t byte_size = num_instructions * m_arch.GetMaximumOpcodeByteSize(); if (target == nullptr || byte_size == 0) return 0; DataBufferHeap *heap_buffer = new DataBufferHeap(byte_size, '\0'); DataBufferSP data_sp(heap_buffer); Error error; lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; const size_t bytes_read = target->ReadMemory(start, prefer_file_cache, heap_buffer->GetBytes(), byte_size, error, &load_addr); const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; if (bytes_read == 0) return 0; DataExtractor data(data_sp, m_arch.GetByteOrder(), m_arch.GetAddressByteSize()); const bool append_instructions = true; DecodeInstructions(start, data, 0, num_instructions, append_instructions, data_from_file); return m_instruction_list.GetSize(); } //---------------------------------------------------------------------- // Disassembler copy constructor //---------------------------------------------------------------------- Disassembler::Disassembler(const ArchSpec &arch, const char *flavor) : m_arch(arch), m_instruction_list(), m_base_addr(LLDB_INVALID_ADDRESS), m_flavor() { if (flavor == nullptr) m_flavor.assign("default"); else m_flavor.assign(flavor); // If this is an arm variant that can only include thumb (T16, T32) // instructions, force the arch triple to be "thumbv.." instead of // "armv..." if (arch.IsAlwaysThumbInstructions()) { std::string thumb_arch_name(arch.GetTriple().getArchName().str()); // Replace "arm" with "thumb" so we get all thumb variants correct if (thumb_arch_name.size() > 3) { thumb_arch_name.erase(0, 3); thumb_arch_name.insert(0, "thumb"); } m_arch.SetTriple(thumb_arch_name.c_str()); } } Disassembler::~Disassembler() = default; InstructionList &Disassembler::GetInstructionList() { return m_instruction_list; } const InstructionList &Disassembler::GetInstructionList() const { return m_instruction_list; } //---------------------------------------------------------------------- // Class PseudoInstruction //---------------------------------------------------------------------- PseudoInstruction::PseudoInstruction() : Instruction(Address(), eAddressClassUnknown), m_description() {} PseudoInstruction::~PseudoInstruction() = default; bool PseudoInstruction::DoesBranch() { // This is NOT a valid question for a pseudo instruction. return false; } bool PseudoInstruction::HasDelaySlot() { // This is NOT a valid question for a pseudo instruction. return false; } size_t PseudoInstruction::Decode(const lldb_private::Disassembler &disassembler, const lldb_private::DataExtractor &data, lldb::offset_t data_offset) { return m_opcode.GetByteSize(); } void PseudoInstruction::SetOpcode(size_t opcode_size, void *opcode_data) { if (!opcode_data) return; switch (opcode_size) { case 8: { uint8_t value8 = *((uint8_t *)opcode_data); m_opcode.SetOpcode8(value8, eByteOrderInvalid); break; } case 16: { uint16_t value16 = *((uint16_t *)opcode_data); m_opcode.SetOpcode16(value16, eByteOrderInvalid); break; } case 32: { uint32_t value32 = *((uint32_t *)opcode_data); m_opcode.SetOpcode32(value32, eByteOrderInvalid); break; } case 64: { uint64_t value64 = *((uint64_t *)opcode_data); m_opcode.SetOpcode64(value64, eByteOrderInvalid); break; } default: break; } } void PseudoInstruction::SetDescription(llvm::StringRef description) { m_description = description; } Instruction::Operand Instruction::Operand::BuildRegister(ConstString &r) { Operand ret; ret.m_type = Type::Register; ret.m_register = r; return ret; } Instruction::Operand Instruction::Operand::BuildImmediate(lldb::addr_t imm, bool neg) { Operand ret; ret.m_type = Type::Immediate; ret.m_immediate = imm; ret.m_negative = neg; return ret; } Instruction::Operand Instruction::Operand::BuildImmediate(int64_t imm) { Operand ret; ret.m_type = Type::Immediate; if (imm < 0) { ret.m_immediate = -imm; ret.m_negative = true; } else { ret.m_immediate = imm; ret.m_negative = false; } return ret; } Instruction::Operand Instruction::Operand::BuildDereference(const Operand &ref) { Operand ret; ret.m_type = Type::Dereference; ret.m_children = {ref}; return ret; } Instruction::Operand Instruction::Operand::BuildSum(const Operand &lhs, const Operand &rhs) { Operand ret; ret.m_type = Type::Sum; ret.m_children = {lhs, rhs}; return ret; } Instruction::Operand Instruction::Operand::BuildProduct(const Operand &lhs, const Operand &rhs) { Operand ret; ret.m_type = Type::Product; ret.m_children = {lhs, rhs}; return ret; } std::function lldb_private::OperandMatchers::MatchBinaryOp( std::function base, std::function left, std::function right) { return [base, left, right](const Instruction::Operand &op) -> bool { return (base(op) && op.m_children.size() == 2 && ((left(op.m_children[0]) && right(op.m_children[1])) || (left(op.m_children[1]) && right(op.m_children[0])))); }; } std::function lldb_private::OperandMatchers::MatchUnaryOp( std::function base, std::function child) { return [base, child](const Instruction::Operand &op) -> bool { return (base(op) && op.m_children.size() == 1 && child(op.m_children[0])); }; } std::function lldb_private::OperandMatchers::MatchRegOp(const RegisterInfo &info) { return [&info](const Instruction::Operand &op) { return (op.m_type == Instruction::Operand::Type::Register && (op.m_register == ConstString(info.name) || op.m_register == ConstString(info.alt_name))); }; } std::function lldb_private::OperandMatchers::FetchRegOp(ConstString ®) { return [®](const Instruction::Operand &op) { if (op.m_type != Instruction::Operand::Type::Register) { return false; } reg = op.m_register; return true; }; } std::function lldb_private::OperandMatchers::MatchImmOp(int64_t imm) { return [imm](const Instruction::Operand &op) { return (op.m_type == Instruction::Operand::Type::Immediate && ((op.m_negative && op.m_immediate == (uint64_t)-imm) || (!op.m_negative && op.m_immediate == (uint64_t)imm))); }; } std::function lldb_private::OperandMatchers::FetchImmOp(int64_t &imm) { return [&imm](const Instruction::Operand &op) { if (op.m_type != Instruction::Operand::Type::Immediate) { return false; } if (op.m_negative) { imm = -((int64_t)op.m_immediate); } else { imm = ((int64_t)op.m_immediate); } return true; }; } std::function lldb_private::OperandMatchers::MatchOpType(Instruction::Operand::Type type) { return [type](const Instruction::Operand &op) { return op.m_type == type; }; } Index: vendor/lldb/dist/source/Host/common/Editline.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Editline.cpp (revision 317958) +++ vendor/lldb/dist/source/Host/common/Editline.cpp (revision 317959) @@ -1,1397 +1,1397 @@ //===-- Editline.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Editline.h" #include "lldb/Host/Host.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/SelectHelper.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringList.h" #include "lldb/Utility/Timeout.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" using namespace lldb_private; using namespace lldb_private::line_editor; // Workaround for what looks like an OS X-specific issue, but other platforms // may benefit from something similar if issues arise. The libedit library // doesn't explicitly initialize the curses termcap library, which it gets away // with until TERM is set to VT100 where it stumbles over an implementation // assumption that may not exist on other platforms. The setupterm() function // would normally require headers that don't work gracefully in this context, so // the function declaraction has been hoisted here. #if defined(__APPLE__) extern "C" { int setupterm(char *term, int fildes, int *errret); } #define USE_SETUPTERM_WORKAROUND #endif // Editline uses careful cursor management to achieve the illusion of editing a // multi-line block of text // with a single line editor. Preserving this illusion requires fairly careful // management of cursor // state. Read and understand the relationship between DisplayInput(), // MoveCursor(), SetCurrentLine(), // and SaveEditedLine() before making changes. #define ESCAPE "\x1b" #define ANSI_FAINT ESCAPE "[2m" #define ANSI_UNFAINT ESCAPE "[22m" #define ANSI_CLEAR_BELOW ESCAPE "[J" #define ANSI_CLEAR_RIGHT ESCAPE "[K" #define ANSI_SET_COLUMN_N ESCAPE "[%dG" #define ANSI_UP_N_ROWS ESCAPE "[%dA" #define ANSI_DOWN_N_ROWS ESCAPE "[%dB" #if LLDB_EDITLINE_USE_WCHAR #define EditLineConstString(str) L##str #define EditLineStringFormatSpec "%ls" #else #define EditLineConstString(str) str #define EditLineStringFormatSpec "%s" // use #defines so wide version functions and structs will resolve to old // versions // for case of libedit not built with wide char support #define history_w history #define history_winit history_init #define history_wend history_end #define HistoryW History #define HistEventW HistEvent #define LineInfoW LineInfo #define el_wgets el_gets #define el_wgetc el_getc #define el_wpush el_push #define el_wparse el_parse #define el_wset el_set #define el_wget el_get #define el_wline el_line #define el_winsertstr el_insertstr #define el_wdeletestr el_deletestr #endif // #if LLDB_EDITLINE_USE_WCHAR bool IsOnlySpaces(const EditLineStringType &content) { for (wchar_t ch : content) { if (ch != EditLineCharType(' ')) return false; } return true; } EditLineStringType CombineLines(const std::vector &lines) { EditLineStringStreamType combined_stream; for (EditLineStringType line : lines) { combined_stream << line.c_str() << "\n"; } return combined_stream.str(); } std::vector SplitLines(const EditLineStringType &input) { std::vector result; size_t start = 0; while (start < input.length()) { size_t end = input.find('\n', start); if (end == std::string::npos) { result.insert(result.end(), input.substr(start)); break; } result.insert(result.end(), input.substr(start, end - start)); start = end + 1; } return result; } EditLineStringType FixIndentation(const EditLineStringType &line, int indent_correction) { if (indent_correction == 0) return line; if (indent_correction < 0) return line.substr(-indent_correction); return EditLineStringType(indent_correction, EditLineCharType(' ')) + line; } int GetIndentation(const EditLineStringType &line) { int space_count = 0; for (EditLineCharType ch : line) { if (ch != EditLineCharType(' ')) break; ++space_count; } return space_count; } bool IsInputPending(FILE *file) { // FIXME: This will be broken on Windows if we ever re-enable Editline. You // can't use select // on something that isn't a socket. This will have to be re-written to not // use a FILE*, but // instead use some kind of yet-to-be-created abstraction that select-like // functionality on // non-socket objects. const int fd = fileno(file); SelectHelper select_helper; select_helper.SetTimeout(std::chrono::microseconds(0)); select_helper.FDSetRead(fd); return select_helper.Select().Success(); } namespace lldb_private { namespace line_editor { typedef std::weak_ptr EditlineHistoryWP; // EditlineHistory objects are sometimes shared between multiple // Editline instances with the same program name. class EditlineHistory { private: // Use static GetHistory() function to get a EditlineHistorySP to one of these // objects EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) : m_history(NULL), m_event(), m_prefix(prefix), m_path() { m_history = history_winit(); history_w(m_history, &m_event, H_SETSIZE, size); if (unique_entries) history_w(m_history, &m_event, H_SETUNIQUE, 1); } const char *GetHistoryFilePath() { if (m_path.empty() && m_history && !m_prefix.empty()) { FileSpec parent_path{"~/.lldb", true}; char history_path[PATH_MAX]; if (!llvm::sys::fs::create_directory(parent_path.GetPath())) { snprintf(history_path, sizeof(history_path), "~/.lldb/%s-history", m_prefix.c_str()); } else { snprintf(history_path, sizeof(history_path), "~/%s-widehistory", m_prefix.c_str()); } m_path = FileSpec(history_path, true).GetPath(); } if (m_path.empty()) return NULL; return m_path.c_str(); } public: ~EditlineHistory() { Save(); if (m_history) { history_wend(m_history); m_history = NULL; } } static EditlineHistorySP GetHistory(const std::string &prefix) { typedef std::map WeakHistoryMap; static std::recursive_mutex g_mutex; static WeakHistoryMap g_weak_map; std::lock_guard guard(g_mutex); WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix); EditlineHistorySP history_sp; if (pos != g_weak_map.end()) { history_sp = pos->second.lock(); if (history_sp) return history_sp; g_weak_map.erase(pos); } history_sp.reset(new EditlineHistory(prefix, 800, true)); g_weak_map[prefix] = history_sp; return history_sp; } bool IsValid() const { return m_history != NULL; } HistoryW *GetHistoryPtr() { return m_history; } void Enter(const EditLineCharType *line_cstr) { if (m_history) history_w(m_history, &m_event, H_ENTER, line_cstr); } bool Load() { if (m_history) { const char *path = GetHistoryFilePath(); if (path) { history_w(m_history, &m_event, H_LOAD, path); return true; } } return false; } bool Save() { if (m_history) { const char *path = GetHistoryFilePath(); if (path) { history_w(m_history, &m_event, H_SAVE, path); return true; } } return false; } protected: HistoryW *m_history; // The history object HistEventW m_event; // The history event needed to contain all history events std::string m_prefix; // The prefix name (usually the editline program name) // to use when loading/saving history std::string m_path; // Path to the history file }; } } //------------------------------------------------------------------ // Editline private methods //------------------------------------------------------------------ void Editline::SetBaseLineNumber(int line_number) { std::stringstream line_number_stream; line_number_stream << line_number; m_base_line_number = line_number; m_line_number_digits = std::max(3, (int)line_number_stream.str().length() + 1); } std::string Editline::PromptForIndex(int line_index) { bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; std::string prompt = m_set_prompt; if (use_line_numbers && prompt.length() == 0) { prompt = ": "; } std::string continuation_prompt = prompt; if (m_set_continuation_prompt.length() > 0) { continuation_prompt = m_set_continuation_prompt; // Ensure that both prompts are the same length through space padding while (continuation_prompt.length() < prompt.length()) { continuation_prompt += ' '; } while (prompt.length() < continuation_prompt.length()) { prompt += ' '; } } if (use_line_numbers) { StreamString prompt_stream; prompt_stream.Printf( "%*d%s", m_line_number_digits, m_base_line_number + line_index, (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); return std::move(prompt_stream.GetString()); } return (line_index == 0) ? prompt : continuation_prompt; } void Editline::SetCurrentLine(int line_index) { m_current_line_index = line_index; m_current_prompt = PromptForIndex(line_index); } int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); } bool Editline::IsEmacs() { const char *editor; el_get(m_editline, EL_EDITOR, &editor); return editor[0] == 'e'; } bool Editline::IsOnlySpaces() { const LineInfoW *info = el_wline(m_editline); for (const EditLineCharType *character = info->buffer; character < info->lastchar; character++) { if (*character != ' ') return false; } return true; } int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) { int line = 0; if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd || location == CursorLocation::EditingCursor) { for (unsigned index = 0; index < m_current_line_index; index++) { line += CountRowsForLine(m_input_lines[index]); } if (location == CursorLocation::EditingCursor) { line += cursor_row; } else if (location == CursorLocation::BlockEnd) { for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++) { line += CountRowsForLine(m_input_lines[index]); } --line; } } return line; } void Editline::MoveCursor(CursorLocation from, CursorLocation to) { const LineInfoW *info = el_wline(m_editline); int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); int editline_cursor_row = editline_cursor_position / m_terminal_width; // Determine relative starting and ending lines int fromLine = GetLineIndexForLocation(from, editline_cursor_row); int toLine = GetLineIndexForLocation(to, editline_cursor_row); if (toLine != fromLine) { fprintf(m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs(toLine - fromLine)); } // Determine target column int toColumn = 1; if (to == CursorLocation::EditingCursor) { toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; - } else if (to == CursorLocation::BlockEnd) { + } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) { toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1; } fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn); } void Editline::DisplayInput(int firstIndex) { fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); int line_count = (int)m_input_lines.size(); const char *faint = m_color_prompts ? ANSI_FAINT : ""; const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; for (int index = firstIndex; index < line_count; index++) { fprintf(m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ", faint, PromptForIndex(index).c_str(), unfaint, m_input_lines[index].c_str()); if (index < line_count - 1) fprintf(m_output_file, "\n"); } } int Editline::CountRowsForLine(const EditLineStringType &content) { auto prompt = PromptForIndex(0); // Prompt width is constant during an edit session int line_length = (int)(content.length() + prompt.length()); return (line_length / m_terminal_width) + 1; } void Editline::SaveEditedLine() { const LineInfoW *info = el_wline(m_editline); m_input_lines[m_current_line_index] = EditLineStringType(info->buffer, info->lastchar - info->buffer); } StringList Editline::GetInputAsStringList(int line_count) { StringList lines; for (EditLineStringType line : m_input_lines) { if (line_count == 0) break; #if LLDB_EDITLINE_USE_WCHAR lines.AppendString(m_utf8conv.to_bytes(line)); #else lines.AppendString(line); #endif --line_count; } return lines; } unsigned char Editline::RecallHistory(bool earlier) { if (!m_history_sp || !m_history_sp->IsValid()) return CC_ERROR; HistoryW *pHistory = m_history_sp->GetHistoryPtr(); HistEventW history_event; std::vector new_input_lines; // Treat moving from the "live" entry differently if (!m_in_history) { if (earlier == false) return CC_ERROR; // Can't go newer than the "live" entry if (history_w(pHistory, &history_event, H_FIRST) == -1) return CC_ERROR; // Save any edits to the "live" entry in case we return by moving forward in // history // (it would be more bash-like to save over any current entry, but libedit // doesn't // offer the ability to add entries anywhere except the end.) SaveEditedLine(); m_live_history_lines = m_input_lines; m_in_history = true; } else { if (history_w(pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) { // Can't move earlier than the earliest entry if (earlier) return CC_ERROR; // ... but moving to newer than the newest yields the "live" entry new_input_lines = m_live_history_lines; m_in_history = false; } } // If we're pulling the lines from history, split them apart if (m_in_history) new_input_lines = SplitLines(history_event.str); // Erase the current edit session and replace it with a new one MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); m_input_lines = new_input_lines; DisplayInput(); // Prepare to edit the last line when moving to previous entry, or the first // line // when moving to next entry SetCurrentLine(m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } int Editline::GetCharacter(EditLineCharType *c) { const LineInfoW *info = el_wline(m_editline); // Paint a faint version of the desired prompt over the version libedit draws // (will only be requested if colors are supported) if (m_needs_prompt_repaint) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); fprintf(m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT); MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor); m_needs_prompt_repaint = false; } if (m_multiline_enabled) { // Detect when the number of rows used for this input line changes due to an // edit int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); int new_line_rows = (lineLength / m_terminal_width) + 1; if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) { // Respond by repainting the current state from this line on MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); SaveEditedLine(); DisplayInput(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } m_current_line_rows = new_line_rows; } // Read an actual character while (true) { lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; char ch = 0; // This mutex is locked by our caller (GetLine). Unlock it while we read a // character // (blocking operation), so we do not hold the mutex indefinitely. This // gives a chance // for someone to interrupt us. After Read returns, immediately lock the // mutex again and // check if we were interrupted. m_output_mutex.unlock(); int read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); m_output_mutex.lock(); if (m_editor_status == EditorStatus::Interrupted) { while (read_count > 0 && status == lldb::eConnectionStatusSuccess) read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); lldbassert(status == lldb::eConnectionStatusInterrupted); return 0; } if (read_count) { if (CompleteCharacter(ch, *c)) return 1; } else { switch (status) { case lldb::eConnectionStatusSuccess: // Success break; case lldb::eConnectionStatusInterrupted: lldbassert(0 && "Interrupts should have been handled above."); case lldb::eConnectionStatusError: // Check GetError() for details case lldb::eConnectionStatusTimedOut: // Request timed out case lldb::eConnectionStatusEndOfFile: // End-of-file encountered case lldb::eConnectionStatusNoConnection: // No connection case lldb::eConnectionStatusLostConnection: // Lost connection while // connected to a valid // connection m_editor_status = EditorStatus::EndOfInput; return 0; } } } } const char *Editline::Prompt() { if (m_color_prompts) m_needs_prompt_repaint = true; return m_current_prompt.c_str(); } unsigned char Editline::BreakLineCommand(int ch) { // Preserve any content beyond the cursor, truncate and save the current line const LineInfoW *info = el_wline(m_editline); auto current_line = EditLineStringType(info->buffer, info->cursor - info->buffer); auto new_line_fragment = EditLineStringType(info->cursor, info->lastchar - info->cursor); m_input_lines[m_current_line_index] = current_line; // Ignore whitespace-only extra fragments when breaking a line if (::IsOnlySpaces(new_line_fragment)) new_line_fragment = EditLineConstString(""); // Establish the new cursor position at the start of a line when inserting a // line break m_revert_cursor_index = 0; // Don't perform automatic formatting when pasting if (!IsInputPending(m_input_file)) { // Apply smart indentation if (m_fix_indentation_callback) { StringList lines = GetInputAsStringList(m_current_line_index + 1); #if LLDB_EDITLINE_USE_WCHAR lines.AppendString(m_utf8conv.to_bytes(new_line_fragment)); #else lines.AppendString(new_line_fragment); #endif int indent_correction = m_fix_indentation_callback( this, lines, 0, m_fix_indentation_callback_baton); new_line_fragment = FixIndentation(new_line_fragment, indent_correction); m_revert_cursor_index = GetIndentation(new_line_fragment); } } // Insert the new line and repaint everything from the split line on down m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); DisplayInput(m_current_line_index); // Reposition the cursor to the right line and prepare to edit the new line SetCurrentLine(m_current_line_index + 1); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } unsigned char Editline::EndOrAddLineCommand(int ch) { // Don't perform end of input detection when pasting, always treat this as a // line break if (IsInputPending(m_input_file)) { return BreakLineCommand(ch); } // Save any edits to this line SaveEditedLine(); // If this is the end of the last line, consider whether to add a line instead const LineInfoW *info = el_wline(m_editline); if (m_current_line_index == m_input_lines.size() - 1 && info->cursor == info->lastchar) { if (m_is_input_complete_callback) { auto lines = GetInputAsStringList(); if (!m_is_input_complete_callback(this, lines, m_is_input_complete_callback_baton)) { return BreakLineCommand(ch); } // The completion test is allowed to change the input lines when complete m_input_lines.clear(); for (unsigned index = 0; index < lines.GetSize(); index++) { #if LLDB_EDITLINE_USE_WCHAR m_input_lines.insert(m_input_lines.end(), m_utf8conv.from_bytes(lines[index])); #else m_input_lines.insert(m_input_lines.end(), lines[index]); #endif } } } MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); fprintf(m_output_file, "\n"); m_editor_status = EditorStatus::Complete; return CC_NEWLINE; } unsigned char Editline::DeleteNextCharCommand(int ch) { LineInfoW *info = const_cast(el_wline(m_editline)); // Just delete the next character normally if possible if (info->cursor < info->lastchar) { info->cursor++; el_deletestr(m_editline, 1); return CC_REFRESH; } // Fail when at the end of the last line, except when ^D is pressed on // the line is empty, in which case it is treated as EOF if (m_current_line_index == m_input_lines.size() - 1) { if (ch == 4 && info->buffer == info->lastchar) { fprintf(m_output_file, "^D\n"); m_editor_status = EditorStatus::EndOfInput; return CC_EOF; } return CC_ERROR; } // Prepare to combine this line with the one below MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); // Insert the next line of text at the cursor and restore the cursor position const EditLineCharType *cursor = info->cursor; el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str()); info->cursor = cursor; SaveEditedLine(); // Delete the extra line m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1); // Clear and repaint from this line on down DisplayInput(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); return CC_REFRESH; } unsigned char Editline::DeletePreviousCharCommand(int ch) { LineInfoW *info = const_cast(el_wline(m_editline)); // Just delete the previous character normally when not at the start of a line if (info->cursor > info->buffer) { el_deletestr(m_editline, 1); return CC_REFRESH; } // No prior line and no prior character? Let the user know if (m_current_line_index == 0) return CC_ERROR; // No prior character, but prior line? Combine with the line above SaveEditedLine(); SetCurrentLine(m_current_line_index - 1); auto priorLine = m_input_lines[m_current_line_index]; m_input_lines.erase(m_input_lines.begin() + m_current_line_index); m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; // Repaint from the new line down fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine(priorLine), 1); DisplayInput(m_current_line_index); // Put the cursor back where libedit expects it to be before returning to // editing // by telling libedit about the newly inserted text MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); el_winsertstr(m_editline, priorLine.c_str()); return CC_REDISPLAY; } unsigned char Editline::PreviousLineCommand(int ch) { SaveEditedLine(); if (m_current_line_index == 0) { return RecallHistory(true); } // Start from a known location MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); // Treat moving up from a blank last line as a deletion of that line if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) { m_input_lines.erase(m_input_lines.begin() + m_current_line_index); fprintf(m_output_file, ANSI_CLEAR_BELOW); } SetCurrentLine(m_current_line_index - 1); fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine(m_input_lines[m_current_line_index]), 1); return CC_NEWLINE; } unsigned char Editline::NextLineCommand(int ch) { SaveEditedLine(); // Handle attempts to move down from the last line if (m_current_line_index == m_input_lines.size() - 1) { // Don't add an extra line if the existing last line is blank, move through // history instead if (IsOnlySpaces()) { return RecallHistory(false); } // Determine indentation for the new line int indentation = 0; if (m_fix_indentation_callback) { StringList lines = GetInputAsStringList(); lines.AppendString(""); indentation = m_fix_indentation_callback( this, lines, 0, m_fix_indentation_callback_baton); } m_input_lines.insert( m_input_lines.end(), EditLineStringType(indentation, EditLineCharType(' '))); } // Move down past the current line using newlines to force scrolling if needed SetCurrentLine(m_current_line_index + 1); const LineInfoW *info = el_wline(m_editline); int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); int cursor_row = cursor_position / m_terminal_width; for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) { fprintf(m_output_file, "\n"); } return CC_NEWLINE; } unsigned char Editline::PreviousHistoryCommand(int ch) { SaveEditedLine(); return RecallHistory(true); } unsigned char Editline::NextHistoryCommand(int ch) { SaveEditedLine(); return RecallHistory(false); } unsigned char Editline::FixIndentationCommand(int ch) { if (!m_fix_indentation_callback) return CC_NORM; // Insert the character typed before proceeding EditLineCharType inserted[] = {(EditLineCharType)ch, 0}; el_winsertstr(m_editline, inserted); LineInfoW *info = const_cast(el_wline(m_editline)); int cursor_position = info->cursor - info->buffer; // Save the edits and determine the correct indentation level SaveEditedLine(); StringList lines = GetInputAsStringList(m_current_line_index + 1); int indent_correction = m_fix_indentation_callback( this, lines, cursor_position, m_fix_indentation_callback_baton); // If it is already correct no special work is needed if (indent_correction == 0) return CC_REFRESH; // Change the indentation level of the line std::string currentLine = lines.GetStringAtIndex(m_current_line_index); if (indent_correction > 0) { currentLine = currentLine.insert(0, indent_correction, ' '); } else { currentLine = currentLine.erase(0, -indent_correction); } #if LLDB_EDITLINE_USE_WCHAR m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine); #else m_input_lines[m_current_line_index] = currentLine; #endif // Update the display to reflect the change MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); DisplayInput(m_current_line_index); // Reposition the cursor back on the original line and prepare to restart // editing // with a new cursor position SetCurrentLine(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); m_revert_cursor_index = cursor_position + indent_correction; return CC_NEWLINE; } unsigned char Editline::RevertLineCommand(int ch) { el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str()); if (m_revert_cursor_index >= 0) { LineInfoW *info = const_cast(el_wline(m_editline)); info->cursor = info->buffer + m_revert_cursor_index; if (info->cursor > info->lastchar) { info->cursor = info->lastchar; } m_revert_cursor_index = -1; } return CC_REFRESH; } unsigned char Editline::BufferStartCommand(int ch) { SaveEditedLine(); MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); SetCurrentLine(0); m_revert_cursor_index = 0; return CC_NEWLINE; } unsigned char Editline::BufferEndCommand(int ch) { SaveEditedLine(); MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); SetCurrentLine((int)m_input_lines.size() - 1); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } unsigned char Editline::TabCommand(int ch) { if (m_completion_callback == nullptr) return CC_ERROR; const LineInfo *line_info = el_line(m_editline); StringList completions; int page_size = 40; const int num_completions = m_completion_callback( line_info->buffer, line_info->cursor, line_info->lastchar, 0, // Don't skip any matches (start at match zero) -1, // Get all the matches completions, m_completion_callback_baton); if (num_completions == 0) return CC_ERROR; // if (num_completions == -1) // { // el_insertstr (m_editline, m_completion_key); // return CC_REDISPLAY; // } // else if (num_completions == -2) { // Replace the entire line with the first string... el_deletestr(m_editline, line_info->cursor - line_info->buffer); el_insertstr(m_editline, completions.GetStringAtIndex(0)); return CC_REDISPLAY; } // If we get a longer match display that first. const char *completion_str = completions.GetStringAtIndex(0); if (completion_str != nullptr && *completion_str != '\0') { el_insertstr(m_editline, completion_str); return CC_REDISPLAY; } if (num_completions > 1) { int num_elements = num_completions + 1; fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); if (num_completions < page_size) { for (int i = 1; i < num_elements; i++) { completion_str = completions.GetStringAtIndex(i); fprintf(m_output_file, "\n\t%s", completion_str); } fprintf(m_output_file, "\n"); } else { int cur_pos = 1; char reply; int got_char; while (cur_pos < num_elements) { int endpoint = cur_pos + page_size; if (endpoint > num_elements) endpoint = num_elements; for (; cur_pos < endpoint; cur_pos++) { completion_str = completions.GetStringAtIndex(cur_pos); fprintf(m_output_file, "\n\t%s", completion_str); } if (cur_pos >= num_elements) { fprintf(m_output_file, "\n"); break; } fprintf(m_output_file, "\nMore (Y/n/a): "); reply = 'n'; got_char = el_getc(m_editline, &reply); if (got_char == -1 || reply == 'n') break; if (reply == 'a') page_size = num_elements - cur_pos; } } DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } return CC_REDISPLAY; } void Editline::ConfigureEditor(bool multiline) { if (m_editline && m_multiline_enabled == multiline) return; m_multiline_enabled = multiline; if (m_editline) { // Disable edit mode to stop the terminal from flushing all input // during the call to el_end() since we expect to have multiple editline // instances in this program. el_set(m_editline, EL_EDITMODE, 0); el_end(m_editline); } m_editline = el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); TerminalSizeChanged(); if (m_history_sp && m_history_sp->IsValid()) { m_history_sp->Load(); el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); } el_set(m_editline, EL_CLIENTDATA, this); el_set(m_editline, EL_SIGNAL, 0); el_set(m_editline, EL_EDITOR, "emacs"); el_set(m_editline, EL_PROMPT, (EditlinePromptCallbackType)([](EditLine *editline) { return Editline::InstanceFor(editline)->Prompt(); })); el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([]( EditLine *editline, EditLineCharType *c) { return Editline::InstanceFor(editline)->GetCharacter(c); })); // Commands used for multiline support, registered whether or not they're used el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), EditLineConstString("Insert a line break"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BreakLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"), EditLineConstString("End editing or continue when incomplete"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), EditLineConstString("Delete next character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); })); el_wset( m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), EditLineConstString("Delete previous character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), EditLineConstString("Move to previous line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->PreviousLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), EditLineConstString("Move to next line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"), EditLineConstString("Move to previous history"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), EditLineConstString("Move to next history"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextHistoryCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), EditLineConstString("Move to start of buffer"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferStartCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), EditLineConstString("Move to end of buffer"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferEndCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), EditLineConstString("Fix line indentation"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->FixIndentationCommand(ch); })); // Register the complete callback under two names for compatibility with older // clients using // custom .editrc files (largely because libedit has a bad bug where if you // have a bind command // that tries to bind to a function name that doesn't exist, it can corrupt // the heap and // crash your process later.) EditlineCommandCallbackType complete_callback = [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->TabCommand(ch); }; el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), EditLineConstString("Invoke completion"), complete_callback); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), EditLineConstString("Invoke completion"), complete_callback); // General bindings we don't mind being overridden if (!multiline) { el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string } el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode el_set(m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete // Allow user-specific customization prior to registering bindings we // absolutely require el_source(m_editline, NULL); // Register an internal binding that external developers shouldn't use el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), EditLineConstString("Revert line to saved state"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->RevertLineCommand(ch); })); // Register keys that perform auto-indent correction if (m_fix_indentation_callback && m_fix_indentation_callback_chars) { char bind_key[2] = {0, 0}; const char *indent_chars = m_fix_indentation_callback_chars; while (*indent_chars) { bind_key[0] = *indent_chars; el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); ++indent_chars; } } // Multi-line editor bindings if (multiline) { el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL); el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL); el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); // Editor-specific bindings if (IsEmacs()) { el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", NULL); el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", NULL); el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", NULL); el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL); } else { el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); // Escape is absorbed exiting edit mode, so re-register important // sequences // without the prefix el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); } } } //------------------------------------------------------------------ // Editline public methods //------------------------------------------------------------------ Editline *Editline::InstanceFor(EditLine *editline) { Editline *editor; el_get(editline, EL_CLIENTDATA, &editor); return editor; } Editline::Editline(const char *editline_name, FILE *input_file, FILE *output_file, FILE *error_file, bool color_prompts) : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts), m_input_file(input_file), m_output_file(output_file), m_error_file(error_file), m_input_connection(fileno(input_file), false) { // Get a shared history instance m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; m_history_sp = EditlineHistory::GetHistory(m_editor_name); #ifdef USE_SETUPTERM_WORKAROUND if (m_output_file) { const int term_fd = fileno(m_output_file); if (term_fd != -1) { static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; static std::set *g_init_terminal_fds_ptr = nullptr; static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, [&]() { g_init_terminal_fds_mutex_ptr = new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues g_init_terminal_fds_ptr = new std::set(); // NOTE: Leak to avoid // C++ destructor chain // issues }); // We must make sure to initialize the terminal a given file descriptor // only once. If we do this multiple times, we start leaking memory. std::lock_guard guard(*g_init_terminal_fds_mutex_ptr); if (g_init_terminal_fds_ptr->find(term_fd) == g_init_terminal_fds_ptr->end()) { g_init_terminal_fds_ptr->insert(term_fd); setupterm((char *)0, term_fd, (int *)0); } } } #endif } Editline::~Editline() { if (m_editline) { // Disable edit mode to stop the terminal from flushing all input // during the call to el_end() since we expect to have multiple editline // instances in this program. el_set(m_editline, EL_EDITMODE, 0); el_end(m_editline); m_editline = nullptr; } // EditlineHistory objects are sometimes shared between multiple // Editline instances with the same program name. So just release // our shared pointer and if we are the last owner, it will save the // history to the history save file automatically. m_history_sp.reset(); } void Editline::SetPrompt(const char *prompt) { m_set_prompt = prompt == nullptr ? "" : prompt; } void Editline::SetContinuationPrompt(const char *continuation_prompt) { m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; } void Editline::TerminalSizeChanged() { if (m_editline != nullptr) { el_resize(m_editline); int columns; // Despite the man page claiming non-zero indicates success, it's actually // zero if (el_get(m_editline, EL_GETTC, "co", &columns) == 0) { m_terminal_width = columns; if (m_current_line_rows != -1) { const LineInfoW *info = el_wline(m_editline); int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); m_current_line_rows = (lineLength / columns) + 1; } } else { m_terminal_width = INT_MAX; m_current_line_rows = 1; } } } const char *Editline::GetPrompt() { return m_set_prompt.c_str(); } uint32_t Editline::GetCurrentLine() { return m_current_line_index; } bool Editline::Interrupt() { bool result = true; std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { fprintf(m_output_file, "^C\n"); result = m_input_connection.InterruptRead(); } m_editor_status = EditorStatus::Interrupted; return result; } bool Editline::Cancel() { bool result = true; std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); fprintf(m_output_file, ANSI_CLEAR_BELOW); result = m_input_connection.InterruptRead(); } m_editor_status = EditorStatus::Interrupted; return result; } void Editline::SetAutoCompleteCallback(CompleteCallbackType callback, void *baton) { m_completion_callback = callback; m_completion_callback_baton = baton; } void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, void *baton) { m_is_input_complete_callback = callback; m_is_input_complete_callback_baton = baton; } bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback, void *baton, const char *indent_chars) { m_fix_indentation_callback = callback; m_fix_indentation_callback_baton = baton; m_fix_indentation_callback_chars = indent_chars; return false; } bool Editline::GetLine(std::string &line, bool &interrupted) { ConfigureEditor(false); m_input_lines = std::vector(); m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); std::lock_guard guard(m_output_mutex); lldbassert(m_editor_status != EditorStatus::Editing); if (m_editor_status == EditorStatus::Interrupted) { m_editor_status = EditorStatus::Complete; interrupted = true; return true; } SetCurrentLine(0); m_in_history = false; m_editor_status = EditorStatus::Editing; m_revert_cursor_index = -1; int count; auto input = el_wgets(m_editline, &count); interrupted = m_editor_status == EditorStatus::Interrupted; if (!interrupted) { if (input == nullptr) { fprintf(m_output_file, "\n"); m_editor_status = EditorStatus::EndOfInput; } else { m_history_sp->Enter(input); #if LLDB_EDITLINE_USE_WCHAR line = m_utf8conv.to_bytes(SplitLines(input)[0]); #else line = SplitLines(input)[0]; #endif m_editor_status = EditorStatus::Complete; } } return m_editor_status != EditorStatus::EndOfInput; } bool Editline::GetLines(int first_line_number, StringList &lines, bool &interrupted) { ConfigureEditor(true); // Print the initial input lines, then move the cursor back up to the start of // input SetBaseLineNumber(first_line_number); m_input_lines = std::vector(); m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); std::lock_guard guard(m_output_mutex); // Begin the line editing loop DisplayInput(); SetCurrentLine(0); MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart); m_editor_status = EditorStatus::Editing; m_in_history = false; m_revert_cursor_index = -1; while (m_editor_status == EditorStatus::Editing) { int count; m_current_line_rows = -1; el_wpush(m_editline, EditLineConstString( "\x1b[^")); // Revert to the existing line content el_wgets(m_editline, &count); } interrupted = m_editor_status == EditorStatus::Interrupted; if (!interrupted) { // Save the completed entry in history before returning m_history_sp->Enter(CombineLines(m_input_lines).c_str()); lines = GetInputAsStringList(); } return m_editor_status != EditorStatus::EndOfInput; } void Editline::PrintAsync(Stream *stream, const char *s, size_t len) { std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); fprintf(m_output_file, ANSI_CLEAR_BELOW); } stream->Write(s, len); stream->Flush(); if (m_editor_status == EditorStatus::Editing) { DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } } bool Editline::CompleteCharacter(char ch, EditLineCharType &out) { #if !LLDB_EDITLINE_USE_WCHAR if (ch == (char)EOF) return false; out = ch; return true; #else std::codecvt_utf8 cvt; llvm::SmallString<4> input; for (;;) { const char *from_next; wchar_t *to_next; std::mbstate_t state = std::mbstate_t(); input.push_back(ch); switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1, to_next)) { case std::codecvt_base::ok: return out != WEOF; case std::codecvt_base::error: case std::codecvt_base::noconv: return false; case std::codecvt_base::partial: lldb::ConnectionStatus status; size_t read_count = m_input_connection.Read( &ch, 1, std::chrono::seconds(0), status, nullptr); if (read_count == 0) return false; break; } } #endif } Index: vendor/lldb/dist/source/Host/common/MainLoop.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/MainLoop.cpp (revision 317958) +++ vendor/lldb/dist/source/Host/common/MainLoop.cpp (revision 317959) @@ -1,382 +1,378 @@ //===-- MainLoop.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/Config/llvm-config.h" #include "lldb/Host/MainLoop.h" #include "lldb/Utility/Error.h" #include #include #include #include #include #include +// Multiplexing is implemented using kqueue on systems that support it (BSD +// variants including OSX). On linux we use ppoll, while android uses pselect +// (ppoll is present but not implemented properly). On windows we use WSApoll +// (which does not support signals). + #if HAVE_SYS_EVENT_H #include #elif defined(LLVM_ON_WIN32) #include #else #include #endif #ifdef LLVM_ON_WIN32 #define POLL WSAPoll #else #define POLL poll #endif #ifdef __ANDROID__ #define FORCE_PSELECT #endif #if SIGNAL_POLLING_UNSUPPORTED #ifdef LLVM_ON_WIN32 typedef int sigset_t; typedef int siginfo_t; #endif int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout_ts, const sigset_t *) { int timeout = (timeout_ts == nullptr) ? -1 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000); return POLL(fds, nfds, timeout); } #endif using namespace lldb; using namespace lldb_private; static sig_atomic_t g_signal_flags[NSIG]; static void SignalHandler(int signo, siginfo_t *info, void *) { assert(signo < NSIG); g_signal_flags[signo] = 1; } class MainLoop::RunImpl { public: - // TODO: Use llvm::Expected - static std::unique_ptr Create(MainLoop &loop, Error &error); - ~RunImpl(); + RunImpl(MainLoop &loop); + ~RunImpl() = default; Error Poll(); + void ProcessEvents(); - template void ForEachReadFD(F &&f); - template void ForEachSignal(F &&f); - private: MainLoop &loop; #if HAVE_SYS_EVENT_H - int queue_id; std::vector in_events; struct kevent out_events[4]; int num_events = -1; - RunImpl(MainLoop &loop, int queue_id) : loop(loop), queue_id(queue_id) { - in_events.reserve(loop.m_read_fds.size() + loop.m_signals.size()); - } #else - std::vector signals; #ifdef FORCE_PSELECT fd_set read_fd_set; #else std::vector read_fds; #endif - RunImpl(MainLoop &loop) : loop(loop) { - signals.reserve(loop.m_signals.size()); - } - sigset_t get_sigmask(); #endif }; #if HAVE_SYS_EVENT_H -MainLoop::RunImpl::~RunImpl() { - int r = close(queue_id); - assert(r == 0); - (void)r; +MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) { + in_events.reserve(loop.m_read_fds.size()); } -std::unique_ptr MainLoop::RunImpl::Create(MainLoop &loop, Error &error) -{ - error.Clear(); - int queue_id = kqueue(); - if(queue_id < 0) { - error = Error(errno, eErrorTypePOSIX); - return nullptr; - } - return std::unique_ptr(new RunImpl(loop, queue_id)); -} Error MainLoop::RunImpl::Poll() { - in_events.resize(loop.m_read_fds.size() + loop.m_signals.size()); + in_events.resize(loop.m_read_fds.size()); unsigned i = 0; for (auto &fd : loop.m_read_fds) EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0); - for (const auto &sig : loop.m_signals) - EV_SET(&in_events[i++], sig.first, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); + num_events = kevent(loop.m_kqueue, in_events.data(), in_events.size(), + out_events, llvm::array_lengthof(out_events), nullptr); - num_events = kevent(queue_id, in_events.data(), in_events.size(), out_events, - llvm::array_lengthof(out_events), nullptr); - if (num_events < 0) return Error("kevent() failed with error %d\n", num_events); return Error(); } -template void MainLoop::RunImpl::ForEachReadFD(F &&f) { +void MainLoop::RunImpl::ProcessEvents() { assert(num_events >= 0); for (int i = 0; i < num_events; ++i) { - f(out_events[i].ident); if (loop.m_terminate_request) return; + switch (out_events[i].filter) { + case EVFILT_READ: + loop.ProcessReadObject(out_events[i].ident); + break; + case EVFILT_SIGNAL: + loop.ProcessSignal(out_events[i].ident); + break; + default: + llvm_unreachable("Unknown event"); + } } } -template void MainLoop::RunImpl::ForEachSignal(F && f) {} #else -MainLoop::RunImpl::~RunImpl() {} -std::unique_ptr MainLoop::RunImpl::Create(MainLoop &loop, Error &error) -{ - error.Clear(); - return std::unique_ptr(new RunImpl(loop)); +MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) { +#ifndef FORCE_PSELECT + read_fds.reserve(loop.m_read_fds.size()); +#endif } sigset_t MainLoop::RunImpl::get_sigmask() { #if SIGNAL_POLLING_UNSUPPORTED return 0; #else sigset_t sigmask; int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask); assert(ret == 0); (void) ret; - for (const auto &sig : loop.m_signals) { - signals.push_back(sig.first); + for (const auto &sig : loop.m_signals) sigdelset(&sigmask, sig.first); - } return sigmask; #endif } #ifdef FORCE_PSELECT Error MainLoop::RunImpl::Poll() { - signals.clear(); - FD_ZERO(&read_fd_set); int nfds = 0; for (const auto &fd : loop.m_read_fds) { FD_SET(fd.first, &read_fd_set); nfds = std::max(nfds, fd.first + 1); } sigset_t sigmask = get_sigmask(); if (pselect(nfds, &read_fd_set, nullptr, nullptr, nullptr, &sigmask) == -1 && errno != EINTR) return Error(errno, eErrorTypePOSIX); return Error(); } - -template void MainLoop::RunImpl::ForEachReadFD(F &&f) { - for (const auto &fd : loop.m_read_fds) { - if(!FD_ISSET(fd.first, &read_fd_set)) - continue; - - f(fd.first); - if (loop.m_terminate_request) - return; - } -} #else Error MainLoop::RunImpl::Poll() { - signals.clear(); read_fds.clear(); sigset_t sigmask = get_sigmask(); for (const auto &fd : loop.m_read_fds) { struct pollfd pfd; pfd.fd = fd.first; pfd.events = POLLIN; pfd.revents = 0; read_fds.push_back(pfd); } if (ppoll(read_fds.data(), read_fds.size(), nullptr, &sigmask) == -1 && errno != EINTR) return Error(errno, eErrorTypePOSIX); return Error(); } +#endif -template void MainLoop::RunImpl::ForEachReadFD(F &&f) { +void MainLoop::RunImpl::ProcessEvents() { +#ifdef FORCE_PSELECT + for (const auto &fd : loop.m_read_fds) { + if (!FD_ISSET(fd.first, &read_fd_set)) + continue; + IOObject::WaitableHandle handle = fd.first; +#else for (const auto &fd : read_fds) { if ((fd.revents & POLLIN) == 0) continue; - - f(fd.fd); + IOObject::WaitableHandle handle = fd.fd; +#endif if (loop.m_terminate_request) return; + + loop.ProcessReadObject(handle); } -} -#endif -template void MainLoop::RunImpl::ForEachSignal(F &&f) { - for (int sig : signals) { - if (g_signal_flags[sig] == 0) - continue; // No signal - g_signal_flags[sig] = 0; - f(sig); - + for (const auto &entry : loop.m_signals) { if (loop.m_terminate_request) return; + if (g_signal_flags[entry.first] == 0) + continue; // No signal + g_signal_flags[entry.first] = 0; + loop.ProcessSignal(entry.first); } } #endif +MainLoop::MainLoop() { +#if HAVE_SYS_EVENT_H + m_kqueue = kqueue(); + assert(m_kqueue >= 0); +#endif +} MainLoop::~MainLoop() { +#if HAVE_SYS_EVENT_H + close(m_kqueue); +#endif assert(m_read_fds.size() == 0); assert(m_signals.size() == 0); } MainLoop::ReadHandleUP MainLoop::RegisterReadObject(const IOObjectSP &object_sp, const Callback &callback, Error &error) { #ifdef LLVM_ON_WIN32 if (object_sp->GetFdType() != IOObject:: eFDTypeSocket) { error.SetErrorString("MainLoop: non-socket types unsupported on Windows"); return nullptr; } #endif if (!object_sp || !object_sp->IsValid()) { error.SetErrorString("IO object is not valid."); return nullptr; } const bool inserted = m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second; if (!inserted) { error.SetErrorStringWithFormat("File descriptor %d already monitored.", object_sp->GetWaitableHandle()); return nullptr; } return CreateReadHandle(object_sp); } // We shall block the signal, then install the signal handler. The signal will // be unblocked in // the Run() function to check for signal delivery. MainLoop::SignalHandleUP MainLoop::RegisterSignal(int signo, const Callback &callback, Error &error) { #ifdef SIGNAL_POLLING_UNSUPPORTED error.SetErrorString("Signal polling is not supported on this platform."); return nullptr; #else if (m_signals.find(signo) != m_signals.end()) { error.SetErrorStringWithFormat("Signal %d already monitored.", signo); return nullptr; } SignalInfo info; info.callback = callback; struct sigaction new_action; new_action.sa_sigaction = &SignalHandler; new_action.sa_flags = SA_SIGINFO; sigemptyset(&new_action.sa_mask); sigaddset(&new_action.sa_mask, signo); - sigset_t old_set; - if (int ret = pthread_sigmask(SIG_BLOCK, &new_action.sa_mask, &old_set)) { - error.SetErrorStringWithFormat("pthread_sigmask failed with error %d\n", - ret); - return nullptr; - } - info.was_blocked = sigismember(&old_set, signo); - if (sigaction(signo, &new_action, &info.old_action) == -1) { - error.SetErrorToErrno(); - if (!info.was_blocked) - pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, nullptr); - return nullptr; - } + g_signal_flags[signo] = 0; + // Even if using kqueue, the signal handler will still be invoked, so it's + // important to replace it with our "bening" handler. + int ret = sigaction(signo, &new_action, &info.old_action); + assert(ret == 0 && "sigaction failed"); + +#if HAVE_SYS_EVENT_H + struct kevent ev; + EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); + ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr); + assert(ret == 0); +#endif + + // If we're using kqueue, the signal needs to be unblocked in order to recieve + // it. If using pselect/ppoll, we need to block it, and later unblock it as a + // part of the system call. + ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK, + &new_action.sa_mask, &old_set); + assert(ret == 0 && "pthread_sigmask failed"); + info.was_blocked = sigismember(&old_set, signo); m_signals.insert({signo, info}); - g_signal_flags[signo] = 0; return SignalHandleUP(new SignalHandle(*this, signo)); #endif } void MainLoop::UnregisterReadObject(IOObject::WaitableHandle handle) { bool erased = m_read_fds.erase(handle); UNUSED_IF_ASSERT_DISABLED(erased); assert(erased); } void MainLoop::UnregisterSignal(int signo) { #if SIGNAL_POLLING_UNSUPPORTED Error("Signal polling is not supported on this platform."); #else - // We undo the actions of RegisterSignal on a best-effort basis. auto it = m_signals.find(signo); assert(it != m_signals.end()); sigaction(signo, &it->second.old_action, nullptr); sigset_t set; sigemptyset(&set); sigaddset(&set, signo); - pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, &set, - nullptr); + int ret = pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, + &set, nullptr); + assert(ret == 0); + (void)ret; +#if HAVE_SYS_EVENT_H + struct kevent ev; + EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0); + ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr); + assert(ret == 0); +#endif + m_signals.erase(it); #endif } Error MainLoop::Run() { m_terminate_request = false; Error error; - auto impl = RunImpl::Create(*this, error); - if (!impl) - return error; + RunImpl impl(*this); // run until termination or until we run out of things to listen to while (!m_terminate_request && (!m_read_fds.empty() || !m_signals.empty())) { - error = impl->Poll(); + error = impl.Poll(); if (error.Fail()) return error; - impl->ForEachSignal([&](int sig) { - auto it = m_signals.find(sig); - if (it != m_signals.end()) - it->second.callback(*this); // Do the work - }); - if (m_terminate_request) - return Error(); + impl.ProcessEvents(); - impl->ForEachReadFD([&](int fd) { - auto it = m_read_fds.find(fd); - if (it != m_read_fds.end()) - it->second(*this); // Do the work - }); if (m_terminate_request) return Error(); } return Error(); +} + +void MainLoop::ProcessSignal(int signo) { + auto it = m_signals.find(signo); + if (it != m_signals.end()) + it->second.callback(*this); // Do the work +} + +void MainLoop::ProcessReadObject(IOObject::WaitableHandle handle) { + auto it = m_read_fds.find(handle); + if (it != m_read_fds.end()) + it->second(*this); // Do the work } Index: vendor/lldb/dist/source/Host/common/UDPSocket.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/UDPSocket.cpp (revision 317958) +++ vendor/lldb/dist/source/Host/common/UDPSocket.cpp (revision 317959) @@ -1,151 +1,137 @@ //===-- UDPSocket.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/common/UDPSocket.h" #include "lldb/Host/Config.h" #include "lldb/Utility/Log.h" #ifndef LLDB_DISABLE_POSIX #include #include #endif #include using namespace lldb; using namespace lldb_private; namespace { const int kDomain = AF_INET; const int kType = SOCK_DGRAM; static const char *g_not_supported_error = "Not supported"; -} // namespace +} -UDPSocket::UDPSocket(bool should_close, bool child_processes_inherit) - : Socket(ProtocolUdp, should_close, child_processes_inherit) {} - -UDPSocket::UDPSocket(NativeSocket socket, const UDPSocket &listen_socket) - : Socket(ProtocolUdp, listen_socket.m_should_close_fd, - listen_socket.m_child_processes_inherit) { +UDPSocket::UDPSocket(NativeSocket socket) : Socket(ProtocolUdp, true, true) { m_socket = socket; } +UDPSocket::UDPSocket(bool should_close, bool child_processes_inherit) + : Socket(ProtocolUdp, should_close, child_processes_inherit) {} + size_t UDPSocket::Send(const void *buf, const size_t num_bytes) { return ::sendto(m_socket, static_cast(buf), num_bytes, 0, m_sockaddr, m_sockaddr.GetLength()); } Error UDPSocket::Connect(llvm::StringRef name) { + return Error("%s", g_not_supported_error); +} + +Error UDPSocket::Listen(llvm::StringRef name, int backlog) { + return Error("%s", g_not_supported_error); +} + +Error UDPSocket::Accept(Socket *&socket) { + return Error("%s", g_not_supported_error); +} + +Error UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit, + Socket *&socket) { + std::unique_ptr final_socket; + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("UDPSocket::%s (host/port = %s)", __FUNCTION__, name.data()); Error error; - if (error.Fail()) - return error; - std::string host_str; std::string port_str; int32_t port = INT32_MIN; if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) return error; // At this point we have setup the receive port, now we need to // setup the UDP send socket struct addrinfo hints; struct addrinfo *service_info_list = nullptr; ::memset(&hints, 0, sizeof(hints)); hints.ai_family = kDomain; hints.ai_socktype = kType; int err = ::getaddrinfo(host_str.c_str(), port_str.c_str(), &hints, &service_info_list); if (err != 0) { error.SetErrorStringWithFormat( #if defined(_MSC_VER) && defined(UNICODE) "getaddrinfo(%s, %s, &hints, &info) returned error %i (%S)", #else "getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)", #endif host_str.c_str(), port_str.c_str(), err, gai_strerror(err)); return error; } for (struct addrinfo *service_info_ptr = service_info_list; service_info_ptr != nullptr; service_info_ptr = service_info_ptr->ai_next) { - m_socket = Socket::CreateSocket( + auto send_fd = CreateSocket( service_info_ptr->ai_family, service_info_ptr->ai_socktype, - service_info_ptr->ai_protocol, m_child_processes_inherit, error); + service_info_ptr->ai_protocol, child_processes_inherit, error); if (error.Success()) { - m_sockaddr = service_info_ptr; + final_socket.reset(new UDPSocket(send_fd)); + final_socket->m_sockaddr = service_info_ptr; break; } else continue; } ::freeaddrinfo(service_info_list); - if (IsValid()) + if (!final_socket) return error; SocketAddress bind_addr; // Only bind to the loopback address if we are expecting a connection from // localhost to avoid any firewall issues. - const bool bind_addr_success = - (host_str == "127.0.0.1" || host_str == "localhost") - ? bind_addr.SetToLocalhost(kDomain, port) - : bind_addr.SetToAnyAddress(kDomain, port); + const bool bind_addr_success = (host_str == "127.0.0.1" || host_str == "localhost") + ? bind_addr.SetToLocalhost(kDomain, port) + : bind_addr.SetToAnyAddress(kDomain, port); if (!bind_addr_success) { error.SetErrorString("Failed to get hostspec to bind for"); return error; } bind_addr.SetPort(0); // Let the source port # be determined dynamically - err = ::bind(m_socket, bind_addr, bind_addr.GetLength()); + err = ::bind(final_socket->GetNativeSocket(), bind_addr, bind_addr.GetLength()); - error.Clear(); - return error; -} + struct sockaddr_in source_info; + socklen_t address_len = sizeof (struct sockaddr_in); + err = ::getsockname(final_socket->GetNativeSocket(), (struct sockaddr *) &source_info, &address_len); -Error UDPSocket::Listen(llvm::StringRef name, int backlog) { - return Error("%s", g_not_supported_error); -} - -Error UDPSocket::Accept(Socket *&socket) { - return Error("%s", g_not_supported_error); -} - -Error UDPSocket::CreateSocket() { - Error error; - if (IsValid()) - error = Close(); - if (error.Fail()) - return error; - m_socket = - Socket::CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error); - return error; -} - -Error UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) { - std::unique_ptr final_socket( - new UDPSocket(true, child_processes_inherit)); - Error error = final_socket->Connect(name); - if (!error.Fail()) - socket = final_socket.release(); + socket = final_socket.release(); + error.Clear(); return error; } Index: vendor/lldb/dist/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp (revision 317958) +++ vendor/lldb/dist/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp (revision 317959) @@ -1,2426 +1,2424 @@ //===-- ABISysV_arm64.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ABISysV_arm64.h" // C Includes // C++ Includes #include // Other libraries and framework includes #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" // Project includes #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/Log.h" #include "Utility/ARM64_DWARF_Registers.h" using namespace lldb; using namespace lldb_private; static RegisterInfo g_register_infos[] = { // NAME ALT SZ OFF ENCODING FORMAT // EH_FRAME DWARF GENERIC // PROCESS PLUGIN LLDB NATIVE // ========== ======= == === ============= =================== // =================== ====================== =========================== // ======================= ====================== {"x0", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x1", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x2", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x3", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x4", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x5", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x6", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x7", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x8", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x9", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x10", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x11", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x12", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x13", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x14", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x15", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x16", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x17", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x18", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x19", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x20", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x21", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x22", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x23", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x24", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x25", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x26", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x27", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"x28", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fp", "x29", 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"lr", "x30", 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"sp", "x31", 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"pc", nullptr, 8, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"cpsr", "psr", 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v8", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v9", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v10", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v11", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v12", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v13", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v14", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v15", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v16", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v17", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v18", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v19", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v20", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v21", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v22", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v23", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v24", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v25", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v26", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v27", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v28", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v29", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v30", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"v31", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fpsr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fpcr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s0", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s1", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s2", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s3", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s4", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s5", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s6", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s7", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s8", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s9", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s10", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s11", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s12", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s13", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s14", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s15", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s16", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s17", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s18", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s19", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s20", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s21", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s22", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s23", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s24", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s25", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s26", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s27", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s28", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s29", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s30", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"s31", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d0", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d1", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d2", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d3", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d4", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d5", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d6", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d7", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d8", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d9", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d10", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d11", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d12", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d13", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d14", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d15", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d16", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d17", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d18", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d19", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d20", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d21", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d22", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d23", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d24", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d25", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d26", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d27", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d28", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d29", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d30", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"d31", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}}; static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos); static bool g_register_info_names_constified = false; const lldb_private::RegisterInfo * ABISysV_arm64::GetRegisterInfoArray(uint32_t &count) { // Make the C-string names and alt_names for the register infos into const // C-string values by having the ConstString unique the names in the global // constant C-string pool. if (!g_register_info_names_constified) { g_register_info_names_constified = true; for (uint32_t i = 0; i < k_num_register_infos; ++i) { if (g_register_infos[i].name) g_register_infos[i].name = ConstString(g_register_infos[i].name).GetCString(); if (g_register_infos[i].alt_name) g_register_infos[i].alt_name = ConstString(g_register_infos[i].alt_name).GetCString(); } } count = k_num_register_infos; return g_register_infos; } bool ABISysV_arm64::GetPointerReturnRegister(const char *&name) { name = "x0"; return true; } size_t ABISysV_arm64::GetRedZoneSize() const { return 128; } //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ ABISP ABISysV_arm64::CreateInstance(const ArchSpec &arch) { static ABISP g_abi_sp; const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch(); const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor(); if (vendor_type != llvm::Triple::Apple) { if (arch_type == llvm::Triple::aarch64) { if (!g_abi_sp) g_abi_sp.reset(new ABISysV_arm64); return g_abi_sp; } } return ABISP(); } bool ABISysV_arm64::PrepareTrivialCall(Thread &thread, addr_t sp, addr_t func_addr, addr_t return_addr, llvm::ArrayRef args) const { RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return false; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) { StreamString s; s.Printf("ABISysV_arm64::PrepareTrivialCall (tid = 0x%" PRIx64 ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 ", return_addr = 0x%" PRIx64, thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, (uint64_t)return_addr); for (size_t i = 0; i < args.size(); ++i) s.Printf(", arg%d = 0x%" PRIx64, static_cast(i + 1), args[i]); s.PutCString(")"); log->PutString(s.GetString()); } // x0 - x7 contain first 8 simple args if (args.size() > 8) return false; for (size_t i = 0; i < args.size(); ++i) { const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i); if (log) log->Printf("About to write arg%d (0x%" PRIx64 ") into %s", static_cast(i + 1), args[i], reg_info->name); if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) return false; } // Set "lr" to the return address if (!reg_ctx->WriteRegisterFromUnsigned( reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA), return_addr)) return false; // Set "sp" to the requested value if (!reg_ctx->WriteRegisterFromUnsigned( reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP), sp)) return false; // Set "pc" to the address requested if (!reg_ctx->WriteRegisterFromUnsigned( reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC), func_addr)) return false; return true; } // TODO: We dont support fp/SIMD arguments in v0-v7 bool ABISysV_arm64::GetArgumentValues(Thread &thread, ValueList &values) const { uint32_t num_values = values.GetSize(); ExecutionContext exe_ctx(thread.shared_from_this()); // Extract the register context so we can read arguments from registers RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return false; addr_t sp = 0; for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) { // We currently only support extracting values with Clang QualTypes. // Do we care about others? Value *value = values.GetValueAtIndex(value_idx); if (!value) return false; CompilerType value_type = value->GetCompilerType(); if (value_type) { bool is_signed = false; size_t bit_width = 0; if (value_type.IsIntegerOrEnumerationType(is_signed)) { bit_width = value_type.GetBitSize(&thread); } else if (value_type.IsPointerOrReferenceType()) { bit_width = value_type.GetBitSize(&thread); } else { // We only handle integer, pointer and reference types currently... return false; } if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) { if (value_idx < 8) { // Arguments 1-8 are in x0-x7... const RegisterInfo *reg_info = nullptr; reg_info = reg_ctx->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx); if (reg_info) { RegisterValue reg_value; if (reg_ctx->ReadRegister(reg_info, reg_value)) { if (is_signed) reg_value.SignExtend(bit_width); if (!reg_value.GetScalarValue(value->GetScalar())) return false; continue; } } return false; } else { // TODO: Verify for stack layout for SysV if (sp == 0) { // Read the stack pointer if we already haven't read it sp = reg_ctx->GetSP(0); if (sp == 0) return false; } // Arguments 5 on up are on the stack const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8; Error error; if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory( sp, arg_byte_size, is_signed, value->GetScalar(), error)) return false; sp += arg_byte_size; // Align up to the next 8 byte boundary if needed if (sp % 8) { sp >>= 3; sp += 1; sp <<= 3; } } } } } return true; } Error ABISysV_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) { Error error; if (!new_value_sp) { error.SetErrorString("Empty value object for return value."); return error; } CompilerType return_value_type = new_value_sp->GetCompilerType(); if (!return_value_type) { error.SetErrorString("Null clang type for return value."); return error; } Thread *thread = frame_sp->GetThread().get(); RegisterContext *reg_ctx = thread->GetRegisterContext().get(); if (reg_ctx) { DataExtractor data; Error data_error; const uint64_t byte_size = new_value_sp->GetData(data, data_error); if (data_error.Fail()) { error.SetErrorStringWithFormat( "Couldn't convert return value to raw data: %s", data_error.AsCString()); return error; } const uint32_t type_flags = return_value_type.GetTypeInfo(nullptr); if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) { if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) { // Extract the register context so we can read arguments from registers lldb::offset_t offset = 0; if (byte_size <= 16) { const RegisterInfo *x0_info = reg_ctx->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); if (byte_size <= 8) { uint64_t raw_value = data.GetMaxU64(&offset, byte_size); if (!reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value)) error.SetErrorString("failed to write register x0"); } else { uint64_t raw_value = data.GetMaxU64(&offset, 8); if (reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value)) { const RegisterInfo *x1_info = reg_ctx->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); raw_value = data.GetMaxU64(&offset, byte_size - offset); if (!reg_ctx->WriteRegisterFromUnsigned(x1_info, raw_value)) error.SetErrorString("failed to write register x1"); } } } else { error.SetErrorString("We don't support returning longer than 128 bit " "integer values at present."); } } else if (type_flags & eTypeIsFloat) { if (type_flags & eTypeIsComplex) { // Don't handle complex yet. error.SetErrorString( "returning complex float values are not supported"); } else { const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0); if (v0_info) { if (byte_size <= 16) { if (byte_size <= RegisterValue::GetMaxByteSize()) { RegisterValue reg_value; error = reg_value.SetValueFromData(v0_info, data, 0, true); if (error.Success()) { if (!reg_ctx->WriteRegister(v0_info, reg_value)) error.SetErrorString("failed to write register v0"); } } else { error.SetErrorStringWithFormat( "returning float values with a byte size of %" PRIu64 " are not supported", byte_size); } } else { error.SetErrorString("returning float values longer than 128 " "bits are not supported"); } } else { error.SetErrorString("v0 register is not available on this target"); } } } } else if (type_flags & eTypeIsVector) { if (byte_size > 0) { const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0); if (v0_info) { if (byte_size <= v0_info->byte_size) { RegisterValue reg_value; error = reg_value.SetValueFromData(v0_info, data, 0, true); if (error.Success()) { if (!reg_ctx->WriteRegister(v0_info, reg_value)) error.SetErrorString("failed to write register v0"); } } } } } } else { error.SetErrorString("no registers are available"); } return error; } bool ABISysV_arm64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); uint32_t lr_reg_num = arm64_dwarf::lr; uint32_t sp_reg_num = arm64_dwarf::sp; uint32_t pc_reg_num = arm64_dwarf::pc; UnwindPlan::RowSP row(new UnwindPlan::Row); // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0); // Our previous PC is in the LR row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); unwind_plan.AppendRow(row); // All other registers are the same. unwind_plan.SetSourceName("arm64 at-func-entry default"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); return true; } bool ABISysV_arm64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); uint32_t fp_reg_num = arm64_dwarf::fp; uint32_t pc_reg_num = arm64_dwarf::pc; UnwindPlan::RowSP row(new UnwindPlan::Row); const int32_t ptr_size = 8; row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size); row->SetOffset(0); row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("arm64 default unwind plan"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); return true; } // AAPCS64 (Procedure Call Standard for the ARM 64-bit Architecture) says // registers x19 through x28 and sp are callee preserved. // v8-v15 are non-volatile (and specifically only the lower 8 bytes of these // regs), // the rest of the fp/SIMD registers are volatile. // We treat x29 as callee preserved also, else the unwinder won't try to // retrieve fp saves. bool ABISysV_arm64::RegisterIsVolatile(const RegisterInfo *reg_info) { if (reg_info) { const char *name = reg_info->name; // Sometimes we'll be called with the "alternate" name for these registers; // recognize them as non-volatile. if (name[0] == 'p' && name[1] == 'c') // pc return false; if (name[0] == 'f' && name[1] == 'p') // fp return false; if (name[0] == 's' && name[1] == 'p') // sp return false; if (name[0] == 'l' && name[1] == 'r') // lr return false; if (name[0] == 'x') { // Volatile registers: x0-x18 // Although documentation says only x19-28 + sp are callee saved // We ll also have to treat x30 as non-volatile. // Each dwarf frame has its own value of lr. // Return false for the non-volatile gpr regs, true for everything else switch (name[1]) { case '1': switch (name[2]) { case '9': return false; // x19 is non-volatile default: return true; } break; case '2': switch (name[2]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': return false; // x20 - 28 are non-volatile case '9': return false; // x29 aka fp treat as non-volatile default: return true; } case '3': // x30 (lr) and x31 (sp) treat as non-volatile if (name[2] == '0' || name[2] == '1') return false; break; default: return true; // all volatile cases not handled above fall here. } } else if (name[0] == 'v' || name[0] == 's' || name[0] == 'd') { // Volatile registers: v0-7, v16-v31 // Return false for non-volatile fp/SIMD regs, true for everything else switch (name[1]) { case '8': case '9': return false; // v8-v9 are non-volatile case '1': switch (name[2]) { case '0': case '1': case '2': case '3': case '4': case '5': return false; // v10-v15 are non-volatile default: return true; } default: return true; } } } return true; } static bool LoadValueFromConsecutiveGPRRegisters( ExecutionContext &exe_ctx, RegisterContext *reg_ctx, const CompilerType &value_type, bool is_return_value, // false => parameter, true => return value uint32_t &NGRN, // NGRN (see ABI documentation) uint32_t &NSRN, // NSRN (see ABI documentation) DataExtractor &data) { const size_t byte_size = value_type.GetByteSize(nullptr); if (byte_size == 0) return false; std::unique_ptr heap_data_ap( new DataBufferHeap(byte_size, 0)); const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder(); Error error; CompilerType base_type; const uint32_t homogeneous_count = value_type.IsHomogeneousAggregate(&base_type); if (homogeneous_count > 0 && homogeneous_count <= 8) { // Make sure we have enough registers if (NSRN < 8 && (8 - NSRN) >= homogeneous_count) { if (!base_type) return false; const size_t base_byte_size = base_type.GetByteSize(nullptr); uint32_t data_offset = 0; for (uint32_t i = 0; i < homogeneous_count; ++i) { char v_name[8]; ::snprintf(v_name, sizeof(v_name), "v%u", NSRN); const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(v_name, 0); if (reg_info == nullptr) return false; if (base_byte_size > reg_info->byte_size) return false; RegisterValue reg_value; if (!reg_ctx->ReadRegister(reg_info, reg_value)) return false; // Make sure we have enough room in "heap_data_ap" if ((data_offset + base_byte_size) <= heap_data_ap->GetByteSize()) { const size_t bytes_copied = reg_value.GetAsMemoryData( reg_info, heap_data_ap->GetBytes() + data_offset, base_byte_size, byte_order, error); if (bytes_copied != base_byte_size) return false; data_offset += bytes_copied; ++NSRN; } else return false; } data.SetByteOrder(byte_order); data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize()); data.SetData(DataBufferSP(heap_data_ap.release())); return true; } } const size_t max_reg_byte_size = 16; if (byte_size <= max_reg_byte_size) { size_t bytes_left = byte_size; uint32_t data_offset = 0; while (data_offset < byte_size) { if (NGRN >= 8) return false; const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN); if (reg_info == nullptr) return false; RegisterValue reg_value; if (!reg_ctx->ReadRegister(reg_info, reg_value)) return false; const size_t curr_byte_size = std::min(8, bytes_left); const size_t bytes_copied = reg_value.GetAsMemoryData( reg_info, heap_data_ap->GetBytes() + data_offset, curr_byte_size, byte_order, error); if (bytes_copied == 0) return false; if (bytes_copied >= bytes_left) break; data_offset += bytes_copied; bytes_left -= bytes_copied; ++NGRN; } } else { const RegisterInfo *reg_info = nullptr; if (is_return_value) { // We are assuming we are decoding this immediately after returning // from a function call and that the address of the structure is in x8 reg_info = reg_ctx->GetRegisterInfoByName("x8", 0); } else { // We are assuming we are stopped at the first instruction in a function // and that the ABI is being respected so all parameters appear where they // should be (functions with no external linkage can legally violate the // ABI). if (NGRN >= 8) return false; reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN); if (reg_info == nullptr) return false; ++NGRN; } if (reg_info == nullptr) return false; const lldb::addr_t value_addr = reg_ctx->ReadRegisterAsUnsigned(reg_info, LLDB_INVALID_ADDRESS); if (value_addr == LLDB_INVALID_ADDRESS) return false; if (exe_ctx.GetProcessRef().ReadMemory( value_addr, heap_data_ap->GetBytes(), heap_data_ap->GetByteSize(), error) != heap_data_ap->GetByteSize()) { return false; } } data.SetByteOrder(byte_order); data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize()); data.SetData(DataBufferSP(heap_data_ap.release())); return true; } ValueObjectSP ABISysV_arm64::GetReturnValueObjectImpl( Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; Value value; ExecutionContext exe_ctx(thread.shared_from_this()); if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr) return return_valobj_sp; // value.SetContext (Value::eContextTypeClangType, return_compiler_type); value.SetCompilerType(return_compiler_type); RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return return_valobj_sp; const size_t byte_size = return_compiler_type.GetByteSize(nullptr); const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr); if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) { value.SetValueType(Value::eValueTypeScalar); bool success = false; if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) { // Extract the register context so we can read arguments from registers if (byte_size <= 8) { const RegisterInfo *x0_reg_info = nullptr; x0_reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); if (x0_reg_info) { uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(x0_reg_info, 0); const bool is_signed = (type_flags & eTypeIsSigned) != 0; switch (byte_size) { default: break; case 16: // uint128_t // In register x0 and x1 { const RegisterInfo *x1_reg_info = nullptr; x1_reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); if (x1_reg_info) { if (byte_size <= x0_reg_info->byte_size + x1_reg_info->byte_size) { std::unique_ptr heap_data_ap( new DataBufferHeap(byte_size, 0)); const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder(); RegisterValue x0_reg_value; RegisterValue x1_reg_value; if (reg_ctx->ReadRegister(x0_reg_info, x0_reg_value) && reg_ctx->ReadRegister(x1_reg_info, x1_reg_value)) { Error error; if (x0_reg_value.GetAsMemoryData( x0_reg_info, heap_data_ap->GetBytes() + 0, 8, byte_order, error) && x1_reg_value.GetAsMemoryData( x1_reg_info, heap_data_ap->GetBytes() + 8, 8, byte_order, error)) { DataExtractor data( DataBufferSP(heap_data_ap.release()), byte_order, exe_ctx.GetProcessRef().GetAddressByteSize()); return_valobj_sp = ValueObjectConstResult::Create( &thread, return_compiler_type, ConstString(""), data); return return_valobj_sp; } } } } } break; case sizeof(uint64_t): if (is_signed) value.GetScalar() = (int64_t)(raw_value); else value.GetScalar() = (uint64_t)(raw_value); success = true; break; case sizeof(uint32_t): if (is_signed) value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); else value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); success = true; break; case sizeof(uint16_t): if (is_signed) value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); else value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); success = true; break; case sizeof(uint8_t): if (is_signed) value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); else value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); success = true; break; } } } } else if (type_flags & eTypeIsFloat) { if (type_flags & eTypeIsComplex) { // Don't handle complex yet. } else { if (byte_size <= sizeof(long double)) { const RegisterInfo *v0_reg_info = reg_ctx->GetRegisterInfoByName("v0", 0); RegisterValue v0_value; if (reg_ctx->ReadRegister(v0_reg_info, v0_value)) { DataExtractor data; if (v0_value.GetData(data)) { lldb::offset_t offset = 0; if (byte_size == sizeof(float)) { value.GetScalar() = data.GetFloat(&offset); success = true; } else if (byte_size == sizeof(double)) { value.GetScalar() = data.GetDouble(&offset); success = true; } else if (byte_size == sizeof(long double)) { value.GetScalar() = data.GetLongDouble(&offset); success = true; } } } } } } if (success) return_valobj_sp = ValueObjectConstResult::Create( thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); - } else if (type_flags & eTypeIsVector) { + } else if (type_flags & eTypeIsVector && byte_size <= 16) { if (byte_size > 0) { const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0); if (v0_info) { - if (byte_size <= v0_info->byte_size) { - std::unique_ptr heap_data_ap( - new DataBufferHeap(byte_size, 0)); - const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder(); - RegisterValue reg_value; - if (reg_ctx->ReadRegister(v0_info, reg_value)) { - Error error; - if (reg_value.GetAsMemoryData(v0_info, heap_data_ap->GetBytes(), - heap_data_ap->GetByteSize(), - byte_order, error)) { - DataExtractor data(DataBufferSP(heap_data_ap.release()), - byte_order, - exe_ctx.GetProcessRef().GetAddressByteSize()); - return_valobj_sp = ValueObjectConstResult::Create( - &thread, return_compiler_type, ConstString(""), data); - } + std::unique_ptr heap_data_ap( + new DataBufferHeap(byte_size, 0)); + const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder(); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(v0_info, reg_value)) { + Error error; + if (reg_value.GetAsMemoryData(v0_info, heap_data_ap->GetBytes(), + heap_data_ap->GetByteSize(), byte_order, + error)) { + DataExtractor data(DataBufferSP(heap_data_ap.release()), byte_order, + exe_ctx.GetProcessRef().GetAddressByteSize()); + return_valobj_sp = ValueObjectConstResult::Create( + &thread, return_compiler_type, ConstString(""), data); } } } } - } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) { + } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass || + (type_flags & eTypeIsVector && byte_size > 16)) { DataExtractor data; uint32_t NGRN = 0; // Search ABI docs for NGRN uint32_t NSRN = 0; // Search ABI docs for NSRN const bool is_return_value = true; if (LoadValueFromConsecutiveGPRRegisters( exe_ctx, reg_ctx, return_compiler_type, is_return_value, NGRN, NSRN, data)) { return_valobj_sp = ValueObjectConstResult::Create( &thread, return_compiler_type, ConstString(""), data); } } return return_valobj_sp; } void ABISysV_arm64::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "SysV ABI for AArch64 targets", CreateInstance); } void ABISysV_arm64::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ABISysV_arm64::GetPluginNameStatic() { static ConstString g_name("SysV-arm64"); return g_name; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString ABISysV_arm64::GetPluginName() { return GetPluginNameStatic(); } uint32_t ABISysV_arm64::GetPluginVersion() { return 1; } Index: vendor/lldb/dist/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp (revision 317958) +++ vendor/lldb/dist/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp (revision 317959) @@ -1,528 +1,529 @@ //===-- DynamicLoaderMacOS.cpp -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/State.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Target/ABI.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" #include "DynamicLoaderDarwin.h" #include "DynamicLoaderMacOS.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // Create an instance of this class. This function is filled into // the plugin info class that gets handed out by the plugin factory and // allows the lldb to instantiate an instance of this class. //---------------------------------------------------------------------- DynamicLoader *DynamicLoaderMacOS::CreateInstance(Process *process, bool force) { bool create = force; if (!create) { create = true; Module *exe_module = process->GetTarget().GetExecutableModulePointer(); if (exe_module) { ObjectFile *object_file = exe_module->GetObjectFile(); if (object_file) { create = (object_file->GetStrata() == ObjectFile::eStrataUser); } } if (create) { const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); switch (triple_ref.getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: case llvm::Triple::TvOS: case llvm::Triple::WatchOS: create = triple_ref.getVendor() == llvm::Triple::Apple; break; default: create = false; break; } } } if (UseDYLDSPI(process) == false) { create = false; } if (create) return new DynamicLoaderMacOS(process); return NULL; } //---------------------------------------------------------------------- // Constructor //---------------------------------------------------------------------- DynamicLoaderMacOS::DynamicLoaderMacOS(Process *process) : DynamicLoaderDarwin(process), m_image_infos_stop_id(UINT32_MAX), m_break_id(LLDB_INVALID_BREAK_ID), m_mutex() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- DynamicLoaderMacOS::~DynamicLoaderMacOS() { if (LLDB_BREAK_ID_IS_VALID(m_break_id)) m_process->GetTarget().RemoveBreakpointByID(m_break_id); } bool DynamicLoaderMacOS::ProcessDidExec() { std::lock_guard baseclass_guard(GetMutex()); bool did_exec = false; if (m_process) { // If we are stopped after an exec, we will have only one thread... if (m_process->GetThreadList().GetSize() == 1) { // See if we are stopped at '_dyld_start' ThreadSP thread_sp(m_process->GetThreadList().GetThreadAtIndex(0)); if (thread_sp) { lldb::StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0)); if (frame_sp) { const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol; if (symbol) { if (symbol->GetName() == ConstString("_dyld_start")) did_exec = true; } } } } } if (did_exec) { m_libpthread_module_wp.reset(); m_pthread_getspecific_addr.Clear(); } return did_exec; } //---------------------------------------------------------------------- // Clear out the state of this class. //---------------------------------------------------------------------- void DynamicLoaderMacOS::DoClear() { std::lock_guard guard(m_mutex); if (LLDB_BREAK_ID_IS_VALID(m_break_id)) m_process->GetTarget().RemoveBreakpointByID(m_break_id); m_break_id = LLDB_INVALID_BREAK_ID; } //---------------------------------------------------------------------- // Check if we have found DYLD yet //---------------------------------------------------------------------- bool DynamicLoaderMacOS::DidSetNotificationBreakpoint() { return LLDB_BREAK_ID_IS_VALID(m_break_id); } void DynamicLoaderMacOS::ClearNotificationBreakpoint() { if (LLDB_BREAK_ID_IS_VALID(m_break_id)) { m_process->GetTarget().RemoveBreakpointByID(m_break_id); m_break_id = LLDB_INVALID_BREAK_ID; } } //---------------------------------------------------------------------- // Try and figure out where dyld is by first asking the Process // if it knows (which currently calls down in the lldb::Process // to get the DYLD info (available on SnowLeopard only). If that fails, // then check in the default addresses. //---------------------------------------------------------------------- void DynamicLoaderMacOS::DoInitialImageFetch() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); // Remove any binaries we pre-loaded in the Target before launching/attaching. // If the same binaries are present in the process, we'll get them from the // shared module cache, we won't need to re-load them from disk. UnloadAllImages(); StructuredData::ObjectSP all_image_info_json_sp( m_process->GetLoadedDynamicLibrariesInfos()); ImageInfo::collection image_infos; if (all_image_info_json_sp.get() && all_image_info_json_sp->GetAsDictionary() && all_image_info_json_sp->GetAsDictionary()->HasKey("images") && all_image_info_json_sp->GetAsDictionary() ->GetValueForKey("images") ->GetAsArray()) { if (JSONImageInformationIntoImageInfo(all_image_info_json_sp, image_infos)) { if (log) log->Printf("Initial module fetch: Adding %" PRId64 " modules.\n", (uint64_t)image_infos.size()); UpdateSpecialBinariesFromNewImageInfos(image_infos); AddModulesUsingImageInfos(image_infos); } } m_dyld_image_infos_stop_id = m_process->GetStopID(); } bool DynamicLoaderMacOS::NeedToDoInitialImageFetch() { return true; } //---------------------------------------------------------------------- // Static callback function that gets called when our DYLD notification // breakpoint gets hit. We update all of our image infos and then // let our super class DynamicLoader class decide if we should stop // or not (based on global preference). //---------------------------------------------------------------------- bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // Let the event know that the images have changed // DYLD passes three arguments to the notification breakpoint. // Arg1: enum dyld_notify_mode mode - 0 = adding, 1 = removing, 2 = remove all // Arg2: unsigned long icount - Number of shared libraries // added/removed // Arg3: uint64_t mach_headers[] - Array of load addresses of binaries // added/removed DynamicLoaderMacOS *dyld_instance = (DynamicLoaderMacOS *)baton; ExecutionContext exe_ctx(context->exe_ctx_ref); Process *process = exe_ctx.GetProcessPtr(); // This is a sanity check just in case this dyld_instance is an old dyld // plugin's breakpoint still lying around. if (process != dyld_instance->m_process) return false; if (dyld_instance->m_image_infos_stop_id != UINT32_MAX && process->GetStopID() < dyld_instance->m_image_infos_stop_id) { return false; } const lldb::ABISP &abi = process->GetABI(); if (abi) { // Build up the value array to store the three arguments given above, then // get the values from the ABI: ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); ValueList argument_values; Value mode_value; // enum dyld_notify_mode { dyld_notify_adding=0, // dyld_notify_removing=1, dyld_notify_remove_all=2 }; Value count_value; // unsigned long count Value headers_value; // uint64_t machHeaders[] (aka void*) CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); CompilerType clang_uint32_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize( lldb::eEncodingUint, 32); CompilerType clang_uint64_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize( lldb::eEncodingUint, 32); mode_value.SetValueType(Value::eValueTypeScalar); mode_value.SetCompilerType(clang_uint32_type); if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 4) { count_value.SetValueType(Value::eValueTypeScalar); count_value.SetCompilerType(clang_uint32_type); } else { count_value.SetValueType(Value::eValueTypeScalar); count_value.SetCompilerType(clang_uint64_type); } headers_value.SetValueType(Value::eValueTypeScalar); headers_value.SetCompilerType(clang_void_ptr_type); argument_values.PushValue(mode_value); argument_values.PushValue(count_value); argument_values.PushValue(headers_value); if (abi->GetArgumentValues(exe_ctx.GetThreadRef(), argument_values)) { uint32_t dyld_mode = argument_values.GetValueAtIndex(0)->GetScalar().UInt(-1); if (dyld_mode != static_cast(-1)) { // Okay the mode was right, now get the number of elements, and the // array of new elements... uint32_t image_infos_count = argument_values.GetValueAtIndex(1)->GetScalar().UInt(-1); if (image_infos_count != static_cast(-1)) { addr_t header_array = argument_values.GetValueAtIndex(2)->GetScalar().ULongLong(-1); if (header_array != static_cast(-1)) { std::vector image_load_addresses; for (uint64_t i = 0; i < image_infos_count; i++) { Error error; addr_t addr = process->ReadUnsignedIntegerFromMemory( header_array + (8 * i), 8, LLDB_INVALID_ADDRESS, error); if (addr != LLDB_INVALID_ADDRESS) { image_load_addresses.push_back(addr); } } if (dyld_mode == 0) { // dyld_notify_adding dyld_instance->AddBinaries(image_load_addresses); } else if (dyld_mode == 1) { // dyld_notify_removing dyld_instance->UnloadImages(image_load_addresses); } else if (dyld_mode == 2) { // dyld_notify_remove_all dyld_instance->UnloadAllImages(); } } } } } } else { process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf( "No ABI plugin located for triple %s -- shared libraries will not be " "registered!\n", process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); } // Return true to stop the target, false to just let the target run return dyld_instance->GetStopWhenImagesChange(); } void DynamicLoaderMacOS::AddBinaries( const std::vector &load_addresses) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); ImageInfo::collection image_infos; if (log) log->Printf("Adding %" PRId64 " modules.", (uint64_t)load_addresses.size()); StructuredData::ObjectSP binaries_info_sp = m_process->GetLoadedDynamicLibrariesInfos(load_addresses); if (binaries_info_sp.get() && binaries_info_sp->GetAsDictionary() && binaries_info_sp->GetAsDictionary()->HasKey("images") && binaries_info_sp->GetAsDictionary() ->GetValueForKey("images") ->GetAsArray() && binaries_info_sp->GetAsDictionary() ->GetValueForKey("images") ->GetAsArray() ->GetSize() == load_addresses.size()) { if (JSONImageInformationIntoImageInfo(binaries_info_sp, image_infos)) { UpdateSpecialBinariesFromNewImageInfos(image_infos); AddModulesUsingImageInfos(image_infos); } m_dyld_image_infos_stop_id = m_process->GetStopID(); } } // Dump the _dyld_all_image_infos members and all current image infos // that we have parsed to the file handle provided. //---------------------------------------------------------------------- void DynamicLoaderMacOS::PutToLog(Log *log) const { if (log == NULL) return; } bool DynamicLoaderMacOS::SetNotificationBreakpoint() { if (m_break_id == LLDB_INVALID_BREAK_ID) { ConstString g_symbol_name("_dyld_debugger_notification"); const Symbol *symbol = nullptr; ModuleSP dyld_sp(GetDYLDModule()); if (dyld_sp) { symbol = dyld_sp->FindFirstSymbolWithNameAndType(g_symbol_name, eSymbolTypeCode); } if (symbol && (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) { addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&m_process->GetTarget()); if (symbol_address != LLDB_INVALID_ADDRESS) { bool internal = true; bool hardware = false; Breakpoint *breakpoint = m_process->GetTarget() .CreateBreakpoint(symbol_address, internal, hardware) .get(); breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this, true); breakpoint->SetBreakpointKind("shared-library-event"); m_break_id = breakpoint->GetID(); } } } return m_break_id != LLDB_INVALID_BREAK_ID; } addr_t DynamicLoaderMacOS::GetDyldLockVariableAddressFromModule(Module *module) { SymbolContext sc; SymbolVendor *sym_vendor = module->GetSymbolVendor(); Target &target = m_process->GetTarget(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { std::vector match_indexes; ConstString g_symbol_name("_dyld_global_lock_held"); uint32_t num_matches = 0; num_matches = symtab->AppendSymbolIndexesWithName(g_symbol_name, match_indexes); if (num_matches == 1) { Symbol *symbol = symtab->SymbolAtIndex(match_indexes[0]); if (symbol && (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) { return symbol->GetAddressRef().GetOpcodeLoadAddress(&target); } } } } return LLDB_INVALID_ADDRESS; } // Look for this symbol: // // int __attribute__((visibility("hidden"))) _dyld_global_lock_held = // 0; // // in libdyld.dylib. Error DynamicLoaderMacOS::CanLoadImage() { Error error; addr_t symbol_address = LLDB_INVALID_ADDRESS; Target &target = m_process->GetTarget(); const ModuleList &target_modules = target.GetImages(); std::lock_guard guard(target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); ConstString g_libdyld_name("libdyld.dylib"); // Find any modules named "libdyld.dylib" and look for the symbol there first for (size_t i = 0; i < num_modules; i++) { Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i); if (module_pointer) { if (module_pointer->GetFileSpec().GetFilename() == g_libdyld_name) { symbol_address = GetDyldLockVariableAddressFromModule(module_pointer); if (symbol_address != LLDB_INVALID_ADDRESS) break; } } } // Search through all modules looking for the symbol in them if (symbol_address == LLDB_INVALID_ADDRESS) { for (size_t i = 0; i < num_modules; i++) { Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i); if (module_pointer) { addr_t symbol_address = GetDyldLockVariableAddressFromModule(module_pointer); if (symbol_address != LLDB_INVALID_ADDRESS) break; } } } // Default assumption is that it is OK to load images. // Only say that we cannot load images if we find the symbol in libdyld and it - // indicates that - // we cannot. + // indicates that we cannot. if (symbol_address != LLDB_INVALID_ADDRESS) { { int lock_held = m_process->ReadUnsignedIntegerFromMemory(symbol_address, 4, 0, error); if (lock_held != 0) { - error.SetErrorToGenericError(); + error.SetErrorString("dyld lock held - unsafe to load images."); } } } else { // If we were unable to find _dyld_global_lock_held in any modules, or it is - // not loaded into - // memory yet, we may be at process startup (sitting at _dyld_start) - so we - // should not allow - // dlopen calls. - error.SetErrorToGenericError(); + // not loaded into memory yet, we may be at process startup (sitting + // at _dyld_start) - so we should not allow dlopen calls. + // But if we found more than one module then we are clearly past _dyld_start + // so in that case we'll default to "it's safe". + if (num_modules <= 1) + error.SetErrorString("could not find the dyld library or " + "the dyld lock symbol"); } return error; } bool DynamicLoaderMacOS::GetSharedCacheInformation( lldb::addr_t &base_address, UUID &uuid, LazyBool &using_shared_cache, LazyBool &private_shared_cache) { base_address = LLDB_INVALID_ADDRESS; uuid.Clear(); using_shared_cache = eLazyBoolCalculate; private_shared_cache = eLazyBoolCalculate; if (m_process) { StructuredData::ObjectSP info = m_process->GetSharedCacheInfo(); StructuredData::Dictionary *info_dict = nullptr; if (info.get() && info->GetAsDictionary()) { info_dict = info->GetAsDictionary(); } // {"shared_cache_base_address":140735683125248,"shared_cache_uuid":"DDB8D70C-C9A2-3561-B2C8-BE48A4F33F96","no_shared_cache":false,"shared_cache_private_cache":false} if (info_dict && info_dict->HasKey("shared_cache_uuid") && info_dict->HasKey("no_shared_cache") && info_dict->HasKey("shared_cache_base_address")) { base_address = info_dict->GetValueForKey("shared_cache_base_address") ->GetIntegerValue(LLDB_INVALID_ADDRESS); std::string uuid_str = info_dict->GetValueForKey("shared_cache_uuid")->GetStringValue(); if (!uuid_str.empty()) uuid.SetFromCString(uuid_str.c_str()); if (info_dict->GetValueForKey("no_shared_cache")->GetBooleanValue() == false) using_shared_cache = eLazyBoolYes; else using_shared_cache = eLazyBoolNo; if (info_dict->GetValueForKey("shared_cache_private_cache") ->GetBooleanValue()) private_shared_cache = eLazyBoolYes; else private_shared_cache = eLazyBoolNo; return true; } } return false; } void DynamicLoaderMacOS::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); } void DynamicLoaderMacOS::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString DynamicLoaderMacOS::GetPluginNameStatic() { static ConstString g_name("macos-dyld"); return g_name; } const char *DynamicLoaderMacOS::GetPluginDescriptionStatic() { return "Dynamic loader plug-in that watches for shared library loads/unloads " "in MacOSX user processes."; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString DynamicLoaderMacOS::GetPluginName() { return GetPluginNameStatic(); } uint32_t DynamicLoaderMacOS::GetPluginVersion() { return 1; } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (revision 317958) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (revision 317959) @@ -1,4299 +1,4286 @@ //===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "SymbolFileDWARF.h" // Other libraries and framework includes #include "llvm/Support/Casting.h" #include "llvm/Support/Threading.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/StreamString.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" #include "lldb/Symbol/DebugMacros.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Target/Language.h" #include "lldb/Utility/TaskPool.h" #include "DWARFASTParser.h" #include "DWARFASTParserClang.h" #include "DWARFCompileUnit.h" #include "DWARFDIECollection.h" #include "DWARFDebugAbbrev.h" #include "DWARFDebugAranges.h" #include "DWARFDebugInfo.h" #include "DWARFDebugLine.h" #include "DWARFDebugMacro.h" #include "DWARFDebugPubnames.h" #include "DWARFDebugRanges.h" #include "DWARFDeclContext.h" #include "DWARFFormValue.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARFDebugMap.h" #include "SymbolFileDWARFDwo.h" #include "llvm/Support/FileSystem.h" #include #include #include //#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN #ifdef ENABLE_DEBUG_PRINTF #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif using namespace lldb; using namespace lldb_private; // static inline bool // child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag) //{ // switch (tag) // { // default: // break; // case DW_TAG_subprogram: // case DW_TAG_inlined_subroutine: // case DW_TAG_class_type: // case DW_TAG_structure_type: // case DW_TAG_union_type: // return true; // } // return false; //} // namespace { PropertyDefinition g_properties[] = { {"comp-dir-symlink-paths", OptionValue::eTypeFileSpecList, true, 0, nullptr, nullptr, "If the DW_AT_comp_dir matches any of these paths the symbolic " "links will be resolved at DWARF parse time."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertySymLinkPaths }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return SymbolFileDWARF::GetPluginNameStatic(); } PluginProperties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } FileSpecList &GetSymLinkPaths() { OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList( nullptr, true, ePropertySymLinkPaths); assert(option_value); return option_value->GetCurrentValue(); } }; typedef std::shared_ptr SymbolFileDWARFPropertiesSP; static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() { static const auto g_settings_sp(std::make_shared()); return g_settings_sp; } } // anonymous namespace end static const char *removeHostnameFromPathname(const char *path_from_dwarf) { if (!path_from_dwarf || !path_from_dwarf[0]) { return path_from_dwarf; } const char *colon_pos = strchr(path_from_dwarf, ':'); if (nullptr == colon_pos) { return path_from_dwarf; } const char *slash_pos = strchr(path_from_dwarf, '/'); if (slash_pos && (slash_pos < colon_pos)) { return path_from_dwarf; } // check whether we have a windows path, and so the first character // is a drive-letter not a hostname. if (colon_pos == path_from_dwarf + 1 && isalpha(*path_from_dwarf) && strlen(path_from_dwarf) > 2 && '\\' == path_from_dwarf[2]) { return path_from_dwarf; } return colon_pos + 1; } static const char *resolveCompDir(const char *path_from_dwarf) { if (!path_from_dwarf) return nullptr; // DWARF2/3 suggests the form hostname:pathname for compilation directory. // Remove the host part if present. const char *local_path = removeHostnameFromPathname(path_from_dwarf); if (!local_path) return nullptr; bool is_symlink = false; FileSpec local_path_spec(local_path, false); const auto &file_specs = GetGlobalPluginProperties()->GetSymLinkPaths(); for (size_t i = 0; i < file_specs.GetSize() && !is_symlink; ++i) is_symlink = FileSpec::Equal(file_specs.GetFileSpecAtIndex(i), local_path_spec, true); if (!is_symlink) return local_path; namespace fs = llvm::sys::fs; if (fs::get_file_type(local_path_spec.GetPath(), false) != fs::file_type::symlink_file) return local_path; FileSpec resolved_local_path_spec; const auto error = FileSystem::Readlink(local_path_spec, resolved_local_path_spec); if (error.Success()) return resolved_local_path_spec.GetCString(); return nullptr; } void SymbolFileDWARF::Initialize() { LogChannelDWARF::Initialize(); PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); } void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) { if (!PluginManager::GetSettingForSymbolFilePlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForSymbolFilePlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the dwarf symbol-file plug-in."), is_global_setting); } } void SymbolFileDWARF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); LogChannelDWARF::Terminate(); } lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() { static ConstString g_name("dwarf"); return g_name; } const char *SymbolFileDWARF::GetPluginDescriptionStatic() { return "DWARF and DWARF3 debug symbol file reader."; } SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFile *obj_file) { return new SymbolFileDWARF(obj_file); } TypeList *SymbolFileDWARF::GetTypeList() { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) return debug_map_symfile->GetTypeList(); else return m_obj_file->GetModule()->GetTypeList(); } void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset, dw_offset_t max_die_offset, uint32_t type_mask, TypeSet &type_set) { if (die) { const dw_offset_t die_offset = die.GetOffset(); if (die_offset >= max_die_offset) return; if (die_offset >= min_die_offset) { const dw_tag_t tag = die.Tag(); bool add_type = false; switch (tag) { case DW_TAG_array_type: add_type = (type_mask & eTypeClassArray) != 0; break; case DW_TAG_unspecified_type: case DW_TAG_base_type: add_type = (type_mask & eTypeClassBuiltin) != 0; break; case DW_TAG_class_type: add_type = (type_mask & eTypeClassClass) != 0; break; case DW_TAG_structure_type: add_type = (type_mask & eTypeClassStruct) != 0; break; case DW_TAG_union_type: add_type = (type_mask & eTypeClassUnion) != 0; break; case DW_TAG_enumeration_type: add_type = (type_mask & eTypeClassEnumeration) != 0; break; case DW_TAG_subroutine_type: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: add_type = (type_mask & eTypeClassFunction) != 0; break; case DW_TAG_pointer_type: add_type = (type_mask & eTypeClassPointer) != 0; break; case DW_TAG_rvalue_reference_type: case DW_TAG_reference_type: add_type = (type_mask & eTypeClassReference) != 0; break; case DW_TAG_typedef: add_type = (type_mask & eTypeClassTypedef) != 0; break; case DW_TAG_ptr_to_member_type: add_type = (type_mask & eTypeClassMemberPointer) != 0; break; } if (add_type) { const bool assert_not_being_parsed = true; Type *type = ResolveTypeUID(die, assert_not_being_parsed); if (type) { if (type_set.find(type) == type_set.end()) type_set.insert(type); } } } for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid(); child_die = child_die.GetSibling()) { GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set); } } } size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope, uint32_t type_mask, TypeList &type_list) { TypeSet type_set; CompileUnit *comp_unit = NULL; DWARFCompileUnit *dwarf_cu = NULL; if (sc_scope) comp_unit = sc_scope->CalculateSymbolContextCompileUnit(); if (comp_unit) { dwarf_cu = GetDWARFCompileUnit(comp_unit); if (dwarf_cu == 0) return 0; GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(), dwarf_cu->GetNextCompileUnitOffset(), type_mask, type_set); } else { DWARFDebugInfo *info = DebugInfo(); if (info) { const size_t num_cus = info->GetNumCompileUnits(); for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) { dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set); } } } } std::set compiler_type_set; size_t num_types_added = 0; for (Type *type : type_set) { CompilerType compiler_type = type->GetForwardCompilerType(); if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) { compiler_type_set.insert(compiler_type); type_list.Insert(type->shared_from_this()); ++num_types_added; } } return num_types_added; } //---------------------------------------------------------------------- // Gets the first parent that is a lexical block, function or inlined // subroutine, or compile unit. //---------------------------------------------------------------------- DWARFDIE SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) { DWARFDIE die; for (die = child_die.GetParent(); die; die = die.GetParent()) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_compile_unit: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: return die; } } return DWARFDIE(); } SymbolFileDWARF::SymbolFileDWARF(ObjectFile *objfile) : SymbolFile(objfile), UserID(0), // Used by SymbolFileDWARFDebugMap to when // this class parses .o files to contain // the .o file index/ID m_debug_map_module_wp(), m_debug_map_symfile(NULL), m_data_debug_abbrev(), m_data_debug_aranges(), m_data_debug_frame(), m_data_debug_info(), m_data_debug_line(), m_data_debug_macro(), m_data_debug_loc(), m_data_debug_ranges(), m_data_debug_str(), m_data_apple_names(), m_data_apple_types(), m_data_apple_namespaces(), m_abbr(), m_info(), m_line(), m_apple_names_ap(), m_apple_types_ap(), m_apple_namespaces_ap(), m_apple_objc_ap(), m_function_basename_index(), m_function_fullname_index(), m_function_method_index(), m_function_selector_index(), m_objc_class_selectors_index(), m_global_index(), m_type_index(), m_namespace_index(), m_indexed(false), m_using_apple_tables(false), m_fetched_external_modules(false), m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate), m_ranges(), m_unique_ast_type_map() {} SymbolFileDWARF::~SymbolFileDWARF() {} static const ConstString &GetDWARFMachOSegmentName() { static ConstString g_dwarf_section_name("__DWARF"); return g_dwarf_section_name; } UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) return debug_map_symfile->GetUniqueDWARFASTTypeMap(); else return m_unique_ast_type_map; } TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); TypeSystem *type_system; if (debug_map_symfile) { type_system = debug_map_symfile->GetTypeSystemForLanguage(language); } else { type_system = m_obj_file->GetModule()->GetTypeSystemForLanguage(language); if (type_system) type_system->SetSymbolFile(this); } return type_system; } void SymbolFileDWARF::InitializeObject() { ModuleSP module_sp(m_obj_file->GetModule()); if (module_sp) { const SectionList *section_list = module_sp->GetSectionList(); const Section *section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); // Memory map the DWARF mach-o segment so we have everything mmap'ed // to keep our heap memory usage down. if (section) m_obj_file->MemoryMapSectionData(section, m_dwarf_data); } get_apple_names_data(); if (m_data_apple_names.m_data.GetByteSize() > 0) { m_apple_names_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_names.m_data, get_debug_str_data(), ".apple_names")); if (m_apple_names_ap->IsValid()) m_using_apple_tables = true; else m_apple_names_ap.reset(); } get_apple_types_data(); if (m_data_apple_types.m_data.GetByteSize() > 0) { m_apple_types_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_types.m_data, get_debug_str_data(), ".apple_types")); if (m_apple_types_ap->IsValid()) m_using_apple_tables = true; else m_apple_types_ap.reset(); } get_apple_namespaces_data(); if (m_data_apple_namespaces.m_data.GetByteSize() > 0) { m_apple_namespaces_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_namespaces.m_data, get_debug_str_data(), ".apple_namespaces")); if (m_apple_namespaces_ap->IsValid()) m_using_apple_tables = true; else m_apple_namespaces_ap.reset(); } get_apple_objc_data(); if (m_data_apple_objc.m_data.GetByteSize() > 0) { m_apple_objc_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_objc.m_data, get_debug_str_data(), ".apple_objc")); if (m_apple_objc_ap->IsValid()) m_using_apple_tables = true; else m_apple_objc_ap.reset(); } } bool SymbolFileDWARF::SupportedVersion(uint16_t version) { return version == 2 || version == 3 || version == 4; } uint32_t SymbolFileDWARF::CalculateAbilities() { uint32_t abilities = 0; if (m_obj_file != NULL) { const Section *section = NULL; const SectionList *section_list = m_obj_file->GetSectionList(); if (section_list == NULL) return 0; uint64_t debug_abbrev_file_size = 0; uint64_t debug_info_file_size = 0; uint64_t debug_line_file_size = 0; section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); if (section) section_list = §ion->GetChildren(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get(); if (section != NULL) { debug_info_file_size = section->GetFileSize(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true) .get(); if (section) debug_abbrev_file_size = section->GetFileSize(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true) .get(); if (section) debug_line_file_size = section->GetFileSize(); } else { const char *symfile_dir_cstr = m_obj_file->GetFileSpec().GetDirectory().GetCString(); if (symfile_dir_cstr) { if (strcasestr(symfile_dir_cstr, ".dsym")) { if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) { // We have a dSYM file that didn't have a any debug info. // If the string table has a size of 1, then it was made from // an executable with no debug info, or from an executable that // was stripped. section = section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true) .get(); if (section && section->GetFileSize() == 1) { m_obj_file->GetModule()->ReportWarning( "empty dSYM file detected, dSYM was created with an " "executable with no debug info."); } } } } } if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; if (debug_line_file_size > 0) abilities |= LineTables; } return abilities; } const DWARFDataExtractor & SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type, DWARFDataSegment &data_segment) { llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] { this->LoadSectionData(sect_type, std::ref(data_segment.m_data)); }); return data_segment.m_data; } void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type, DWARFDataExtractor &data) { ModuleSP module_sp(m_obj_file->GetModule()); const SectionList *section_list = module_sp->GetSectionList(); if (section_list) { SectionSP section_sp(section_list->FindSectionByType(sect_type, true)); if (section_sp) { // See if we memory mapped the DWARF segment? if (m_dwarf_data.GetByteSize()) { data.SetData(m_dwarf_data, section_sp->GetOffset(), section_sp->GetFileSize()); } else { if (m_obj_file->ReadSectionData(section_sp.get(), data) == 0) data.Clear(); } } } } const DWARFDataExtractor &SymbolFileDWARF::get_debug_abbrev_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAbbrev, m_data_debug_abbrev); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_addr_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAddr, m_data_debug_addr); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_aranges_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAranges, m_data_debug_aranges); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_frame_data() { return GetCachedSectionData(eSectionTypeDWARFDebugFrame, m_data_debug_frame); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_info_data() { return GetCachedSectionData(eSectionTypeDWARFDebugInfo, m_data_debug_info); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_line_data() { return GetCachedSectionData(eSectionTypeDWARFDebugLine, m_data_debug_line); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_macro_data() { return GetCachedSectionData(eSectionTypeDWARFDebugMacro, m_data_debug_macro); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() { return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_ranges_data() { return GetCachedSectionData(eSectionTypeDWARFDebugRanges, m_data_debug_ranges); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_str_data() { return GetCachedSectionData(eSectionTypeDWARFDebugStr, m_data_debug_str); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_str_offsets_data() { return GetCachedSectionData(eSectionTypeDWARFDebugStrOffsets, m_data_debug_str_offsets); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_names_data() { return GetCachedSectionData(eSectionTypeDWARFAppleNames, m_data_apple_names); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_types_data() { return GetCachedSectionData(eSectionTypeDWARFAppleTypes, m_data_apple_types); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_namespaces_data() { return GetCachedSectionData(eSectionTypeDWARFAppleNamespaces, m_data_apple_namespaces); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_objc_data() { return GetCachedSectionData(eSectionTypeDWARFAppleObjC, m_data_apple_objc); } DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() { if (m_abbr.get() == NULL) { const DWARFDataExtractor &debug_abbrev_data = get_debug_abbrev_data(); if (debug_abbrev_data.GetByteSize() > 0) { m_abbr.reset(new DWARFDebugAbbrev()); if (m_abbr.get()) m_abbr->Parse(debug_abbrev_data); } } return m_abbr.get(); } const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const { return m_abbr.get(); } DWARFDebugInfo *SymbolFileDWARF::DebugInfo() { if (m_info.get() == NULL) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "%s this = %p", LLVM_PRETTY_FUNCTION, static_cast(this)); if (get_debug_info_data().GetByteSize() > 0) { m_info.reset(new DWARFDebugInfo()); if (m_info.get()) { m_info->SetDwarfData(this); } } } return m_info.get(); } const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const { return m_info.get(); } DWARFCompileUnit * SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { if (!comp_unit) return nullptr; DWARFDebugInfo *info = DebugInfo(); if (info) { // Just a normal DWARF file whose user ID for the compile unit is // the DWARF offset itself DWARFCompileUnit *dwarf_cu = info->GetCompileUnit((dw_offset_t)comp_unit->GetID()); if (dwarf_cu && dwarf_cu->GetUserData() == NULL) dwarf_cu->SetUserData(comp_unit); return dwarf_cu; } return NULL; } DWARFDebugRanges *SymbolFileDWARF::DebugRanges() { if (m_ranges.get() == NULL) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "%s this = %p", LLVM_PRETTY_FUNCTION, static_cast(this)); if (get_debug_ranges_data().GetByteSize() > 0) { m_ranges.reset(new DWARFDebugRanges()); if (m_ranges.get()) m_ranges->Extract(this); } } return m_ranges.get(); } const DWARFDebugRanges *SymbolFileDWARF::DebugRanges() const { return m_ranges.get(); } lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit *dwarf_cu, uint32_t cu_idx) { CompUnitSP cu_sp; if (dwarf_cu) { CompileUnit *comp_unit = (CompileUnit *)dwarf_cu->GetUserData(); if (comp_unit) { // We already parsed this compile unit, had out a shared pointer to it cu_sp = comp_unit->shared_from_this(); } else { if (dwarf_cu->GetSymbolFileDWARF() != this) { return dwarf_cu->GetSymbolFileDWARF()->ParseCompileUnit(dwarf_cu, cu_idx); } else if (dwarf_cu->GetOffset() == 0 && GetDebugMapSymfile()) { // Let the debug map create the compile unit cu_sp = m_debug_map_symfile->GetCompileUnit(this); dwarf_cu->SetUserData(cu_sp.get()); } else { ModuleSP module_sp(m_obj_file->GetModule()); if (module_sp) { const DWARFDIE cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (cu_die) { FileSpec cu_file_spec{cu_die.GetName(), false}; if (cu_file_spec) { // If we have a full path to the compile unit, we don't need to // resolve // the file. This can be expensive e.g. when the source files are // NFS mounted. if (cu_file_spec.IsRelative()) { const char *cu_comp_dir{ cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)}; cu_file_spec.PrependPathComponent(resolveCompDir(cu_comp_dir)); } std::string remapped_file; if (module_sp->RemapSourceFile(cu_file_spec.GetPath(), remapped_file)) cu_file_spec.SetFile(remapped_file, false); } LanguageType cu_language = DWARFCompileUnit::LanguageTypeFromDWARF( cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0)); bool is_optimized = dwarf_cu->GetIsOptimized(); cu_sp.reset(new CompileUnit( module_sp, dwarf_cu, cu_file_spec, dwarf_cu->GetID(), cu_language, is_optimized ? eLazyBoolYes : eLazyBoolNo)); if (cu_sp) { // If we just created a compile unit with an invalid file spec, // try and get the // first entry in the supports files from the line table as that // should be the // compile unit. if (!cu_file_spec) { cu_file_spec = cu_sp->GetSupportFiles().GetFileSpecAtIndex(1); if (cu_file_spec) { (FileSpec &)(*cu_sp) = cu_file_spec; // Also fix the invalid file spec which was copied from the // compile unit. cu_sp->GetSupportFiles().Replace(0, cu_file_spec); } } dwarf_cu->SetUserData(cu_sp.get()); // Figure out the compile unit index if we weren't given one if (cu_idx == UINT32_MAX) DebugInfo()->GetCompileUnit(dwarf_cu->GetOffset(), &cu_idx); m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( cu_idx, cu_sp); } } } } } } return cu_sp; } uint32_t SymbolFileDWARF::GetNumCompileUnits() { DWARFDebugInfo *info = DebugInfo(); if (info) return info->GetNumCompileUnits(); return 0; } CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) { CompUnitSP cu_sp; DWARFDebugInfo *info = DebugInfo(); if (info) { DWARFCompileUnit *dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) cu_sp = ParseCompileUnit(dwarf_cu, cu_idx); } return cu_sp; } Function *SymbolFileDWARF::ParseCompileUnitFunction(const SymbolContext &sc, const DWARFDIE &die) { if (die.IsValid()) { TypeSystem *type_system = GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); if (type_system) { DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); if (dwarf_ast) return dwarf_ast->ParseFunctionFromDWARF(sc, die); } } return nullptr; } bool SymbolFileDWARF::FixupAddress(Address &addr) { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) { return debug_map_symfile->LinkOSOAddress(addr); } // This is a normal DWARF file, no address fixups need to happen return true; } lldb::LanguageType SymbolFileDWARF::ParseCompileUnitLanguage(const SymbolContext &sc) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) return dwarf_cu->GetLanguageType(); else return eLanguageTypeUnknown; } size_t SymbolFileDWARF::ParseCompileUnitFunctions(const SymbolContext &sc) { assert(sc.comp_unit); size_t functions_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { DWARFDIECollection function_dies; const size_t num_functions = dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies); size_t func_idx; for (func_idx = 0; func_idx < num_functions; ++func_idx) { DWARFDIE die = function_dies.GetDIEAtIndex(func_idx); if (sc.comp_unit->FindFunctionByUID(die.GetID()).get() == NULL) { if (ParseCompileUnitFunction(sc, die)) ++functions_added; } } // FixupTypes(); } return functions_added; } bool SymbolFileDWARF::ParseCompileUnitSupportFiles( const SymbolContext &sc, FileSpecList &support_files) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const DWARFDIE cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (cu_die) { const char *cu_comp_dir = resolveCompDir( cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)); const dw_offset_t stmt_list = cu_die.GetAttributeValueAsUnsigned( DW_AT_stmt_list, DW_INVALID_OFFSET); if (stmt_list != DW_INVALID_OFFSET) { // All file indexes in DWARF are one based and a file of index zero is // supposed to be the compile unit itself. support_files.Append(*sc.comp_unit); return DWARFDebugLine::ParseSupportFiles( sc.comp_unit->GetModule(), get_debug_line_data(), cu_comp_dir, stmt_list, support_files); } } } return false; } bool SymbolFileDWARF::ParseCompileUnitIsOptimized( const lldb_private::SymbolContext &sc) { DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) return dwarf_cu->GetIsOptimized(); return false; } bool SymbolFileDWARF::ParseImportedModules( const lldb_private::SymbolContext &sc, std::vector &imported_modules) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { if (ClangModulesDeclVendor::LanguageSupportsClangModules( sc.comp_unit->GetLanguage())) { UpdateExternalModuleListIfNeeded(); if (sc.comp_unit) { const DWARFDIE die = dwarf_cu->GetCompileUnitDIEOnly(); if (die) { for (DWARFDIE child_die = die.GetFirstChild(); child_die; child_die = child_die.GetSibling()) { if (child_die.Tag() == DW_TAG_imported_declaration) { if (DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import)) { if (module_die.Tag() == DW_TAG_module) { if (const char *name = module_die.GetAttributeValueAsString( DW_AT_name, nullptr)) { ConstString const_name(name); imported_modules.push_back(const_name); } } } } } } } else { for (const auto &pair : m_external_type_modules) { imported_modules.push_back(pair.first); } } } } return false; } struct ParseDWARFLineTableCallbackInfo { LineTable *line_table; std::unique_ptr sequence_ap; lldb::addr_t addr_mask; }; //---------------------------------------------------------------------- // ParseStatementTableCallback //---------------------------------------------------------------------- static void ParseDWARFLineTableCallback(dw_offset_t offset, const DWARFDebugLine::State &state, void *userData) { if (state.row == DWARFDebugLine::State::StartParsingLineTable) { // Just started parsing the line table } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) { // Done parsing line table, nothing to do for the cleanup } else { ParseDWARFLineTableCallbackInfo *info = (ParseDWARFLineTableCallbackInfo *)userData; LineTable *line_table = info->line_table; // If this is our first time here, we need to create a // sequence container. if (!info->sequence_ap.get()) { info->sequence_ap.reset(line_table->CreateLineSequenceContainer()); assert(info->sequence_ap.get()); } line_table->AppendLineEntryToSequence( info->sequence_ap.get(), state.address & info->addr_mask, state.line, state.column, state.file, state.is_stmt, state.basic_block, state.prologue_end, state.epilogue_begin, state.end_sequence); if (state.end_sequence) { // First, put the current sequence into the line table. line_table->InsertSequence(info->sequence_ap.get()); // Then, empty it to prepare for the next sequence. info->sequence_ap->Clear(); } } } bool SymbolFileDWARF::ParseCompileUnitLineTable(const SymbolContext &sc) { assert(sc.comp_unit); if (sc.comp_unit->GetLineTable() != NULL) return true; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const DWARFDIE dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (dwarf_cu_die) { const dw_offset_t cu_line_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list, DW_INVALID_OFFSET); if (cu_line_offset != DW_INVALID_OFFSET) { std::unique_ptr line_table_ap(new LineTable(sc.comp_unit)); if (line_table_ap.get()) { ParseDWARFLineTableCallbackInfo info; info.line_table = line_table_ap.get(); /* * MIPS: * The SymbolContext may not have a valid target, thus we may not be * able * to call Address::GetOpcodeLoadAddress() which would clear the bit * #0 * for MIPS. Use ArchSpec to clear the bit #0. */ ArchSpec arch; GetObjectFile()->GetArchitecture(arch); switch (arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: info.addr_mask = ~((lldb::addr_t)1); break; default: info.addr_mask = ~((lldb::addr_t)0); break; } lldb::offset_t offset = cu_line_offset; DWARFDebugLine::ParseStatementTable(get_debug_line_data(), &offset, ParseDWARFLineTableCallback, &info); SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) { // We have an object file that has a line table with addresses // that are not linked. We need to link the line table and convert // the addresses that are relative to the .o file into addresses // for the main executable. sc.comp_unit->SetLineTable( debug_map_symfile->LinkOSOLineTable(this, line_table_ap.get())); } else { sc.comp_unit->SetLineTable(line_table_ap.release()); return true; } } } } } return false; } lldb_private::DebugMacrosSP SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) { auto iter = m_debug_macros_map.find(*offset); if (iter != m_debug_macros_map.end()) return iter->second; const DWARFDataExtractor &debug_macro_data = get_debug_macro_data(); if (debug_macro_data.GetByteSize() == 0) return DebugMacrosSP(); lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros()); m_debug_macros_map[*offset] = debug_macros_sp; const DWARFDebugMacroHeader &header = DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset); DWARFDebugMacroEntry::ReadMacroEntries(debug_macro_data, get_debug_str_data(), header.OffsetIs64Bit(), offset, this, debug_macros_sp); return debug_macros_sp; } bool SymbolFileDWARF::ParseCompileUnitDebugMacros(const SymbolContext &sc) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu == nullptr) return false; const DWARFDIE dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (!dwarf_cu_die) return false; lldb::offset_t sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET); if (sect_offset == DW_INVALID_OFFSET) sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros, DW_INVALID_OFFSET); if (sect_offset == DW_INVALID_OFFSET) return false; sc.comp_unit->SetDebugMacros(ParseDebugMacros(§_offset)); return true; } size_t SymbolFileDWARF::ParseFunctionBlocks(const SymbolContext &sc, Block *parent_block, const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) { size_t blocks_added = 0; DWARFDIE die = orig_die; while (die) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: case DW_TAG_lexical_block: { Block *block = NULL; if (tag == DW_TAG_subprogram) { // Skip any DW_TAG_subprogram DIEs that are inside // of a normal or inlined functions. These will be // parsed on their own as separate entities. if (depth > 0) break; block = parent_block; } else { BlockSP block_sp(new Block(die.GetID())); parent_block->AddChild(block_sp); block = block_sp.get(); } DWARFRangeList ranges; const char *name = NULL; const char *mangled_name = NULL; int decl_file = 0; int decl_line = 0; int decl_column = 0; int call_file = 0; int call_line = 0; int call_column = 0; if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, nullptr)) { if (tag == DW_TAG_subprogram) { assert(subprogram_low_pc == LLDB_INVALID_ADDRESS); subprogram_low_pc = ranges.GetMinRangeBase(0); } else if (tag == DW_TAG_inlined_subroutine) { // We get called here for inlined subroutines in two ways. // The first time is when we are making the Function object // for this inlined concrete instance. Since we're creating a top // level block at // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we // need to // adjust the containing address. // The second time is when we are parsing the blocks inside the // function that contains // the inlined concrete instance. Since these will be blocks inside // the containing "real" // function the offset will be for that function. if (subprogram_low_pc == LLDB_INVALID_ADDRESS) { subprogram_low_pc = ranges.GetMinRangeBase(0); } } const size_t num_ranges = ranges.GetSize(); for (size_t i = 0; i < num_ranges; ++i) { const DWARFRangeList::Entry &range = ranges.GetEntryRef(i); const addr_t range_base = range.GetRangeBase(); if (range_base >= subprogram_low_pc) block->AddRange(Block::Range(range_base - subprogram_low_pc, range.GetByteSize())); else { GetObjectFile()->GetModule()->ReportError( "0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64 ") which has a base that is less than the function's low PC " "0x%" PRIx64 ". Please file a bug and attach the file at the " "start of this error message", block->GetID(), range_base, range.GetRangeEnd(), subprogram_low_pc); } } block->FinalizeRanges(); if (tag != DW_TAG_subprogram && (name != NULL || mangled_name != NULL)) { std::unique_ptr decl_ap; if (decl_file != 0 || decl_line != 0 || decl_column != 0) decl_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); std::unique_ptr call_ap; if (call_file != 0 || call_line != 0 || call_column != 0) call_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(call_file), call_line, call_column)); block->SetInlinedFunctionInfo(name, mangled_name, decl_ap.get(), call_ap.get()); } ++blocks_added; if (die.HasChildren()) { blocks_added += ParseFunctionBlocks(sc, block, die.GetFirstChild(), subprogram_low_pc, depth + 1); } } } break; default: break; } // Only parse siblings of the block if we are not at depth zero. A depth // of zero indicates we are currently parsing the top level // DW_TAG_subprogram DIE if (depth == 0) die.Clear(); else die = die.GetSibling(); } return blocks_added; } bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) { if (parent_die) { for (DWARFDIE die = parent_die.GetFirstChild(); die; die = die.GetSibling()) { dw_tag_t tag = die.Tag(); bool check_virtuality = false; switch (tag) { case DW_TAG_inheritance: case DW_TAG_subprogram: check_virtuality = true; break; default: break; } if (check_virtuality) { if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0) return true; } } } return false; } void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) { TypeSystem *type_system = decl_ctx.GetTypeSystem(); DWARFASTParser *ast_parser = type_system->GetDWARFParser(); std::vector decl_ctx_die_list = ast_parser->GetDIEForDeclContext(decl_ctx); for (DWARFDIE decl_ctx_die : decl_ctx_die_list) for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl; decl = decl.GetSibling()) ast_parser->GetDeclForUIDFromDWARF(decl); } SymbolFileDWARF *SymbolFileDWARF::GetDWARFForUID(lldb::user_id_t uid) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API // we must make sure we use the correct DWARF file when resolving things. // On MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple // SymbolFileDWARF classes, one for each .o file. We can often end up // with references to other DWARF objects and we must be ready to receive // a "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile(); if (debug_map) return debug_map->GetSymbolFileByOSOIndex( debug_map->GetOSOIndexFromUserID(uid)); return this; } DWARFDIE SymbolFileDWARF::GetDIEFromUID(lldb::user_id_t uid) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API // we must make sure we use the correct DWARF file when resolving things. // On MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple // SymbolFileDWARF classes, one for each .o file. We can often end up // with references to other DWARF objects and we must be ready to receive // a "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. SymbolFileDWARF *dwarf = GetDWARFForUID(uid); if (dwarf) return dwarf->GetDIE(DIERef(uid, dwarf)); return DWARFDIE(); } CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetDecl(); return CompilerDecl(); } CompilerDeclContext SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetDeclContext(); return CompilerDeclContext(); } CompilerDeclContext SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetContainingDeclContext(); return CompilerDeclContext(); } Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE type_die = GetDIEFromUID(type_uid); if (type_die) return type_die.ResolveType(); else return nullptr; } Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) { return ResolveType(GetDIE(die_ref), true); } Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die, bool assert_not_being_parsed) { if (die) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'", die.GetOffset(), die.GetTagAsCString(), die.GetName()); // We might be coming in in the middle of a type tree (a class // within a class, an enum within a class), so parse any needed // parent DIEs before we get to this one... DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die); if (decl_ctx_die) { if (log) { switch (decl_ctx_die.Tag()) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { // Get the type, which could be a forward declaration if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' " "resolve parent forward type for 0x%8.8x", die.GetOffset(), die.GetTagAsCString(), die.GetName(), decl_ctx_die.GetOffset()); } break; default: break; } } } return ResolveType(die); } return NULL; } // This function is used when SymbolFileDWARFDebugMap owns a bunch of // SymbolFileDWARF objects to detect if this DWARF file is the one that // can resolve a compiler_type. bool SymbolFileDWARF::HasForwardDeclForClangType( const CompilerType &compiler_type) { CompilerType compiler_type_no_qualifiers = ClangUtil::RemoveFastQualifiers(compiler_type); if (GetForwardDeclClangTypeToDie().count( compiler_type_no_qualifiers.GetOpaqueQualType())) { return true; } TypeSystem *type_system = compiler_type.GetTypeSystem(); ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(type_system); if (!clang_type_system) return false; DWARFASTParserClang *ast_parser = static_cast(clang_type_system->GetDWARFParser()); return ast_parser->GetClangASTImporter().CanImport(compiler_type); } bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) { std::lock_guard guard( GetObjectFile()->GetModule()->GetMutex()); ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(compiler_type.GetTypeSystem()); if (clang_type_system) { DWARFASTParserClang *ast_parser = static_cast(clang_type_system->GetDWARFParser()); if (ast_parser && ast_parser->GetClangASTImporter().CanImport(compiler_type)) return ast_parser->GetClangASTImporter().CompleteType(compiler_type); } // We have a struct/union/class/enum that needs to be fully resolved. CompilerType compiler_type_no_qualifiers = ClangUtil::RemoveFastQualifiers(compiler_type); auto die_it = GetForwardDeclClangTypeToDie().find( compiler_type_no_qualifiers.GetOpaqueQualType()); if (die_it == GetForwardDeclClangTypeToDie().end()) { // We have already resolved this type... return true; } DWARFDIE dwarf_die = GetDIE(die_it->getSecond()); if (dwarf_die) { // Once we start resolving this type, remove it from the forward declaration // map in case anyone child members or other types require this type to get // resolved. // The type will get resolved when all of the calls to // SymbolFileDWARF::ResolveClangOpaqueTypeDefinition // are done. GetForwardDeclClangTypeToDie().erase(die_it); Type *type = GetDIEToType().lookup(dwarf_die.GetDIE()); Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | DWARF_LOG_TYPE_COMPLETION)); if (log) GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", dwarf_die.GetID(), dwarf_die.GetTagAsCString(), type->GetName().AsCString()); assert(compiler_type); DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser(); if (dwarf_ast) return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type); } return false; } Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die, bool assert_not_being_parsed, bool resolve_function_context) { if (die) { Type *type = GetTypeForDIE(die, resolve_function_context).get(); if (assert_not_being_parsed) { if (type != DIE_IS_BEING_PARSED) return type; GetObjectFile()->GetModule()->ReportError( "Parsing a die that is being parsed die: 0x%8.8x: %s %s", die.GetOffset(), die.GetTagAsCString(), die.GetName()); } else return type; } return nullptr; } CompileUnit * SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit *dwarf_cu, uint32_t cu_idx) { // Check if the symbol vendor already knows about this compile unit? if (dwarf_cu->GetUserData() == NULL) { // The symbol vendor doesn't know about this compile unit, we // need to parse and add it to the symbol vendor object. return ParseCompileUnit(dwarf_cu, cu_idx).get(); } return (CompileUnit *)dwarf_cu->GetUserData(); } size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name, DIEArray &method_die_offsets) { method_die_offsets.clear(); if (m_using_apple_tables) { if (m_apple_objc_ap.get()) m_apple_objc_ap->FindByName(class_name.GetCString(), method_die_offsets); } else { if (!m_indexed) Index(); m_objc_class_selectors_index.Find(class_name, method_die_offsets); } return method_die_offsets.size(); } bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) { sc.Clear(false); if (die) { // Check if the symbol vendor already knows about this compile unit? sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, die); if (sc.function) { sc.module_sp = sc.function->CalculateSymbolContextModule(); return true; } } return false; } lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) { UpdateExternalModuleListIfNeeded(); const auto &pos = m_external_type_modules.find(name); if (pos != m_external_type_modules.end()) return pos->second; else return lldb::ModuleSP(); } DWARFDIE SymbolFileDWARF::GetDIE(const DIERef &die_ref) { DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) return debug_info->GetDIE(die_ref); else return DWARFDIE(); } std::unique_ptr SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( DWARFCompileUnit &dwarf_cu, const DWARFDebugInfoEntry &cu_die) { // If we are using a dSYM file, we never want the standard DWO files since // the -gmodule support uses the same DWO machanism to specify full debug // info files for modules. if (GetDebugMapSymfile()) return nullptr; const char *dwo_name = cu_die.GetAttributeValueAsString( this, &dwarf_cu, DW_AT_GNU_dwo_name, nullptr); if (!dwo_name) return nullptr; FileSpec dwo_file(dwo_name, true); if (dwo_file.IsRelative()) { const char *comp_dir = cu_die.GetAttributeValueAsString( this, &dwarf_cu, DW_AT_comp_dir, nullptr); if (!comp_dir) return nullptr; dwo_file.SetFile(comp_dir, true); dwo_file.AppendPathComponent(dwo_name); } if (!dwo_file.Exists()) return nullptr; const lldb::offset_t file_offset = 0; DataBufferSP dwo_file_data_sp; lldb::offset_t dwo_file_data_offset = 0; ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin( GetObjectFile()->GetModule(), &dwo_file, file_offset, dwo_file.GetByteSize(), dwo_file_data_sp, dwo_file_data_offset); if (dwo_obj_file == nullptr) return nullptr; return llvm::make_unique(dwo_obj_file, &dwarf_cu); } void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() { if (m_fetched_external_modules) return; m_fetched_external_modules = true; DWARFDebugInfo *debug_info = DebugInfo(); const uint32_t num_compile_units = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); const DWARFDIE die = dwarf_cu->GetCompileUnitDIEOnly(); if (die && die.HasChildren() == false) { const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr); if (name) { ConstString const_name(name); if (m_external_type_modules.find(const_name) == m_external_type_modules.end()) { ModuleSP module_sp; const char *dwo_path = die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr); if (dwo_path) { ModuleSpec dwo_module_spec; dwo_module_spec.GetFileSpec().SetFile(dwo_path, false); if (dwo_module_spec.GetFileSpec().IsRelative()) { const char *comp_dir = die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr); if (comp_dir) { dwo_module_spec.GetFileSpec().SetFile(comp_dir, true); dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path); } } dwo_module_spec.GetArchitecture() = m_obj_file->GetModule()->GetArchitecture(); // printf ("Loading dwo = '%s'\n", dwo_path); Error error = ModuleList::GetSharedModule( dwo_module_spec, module_sp, NULL, NULL, NULL); if (!module_sp) { GetObjectFile()->GetModule()->ReportWarning( "0x%8.8x: unable to locate module needed for external types: " "%s\nerror: %s\nDebugging will be degraded due to missing " "types. Rebuilding your project will regenerate the needed " "module files.", die.GetOffset(), dwo_module_spec.GetFileSpec().GetPath().c_str(), error.AsCString("unknown error")); } } m_external_type_modules[const_name] = module_sp; } } } } } SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { if (!m_global_aranges_ap) { m_global_aranges_ap.reset(new GlobalVariableMap()); ModuleSP module_sp = GetObjectFile()->GetModule(); if (module_sp) { const size_t num_cus = module_sp->GetNumCompileUnits(); for (size_t i = 0; i < num_cus; ++i) { CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i); if (cu_sp) { VariableListSP globals_sp = cu_sp->GetVariableList(true); if (globals_sp) { const size_t num_globals = globals_sp->GetSize(); 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(); Value location_result; Error error; if (location.Evaluate(nullptr, nullptr, nullptr, LLDB_INVALID_ADDRESS, nullptr, nullptr, location_result, &error)) { if (location_result.GetValueType() == Value::eValueTypeFileAddress) { lldb::addr_t file_addr = location_result.GetScalar().ULongLong(); lldb::addr_t byte_size = 1; if (var_sp->GetType()) byte_size = var_sp->GetType()->GetByteSize(); m_global_aranges_ap->Append(GlobalVariableMap::Entry( file_addr, byte_size, var_sp.get())); } } } } } } } } m_global_aranges_ap->Sort(); } return *m_global_aranges_ap; } uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr, uint32_t resolve_scope, SymbolContext &sc) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::" "ResolveSymbolContext (so_addr = { " "section = %p, offset = 0x%" PRIx64 " }, resolve_scope = 0x%8.8x)", static_cast(so_addr.GetSection().get()), so_addr.GetOffset(), resolve_scope); uint32_t resolved = 0; if (resolve_scope & (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock | eSymbolContextLineEntry | eSymbolContextVariable)) { lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { const dw_offset_t cu_offset = debug_info->GetCompileUnitAranges().FindAddress(file_vm_addr); if (cu_offset == DW_INVALID_OFFSET) { // Global variables are not in the compile unit address ranges. The only // way to // currently find global variables is to iterate over the // .debug_pubnames or the // __apple_names table and find all items in there that point to // DW_TAG_variable // DIEs and then find the address that matches. if (resolve_scope & eSymbolContextVariable) { GlobalVariableMap &map = GetGlobalAranges(); const GlobalVariableMap::Entry *entry = map.FindEntryThatContains(file_vm_addr); if (entry && entry->data) { Variable *variable = entry->data; SymbolContextScope *scc = variable->GetSymbolContextScope(); if (scc) { scc->CalculateSymbolContext(&sc); sc.variable = variable; } return sc.GetResolvedMask(); } } } else { uint32_t cu_idx = DW_INVALID_INDEX; DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnit(cu_offset, &cu_idx); if (dwarf_cu) { sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); if (sc.comp_unit) { resolved |= eSymbolContextCompUnit; bool force_check_line_table = false; if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); DWARFDIE block_die; if (function_die) { sc.function = sc.comp_unit->FindFunctionByUID(function_die.GetID()).get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, function_die); if (sc.function && (resolve_scope & eSymbolContextBlock)) block_die = function_die.LookupDeepestBlock(file_vm_addr); } else { // We might have had a compile unit that had discontiguous // address ranges where the gaps are symbols that don't have // any debug info. Discontiguous compile unit address ranges // should only happen when there aren't other functions from // other compile units in these gaps. This helps keep the size // of the aranges down. force_check_line_table = true; } if (sc.function != NULL) { resolved |= eSymbolContextFunction; if (resolve_scope & eSymbolContextBlock) { Block &block = sc.function->GetBlock(true); if (block_die) sc.block = block.FindBlockByID(block_die.GetID()); else sc.block = block.FindBlockByID(function_die.GetID()); if (sc.block) resolved |= eSymbolContextBlock; } } } if ((resolve_scope & eSymbolContextLineEntry) || force_check_line_table) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != NULL) { // And address that makes it into this function should be in // terms // of this debug file if there is no debug map, or it will be an // address in the .o file which needs to be fixed up to be in // terms // of the debug map executable. Either way, calling // FixupAddress() // will work for us. Address exe_so_addr(so_addr); if (FixupAddress(exe_so_addr)) { if (line_table->FindLineEntryByAddress(exe_so_addr, sc.line_entry)) { resolved |= eSymbolContextLineEntry; } } } } if (force_check_line_table && !(resolved & eSymbolContextLineEntry)) { // We might have had a compile unit that had discontiguous // address ranges where the gaps are symbols that don't have // any debug info. Discontiguous compile unit address ranges // should only happen when there aren't other functions from // other compile units in these gaps. This helps keep the size // of the aranges down. sc.comp_unit = NULL; resolved &= ~eSymbolContextCompUnit; } } else { GetObjectFile()->GetModule()->ReportWarning( "0x%8.8x: compile unit %u failed to create a valid " "lldb_private::CompileUnit class.", cu_offset, cu_idx); } } } } } return resolved; } uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList &sc_list) { const uint32_t prev_size = sc_list.GetSize(); if (resolve_scope & eSymbolContextCompUnit) { DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { uint32_t cu_idx; DWARFCompileUnit *dwarf_cu = NULL; for (cu_idx = 0; (dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx)) != NULL; ++cu_idx) { CompileUnit *dc_cu = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); const bool full_match = (bool)file_spec.GetDirectory(); bool file_spec_matches_cu_file_spec = dc_cu != NULL && FileSpec::Equal(file_spec, *dc_cu, full_match); if (check_inlines || file_spec_matches_cu_file_spec) { SymbolContext sc(m_obj_file->GetModule()); sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); if (sc.comp_unit) { uint32_t file_idx = UINT32_MAX; // If we are looking for inline functions only and we don't // find it in the support files, we are done. if (check_inlines) { file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( 1, file_spec, true); if (file_idx == UINT32_MAX) continue; } if (line != 0) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != NULL && line != 0) { // We will have already looked up the file index if // we are searching for inline entries. if (!check_inlines) file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( 1, file_spec, true); if (file_idx != UINT32_MAX) { uint32_t found_line; uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex( 0, file_idx, line, false, &sc.line_entry); found_line = sc.line_entry.line; while (line_idx != UINT32_MAX) { sc.function = NULL; sc.block = NULL; if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { const lldb::addr_t file_vm_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); if (file_vm_addr != LLDB_INVALID_ADDRESS) { DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); DWARFDIE block_die; if (function_die) { sc.function = sc.comp_unit ->FindFunctionByUID(function_die.GetID()) .get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, function_die); if (sc.function && (resolve_scope & eSymbolContextBlock)) block_die = function_die.LookupDeepestBlock(file_vm_addr); } if (sc.function != NULL) { Block &block = sc.function->GetBlock(true); if (block_die) sc.block = block.FindBlockByID(block_die.GetID()); else if (function_die) sc.block = block.FindBlockByID(function_die.GetID()); } } } sc_list.Append(sc); line_idx = line_table->FindLineEntryIndexByFileIndex( line_idx + 1, file_idx, found_line, true, &sc.line_entry); } } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call // sites // by file and line and if the file spec matches that of the // compile unit sc_list.Append(sc); } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call // sites // by file and line and if the file spec matches that of the // compile unit sc_list.Append(sc); } if (!check_inlines) break; } } } } } return sc_list.GetSize() - prev_size; } void SymbolFileDWARF::PreloadSymbols() { std::lock_guard guard( GetObjectFile()->GetModule()->GetMutex()); Index(); } void SymbolFileDWARF::Index() { if (m_indexed) return; m_indexed = true; Timer scoped_timer( LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::Index (%s)", GetObjectFile()->GetFileSpec().GetFilename().AsCString("")); DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { const uint32_t num_compile_units = GetNumCompileUnits(); if (num_compile_units == 0) return; std::vector function_basename_index(num_compile_units); std::vector function_fullname_index(num_compile_units); std::vector function_method_index(num_compile_units); std::vector function_selector_index(num_compile_units); std::vector objc_class_selectors_index(num_compile_units); std::vector global_index(num_compile_units); std::vector type_index(num_compile_units); std::vector namespace_index(num_compile_units); - std::vector clear_cu_dies(num_compile_units, false); + // std::vector might be implemented using bit test-and-set, so use + // uint8_t instead. + std::vector clear_cu_dies(num_compile_units, false); auto parser_fn = [debug_info, &function_basename_index, &function_fullname_index, &function_method_index, &function_selector_index, &objc_class_selectors_index, &global_index, &type_index, &namespace_index](uint32_t cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { dwarf_cu->Index( function_basename_index[cu_idx], function_fullname_index[cu_idx], function_method_index[cu_idx], function_selector_index[cu_idx], objc_class_selectors_index[cu_idx], global_index[cu_idx], type_index[cu_idx], namespace_index[cu_idx]); } return cu_idx; }; - auto extract_fn = [debug_info](uint32_t cu_idx) { + auto extract_fn = [debug_info, &clear_cu_dies](uint32_t cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { // dwarf_cu->ExtractDIEsIfNeeded(false) will return zero if the // DIEs for a compile unit have already been parsed. - return std::make_pair(cu_idx, dwarf_cu->ExtractDIEsIfNeeded(false) > 1); + if (dwarf_cu->ExtractDIEsIfNeeded(false) > 1) + clear_cu_dies[cu_idx] = true; } - return std::make_pair(cu_idx, false); }; // Create a task runner that extracts dies for each DWARF compile unit in a // separate thread - TaskRunner> task_runner_extract; - for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) - task_runner_extract.AddTask(extract_fn, cu_idx); - //---------------------------------------------------------------------- // First figure out which compile units didn't have their DIEs already // parsed and remember this. If no DIEs were parsed prior to this index // function call, we are going to want to clear the CU dies after we // are done indexing to make sure we don't pull in all DWARF dies, but // we need to wait until all compile units have been indexed in case // a DIE in one compile unit refers to another and the indexes accesses // those DIEs. //---------------------------------------------------------------------- - while (true) { - auto f = task_runner_extract.WaitForNextCompletedTask(); - if (!f.valid()) - break; - unsigned cu_idx; - bool clear; - std::tie(cu_idx, clear) = f.get(); - clear_cu_dies[cu_idx] = clear; - } + TaskMapOverInt(0, num_compile_units, extract_fn); // Now create a task runner that can index each DWARF compile unit in a // separate // thread so we can index quickly. - TaskRunner task_runner; - for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) - task_runner.AddTask(parser_fn, cu_idx); + TaskMapOverInt(0, num_compile_units, parser_fn); - while (true) { - std::future f = task_runner.WaitForNextCompletedTask(); - if (!f.valid()) - break; - uint32_t cu_idx = f.get(); + auto finalize_fn = [](NameToDIE &index, std::vector &srcs) { + for (auto &src : srcs) + index.Append(src); + index.Finalize(); + }; - m_function_basename_index.Append(function_basename_index[cu_idx]); - m_function_fullname_index.Append(function_fullname_index[cu_idx]); - m_function_method_index.Append(function_method_index[cu_idx]); - m_function_selector_index.Append(function_selector_index[cu_idx]); - m_objc_class_selectors_index.Append(objc_class_selectors_index[cu_idx]); - m_global_index.Append(global_index[cu_idx]); - m_type_index.Append(type_index[cu_idx]); - m_namespace_index.Append(namespace_index[cu_idx]); - } - - TaskPool::RunTasks([&]() { m_function_basename_index.Finalize(); }, - [&]() { m_function_fullname_index.Finalize(); }, - [&]() { m_function_method_index.Finalize(); }, - [&]() { m_function_selector_index.Finalize(); }, - [&]() { m_objc_class_selectors_index.Finalize(); }, - [&]() { m_global_index.Finalize(); }, - [&]() { m_type_index.Finalize(); }, - [&]() { m_namespace_index.Finalize(); }); + TaskPool::RunTasks( + [&]() { + finalize_fn(m_function_basename_index, function_basename_index); + }, + [&]() { + finalize_fn(m_function_fullname_index, function_fullname_index); + }, + [&]() { finalize_fn(m_function_method_index, function_method_index); }, + [&]() { + finalize_fn(m_function_selector_index, function_selector_index); + }, + [&]() { + finalize_fn(m_objc_class_selectors_index, objc_class_selectors_index); + }, + [&]() { finalize_fn(m_global_index, global_index); }, + [&]() { finalize_fn(m_type_index, type_index); }, + [&]() { finalize_fn(m_namespace_index, namespace_index); }); //---------------------------------------------------------------------- // Keep memory down by clearing DIEs for any compile units if indexing // caused us to load the compile unit's DIEs. //---------------------------------------------------------------------- for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { if (clear_cu_dies[cu_idx]) debug_info->GetCompileUnitAtIndex(cu_idx)->ClearDIEs(true); } #if defined(ENABLE_DEBUG_PRINTF) StreamFile s(stdout, false); s.Printf("DWARF index for '%s':", GetObjectFile()->GetFileSpec().GetPath().c_str()); s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump(&s); s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump(&s); s.Printf("\nFunction methods:\n"); m_function_method_index.Dump(&s); s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump(&s); s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump(&s); s.Printf("\nGlobals and statics:\n"); m_global_index.Dump(&s); s.Printf("\nTypes:\n"); m_type_index.Dump(&s); s.Printf("\nNamespaces:\n"); m_namespace_index.Dump(&s); #endif } } bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile( const lldb_private::CompilerDeclContext *decl_ctx) { if (decl_ctx == nullptr || !decl_ctx->IsValid()) { // Invalid namespace decl which means we aren't matching only things // in this symbol file, so return true to indicate it matches this // symbol file. return true; } TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem(); TypeSystem *type_system = GetTypeSystemForLanguage( decl_ctx_type_system->GetMinimumLanguage(nullptr)); if (decl_ctx_type_system == type_system) return true; // The type systems match, return true // The namespace AST was valid, and it does not match... Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "Valid namespace does not match symbol file"); return false; } uint32_t SymbolFileDWARF::FindGlobalVariables( const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, VariableList &variables) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " "parent_decl_ctx=%p, append=%u, max_matches=%u, variables)", name.GetCString(), static_cast(parent_decl_ctx), append, max_matches); if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { const char *name_cstr = name.GetCString(); llvm::StringRef basename; llvm::StringRef context; if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context, basename)) basename = name_cstr; m_apple_names_ap->FindByName(basename.data(), die_offsets); } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); m_global_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { SymbolContext sc; sc.module_sp = m_obj_file->GetModule(); assert(sc.module_sp); bool done = false; for (size_t i = 0; i < num_die_matches && !done; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { switch (die.Tag()) { default: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_try_block: case DW_TAG_catch_block: break; case DW_TAG_variable: { sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); if (parent_decl_ctx) { DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { CompilerDeclContext actual_parent_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); if (!actual_parent_decl_ctx || actual_parent_decl_ctx != *parent_decl_ctx) continue; } } ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); if (variables.GetSize() - original_size >= max_matches) done = true; } break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } } // Return the number of variable that were appended to the list const uint32_t num_matches = variables.GetSize() - original_size; if (log && num_matches > 0) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " "parent_decl_ctx=%p, append=%u, max_matches=%u, variables) => %u", name.GetCString(), static_cast(parent_decl_ctx), append, max_matches, num_matches); } return num_matches; } uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression ®ex, bool append, uint32_t max_matches, VariableList &variables) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", append=%u, " "max_matches=%u, variables)", regex.GetText().str().c_str(), append, max_matches); } DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DWARFMappedHash::DIEInfoArray hash_data_array; if (m_apple_names_ap->AppendAllDIEsThatMatchingRegex(regex, hash_data_array)) DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); m_global_index.Find(regex, die_offsets); } SymbolContext sc; sc.module_sp = m_obj_file->GetModule(); assert(sc.module_sp); const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); if (variables.GetSize() - original_size >= max_matches) break; } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for regex '%s')\n", die_ref.die_offset, regex.GetText().str().c_str()); } } } } // Return the number of variable that were appended to the list return variables.GetSize() - original_size; } bool SymbolFileDWARF::ResolveFunction(const DIERef &die_ref, bool include_inlines, SymbolContextList &sc_list) { DWARFDIE die = DebugInfo()->GetDIE(die_ref); return ResolveFunction(die, include_inlines, sc_list); } bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, bool include_inlines, SymbolContextList &sc_list) { SymbolContext sc; if (!orig_die) return false; // If we were passed a die that is not a function, just return false... if (!(orig_die.Tag() == DW_TAG_subprogram || (include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine))) return false; DWARFDIE die = orig_die; DWARFDIE inlined_die; if (die.Tag() == DW_TAG_inlined_subroutine) { inlined_die = die; while (1) { die = die.GetParent(); if (die) { if (die.Tag() == DW_TAG_subprogram) break; } else break; } } assert(die && die.Tag() == DW_TAG_subprogram); if (GetFunction(die, sc)) { Address addr; // Parse all blocks if needed if (inlined_die) { Block &function_block = sc.function->GetBlock(true); sc.block = function_block.FindBlockByID(inlined_die.GetID()); if (sc.block == NULL) sc.block = function_block.FindBlockByID(inlined_die.GetOffset()); if (sc.block == NULL || sc.block->GetStartAddress(addr) == false) addr.Clear(); } else { sc.block = NULL; addr = sc.function->GetAddressRange().GetBaseAddress(); } if (addr.IsValid()) { sc_list.Append(sc); return true; } } return false; } void SymbolFileDWARF::FindFunctions(const ConstString &name, const NameToDIE &name_to_die, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; if (name_to_die.Find(name, die_offsets)) { ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, const NameToDIE &name_to_die, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; if (name_to_die.Find(regex, die_offsets)) { ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::FindFunctions( const RegularExpression ®ex, const DWARFMappedHash::MemoryTable &memory_table, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; DWARFMappedHash::DIEInfoArray hash_data_array; if (memory_table.AppendAllDIEsThatMatchingRegex(regex, hash_data_array)) { DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::ParseFunctions(const DIEArray &die_offsets, bool include_inlines, SymbolContextList &sc_list) { const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) ResolveFunction(die_offsets[i], include_inlines, sc_list); } } bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx, const DWARFDIE &die) { // If we have no parent decl context to match this DIE matches, and if the // parent // decl context isn't valid, we aren't trying to look for any particular decl // context so any die matches. if (decl_ctx == nullptr || !decl_ctx->IsValid()) return true; if (die) { DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { CompilerDeclContext actual_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); if (actual_decl_ctx) return actual_decl_ctx == *decl_ctx; } } return false; } uint32_t SymbolFileDWARF::FindFunctions(const ConstString &name, const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::FindFunctions (name = '%s')", name.AsCString()); // eFunctionNameTypeAuto should be pre-resolved by a call to // Module::LookupInfo::LookupInfo() assert((name_type_mask & eFunctionNameTypeAuto) == 0); Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " "name_type_mask=0x%x, append=%u, sc_list)", name.GetCString(), name_type_mask, append); } // If we aren't appending the results to this list, then clear the list if (!append) sc_list.Clear(); if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; // If name is empty then we won't find anything. if (name.IsEmpty()) return 0; // Remember how many sc_list are in the list before we search in case // we are appending the results to a variable list. const char *name_cstr = name.GetCString(); const uint32_t original_size = sc_list.GetSize(); DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; std::set resolved_dies; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DIEArray die_offsets; uint32_t num_matches = 0; if (name_type_mask & eFunctionNameTypeFull) { // If they asked for the full name, match what they typed. At some // point we may // want to canonicalize this (strip double spaces, etc. For now, we // just add all the // dies that we find by exact match. num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } else { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } } if (name_type_mask & eFunctionNameTypeSelector) { if (parent_decl_ctx && parent_decl_ctx->IsValid()) return 0; // no selectors in namespaces num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); // Now make sure these are actually ObjC methods. In this case we can // simply look up the name, // and if it is an ObjC method name, we're good. for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { const char *die_name = die.GetName(); if (ObjCLanguage::IsPossibleObjCMethodName(die_name)) { if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } else { GetObjectFile()->GetModule()->ReportError( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } die_offsets.clear(); } if (((name_type_mask & eFunctionNameTypeMethod) && !parent_decl_ctx) || name_type_mask & eFunctionNameTypeBase) { // The apple_names table stores just the "base name" of C++ methods in // the table. So we have to // extract the base name, look that up, and if there is any other // information in the name we were // passed in we have to post-filter based on that. // FIXME: Arrange the logic above so that we don't calculate the base // name twice: num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end() && ResolveFunction(die, include_inlines, sc_list)) { bool keep_die = true; if ((name_type_mask & (eFunctionNameTypeBase | eFunctionNameTypeMethod)) != (eFunctionNameTypeBase | eFunctionNameTypeMethod)) { // We are looking for either basenames or methods, so we need to // trim out the ones we won't want by looking at the type SymbolContext sc; if (sc_list.GetLastContext(sc)) { if (sc.block) { // We have an inlined function } else if (sc.function) { Type *type = sc.function->GetType(); if (type) { CompilerDeclContext decl_ctx = GetDeclContextContainingUID(type->GetID()); if (decl_ctx.IsStructUnionOrClass()) { if (name_type_mask & eFunctionNameTypeBase) { sc_list.RemoveContextAtIndex(sc_list.GetSize() - 1); keep_die = false; } } else { if (name_type_mask & eFunctionNameTypeMethod) { sc_list.RemoveContextAtIndex(sc_list.GetSize() - 1); keep_die = false; } } } else { GetObjectFile()->GetModule()->ReportWarning( "function at die offset 0x%8.8x had no function type", die_ref.die_offset); } } } } if (keep_die) resolved_dies.insert(die.GetDIE()); } } else { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } die_offsets.clear(); } } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); if (name_type_mask & eFunctionNameTypeFull) { FindFunctions(name, m_function_fullname_index, include_inlines, sc_list); // FIXME Temporary workaround for global/anonymous namespace // functions debugging FreeBSD and Linux binaries. // If we didn't find any functions in the global namespace try // looking in the basename index but ignore any returned // functions that have a namespace but keep functions which // have an anonymous namespace // TODO: The arch in the object file isn't correct for MSVC // binaries on windows, we should find a way to make it // correct and handle those symbols as well. if (sc_list.GetSize() == original_size) { ArchSpec arch; if (!parent_decl_ctx && GetObjectFile()->GetArchitecture(arch) && arch.GetTriple().isOSBinFormatELF()) { SymbolContextList temp_sc_list; FindFunctions(name, m_function_basename_index, include_inlines, temp_sc_list); SymbolContext sc; for (uint32_t i = 0; i < temp_sc_list.GetSize(); i++) { if (temp_sc_list.GetContextAtIndex(i, sc)) { ConstString mangled_name = sc.GetFunctionName(Mangled::ePreferMangled); ConstString demangled_name = sc.GetFunctionName(Mangled::ePreferDemangled); // Mangled names on Linux and FreeBSD are of the form: // _ZN18function_namespace13function_nameEv. if (strncmp(mangled_name.GetCString(), "_ZN", 3) || !strncmp(demangled_name.GetCString(), "(anonymous namespace)", 21)) { sc_list.Append(sc); } } } } } } DIEArray die_offsets; if (name_type_mask & eFunctionNameTypeBase) { uint32_t num_base = m_function_basename_index.Find(name, die_offsets); for (uint32_t i = 0; i < num_base; i++) { DWARFDIE die = info->GetDIE(die_offsets[i]); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } die_offsets.clear(); } if (name_type_mask & eFunctionNameTypeMethod) { if (parent_decl_ctx && parent_decl_ctx->IsValid()) return 0; // no methods in namespaces uint32_t num_base = m_function_method_index.Find(name, die_offsets); { for (uint32_t i = 0; i < num_base; i++) { DWARFDIE die = info->GetDIE(die_offsets[i]); if (die) { // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } } die_offsets.clear(); } if ((name_type_mask & eFunctionNameTypeSelector) && (!parent_decl_ctx || !parent_decl_ctx->IsValid())) { FindFunctions(name, m_function_selector_index, include_inlines, sc_list); } } // Return the number of variable that were appended to the list const uint32_t num_matches = sc_list.GetSize() - original_size; if (log && num_matches > 0) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " "name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => " "%u", name.GetCString(), name_type_mask, include_inlines, append, num_matches); } return num_matches; } uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, bool include_inlines, bool append, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::FindFunctions (regex = '%s')", regex.GetText().str().c_str()); Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)", regex.GetText().str().c_str(), append); } // If we aren't appending the results to this list, then clear the list if (!append) sc_list.Clear(); // Remember how many sc_list are in the list before we search in case // we are appending the results to a variable list. uint32_t original_size = sc_list.GetSize(); if (m_using_apple_tables) { if (m_apple_names_ap.get()) FindFunctions(regex, *m_apple_names_ap, include_inlines, sc_list); } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); FindFunctions(regex, m_function_basename_index, include_inlines, sc_list); FindFunctions(regex, m_function_fullname_index, include_inlines, sc_list); } // Return the number of variable that were appended to the list return sc_list.GetSize() - original_size; } void SymbolFileDWARF::GetMangledNamesForFunction( const std::string &scope_qualified_name, std::vector &mangled_names) { DWARFDebugInfo *info = DebugInfo(); uint32_t num_comp_units = 0; if (info) num_comp_units = info->GetNumCompileUnits(); for (uint32_t i = 0; i < num_comp_units; i++) { DWARFCompileUnit *cu = info->GetCompileUnitAtIndex(i); if (cu == nullptr) continue; SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile(); if (dwo) dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names); } NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find(scope_qualified_name); if (iter == m_function_scope_qualified_name_map.end()) return; DIERefSetSP set_sp = (*iter).second; std::set::iterator set_iter; for (set_iter = set_sp->begin(); set_iter != set_sp->end(); set_iter++) { DWARFDIE die = DebugInfo()->GetDIE(*set_iter); mangled_names.push_back(ConstString(die.GetMangledName())); } } uint32_t SymbolFileDWARF::FindTypes( const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, llvm::DenseSet &searched_symbol_files, TypeMap &types) { // If we aren't appending the results to this list, then clear the list if (!append) types.Clear(); // Make sure we haven't already searched this SymbolFile before... if (searched_symbol_files.count(this)) return 0; else searched_symbol_files.insert(this); DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { if (parent_decl_ctx) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " "%p (\"%s\"), append=%u, max_matches=%u, type_list)", name.GetCString(), static_cast(parent_decl_ctx), parent_decl_ctx->GetName().AsCString(""), append, max_matches); else GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " "NULL, append=%u, max_matches=%u, type_list)", name.GetCString(), append, max_matches); } if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_types_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_type_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { const uint32_t initial_types_size = types.GetSize(); for (size_t i = 0; i < num_die_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match Type *matching_type = ResolveType(die, true, true); if (matching_type) { // We found a type pointer, now find the shared pointer form our type // list types.InsertUnique(matching_type->shared_from_this()); if (types.GetSize() >= max_matches) break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } const uint32_t num_matches = types.GetSize() - initial_types_size; if (log && num_matches) { if (parent_decl_ctx) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " "= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u", name.GetCString(), static_cast(parent_decl_ctx), parent_decl_ctx->GetName().AsCString(""), append, max_matches, num_matches); } else { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " "= NULL, append=%u, max_matches=%u, type_list) => %u", name.GetCString(), append, max_matches, num_matches); } } return num_matches; } else { UpdateExternalModuleListIfNeeded(); for (const auto &pair : m_external_type_modules) { ModuleSP external_module_sp = pair.second; if (external_module_sp) { SymbolVendor *sym_vendor = external_module_sp->GetSymbolVendor(); if (sym_vendor) { const uint32_t num_external_matches = sym_vendor->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types); if (num_external_matches) return num_external_matches; } } } } return 0; } size_t SymbolFileDWARF::FindTypes(const std::vector &context, bool append, TypeMap &types) { if (!append) types.Clear(); if (context.empty()) return 0; DIEArray die_offsets; ConstString name = context.back().name; if (!name) return 0; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_types_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_type_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { size_t num_matches = 0; for (size_t i = 0; i < num_die_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { std::vector die_context; die.GetDWOContext(die_context); if (die_context != context) continue; Type *matching_type = ResolveType(die, true, true); if (matching_type) { // We found a type pointer, now find the shared pointer form our type // list types.InsertUnique(matching_type->shared_from_this()); ++num_matches; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } return num_matches; } return 0; } CompilerDeclContext SymbolFileDWARF::FindNamespace(const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")", name.GetCString()); } CompilerDeclContext namespace_decl_ctx; if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return namespace_decl_ctx; DWARFDebugInfo *info = DebugInfo(); if (info) { DIEArray die_offsets; // Index if we already haven't to make sure the compile units // get indexed and make their global DIE index list if (m_using_apple_tables) { if (m_apple_namespaces_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_namespaces_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_namespace_index.Find(name, die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die); if (namespace_decl_ctx) break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified " "(.apple_namespaces accelerator table had bad die 0x%8.8x for " "'%s')\n", die_ref.die_offset, name.GetCString()); } } } } } if (log && namespace_decl_ctx) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => " "CompilerDeclContext(%p/%p) \"%s\"", name.GetCString(), static_cast(namespace_decl_ctx.GetTypeSystem()), static_cast(namespace_decl_ctx.GetOpaqueDeclContext()), namespace_decl_ctx.GetName().AsCString("")); } return namespace_decl_ctx; } TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die, bool resolve_function_context) { TypeSP type_sp; if (die) { Type *type_ptr = GetDIEToType().lookup(die.GetDIE()); if (type_ptr == NULL) { CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(die.GetCU()); assert(lldb_cu); SymbolContext sc(lldb_cu); const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE(); while (parent_die != nullptr) { if (parent_die->Tag() == DW_TAG_subprogram) break; parent_die = parent_die->GetParent(); } SymbolContext sc_backup = sc; if (resolve_function_context && parent_die != nullptr && !GetFunction(DWARFDIE(die.GetCU(), parent_die), sc)) sc = sc_backup; type_sp = ParseType(sc, die, NULL); } else if (type_ptr != DIE_IS_BEING_PARSED) { // Grab the existing type from the master types lists type_sp = type_ptr->shared_from_this(); } } return type_sp; } DWARFDIE SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) { if (orig_die) { DWARFDIE die = orig_die; while (die) { // If this is the original DIE that we are searching for a declaration // for, then don't look in the cache as we don't want our own decl // context to be our decl context... if (orig_die != die) { switch (die.Tag()) { case DW_TAG_compile_unit: case DW_TAG_namespace: case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: case DW_TAG_lexical_block: case DW_TAG_subprogram: return die; case DW_TAG_inlined_subroutine: { DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); if (abs_die) { return abs_die; } break; } default: break; } } DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification); if (spec_die) { DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die); if (decl_ctx_die) return decl_ctx_die; } DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); if (abs_die) { DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die); if (decl_ctx_die) return decl_ctx_die; } die = die.GetParent(); } } return DWARFDIE(); } Symbol * SymbolFileDWARF::GetObjCClassSymbol(const ConstString &objc_class_name) { Symbol *objc_class_symbol = NULL; if (m_obj_file) { Symtab *symtab = m_obj_file->GetSymtab(); if (symtab) { objc_class_symbol = symtab->FindFirstSymbolWithNameAndType( objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo, Symtab::eVisibilityAny); } } return objc_class_symbol; } // Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If // they don't // then we can end up looking through all class types for a complete type and // never find // the full definition. We need to know if this attribute is supported, so we // determine // this here and cache th result. We also need to worry about the debug map // DWARF file // if we are doing darwin DWARF in .o file debugging. bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type( DWARFCompileUnit *cu) { if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type()) m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; else { DWARFDebugInfo *debug_info = DebugInfo(); const uint32_t num_compile_units = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu != cu && dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; break; } } } if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo && GetDebugMapSymfile()) return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this); } return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; } // This function can be used when a DIE is found that is a forward declaration // DIE and we want to try and find a type that has the complete definition. TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE( const DWARFDIE &die, const ConstString &type_name, bool must_be_implementation) { TypeSP type_sp; if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name))) return type_sp; DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = type_name.GetCString(); m_apple_types_ap->FindCompleteObjCClassByName(name_cstr, die_offsets, must_be_implementation); } } else { if (!m_indexed) Index(); m_type_index.Find(type_name, die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE type_die = GetDIE(die_ref); if (type_die) { bool try_resolving_type = false; // Don't try and resolve the DIE we are looking for with the DIE itself! if (type_die != die) { switch (type_die.Tag()) { case DW_TAG_class_type: case DW_TAG_structure_type: try_resolving_type = true; break; default: break; } } if (try_resolving_type) { if (must_be_implementation && type_die.Supports_DW_AT_APPLE_objc_complete_type()) try_resolving_type = type_die.GetAttributeValueAsUnsigned( DW_AT_APPLE_objc_complete_type, 0); if (try_resolving_type) { Type *resolved_type = ResolveType(type_die, false, true); if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", die.GetID(), m_obj_file->GetFileSpec().GetFilename().AsCString( ""), type_die.GetID(), type_cu->GetID()); if (die) GetDIEToType()[die.GetDIE()] = resolved_type; type_sp = resolved_type->shared_from_this(); break; } } } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, type_name.GetCString()); } } } } return type_sp; } //---------------------------------------------------------------------- // This function helps to ensure that the declaration contexts match for // two different DIEs. Often times debug information will refer to a // forward declaration of a type (the equivalent of "struct my_struct;". // There will often be a declaration of that type elsewhere that has the // full definition. When we go looking for the full type "my_struct", we // will find one or more matches in the accelerator tables and we will // then need to make sure the type was in the same declaration context // as the original DIE. This function can efficiently compare two DIEs // and will return true when the declaration context matches, and false // when they don't. //---------------------------------------------------------------------- bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2) { if (die1 == die2) return true; DWARFDIECollection decl_ctx_1; DWARFDIECollection decl_ctx_2; // The declaration DIE stack is a stack of the declaration context // DIEs all the way back to the compile unit. If a type "T" is // declared inside a class "B", and class "B" is declared inside // a class "A" and class "A" is in a namespace "lldb", and the // namespace is in a compile unit, there will be a stack of DIEs: // // [0] DW_TAG_class_type for "B" // [1] DW_TAG_class_type for "A" // [2] DW_TAG_namespace for "lldb" // [3] DW_TAG_compile_unit for the source file. // // We grab both contexts and make sure that everything matches // all the way back to the compiler unit. // First lets grab the decl contexts for both DIEs die1.GetDeclContextDIEs(decl_ctx_1); die2.GetDeclContextDIEs(decl_ctx_2); // Make sure the context arrays have the same size, otherwise // we are done const size_t count1 = decl_ctx_1.Size(); const size_t count2 = decl_ctx_2.Size(); if (count1 != count2) return false; // Make sure the DW_TAG values match all the way back up the // compile unit. If they don't, then we are done. DWARFDIE decl_ctx_die1; DWARFDIE decl_ctx_die2; size_t i; for (i = 0; i < count1; i++) { decl_ctx_die1 = decl_ctx_1.GetDIEAtIndex(i); decl_ctx_die2 = decl_ctx_2.GetDIEAtIndex(i); if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag()) return false; } #if defined LLDB_CONFIGURATION_DEBUG // Make sure the top item in the decl context die array is always // DW_TAG_compile_unit. If it isn't then something went wrong in // the DWARFDIE::GetDeclContextDIEs() function... assert(decl_ctx_1.GetDIEAtIndex(count1 - 1).Tag() == DW_TAG_compile_unit); #endif // Always skip the compile unit when comparing by only iterating up to // "count - 1". Here we compare the names as we go. for (i = 0; i < count1 - 1; i++) { decl_ctx_die1 = decl_ctx_1.GetDIEAtIndex(i); decl_ctx_die2 = decl_ctx_2.GetDIEAtIndex(i); const char *name1 = decl_ctx_die1.GetName(); const char *name2 = decl_ctx_die2.GetName(); // If the string was from a DW_FORM_strp, then the pointer will often // be the same! if (name1 == name2) continue; // Name pointers are not equal, so only compare the strings // if both are not NULL. if (name1 && name2) { // If the strings don't compare, we are done... if (strcmp(name1, name2) != 0) return false; } else { // One name was NULL while the other wasn't return false; } } // We made it through all of the checks and the declaration contexts // are equal. return true; } TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext( const DWARFDeclContext &dwarf_decl_ctx) { TypeSP type_sp; const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize(); if (dwarf_decl_ctx_count > 0) { const ConstString type_name(dwarf_decl_ctx[0].name); const dw_tag_t tag = dwarf_decl_ctx[0].tag; if (type_name) { Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION | DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%" "s, qualified-name='%s')", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName()); } DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const bool has_tag = m_apple_types_ap->GetHeader().header_data.ContainsAtom( DWARFMappedHash::eAtomTypeTag); const bool has_qualified_name_hash = m_apple_types_ap->GetHeader().header_data.ContainsAtom( DWARFMappedHash::eAtomTypeQualNameHash); if (has_tag && has_qualified_name_hash) { const char *qualified_name = dwarf_decl_ctx.GetQualifiedName(); const uint32_t qualified_name_hash = MappedHash::HashStringUsingDJB(qualified_name); if (log) GetObjectFile()->GetModule()->LogMessage( log, "FindByNameAndTagAndQualifiedNameHash()"); m_apple_types_ap->FindByNameAndTagAndQualifiedNameHash( type_name.GetCString(), tag, qualified_name_hash, die_offsets); } else if (has_tag) { if (log) GetObjectFile()->GetModule()->LogMessage(log, "FindByNameAndTag()"); m_apple_types_ap->FindByNameAndTag(type_name.GetCString(), tag, die_offsets); } else { m_apple_types_ap->FindByName(type_name.GetCString(), die_offsets); } } } else { if (!m_indexed) Index(); m_type_index.Find(type_name, die_offsets); } const size_t num_matches = die_offsets.size(); // Get the type system that we are looking to find a type for. We will use // this // to ensure any matches we find are in a language that this type system // supports const LanguageType language = dwarf_decl_ctx.GetLanguage(); TypeSystem *type_system = (language == eLanguageTypeUnknown) ? nullptr : GetTypeSystemForLanguage(language); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE type_die = GetDIE(die_ref); if (type_die) { // Make sure type_die's langauge matches the type system we are // looking for. // We don't want to find a "Foo" type from Java if we are looking // for a "Foo" // type for C, C++, ObjC, or ObjC++. if (type_system && !type_system->SupportsLanguage(type_die.GetLanguage())) continue; bool try_resolving_type = false; // Don't try and resolve the DIE we are looking for with the DIE // itself! const dw_tag_t type_tag = type_die.Tag(); // Make sure the tags match if (type_tag == tag) { // The tags match, lets try resolving this type try_resolving_type = true; } else { // The tags don't match, but we need to watch our for a // forward declaration for a struct and ("struct foo") // ends up being a class ("class foo { ... };") or // vice versa. switch (type_tag) { case DW_TAG_class_type: // We had a "class foo", see if we ended up with a "struct foo { // ... };" try_resolving_type = (tag == DW_TAG_structure_type); break; case DW_TAG_structure_type: // We had a "struct foo", see if we ended up with a "class foo { // ... };" try_resolving_type = (tag == DW_TAG_class_type); break; default: // Tags don't match, don't event try to resolve // using this type whose name matches.... break; } } if (try_resolving_type) { DWARFDeclContext type_dwarf_decl_ctx; type_die.GetDWARFDeclContext(type_dwarf_decl_ctx); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::" "FindDefinitionTypeForDWARFDeclContext(tag=%s, " "qualified-name='%s') trying die=0x%8.8x (%s)", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), type_dwarf_decl_ctx.GetQualifiedName()); } // Make sure the decl contexts match all the way up if (dwarf_decl_ctx == type_dwarf_decl_ctx) { Type *resolved_type = ResolveType(type_die, false); if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { type_sp = resolved_type->shared_from_this(); break; } } } else { if (log) { std::string qualified_name; type_die.GetQualifiedName(qualified_name); GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::" "FindDefinitionTypeForDWARFDeclContext(tag=%s, " "qualified-name='%s') ignoring die=0x%8.8x (%s)", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), qualified_name.c_str()); } } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, type_name.GetCString()); } } } } } } return type_sp; } TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die, bool *type_is_new_ptr) { TypeSP type_sp; if (die) { TypeSystem *type_system = GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); if (type_system) { DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); if (dwarf_ast) { Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr); if (type_sp) { TypeList *type_list = GetTypeList(); if (type_list) type_list->Insert(type_sp); if (die.Tag() == DW_TAG_subprogram) { DIERef die_ref = die.GetDIERef(); std::string scope_qualified_name(GetDeclContextForUID(die.GetID()) .GetScopeQualifiedName() .AsCString("")); if (scope_qualified_name.size()) { NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find( scope_qualified_name); if (iter != m_function_scope_qualified_name_map.end()) (*iter).second->insert(die_ref); else { DIERefSetSP new_set(new std::set); new_set->insert(die_ref); m_function_scope_qualified_name_map.emplace( std::make_pair(scope_qualified_name, new_set)); } } } } } } } return type_sp; } size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc, const DWARFDIE &orig_die, bool parse_siblings, bool parse_children) { size_t types_added = 0; DWARFDIE die = orig_die; while (die) { bool type_is_new = false; if (ParseType(sc, die, &type_is_new).get()) { if (type_is_new) ++types_added; } if (parse_children && die.HasChildren()) { if (die.Tag() == DW_TAG_subprogram) { SymbolContext child_sc(sc); child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true); } else types_added += ParseTypes(sc, die.GetFirstChild(), true, true); } if (parse_siblings) die = die.GetSibling(); else die.Clear(); } return types_added; } size_t SymbolFileDWARF::ParseFunctionBlocks(const SymbolContext &sc) { assert(sc.comp_unit && sc.function); size_t functions_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const dw_offset_t function_die_offset = sc.function->GetID(); DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset); if (function_die) { ParseFunctionBlocks(sc, &sc.function->GetBlock(false), function_die, LLDB_INVALID_ADDRESS, 0); } } return functions_added; } size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc) { // At least a compile unit must be valid assert(sc.comp_unit); size_t types_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { if (sc.function) { dw_offset_t function_die_offset = sc.function->GetID(); DWARFDIE func_die = dwarf_cu->GetDIE(function_die_offset); if (func_die && func_die.HasChildren()) { types_added = ParseTypes(sc, func_die.GetFirstChild(), true, true); } } else { DWARFDIE dwarf_cu_die = dwarf_cu->DIE(); if (dwarf_cu_die && dwarf_cu_die.HasChildren()) { types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true); } } } return types_added; } size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) { if (sc.comp_unit != NULL) { DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; if (sc.function) { DWARFDIE function_die = info->GetDIE(DIERef(sc.function->GetID(), this)); const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress( DW_AT_low_pc, LLDB_INVALID_ADDRESS); if (func_lo_pc != LLDB_INVALID_ADDRESS) { const size_t num_variables = ParseVariables( sc, function_die.GetFirstChild(), func_lo_pc, true, true); // Let all blocks know they have parse all their variables sc.function->GetBlock(false).SetDidParseVariables(true, true); return num_variables; } } else if (sc.comp_unit) { DWARFCompileUnit *dwarf_cu = info->GetCompileUnit(sc.comp_unit->GetID()); if (dwarf_cu == NULL) return 0; uint32_t vars_added = 0; VariableListSP variables(sc.comp_unit->GetVariableList(false)); if (variables.get() == NULL) { variables.reset(new VariableList()); sc.comp_unit->SetVariableList(variables); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DWARFMappedHash::DIEInfoArray hash_data_array; if (m_apple_names_ap->AppendAllDIEsInRange( dwarf_cu->GetOffset(), dwarf_cu->GetNextCompileUnitOffset(), hash_data_array)) { DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); } } } else { // Index if we already haven't to make sure the compile units // get indexed and make their global DIE index list if (!m_indexed) Index(); m_global_index.FindAllEntriesForCompileUnit(dwarf_cu->GetOffset(), die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { VariableSP var_sp( ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS)); if (var_sp) { variables->AddVariableIfUnique(var_sp); ++vars_added; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified " "(.apple_names accelerator table had bad die 0x%8.8x)\n", die_ref.die_offset); } } } } } return vars_added; } } return 0; } VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc) { if (die.GetDWARF() != this) return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc); VariableSP var_sp; if (!die) return var_sp; var_sp = GetDIEToVariable()[die.GetDIE()]; if (var_sp) return var_sp; // Already been parsed! const dw_tag_t tag = die.Tag(); ModuleSP module = GetObjectFile()->GetModule(); if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter && sc.function)) { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); DWARFDIE spec_die; if (num_attributes > 0) { const char *name = NULL; const char *mangled = NULL; Declaration decl; uint32_t i; DWARFFormValue type_die_form; DWARFExpression location(die.GetCU()); bool is_external = false; bool is_artificial = false; bool location_is_const_value_data = false; bool has_explicit_location = false; DWARFFormValue const_value; Variable::RangeList scope_ranges; // AccessType accessibility = eAccessNone; for (i = 0; i < num_attributes; ++i) { dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(); break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_external: is_external = form_value.Boolean(); break; case DW_AT_const_value: // If we have already found a DW_AT_location attribute, ignore this // attribute. if (!has_explicit_location) { location_is_const_value_data = true; // The constant value will be either a block, a data value or a // string. const DWARFDataExtractor &debug_info_data = get_debug_info_data(); if (DWARFFormValue::IsBlockForm(form_value.Form())) { // Retrieve the value as a block expression. uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); location.CopyOpcodeData(module, debug_info_data, block_offset, block_length); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { // Retrieve the value as a data expression. DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize( attributes.CompileUnitAtIndex(i)->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes.GetSize(form_value.Form()); if (data_length == 0) { const uint8_t *data_pointer = form_value.BlockData(); if (data_pointer) { form_value.Unsigned(); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { // we need to get the byte size of the type later after we // create the variable const_value = form_value; } } else location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { // Retrieve the value as a string expression. if (form_value.Form() == DW_FORM_strp) { DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize( attributes.CompileUnitAtIndex(i) ->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes.GetSize(form_value.Form()); location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { const char *str = form_value.AsCString(); uint32_t string_offset = str - (const char *)debug_info_data.GetDataStart(); uint32_t string_length = strlen(str) + 1; location.CopyOpcodeData(module, debug_info_data, string_offset, string_length); } } } break; case DW_AT_location: { location_is_const_value_data = false; has_explicit_location = true; if (DWARFFormValue::IsBlockForm(form_value.Form())) { const DWARFDataExtractor &debug_info_data = get_debug_info_data(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); location.CopyOpcodeData(module, get_debug_info_data(), block_offset, block_length); } else { const DWARFDataExtractor &debug_loc_data = get_debug_loc_data(); const dw_offset_t debug_loc_offset = form_value.Unsigned(); size_t loc_list_length = DWARFExpression::LocationListSize( die.GetCU(), debug_loc_data, debug_loc_offset); if (loc_list_length > 0) { location.CopyOpcodeData(module, debug_loc_data, debug_loc_offset, loc_list_length); assert(func_low_pc != LLDB_INVALID_ADDRESS); location.SetLocationListSlide( func_low_pc - attributes.CompileUnitAtIndex(i)->GetBaseAddress()); } } } break; case DW_AT_specification: spec_die = GetDIE(DIERef(form_value)); break; case DW_AT_start_scope: { if (form_value.Form() == DW_FORM_sec_offset) { DWARFRangeList dwarf_scope_ranges; const DWARFDebugRanges *debug_ranges = DebugRanges(); debug_ranges->FindRanges(die.GetCU()->GetRangesBase(), form_value.Unsigned(), dwarf_scope_ranges); // All DW_AT_start_scope are relative to the base address of the // compile unit. We add the compile unit base address to make // sure all the addresses are properly fixed up. for (size_t i = 0, count = dwarf_scope_ranges.GetSize(); i < count; ++i) { const DWARFRangeList::Entry &range = dwarf_scope_ranges.GetEntryRef(i); scope_ranges.Append(range.GetRangeBase() + die.GetCU()->GetBaseAddress(), range.GetByteSize()); } } else { // TODO: Handle the case when DW_AT_start_scope have form // constant. The // dwarf spec is a bit ambiguous about what is the expected // behavior in // case the enclosing block have a non coninious address range and // the // DW_AT_start_scope entry have a form constant. GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_start_scope has unsupported form type (0x%x)\n", die.GetID(), form_value.Form()); } scope_ranges.Sort(); scope_ranges.CombineConsecutiveRanges(); } break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: case DW_AT_description: case DW_AT_endianity: case DW_AT_segment: case DW_AT_visibility: default: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die); const dw_tag_t parent_tag = die.GetParent().Tag(); bool is_static_member = parent_tag == DW_TAG_compile_unit && (parent_context_die.Tag() == DW_TAG_class_type || parent_context_die.Tag() == DW_TAG_structure_type); ValueType scope = eValueTypeInvalid; const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); SymbolContextScope *symbol_context_scope = NULL; bool has_explicit_mangled = mangled != nullptr; if (!mangled) { // LLDB relies on the mangled name (DW_TAG_linkage_name or // DW_AT_MIPS_linkage_name) to // generate fully qualified names of global variables with commands like // "frame var j". // For example, if j were an int variable holding a value 4 and declared // in a namespace // B which in turn is contained in a namespace A, the command "frame var // j" returns // "(int) A::B::j = 4". If the compiler does not emit a linkage name, we // should be able // to generate a fully qualified name from the declaration context. if (parent_tag == DW_TAG_compile_unit && Language::LanguageIsCPlusPlus(die.GetLanguage())) { DWARFDeclContext decl_ctx; die.GetDWARFDeclContext(decl_ctx); mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString(); } } if (tag == DW_TAG_formal_parameter) scope = eValueTypeVariableArgument; else { // DWARF doesn't specify if a DW_TAG_variable is a local, global // or static variable, so we have to do a little digging: // 1) DW_AT_linkage_name implies static lifetime (but may be missing) // 2) An empty DW_AT_location is an (optimized-out) static lifetime var. // 3) DW_AT_location containing a DW_OP_addr implies static lifetime. // Clang likes to combine small global variables into the same symbol // 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()); // 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); if (op_error) { StreamString strm; location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0, NULL); GetObjectFile()->GetModule()->ReportError( "0x%8.8x: %s has an invalid location: %s", die.GetOffset(), die.GetTagAsCString(), strm.GetData()); } if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) is_static_lifetime = true; } SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (is_static_lifetime) { if (is_external) scope = eValueTypeVariableGlobal; else scope = eValueTypeVariableStatic; if (debug_map_symfile) { // When leaving the DWARF in the .o files on darwin, // when we have a global variable that wasn't initialized, // the .o file might not have allocated a virtual // address for the global variable. In this case it will // have created a symbol for the global variable // that is undefined/data and external and the value will // be the byte size of the variable. When we do the // address map in SymbolFileDWARFDebugMap we rely on // having an address, we need to do some magic here // so we can get the correct address for our global // variable. The address for all of these entries // will be zero, and there will be an undefined symbol // in this object file, and the executable will have // a matching symbol with a good address. So here we // dig up the correct address and replace it in the // location for the variable, and set the variable's // symbol context scope to be that of the main executable // so the file address will resolve correctly. bool linked_oso_file_addr = false; if (is_external && location_DW_OP_addr == 0) { // we have a possible uninitialized extern global ConstString const_name(mangled ? mangled : name); ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile(); if (debug_map_objfile) { Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); if (debug_map_symtab) { Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType( const_name, eSymbolTypeData, Symtab::eDebugYes, Symtab::eVisibilityExtern); if (exe_symbol) { if (exe_symbol->ValueIsAddress()) { 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)) { linked_oso_file_addr = true; symbol_context_scope = exe_symbol; } } } } } } } if (!linked_oso_file_addr) { // The DW_OP_addr is not zero, but it contains a .o file address // which // needs to be linked up correctly. const lldb::addr_t exe_file_addr = 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); } else { // Variable didn't make it into the final executable return var_sp; } } } } else { if (location_is_const_value_data) scope = eValueTypeVariableStatic; else { scope = eValueTypeVariableLocal; if (debug_map_symfile) { // We need to check for TLS addresses that we need to fixup if (location.ContainsThreadLocalStorage()) { location.LinkThreadLocalStorage( debug_map_symfile->GetObjectFile()->GetModule(), [this, debug_map_symfile]( lldb::addr_t unlinked_file_addr) -> lldb::addr_t { return debug_map_symfile->LinkOSOFileAddress( this, unlinked_file_addr); }); scope = eValueTypeVariableThreadLocal; } } } } } if (symbol_context_scope == NULL) { switch (parent_tag) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: if (sc.function) { symbol_context_scope = sc.function->GetBlock(true).FindBlockByID( sc_parent_die.GetID()); if (symbol_context_scope == NULL) symbol_context_scope = sc.function; } break; default: symbol_context_scope = sc.comp_unit; break; } } if (symbol_context_scope) { SymbolFileTypeSP type_sp( new SymbolFileType(*this, DIERef(type_die_form).GetUID(this))); if (const_value.Form() && type_sp && type_sp->GetType()) location.CopyOpcodeData(const_value.Unsigned(), type_sp->GetType()->GetByteSize(), die.GetCU()->GetAddressByteSize()); var_sp.reset(new Variable(die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, scope_ranges, &decl, location, is_external, is_artificial, is_static_member)); var_sp->SetLocationIsConstantValueData(location_is_const_value_data); } else { // Not ready to parse this variable yet. It might be a global // or static variable that is in a function scope and the function // in the symbol context wasn't filled in yet return var_sp; } } // Cache var_sp even if NULL (the variable was just a specification or // was missing vital information to be able to be displayed in the debugger // (missing location due to optimization, etc)) so we don't re-parse // this DIE over and over later... GetDIEToVariable()[die.GetDIE()] = var_sp; if (spec_die) GetDIEToVariable()[spec_die.GetDIE()] = var_sp; } return var_sp; } DWARFDIE SymbolFileDWARF::FindBlockContainingSpecification( const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) { // Give the concrete function die specified by "func_die_offset", find the // concrete block whose DW_AT_specification or DW_AT_abstract_origin points // to "spec_block_die_offset" return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref), spec_block_die_offset); } DWARFDIE SymbolFileDWARF::FindBlockContainingSpecification( const DWARFDIE &die, dw_offset_t spec_block_die_offset) { if (die) { switch (die.Tag()) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: { if (die.GetAttributeValueAsReference( DW_AT_specification, DW_INVALID_OFFSET) == spec_block_die_offset) return die; if (die.GetAttributeValueAsReference(DW_AT_abstract_origin, DW_INVALID_OFFSET) == spec_block_die_offset) return die; } break; } // Give the concrete function die specified by "func_die_offset", find the // concrete block whose DW_AT_specification or DW_AT_abstract_origin points // to "spec_block_die_offset" for (DWARFDIE child_die = die.GetFirstChild(); child_die; child_die = child_die.GetSibling()) { DWARFDIE result_die = FindBlockContainingSpecification(child_die, spec_block_die_offset); if (result_die) return result_die; } } return DWARFDIE(); } size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc, const DWARFDIE &orig_die, const lldb::addr_t func_low_pc, bool parse_siblings, bool parse_children, VariableList *cc_variable_list) { if (!orig_die) return 0; VariableListSP variable_list_sp; size_t vars_added = 0; DWARFDIE die = orig_die; while (die) { dw_tag_t tag = die.Tag(); // Check to see if we have already parsed this variable or constant? VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]; if (var_sp) { if (cc_variable_list) cc_variable_list->AddVariableIfUnique(var_sp); } else { // We haven't already parsed it, lets do that now. if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter && sc.function)) { if (variable_list_sp.get() == NULL) { DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die); dw_tag_t parent_tag = sc_parent_die.Tag(); switch (parent_tag) { case DW_TAG_compile_unit: if (sc.comp_unit != NULL) { variable_list_sp = sc.comp_unit->GetVariableList(false); if (variable_list_sp.get() == NULL) { variable_list_sp.reset(new VariableList()); sc.comp_unit->SetVariableList(variable_list_sp); } } else { GetObjectFile()->GetModule()->ReportError( "parent 0x%8.8" PRIx64 " %s with no valid compile unit in " "symbol context for 0x%8.8" PRIx64 " %s.\n", sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(), orig_die.GetID(), orig_die.GetTagAsCString()); } break; case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: if (sc.function != NULL) { // Check to see if we already have parsed the variables for the // given scope Block *block = sc.function->GetBlock(true).FindBlockByID( sc_parent_die.GetID()); if (block == NULL) { // This must be a specification or abstract origin with // a concrete block counterpart in the current function. We need // to find the concrete block so we can correctly add the // variable to it const DWARFDIE concrete_block_die = FindBlockContainingSpecification( DIERef(sc.function->GetID(), this), sc_parent_die.GetOffset()); if (concrete_block_die) block = sc.function->GetBlock(true).FindBlockByID( concrete_block_die.GetID()); } if (block != NULL) { const bool can_create = false; variable_list_sp = block->GetBlockVariableList(can_create); if (variable_list_sp.get() == NULL) { variable_list_sp.reset(new VariableList()); block->SetVariableList(variable_list_sp); } } } break; default: GetObjectFile()->GetModule()->ReportError( "didn't find appropriate parent DIE for variable list for " "0x%8.8" PRIx64 " %s.\n", orig_die.GetID(), orig_die.GetTagAsCString()); break; } } if (variable_list_sp) { VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc)); if (var_sp) { variable_list_sp->AddVariableIfUnique(var_sp); if (cc_variable_list) cc_variable_list->AddVariableIfUnique(var_sp); ++vars_added; } } } } bool skip_children = (sc.function == NULL && tag == DW_TAG_subprogram); if (!skip_children && parse_children && die.HasChildren()) { vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true, true, cc_variable_list); } if (parse_siblings) die = die.GetSibling(); else die.Clear(); } return vars_added; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); } uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; } void SymbolFileDWARF::DumpIndexes() { StreamFile s(stdout, false); s.Printf( "DWARF index for (%s) '%s':", GetObjectFile()->GetModule()->GetArchitecture().GetArchitectureName(), GetObjectFile()->GetFileSpec().GetPath().c_str()); s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump(&s); s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump(&s); s.Printf("\nFunction methods:\n"); m_function_method_index.Dump(&s); s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump(&s); s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump(&s); s.Printf("\nGlobals and statics:\n"); m_global_index.Dump(&s); s.Printf("\nTypes:\n"); m_type_index.Dump(&s); s.Printf("\nNamespaces:\n"); m_namespace_index.Dump(&s); } SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() { if (m_debug_map_symfile == NULL && !m_debug_map_module_wp.expired()) { lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); if (module_sp) { SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); if (sym_vendor) m_debug_map_symfile = (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile(); } } return m_debug_map_symfile; } DWARFExpression::LocationListFormat SymbolFileDWARF::GetLocationListFormat() const { return DWARFExpression::RegularLocationList; } Index: vendor/lldb/dist/source/Target/ThreadPlanCallUserExpression.cpp =================================================================== --- vendor/lldb/dist/source/Target/ThreadPlanCallUserExpression.cpp (revision 317958) +++ vendor/lldb/dist/source/Target/ThreadPlanCallUserExpression.cpp (revision 317959) @@ -1,115 +1,126 @@ //===-- ThreadPlanCallUserExpression.cpp -------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Target/ThreadPlanCallUserExpression.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Address.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/IRDynamicChecks.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanRunToAddress.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // ThreadPlanCallUserExpression: Plan to call a single function //---------------------------------------------------------------------- ThreadPlanCallUserExpression::ThreadPlanCallUserExpression( Thread &thread, Address &function, llvm::ArrayRef args, const EvaluateExpressionOptions &options, lldb::UserExpressionSP &user_expression_sp) : ThreadPlanCallFunction(thread, function, CompilerType(), args, options), m_user_expression_sp(user_expression_sp) { // User expressions are generally "User generated" so we should set them up to // stop when done. SetIsMasterPlan(true); SetOkayToDiscard(false); } ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression() {} void ThreadPlanCallUserExpression::GetDescription( Stream *s, lldb::DescriptionLevel level) { if (level == eDescriptionLevelBrief) s->Printf("User Expression thread plan"); else ThreadPlanCallFunction::GetDescription(s, level); } +void ThreadPlanCallUserExpression::DidPush() { + ThreadPlanCallFunction::DidPush(); + if (m_user_expression_sp) + m_user_expression_sp->WillStartExecuting(); +} + void ThreadPlanCallUserExpression::WillPop() { ThreadPlanCallFunction::WillPop(); if (m_user_expression_sp) m_user_expression_sp.reset(); } bool ThreadPlanCallUserExpression::MischiefManaged() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (IsPlanComplete()) { if (log) log->Printf("ThreadPlanCallFunction(%p): Completed call function plan.", static_cast(this)); if (m_manage_materialization && PlanSucceeded() && m_user_expression_sp) { lldb::addr_t function_stack_top; lldb::addr_t function_stack_bottom; lldb::addr_t function_stack_pointer = GetFunctionStackPointer(); function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize(); function_stack_top = function_stack_pointer; DiagnosticManager diagnostics; ExecutionContext exe_ctx(GetThread()); m_user_expression_sp->FinalizeJITExecution( diagnostics, exe_ctx, m_result_var_sp, function_stack_bottom, function_stack_top); } ThreadPlan::MischiefManaged(); return true; } else { return false; } } StopInfoSP ThreadPlanCallUserExpression::GetRealStopInfo() { StopInfoSP stop_info_sp = ThreadPlanCallFunction::GetRealStopInfo(); if (stop_info_sp) { lldb::addr_t addr = GetStopAddress(); DynamicCheckerFunctions *checkers = m_thread.GetProcess()->GetDynamicCheckers(); StreamString s; if (checkers && checkers->DoCheckersExplainStop(addr, s)) stop_info_sp->SetDescription(s.GetData()); } return stop_info_sp; +} + +void ThreadPlanCallUserExpression::DoTakedown(bool success) { + ThreadPlanCallFunction::DoTakedown(success); + m_user_expression_sp->DidFinishExecuting(); } Index: vendor/lldb/dist/source/Utility/TaskPool.cpp =================================================================== --- vendor/lldb/dist/source/Utility/TaskPool.cpp (revision 317958) +++ vendor/lldb/dist/source/Utility/TaskPool.cpp (revision 317959) @@ -1,75 +1,98 @@ //===--------------------- TaskPool.cpp -------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Utility/TaskPool.h" #include // for uint32_t #include // for queue #include // for thread namespace { class TaskPoolImpl { public: static TaskPoolImpl &GetInstance(); void AddTask(std::function &&task_fn); private: TaskPoolImpl(); static void Worker(TaskPoolImpl *pool); std::queue> m_tasks; std::mutex m_tasks_mutex; uint32_t m_thread_count; }; } // end of anonymous namespace TaskPoolImpl &TaskPoolImpl::GetInstance() { static TaskPoolImpl g_task_pool_impl; return g_task_pool_impl; } void TaskPool::AddTaskImpl(std::function &&task_fn) { TaskPoolImpl::GetInstance().AddTask(std::move(task_fn)); } TaskPoolImpl::TaskPoolImpl() : m_thread_count(0) {} void TaskPoolImpl::AddTask(std::function &&task_fn) { static const uint32_t max_threads = std::thread::hardware_concurrency(); std::unique_lock lock(m_tasks_mutex); m_tasks.emplace(std::move(task_fn)); if (m_thread_count < max_threads) { m_thread_count++; // Note that this detach call needs to happen with the m_tasks_mutex held. // This prevents the thread // from exiting prematurely and triggering a linux libc bug // (https://sourceware.org/bugzilla/show_bug.cgi?id=19951). std::thread(Worker, this).detach(); } } void TaskPoolImpl::Worker(TaskPoolImpl *pool) { while (true) { std::unique_lock lock(pool->m_tasks_mutex); if (pool->m_tasks.empty()) { pool->m_thread_count--; break; } std::function f = pool->m_tasks.front(); pool->m_tasks.pop(); lock.unlock(); f(); } } + +void TaskMapOverInt(size_t begin, size_t end, + std::function const &func) { + std::atomic idx{begin}; + size_t num_workers = + std::min(end, std::thread::hardware_concurrency()); + + auto wrapper = [&idx, end, &func]() { + while (true) { + size_t i = idx.fetch_add(1); + if (i >= end) + break; + func(i); + } + }; + + std::vector> futures; + futures.reserve(num_workers); + for (size_t i = 0; i < num_workers; i++) + futures.push_back(TaskPool::AddTask(wrapper)); + for (size_t i = 0; i < num_workers; i++) + futures[i].wait(); +} Index: vendor/lldb/dist/unittests/Host/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/Host/CMakeLists.txt (revision 317958) +++ vendor/lldb/dist/unittests/Host/CMakeLists.txt (revision 317959) @@ -1,21 +1,22 @@ set (FILES FileSpecTest.cpp FileSystemTest.cpp + MainLoopTest.cpp SocketAddressTest.cpp SocketTest.cpp SymbolsTest.cpp ) if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") list(APPEND FILES linux/HostTest.cpp linux/SupportTest.cpp ) endif() add_lldb_unittest(HostTests ${FILES} LINK_LIBS lldbCore lldbHost ) Index: vendor/lldb/dist/unittests/Host/MainLoopTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Host/MainLoopTest.cpp (nonexistent) +++ vendor/lldb/dist/unittests/Host/MainLoopTest.cpp (revision 317959) @@ -0,0 +1,120 @@ +//===-- MainLoopTest.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/MainLoop.h" +#include "lldb/Host/common/TCPSocket.h" +#include "gtest/gtest.h" +#include + +using namespace lldb_private; + +namespace { +class MainLoopTest : public testing::Test { +public: + static void SetUpTestCase() { +#ifdef _MSC_VER + WSADATA data; + ASSERT_EQ(0, WSAStartup(MAKEWORD(2, 2), &data)); +#endif + } + + static void TearDownTestCase() { +#ifdef _MSC_VER + ASSERT_EQ(0, WSACleanup()); +#endif + } + + void SetUp() override { + bool child_processes_inherit = false; + Error error; + std::unique_ptr listen_socket_up( + new TCPSocket(true, child_processes_inherit)); + ASSERT_TRUE(error.Success()); + error = listen_socket_up->Listen("localhost:0", 5); + ASSERT_TRUE(error.Success()); + + Socket *accept_socket; + std::future accept_error = std::async(std::launch::async, [&] { + return listen_socket_up->Accept(accept_socket); + }); + + std::unique_ptr connect_socket_up( + new TCPSocket(true, child_processes_inherit)); + error = connect_socket_up->Connect( + llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber()) + .str()); + ASSERT_TRUE(error.Success()); + ASSERT_TRUE(accept_error.get().Success()); + + callback_count = 0; + socketpair[0] = std::move(connect_socket_up); + socketpair[1].reset(accept_socket); + } + + void TearDown() override { + socketpair[0].reset(); + socketpair[1].reset(); + } + +protected: + MainLoop::Callback make_callback() { + return [&](MainLoopBase &loop) { + ++callback_count; + loop.RequestTermination(); + }; + } + std::shared_ptr socketpair[2]; + unsigned callback_count; +}; +} // namespace + +TEST_F(MainLoopTest, ReadObject) { + char X = 'X'; + size_t len = sizeof(X); + ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); + + MainLoop loop; + + Error error; + auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error); + ASSERT_TRUE(error.Success()); + ASSERT_TRUE(handle); + ASSERT_TRUE(loop.Run().Success()); + ASSERT_EQ(1u, callback_count); +} + +TEST_F(MainLoopTest, TerminatesImmediately) { + char X = 'X'; + size_t len = sizeof(X); + ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); + ASSERT_TRUE(socketpair[1]->Write(&X, len).Success()); + + MainLoop loop; + Error error; + auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error); + ASSERT_TRUE(error.Success()); + auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error); + ASSERT_TRUE(error.Success()); + + ASSERT_TRUE(loop.Run().Success()); + ASSERT_EQ(1u, callback_count); +} + +#ifdef LLVM_ON_UNIX +TEST_F(MainLoopTest, Signal) { + MainLoop loop; + Error error; + + auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); + ASSERT_TRUE(error.Success()); + kill(getpid(), SIGUSR1); + ASSERT_TRUE(loop.Run().Success()); + ASSERT_EQ(1u, callback_count); +} +#endif Property changes on: vendor/lldb/dist/unittests/Host/MainLoopTest.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/unittests/Utility/TaskPoolTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Utility/TaskPoolTest.cpp (revision 317958) +++ vendor/lldb/dist/unittests/Utility/TaskPoolTest.cpp (revision 317959) @@ -1,54 +1,43 @@ #include "gtest/gtest.h" #include "lldb/Utility/TaskPool.h" TEST(TaskPoolTest, AddTask) { auto fn = [](int x) { return x * x + 1; }; auto f1 = TaskPool::AddTask(fn, 1); auto f2 = TaskPool::AddTask(fn, 2); auto f3 = TaskPool::AddTask(fn, 3); auto f4 = TaskPool::AddTask(fn, 4); ASSERT_EQ(10, f3.get()); ASSERT_EQ(2, f1.get()); ASSERT_EQ(17, f4.get()); ASSERT_EQ(5, f2.get()); } TEST(TaskPoolTest, RunTasks) { std::vector r(4); auto fn = [](int x, int &y) { y = x * x + 1; }; TaskPool::RunTasks([fn, &r]() { fn(1, r[0]); }, [fn, &r]() { fn(2, r[1]); }, [fn, &r]() { fn(3, r[2]); }, [fn, &r]() { fn(4, r[3]); }); ASSERT_EQ(2, r[0]); ASSERT_EQ(5, r[1]); ASSERT_EQ(10, r[2]); ASSERT_EQ(17, r[3]); } -TEST(TaskPoolTest, TaskRunner) { - auto fn = [](int x) { return std::make_pair(x, x * x); }; +TEST(TaskPoolTest, TaskMap) { + int data[4]; + auto fn = [&data](int x) { data[x] = x * x; }; - TaskRunner> tr; - tr.AddTask(fn, 1); - tr.AddTask(fn, 2); - tr.AddTask(fn, 3); - tr.AddTask(fn, 4); + TaskMapOverInt(0, 4, fn); - int count = 0; - while (true) { - auto f = tr.WaitForNextCompletedTask(); - if (!f.valid()) - break; - - ++count; - std::pair v = f.get(); - ASSERT_EQ(v.first * v.first, v.second); - } - - ASSERT_EQ(4, count); + ASSERT_EQ(data[0], 0); + ASSERT_EQ(data[1], 1); + ASSERT_EQ(data[2], 4); + ASSERT_EQ(data[3], 9); } Index: vendor/lldb/dist/www/lldb-gdb.html =================================================================== --- vendor/lldb/dist/www/lldb-gdb.html (revision 317958) +++ vendor/lldb/dist/www/lldb-gdb.html (revision 317959) @@ -1,1297 +1,1328 @@ LLDB to GDB Command Map
The LLDB Debugger

GDB to LLDB Command Map

Below is a table of GDB commands with the LLDB counterparts. The built in GDB-compatibility aliases in LLDB are also listed. The full lldb command names are often long, but any unique short form can be used. Instead of "breakpoint set", "br se" is also acceptable.

Execution Commands

GDB LLDB
Launch a process no arguments.
(gdb) run
(gdb) r
(lldb) process launch
(lldb) run
(lldb) r
Launch a process with arguments <args>.
(gdb) run <args>
(gdb) r <args>
(lldb) process launch -- <args>
(lldb) r <args>
Launch a process for with arguments a.out 1 2 3 without having to supply the args every time.
% gdb --args a.out 1 2 3
(gdb) run
...
(gdb) run
...
% lldb -- a.out 1 2 3
(lldb) run
...
(lldb) run
...
Or:
(gdb) set args 1 2 3
(gdb) run
...
(gdb) run
...
(lldb) settings set target.run-args 1 2 3
(lldb) run
...
(lldb) run
...
Launch a process with arguments in new terminal window (Mac OS X only).
(lldb) process launch --tty -- <args>
(lldb) pro la -t -- <args>
Launch a process with arguments in existing terminal /dev/ttys006 (Mac OS X only).
(lldb) process launch --tty=/dev/ttys006 -- <args>
(lldb) pro la -t/dev/ttys006 -- <args>
Set environment variables for process before launching.
(gdb) set env DEBUG 1
(lldb) settings set target.env-vars DEBUG=1
(lldb) set se target.env-vars DEBUG=1
(lldb) env DEBUG=1
Unset environment variables for process before launching.
(gdb) unset env DEBUG
(lldb) settings remove target.env-vars DEBUG
(lldb) set rem target.env-vars DEBUG
Show the arguments that will be or were passed to the program when run.
(gdb) show args
Argument list to give program being debugged when it is started is "1 2 3".
(lldb) settings show target.run-args
target.run-args (array of strings) =
[0]: "1"
[1]: "2"
[2]: "3"
Set environment variables for process and launch process in one command.
(lldb) process launch -v DEBUG=1
Attach to a process with process ID 123.
(gdb) attach 123 (lldb) process attach --pid 123
(lldb) attach -p 123
Attach to a process named "a.out".
(gdb) attach a.out (lldb) process attach --name a.out
(lldb) pro at -n a.out
Wait for a process named "a.out" to launch and attach.
(gdb) attach -waitfor a.out (lldb) process attach --name a.out --waitfor
(lldb) pro at -n a.out -w
Attach to a remote gdb protocol server running on system "eorgadd", port 8000.
(gdb) target remote eorgadd:8000 (lldb) gdb-remote eorgadd:8000
Attach to a remote gdb protocol server running on the local system, port 8000.
(gdb) target remote localhost:8000 (lldb) gdb-remote 8000
Attach to a Darwin kernel in kdp mode on system "eorgadd".
(gdb) kdp-reattach eorgadd (lldb) kdp-remote eorgadd
Do a source level single step in the currently selected thread.
(gdb) step
(gdb) s
(lldb) thread step-in
(lldb) step
(lldb) s
Do a source level single step over in the currently selected thread.
(gdb) next
(gdb) n
(lldb) thread step-over
(lldb) next
(lldb) n
Do an instruction level single step in the currently selected thread.
(gdb) stepi
(gdb) si
(lldb) thread step-inst
(lldb) si
Do an instruction level single step over in the currently selected thread.
(gdb) nexti
(gdb) ni
(lldb) thread step-inst-over
(lldb) ni
Step out of the currently selected frame.
(gdb) finish
(lldb) thread step-out
(lldb) finish
Return immediately from the currently selected frame, with an optional return value.
(gdb) return <RETURN EXPRESSION>
(lldb) thread return <RETURN EXPRESSION>
Backtrace and disassemble every time you stop.
(lldb) target stop-hook add
Enter your stop hook command(s). Type 'DONE' to end.
> bt
> disassemble --pc
> DONE
Stop hook #1 added.
Run until we hit line 12 or control leaves the current function.
(gdb) until 12 (lldb) thread until 12

Breakpoint Commands

GDB LLDB
Set a breakpoint at all functions named main.
(gdb) break main (lldb) breakpoint set --name main
(lldb) br s -n main
(lldb) b main
Set a breakpoint in file test.c at line 12.
(gdb) break test.c:12 (lldb) breakpoint set --file test.c --line 12
(lldb) br s -f test.c -l 12
(lldb) b test.c:12
Set a breakpoint at all C++ methods whose basename is main.
(gdb) break main
(Hope that there are no C functions named main).
(lldb) breakpoint set --method main
(lldb) br s -M main
Set a breakpoint at and object C function: -[NSString stringWithFormat:].
(gdb) break -[NSString stringWithFormat:]
(lldb) breakpoint set --name "-[NSString stringWithFormat:]"
(lldb) b -[NSString stringWithFormat:]
Set a breakpoint at all Objective C methods whose selector is count.
(gdb) break count
(Hope that there are no C or C++ functions named count).
(lldb) breakpoint set --selector count
(lldb) br s -S count
Set a breakpoint by regular expression on function name.
(gdb) rbreak regular-expression
(lldb) breakpoint set --func-regex regular-expression
(lldb) br s -r regular-expression
Ensure that breakpoints by file and line work for #included .c/.cpp/.m files.
(gdb) b foo.c:12
(lldb) settings set target.inline-breakpoint-strategy always
(lldb) br s -f foo.c -l 12
Set a breakpoint by regular expression on source file contents.
(gdb) shell grep -e -n pattern source-file
(gdb) break source-file:CopyLineNumbers
(lldb) breakpoint set --source-pattern regular-expression --file SourceFile
(lldb) br s -p regular-expression -f file
Set a conditional breakpoint
(gdb) break foo if strcmp(y,"hello") == 0
(lldb) breakpoint set --name foo --condition '(int)strcmp(y,"hello") == 0'
(lldb) br s -n foo -c '(int)strcmp(y,"hello") == 0'
List all breakpoints.
(gdb) info break
(lldb) breakpoint list
(lldb) br l
Delete a breakpoint.
(gdb) delete 1
(lldb) breakpoint delete 1
(lldb) br del 1

Watchpoint Commands

GDB LLDB
Set a watchpoint on a variable when it is written to.
(gdb) watch global_var (lldb) watchpoint set variable global_var
(lldb) wa s v global_var
Set a watchpoint on a memory location when it is written into. The size of the region to watch for defaults to the pointer size if no '-x byte_size' is specified. This command takes raw input, evaluated as an expression returning an unsigned integer pointing to the start of the region, after the '--' option terminator.
(gdb) watch -location g_char_ptr (lldb) watchpoint set expression -- my_ptr
(lldb) wa s e -- my_ptr
Set a condition on a watchpoint.
(lldb) watch set var global
(lldb) watchpoint modify -c '(global==5)'
(lldb) c
...
(lldb) bt
* thread #1: tid = 0x1c03, 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16, stop reason = watchpoint 1
frame #0: 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16
frame #1: 0x0000000100000eac a.out`main + 108 at main.cpp:25
frame #2: 0x00007fff8ac9c7e1 libdyld.dylib`start + 1
(lldb) frame var global
(int32_t) global = 5
List all watchpoints.
(gdb) info break
(lldb) watchpoint list
(lldb) watch l
Delete a watchpoint.
(gdb) delete 1
(lldb) watchpoint delete 1
(lldb) watch del 1

Examining Variables

GDB LLDB
Show the arguments and local variables for the current frame.
(gdb) info args
and
(gdb) info locals
(lldb) frame variable
(lldb) fr v
Show the local variables for the current frame.
(gdb) info locals
(lldb) frame variable --no-args
(lldb) fr v -a
Show the contents of local variable "bar".
(gdb) p bar
(lldb) frame variable bar
(lldb) fr v bar
(lldb) p bar
Show the contents of local variable "bar" formatted as hex.
(gdb) p/x bar
(lldb) frame variable --format x bar
(lldb) fr v -f x bar
Show the contents of global variable "baz".
(gdb) p baz
(lldb) target variable baz
(lldb) ta v baz
Show the global/static variables defined in the current source file.
n/a
(lldb) target variable
(lldb) ta v
Display the variables "argc" and "argv" every time you stop.
(gdb) display argc
(gdb) display argv
(lldb) target stop-hook add --one-liner "frame variable argc argv"
(lldb) ta st a -o "fr v argc argv"
(lldb) display argc
(lldb) display argv
Display the variables "argc" and "argv" only when you stop in the function named main.
(lldb) target stop-hook add --name main --one-liner "frame variable argc argv"
(lldb) ta st a -n main -o "fr v argc argv"
Display the variable "*this" only when you stop in c class named MyClass.
(lldb) target stop-hook add --classname MyClass --one-liner "frame variable *this"
(lldb) ta st a -c MyClass -o "fr v *this"

Evaluating expressions

GDB LLDB
Evaluating a generalized expression in the current frame.
(gdb) print (int) printf ("Print nine: %d.", 4 + 5)
or if you don't want to see void returns:
(gdb) call (int) printf ("Print nine: %d.", 4 + 5)
(lldb) expr (int) printf ("Print nine: %d.", 4 + 5)
or using the print alias:
(lldb) print (int) printf ("Print nine: %d.", 4 + 5)
Creating and assigning a value to a convenience variable.
(gdb) set $foo = 5
(gdb) set variable $foo = 5
or using the print command
(gdb) print $foo = 5
or using the call command
(gdb) call $foo = 5
and if you want to specify the type of the variable: (gdb) set $foo = (unsigned int) 5
In lldb you evaluate a variable declaration expression as you would write it in C:
(lldb) expr unsigned int $foo = 5
Printing the ObjC "description" of an object.
(gdb) po [SomeClass returnAnObject]
(lldb) expr -o -- [SomeClass returnAnObject]
or using the po alias:
(lldb) po [SomeClass returnAnObject]
Print the dynamic type of the result of an expression.
(gdb) set print object 1
(gdb) p someCPPObjectPtrOrReference
only works for C++ objects.
(lldb) expr -d 1 -- [SomeClass returnAnObject]
(lldb) expr -d 1 -- someCPPObjectPtrOrReference
or set dynamic type printing to be the default: (lldb) settings set target.prefer-dynamic run-target
Calling a function so you can stop at a breakpoint in the function.
(gdb) set unwindonsignal 0
(gdb) p function_with_a_breakpoint()
(lldb) expr -i 0 -- function_with_a_breakpoint()
Calling a function that crashes, and stopping when the function crashes.
(gdb) set unwindonsignal 0
(gdb) p function_which_crashes()
(lldb) expr -u 0 -- function_which_crashes()

Examining Thread State

+ + + + + + + + + + + +
GDB LLDB
List the threads in your program.
+ (gdb) info threads
+
+ (lldb) thread list
+
Select thread 1 as the default thread for subsequent commands.
+ (gdb) thread 1
+
+ (lldb) thread select 1
+ (lldb) t 1
+
Show the stack backtrace for the current thread.
(gdb) bt
(lldb) thread backtrace
(lldb) bt
Show the stack backtraces for all threads.
(gdb) thread apply all bt (lldb) thread backtrace all
(lldb) bt all
Backtrace the first five frames of the current thread.
(gdb) bt 5 (lldb) thread backtrace -c 5
(lldb) bt 5 (lldb-169 and later)
(lldb) bt -c 5 (lldb-168 and earlier)
Select a different stack frame by index for the current thread.
(gdb) frame 12 (lldb) frame select 12
(lldb) fr s 12
(lldb) f 12
List information about the currently selected frame in the current thread.
(lldb) frame info
Select the stack frame that called the current stack frame.
(gdb) up (lldb) up
(lldb) frame select --relative=1
Select the stack frame that is called by the current stack frame.
(gdb) down (lldb) down
(lldb) frame select --relative=-1
(lldb) fr s -r-1
Select a different stack frame using a relative offset.
(gdb) up 2
(gdb) down 3
(lldb) frame select --relative 2
(lldb) fr s -r2

(lldb) frame select --relative -3
(lldb) fr s -r-3
Show the general purpose registers for the current thread.
(gdb) info registers
(lldb) register read
Write a new decimal value '123' to the current thread register 'rax'.
(gdb) p $rax = 123
(lldb) register write rax 123
Skip 8 bytes ahead of the current program counter (instruction pointer). Note that we use backticks to evaluate an expression and insert the scalar result in LLDB.
(gdb) jump *$pc+8
(lldb) register write pc `$pc+8`
Show the general purpose registers for the current thread formatted as signed decimal. LLDB tries to use the same format characters as printf(3) when possible. Type "help format" to see the full list of format specifiers.
(lldb) register read --format i
(lldb) re r -f i

LLDB now supports the GDB shorthand format syntax but there can't be space after the command:
(lldb) register read/d
Show all registers in all register sets for the current thread.
(gdb) info all-registers
(lldb) register read --all
(lldb) re r -a
Show the values for the registers named "rax", "rsp" and "rbp" in the current thread.
(gdb) info all-registers rax rsp rbp
(lldb) register read rax rsp rbp
Show the values for the register named "rax" in the current thread formatted as binary.
(gdb) p/t $rax
(lldb) register read --format binary rax
(lldb) re r -f b rax

LLDB now supports the GDB shorthand format syntax but there can't be space after the command:
(lldb) register read/t rax
(lldb) p/t $rax
Read memory from address 0xbffff3c0 and show 4 hex uint32_t values.
(gdb) x/4xw 0xbffff3c0
(lldb) memory read --size 4 --format x --count 4 0xbffff3c0
(lldb) me r -s4 -fx -c4 0xbffff3c0
(lldb) x -s4 -fx -c4 0xbffff3c0

LLDB now supports the GDB shorthand format syntax but there can't be space after the command:
(lldb) memory read/4xw 0xbffff3c0
(lldb) x/4xw 0xbffff3c0
(lldb) memory read --gdb-format 4xw 0xbffff3c0
Read memory starting at the expression "argv[0]".
(gdb) x argv[0]
(lldb) memory read `argv[0]`
NOTE: any command can inline a scalar expression result (as long as the target is stopped) using backticks around any expression:
(lldb) memory read --size `sizeof(int)` `argv[0]`
Read 512 bytes of memory from address 0xbffff3c0 and save results to a local file as text.
(gdb) set logging on
(gdb) set logging file /tmp/mem.txt
(gdb) x/512bx 0xbffff3c0
(gdb) set logging off
(lldb) memory read --outfile /tmp/mem.txt --count 512 0xbffff3c0
(lldb) me r -o/tmp/mem.txt -c512 0xbffff3c0
(lldb) x/512bx -o/tmp/mem.txt 0xbffff3c0
Save binary memory data starting at 0x1000 and ending at 0x2000 to a file.
(gdb) dump memory /tmp/mem.bin 0x1000 0x2000 (lldb) memory read --outfile /tmp/mem.bin --binary 0x1000 0x2000
(lldb) me r -o /tmp/mem.bin -b 0x1000 0x2000
Get information about a specific heap allocation (available on Mac OS X only).
(gdb) info malloc 0x10010d680 (lldb) command script import lldb.macosx.heap
(lldb) process launch --environment MallocStackLogging=1 -- [ARGS]
(lldb) malloc_info --stack-history 0x10010d680
Get information about a specific heap allocation and cast the result to any dynamic type that can be deduced (available on Mac OS X only)
(lldb) command script import lldb.macosx.heap
(lldb) malloc_info --type 0x10010d680
Find all heap blocks that contain a pointer specified by an expression EXPR (available on Mac OS X only).
(lldb) command script import lldb.macosx.heap
(lldb) ptr_refs EXPR
Find all heap blocks that contain a C string anywhere in the block (available on Mac OS X only).
(lldb) command script import lldb.macosx.heap
(lldb) cstr_refs CSTRING
Disassemble the current function for the current frame.
(gdb) disassemble (lldb) disassemble --frame
(lldb) di -f
Disassemble any functions named main.
(gdb) disassemble main (lldb) disassemble --name main
(lldb) di -n main
Disassemble an address range.
(gdb) disassemble 0x1eb8 0x1ec3 (lldb) disassemble --start-address 0x1eb8 --end-address 0x1ec3
(lldb) di -s 0x1eb8 -e 0x1ec3
Disassemble 20 instructions from a given address.
(gdb) x/20i 0x1eb8 (lldb) disassemble --start-address 0x1eb8 --count 20
(lldb) di -s 0x1eb8 -c 20
Show mixed source and disassembly for the current function for the current frame.
n/a (lldb) disassemble --frame --mixed
(lldb) di -f -m
Disassemble the current function for the current frame and show the opcode bytes.
n/a (lldb) disassemble --frame --bytes
(lldb) di -f -b
Disassemble the current source line for the current frame.
n/a (lldb) disassemble --line
(lldb) di -l

Executable and Shared Library Query Commands

GDB LLDB
List the main executable and all dependent shared libraries.
(gdb) info shared
(lldb) image list
Look up information for a raw address in the executable or any shared libraries.
(gdb) info symbol 0x1ec4
(lldb) image lookup --address 0x1ec4
(lldb) im loo -a 0x1ec4
Look up functions matching a regular expression in a binary.
(gdb) info function <FUNC_REGEX>
This one finds debug symbols:
(lldb) image lookup -r -n <FUNC_REGEX>

This one finds non-debug symbols:
(lldb) image lookup -r -s <FUNC_REGEX>

Provide a list of binaries as arguments to limit the search.
Find full source line information.
(gdb) info line 0x1ec4
This one is a bit messy at present. Do:

(lldb) image lookup -v --address 0x1ec4

and look for the LineEntry line, which will have the full source path and line range information.
Look up information for an address in a.out only.
(lldb) image lookup --address 0x1ec4 a.out
(lldb) im loo -a 0x1ec4 a.out
Look up information for for a type Point by name.
(gdb) ptype Point
(lldb) image lookup --type Point
(lldb) im loo -t Point
Dump all sections from the main executable and any shared libraries.
(gdb) maintenance info sections
(lldb) image dump sections
Dump all sections in the a.out module.
(lldb) image dump sections a.out
Dump all symbols from the main executable and any shared libraries.
(lldb) image dump symtab
Dump all symbols in a.out and liba.so.
(lldb) image dump symtab a.out liba.so

Miscellaneous

+ + + + + +
GDB LLDB
Search command help for a keyword.
+ (gdb) apropos keyword
+
+ (lldb) apropos keyword
+
Echo text to the screen.
(gdb) echo Here is some text\n
(lldb) script print "Here is some text"
Remap source file pathnames for the debug session. If your source files are no longer located in the same location as when the program was built --- maybe the program was built on a different computer --- you need to tell the debugger how to find the sources at their local file path instead of the build system's file path.
(gdb) set pathname-substitutions /buildbot/path /my/path
(lldb) settings set target.source-map /buildbot/path /my/path
Supply a catchall directory to search for source files in.
(gdb) directory /my/path
(No equivalent command - use the source-map instead.)