From aff1b357b0dd8a4035b87ca45df510db0dc9f4ae Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 10 Oct 2014 23:07:36 +0000 Subject: [PATCH] Add a new disassembly-format specification so that the disassembler output style can be customized. Change the built-in default to be more similar to gdb's disassembly formatting. The disassembly-format for a gdb-like output is ${addr-file-or-load} <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>: The disassembly-format for the lldb style output is {${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow} }{${addr-file-or-load}}: The two backticks in the lldb style formatter triggers the sub-expression evaluation in CommandInterpreter::PreprocessCommand() so you can't use that one as-is ... changing to use ' characters instead of ` would work around that. llvm-svn: 219544 --- lldb/include/lldb/Core/Address.h | 1 + lldb/include/lldb/Core/Debugger.h | 14 +- lldb/include/lldb/Core/Disassembler.h | 50 ++++- lldb/include/lldb/Core/Mangled.h | 3 +- lldb/include/lldb/Core/StreamString.h | 3 + lldb/include/lldb/Symbol/SymbolContext.h | 3 +- lldb/source/API/SBInstruction.cpp | 17 +- lldb/source/API/SBInstructionList.cpp | 16 +- lldb/source/Breakpoint/BreakpointLocation.cpp | 2 +- lldb/source/Commands/CommandObjectSource.cpp | 4 +- lldb/source/Core/Address.cpp | 9 +- lldb/source/Core/Debugger.cpp | 177 ++++++++++++++++-- lldb/source/Core/Disassembler.cpp | 57 +++--- lldb/source/Core/Mangled.cpp | 62 ++++++ lldb/source/Core/StreamString.cpp | 16 ++ lldb/source/Expression/IRExecutionUnit.cpp | 11 +- .../Disassembler/llvm/DisassemblerLLVMC.cpp | 10 +- .../UnwindAssemblyInstEmulation.cpp | 3 +- lldb/source/Symbol/SymbolContext.cpp | 12 +- lldb/source/Symbol/Variable.cpp | 4 +- lldb/source/Target/StackFrame.cpp | 4 +- lldb/source/Target/ThreadPlanTracer.cpp | 6 +- .../abbreviation/TestAbbreviations.py | 4 +- .../inferior-assert/TestInferiorAssert.py | 7 +- 24 files changed, 419 insertions(+), 76 deletions(-) diff --git a/lldb/include/lldb/Core/Address.h b/lldb/include/lldb/Core/Address.h index 8dd2339f9207..b430ef7cec21 100644 --- a/lldb/include/lldb/Core/Address.h +++ b/lldb/include/lldb/Core/Address.h @@ -87,6 +87,7 @@ public: ///< and file and line), to information about what the pointer points to ///< if the address is in a section (section of pointers, c strings, etc). DumpStyleResolvedDescriptionNoModule, + DumpStyleResolvedDescriptionNoFunctionArguments, DumpStyleDetailedSymbolContext, ///< Detailed symbol context information for an address for all symbol ///< context members. DumpStyleResolvedPointerDescription ///< Dereference a pointer at the current address and then lookup the diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 4f866238cfe3..69948a6fb417 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -250,6 +250,13 @@ public: Stream &s, ValueObject* valobj = NULL); + static bool + FormatDisassemblerAddress (const char *format, + const SymbolContext *sc, + const SymbolContext *prev_sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s); void ClearIOHandlers (); @@ -288,10 +295,13 @@ public: bool GetAutoConfirm () const; - + + const char * + GetDisassemblyFormat() const; + const char * GetFrameFormat() const; - + const char * GetThreadFormat() const; diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 06a703b4c1aa..b0b841b0a925 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -77,12 +77,60 @@ public: 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_spec + /// The format specification for how addresses are printed. + /// Only needed if show_address is true. + //------------------------------------------------------------------ + virtual void Dump (Stream *s, uint32_t max_opcode_byte_size, bool show_address, bool show_bytes, - const ExecutionContext* exe_ctx); + const ExecutionContext* exe_ctx, + const SymbolContext *sym_ctx, + const SymbolContext *prev_sym_ctx, + const char *disassembly_addr_format_spec); virtual bool DoesBranch () = 0; diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 7dc0eca3e8db..2f3df6afd8dc 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -37,7 +37,8 @@ public: enum NamePreference { ePreferMangled, - ePreferDemangled + ePreferDemangled, + ePreferDemangledWithoutArguments }; //---------------------------------------------------------------------- diff --git a/lldb/include/lldb/Core/StreamString.h b/lldb/include/lldb/Core/StreamString.h index a26ad2d16a05..1aa46dd80a68 100644 --- a/lldb/include/lldb/Core/StreamString.h +++ b/lldb/include/lldb/Core/StreamString.h @@ -46,6 +46,9 @@ public: size_t GetSize() const; + size_t + GetSizeOfLastLine () const; + std::string & GetString(); diff --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h index c26b7a0b9a10..d40d1453cb17 100644 --- a/lldb/include/lldb/Symbol/SymbolContext.h +++ b/lldb/include/lldb/Symbol/SymbolContext.h @@ -168,7 +168,8 @@ public: const Address &so_addr, bool show_fullpaths, bool show_module, - bool show_inlined_frames) const; + bool show_inlined_frames, + bool show_function_arguments) const; //------------------------------------------------------------------ /// Get the address range contained within a symbol context. diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 2334cc0d124a..eccc4e29aadf 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -20,6 +20,7 @@ #include "lldb/Core/DataExtractor.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" @@ -170,9 +171,15 @@ SBInstruction::GetDescription (lldb::SBStream &s) { if (m_opaque_sp) { + SymbolContext sc; + const Address &addr = m_opaque_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... - m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL); + const char *disassemble_format = "${addr-file-or-load}: "; + m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL, &sc, NULL, disassemble_format); return true; } return false; @@ -186,8 +193,14 @@ SBInstruction::Print (FILE *out) if (m_opaque_sp) { + SymbolContext sc; + const Address &addr = m_opaque_sp->GetAddress(); + ModuleSP module_sp (addr.GetModule()); + if (module_sp) + module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); StreamFile out_stream (out, false); - m_opaque_sp->Dump (&out_stream, 0, true, false, NULL); + const char *disassemble_format = "${addr-file-or-load}: "; + m_opaque_sp->Dump (&out_stream, 0, true, false, NULL, &sc, NULL, disassemble_format); } } diff --git a/lldb/source/API/SBInstructionList.cpp b/lldb/source/API/SBInstructionList.cpp index fe22d9c29e4a..31585b3e6868 100644 --- a/lldb/source/API/SBInstructionList.cpp +++ b/lldb/source/API/SBInstructionList.cpp @@ -11,7 +11,9 @@ #include "lldb/API/SBInstruction.h" #include "lldb/API/SBStream.h" #include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" #include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolContext.h" using namespace lldb; using namespace lldb_private; @@ -100,12 +102,24 @@ SBInstructionList::GetDescription (lldb::SBStream &description) // exist already inside description... Stream &sref = description.ref(); const uint32_t max_opcode_byte_size = m_opaque_sp->GetInstructionList().GetMaxOpcocdeByteSize(); + const char *disassemble_format = "${addr-file-or-load}: "; + SymbolContext sc; + SymbolContext prev_sc; for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i).get(); if (inst == NULL) break; - inst->Dump (&sref, max_opcode_byte_size, true, false, NULL); + + 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, disassemble_format); sref.EOL(); } return true; diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp index dbc00b7a1c93..02d9609e2484 100644 --- a/lldb/source/Breakpoint/BreakpointLocation.cpp +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -569,7 +569,7 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) s->PutCString ("re-exported target = "); else s->PutCString("where = "); - sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); + sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false, true); } else { diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 6b1b6aacc857..f8a5c42161f5 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -689,12 +689,14 @@ protected: bool show_fullpaths = true; bool show_module = true; bool show_inlined_frames = true; + const bool show_function_arguments = true; sc.DumpStopContext(&result.GetOutputStream(), m_exe_ctx.GetBestExecutionContextScope(), sc.line_entry.range.GetBaseAddress(), show_fullpaths, show_module, - show_inlined_frames); + show_inlined_frames, + show_function_arguments); result.GetOutputStream().EOL(); if (m_options.num_lines == 0) diff --git a/lldb/source/Core/Address.cpp b/lldb/source/Core/Address.cpp index fa9197d12b70..8f5e19f8c3aa 100644 --- a/lldb/source/Core/Address.cpp +++ b/lldb/source/Core/Address.cpp @@ -465,6 +465,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum case DumpStyleResolvedDescription: case DumpStyleResolvedDescriptionNoModule: + case DumpStyleResolvedDescriptionNoFunctionArguments: if (IsSectionOffset()) { uint32_t pointer_size = 4; @@ -550,7 +551,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum #endif Address cstr_addr(*this); cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); - func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false); + func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false, true); if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT @@ -633,7 +634,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (pointer_sc.function || pointer_sc.symbol) { s->PutCString(": "); - pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false); + pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false, true); } } } @@ -658,6 +659,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum const bool show_module = (style == DumpStyleResolvedDescription); const bool show_fullpaths = false; const bool show_inlined_frames = true; + const bool show_function_arguments = (style != DumpStyleResolvedDescriptionNoFunctionArguments); if (sc.function == NULL && sc.symbol != NULL) { // If we have just a symbol make sure it is in the right section @@ -679,7 +681,8 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum *this, show_fullpaths, show_module, - show_inlined_frames); + show_inlined_frames, + show_function_arguments); } else { diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 5fb60cfd0b6e..5a910a9040ae 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -45,6 +45,8 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" @@ -122,12 +124,14 @@ g_language_enumerators[] = FILE_AND_LINE\ "\\n" +#define DEFAULT_DISASSEMBLY_FORMAT "${addr-file-or-load} <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>: " static PropertyDefinition g_properties[] = { { "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, +{ "disassembly-format", OptionValue::eTypeString , true, 0 , DEFAULT_DISASSEMBLY_FORMAT, NULL, "The default disassembly format string to use when disassembling instruction sequences." }, { "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, { "notify-void", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, { "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, @@ -148,6 +152,7 @@ g_properties[] = enum { ePropertyAutoConfirm = 0, + ePropertyDisassemblyFormat, ePropertyFrameFormat, ePropertyNotiftVoid, ePropertyPrompt, @@ -230,6 +235,13 @@ Debugger::GetAutoConfirm () const return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } +const char * +Debugger::GetDisassemblyFormat() const +{ + const uint32_t idx = ePropertyDisassemblyFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + const char * Debugger::GetFrameFormat() const { @@ -1128,6 +1140,8 @@ TestPromptFormats (StackFrame *frame) StreamString s; const char *prompt_format = "{addr = '${addr}'\n}" + "{addr-file-or-load = '${addr-file-or-load}'\n}" + "{current-pc-arrow = '${current-pc-arrow}'\n}" "{process.id = '${process.id}'\n}" "{process.name = '${process.name}'\n}" "{process.file.basename = '${process.file.basename}'\n}" @@ -1155,9 +1169,13 @@ TestPromptFormats (StackFrame *frame) "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" "{frame.reg.carp = '${frame.reg.carp}'\n}" "{function.id = '${function.id}'\n}" + "{function.changed = '${function.changed}'\n}" + "{function.initial-function = '${function.initial-function}'\n}" "{function.name = '${function.name}'\n}" + "{function.name-without-args = '${function.name-without-args}'\n}" "{function.name-with-args = '${function.name-with-args}'\n}" "{function.addr-offset = '${function.addr-offset}'\n}" + "{function.concrete-only-addr-offset-no-padding = '${function.concrete-only-addr-offset-no-padding}'\n}" "{function.line-offset = '${function.line-offset}'\n}" "{function.pc-offset = '${function.pc-offset}'\n}" "{line.file.basename = '${line.file.basename}'\n}" @@ -1555,7 +1573,9 @@ FormatPromptRecurse const Address *addr, Stream &s, const char **end, - ValueObject* valobj + ValueObject* valobj, + bool function_changed, + bool initial_function ) { ValueObject* realvalobj = NULL; // makes it super-easy to parse pointers @@ -1597,7 +1617,7 @@ FormatPromptRecurse ++p; // Skip the '{' - if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) + if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj, function_changed, initial_function)) { // The stream had all it needed s.Write(sub_strm.GetData(), sub_strm.GetSize()); @@ -1631,6 +1651,12 @@ FormatPromptRecurse const char *cstr = NULL; std::string token_format; Address format_addr; + + // normally "addr" means print a raw address but + // "file-addr-or-load-addr" means print a module + file addr if there's no load addr + bool print_file_addr_or_load_addr = false; + bool addr_offset_concrete_func_only = false; + bool addr_offset_print_with_no_padding = false; bool calculate_format_addr_function_offset = false; // Set reg_kind and reg_num to invalid values RegisterKind reg_kind = kNumRegisterKinds; @@ -1941,7 +1967,7 @@ FormatPromptRecurse if (!special_directions) var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); else - var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item); + var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item, function_changed, initial_function); if (--max_num_children == 0) { @@ -1957,7 +1983,12 @@ FormatPromptRecurse } break; case 'a': - if (IsToken (var_name_begin, "addr}")) + if (IsToken (var_name_begin, "addr-file-or-load}")) + { + print_file_addr_or_load_addr = true; + } + if (IsToken (var_name_begin, "addr}") + || IsToken (var_name_begin, "addr-file-or-load}")) { if (addr && addr->IsValid()) { @@ -2159,8 +2190,7 @@ FormatPromptRecurse } } break; - - + case 'm': if (IsToken (var_name_begin, "module.")) { @@ -2288,6 +2318,14 @@ FormatPromptRecurse var_success = true; } + if (IsToken (var_name_begin, "changed}") && function_changed) + { + var_success = true; + } + if (IsToken (var_name_begin, "initial-function}") && initial_function) + { + var_success = true; + } else if (IsToken (var_name_begin, "name}")) { if (sc->function) @@ -2314,6 +2352,19 @@ FormatPromptRecurse var_success = true; } } + else if (IsToken (var_name_begin, "name-without-args}")) + { + ConstString name; + if (sc->function) + name = sc->function->GetMangled().GetName (Mangled::ePreferDemangledWithoutArguments); + else if (sc->symbol) + name = sc->symbol->GetMangled().GetName (Mangled::ePreferDemangledWithoutArguments); + if (name) + { + s.PutCString(name.GetCString()); + var_success = true; + } + } else if (IsToken (var_name_begin, "name-with-args}")) { // Print the function name with arguments in it @@ -2457,8 +2508,14 @@ FormatPromptRecurse } } } - else if (IsToken (var_name_begin, "addr-offset}")) + else if (IsToken (var_name_begin, "addr-offset}") + || IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) { + if (IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) + { + addr_offset_print_with_no_padding = true; + addr_offset_concrete_func_only = true; + } var_success = addr != NULL; if (var_success) { @@ -2529,6 +2586,34 @@ FormatPromptRecurse } } break; + case 'c': + if (IsToken (var_name_begin, "current-pc-arrow")) + { + if (addr && exe_ctx && exe_ctx->GetFramePtr()) + { + RegisterContextSP reg_ctx = exe_ctx->GetFramePtr()->GetRegisterContextSP(); + if (reg_ctx.get()) + { + addr_t pc_loadaddr = reg_ctx->GetPC(); + if (pc_loadaddr != LLDB_INVALID_ADDRESS) + { + Address pc; + pc.SetLoadAddress (pc_loadaddr, exe_ctx->GetTargetPtr()); + if (pc == *addr) + { + s.Printf ("->"); + var_success = true; + } + else + { + s.Printf(" "); + var_success = true; + } + } + } + } + } + break; } if (var_success) @@ -2586,7 +2671,7 @@ FormatPromptRecurse if (sc->function) { func_addr = sc->function->GetAddressRange().GetBaseAddress(); - if (sc->block) + if (sc->block && addr_offset_concrete_func_only == false) { // Check to make sure we aren't in an inline // function. If we are, use the inline block @@ -2604,14 +2689,19 @@ FormatPromptRecurse if (func_addr.IsValid()) { + const char *addr_offset_padding = " "; + if (addr_offset_print_with_no_padding) + { + addr_offset_padding = ""; + } if (func_addr.GetSection() == format_addr.GetSection()) { addr_t func_file_addr = func_addr.GetFileAddress(); addr_t addr_file_addr = format_addr.GetFileAddress(); if (addr_file_addr > func_file_addr) - s.Printf(" + %" PRIu64, addr_file_addr - func_file_addr); + s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_file_addr - func_file_addr); else if (addr_file_addr < func_file_addr) - s.Printf(" - %" PRIu64, func_file_addr - addr_file_addr); + s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_file_addr - addr_file_addr); var_success = true; } else @@ -2622,9 +2712,9 @@ FormatPromptRecurse addr_t func_load_addr = func_addr.GetLoadAddress (target); addr_t addr_load_addr = format_addr.GetLoadAddress (target); if (addr_load_addr > func_load_addr) - s.Printf(" + %" PRIu64, addr_load_addr - func_load_addr); + s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_load_addr - func_load_addr); else if (addr_load_addr < func_load_addr) - s.Printf(" - %" PRIu64, func_load_addr - addr_load_addr); + s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_load_addr - addr_load_addr); var_success = true; } } @@ -2641,10 +2731,21 @@ FormatPromptRecurse if (vaddr != LLDB_INVALID_ADDRESS) { - int addr_width = target->GetArchitecture().GetAddressByteSize() * 2; + int addr_width = 0; + if (exe_ctx && target) + { + target->GetArchitecture().GetAddressByteSize() * 2; + } if (addr_width == 0) addr_width = 16; - s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + if (print_file_addr_or_load_addr) + { + format_addr.Dump (&s, exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, 0); + } + else + { + s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + } var_success = true; } } @@ -2758,9 +2859,55 @@ Debugger::FormatPrompt std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); if (format_str.length()) format = format_str.c_str(); - return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj); + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj, false, false); } +bool +Debugger::FormatDisassemblerAddress (const char *format, + const SymbolContext *sc, + const SymbolContext *prev_sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s) +{ + if (format == NULL && exe_ctx != NULL && exe_ctx->HasTargetScope()) + { + format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); + } + bool function_changed = false; + bool initial_function = false; + if (prev_sc && (prev_sc->function || prev_sc->symbol)) + { + if (sc && (sc->function || sc->symbol)) + { + if (prev_sc->symbol && sc->symbol) + { + if (!sc->symbol->Compare (prev_sc->symbol->GetName(), prev_sc->symbol->GetType())) + { + function_changed = true; + } + } + else if (prev_sc->function && sc->function) + { + if (prev_sc->function->GetMangled() != sc->function->GetMangled()) + { + function_changed = true; + } + } + } + } + // The first context on a list of instructions will have a prev_sc that + // has no Function or Symbol -- if SymbolContext had an IsValid() method, it + // would return false. But we do get a prev_sc pointer. + if ((sc && (sc->function || sc->symbol)) + && prev_sc && (prev_sc->function == NULL && prev_sc->symbol == NULL)) + { + initial_function = true; + } + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, NULL, function_changed, initial_function); +} + + void Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) { diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 1d2b8cf04c32..649f0c5bcb26 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -410,17 +410,18 @@ Disassembler::PrintInstructions SymbolContext prev_sc; AddressRange sc_range; const Address *pc_addr_ptr = NULL; - ExecutionContextScope *exe_scope = exe_ctx.GetBestExecutionContextScope(); 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; - for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i).get(); if (inst) @@ -447,7 +448,7 @@ Disassembler::PrintInstructions if (offset != 0) strm.EOL(); - sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false); + sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false, false); strm.EOL(); if (sc.comp_unit && sc.line_entry.IsValid()) @@ -462,23 +463,6 @@ Disassembler::PrintInstructions } } } - else if ((sc.function || sc.symbol) && (sc.function != prev_sc.function || sc.symbol != prev_sc.symbol)) - { - if (prev_sc.function || prev_sc.symbol) - strm.EOL(); - - bool show_fullpaths = false; - bool show_module = true; - bool show_inlined_frames = true; - sc.DumpStopContext (&strm, - exe_scope, - addr, - show_fullpaths, - show_module, - show_inlined_frames); - - strm << ":\n"; - } } else { @@ -486,12 +470,13 @@ Disassembler::PrintInstructions } } - if ((options & eOptionMarkPCAddress) && pc_addr_ptr) - { - strm.PutCString(inst_is_at_pc ? "-> " : " "); - } const bool show_bytes = (options & eOptionShowBytes) != 0; - inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx); + const char *disassembly_format = "${addr-file-or-load}: "; + if (exe_ctx.HasTargetScope()) + { + disassembly_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat (); + } + inst->Dump (&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, &prev_sc, disassembly_format); strm.EOL(); } else @@ -578,7 +563,10 @@ Instruction::Dump (lldb_private::Stream *s, uint32_t max_opcode_byte_size, bool show_address, bool show_bytes, - const ExecutionContext* exe_ctx) + const ExecutionContext* exe_ctx, + const SymbolContext *sym_ctx, + const SymbolContext *prev_sym_ctx, + const char *disassembly_addr_format_spec) { size_t opcode_column_width = 7; const size_t operand_column_width = 25; @@ -589,13 +577,7 @@ Instruction::Dump (lldb_private::Stream *s, if (show_address) { - m_address.Dump(&ss, - exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, - Address::DumpStyleLoadAddress, - Address::DumpStyleModuleWithFileAddress, - 0); - - ss.PutCString(": "); + Debugger::FormatDisassemblerAddress (disassembly_addr_format_spec, sym_ctx, prev_sym_ctx, exe_ctx, &m_address, ss); } if (show_bytes) @@ -621,7 +603,7 @@ Instruction::Dump (lldb_private::Stream *s, } } - const size_t opcode_pos = ss.GetSize(); + 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 @@ -1003,13 +985,18 @@ InstructionList::Dump (Stream *s, { const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); collection::const_iterator pos, begin, end; + const char *disassemble_format = "${addr-file-or-load}: "; + if (exe_ctx) + { + disassemble_format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat (); + } 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); + (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, NULL, NULL, disassemble_format); } } diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index c4fa10fedd86..a7dbc5fe31be 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -4986,10 +4986,12 @@ __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Stream.h" #include "lldb/Core/Timer.h" +#include "lldb/Target/CPPLanguageRuntime.h" #include #include #include + using namespace lldb_private; static inline bool @@ -5000,6 +5002,58 @@ cstring_is_mangled (const char *s) return false; } +static const ConstString & +get_demangled_name_without_arguments (const Mangled *obj) +{ + // This pair is + static std::pair g_most_recent_mangled_to_name_sans_args; + + // Need to have the mangled & demangled names we're currently examining as statics + // so we can return a const ref to them at the end of the func if we don't have + // anything better. + static ConstString g_last_mangled; + static ConstString g_last_demangled; + + ConstString mangled = obj->GetMangledName (); + ConstString demangled = obj->GetDemangledName (); + + if (mangled && g_most_recent_mangled_to_name_sans_args.first == mangled) + { + return g_most_recent_mangled_to_name_sans_args.second; + } + + g_last_demangled = demangled; + g_last_mangled = mangled; + + const char *mangled_name_cstr = mangled.GetCString(); + const char *demangled_name_cstr = demangled.GetCString(); + + if (demangled && mangled_name_cstr && mangled_name_cstr[0]) + { + if (mangled_name_cstr[0] == '_' && mangled_name_cstr[1] == 'Z' && + (mangled_name_cstr[2] != 'T' && // avoid virtual table, VTT structure, typeinfo structure, and typeinfo mangled_name + mangled_name_cstr[2] != 'G' && // avoid guard variables + mangled_name_cstr[2] != 'Z')) // named local entities (if we eventually handle eSymbolTypeData, we will want this back) + { + CPPLanguageRuntime::MethodName cxx_method (demangled); + if (!cxx_method.GetBasename().empty() && !cxx_method.GetContext().empty()) + { + std::string shortname = cxx_method.GetContext().str(); + shortname += "::"; + shortname += cxx_method.GetBasename().str(); + ConstString result(shortname.c_str()); + g_most_recent_mangled_to_name_sans_args.first = mangled; + g_most_recent_mangled_to_name_sans_args.second = result; + return g_most_recent_mangled_to_name_sans_args.second; + } + } + } + + if (demangled) + return g_last_demangled; + return g_last_mangled; +} + #pragma mark Mangled //---------------------------------------------------------------------- // Default constructor @@ -5215,6 +5269,14 @@ Mangled::NameMatches (const RegularExpression& regex) const const ConstString& Mangled::GetName (Mangled::NamePreference preference) const { + if (preference == ePreferDemangledWithoutArguments) + { + // Call the accessor to make sure we get a demangled name in case + // it hasn't been demangled yet... + GetDemangledName(); + + return get_demangled_name_without_arguments (this); + } if (preference == ePreferDemangled) { // Call the accessor to make sure we get a demangled name in case diff --git a/lldb/source/Core/StreamString.cpp b/lldb/source/Core/StreamString.cpp index 8d7d039fd65c..ef2b70583ebd 100644 --- a/lldb/source/Core/StreamString.cpp +++ b/lldb/source/Core/StreamString.cpp @@ -65,6 +65,22 @@ StreamString::GetSize () const return m_packet.size(); } +size_t +StreamString::GetSizeOfLastLine () const +{ + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) + { + return length; + } + else + { + ++last_line_begin_pos; + return length - last_line_begin_pos; + } +} + std::string & StreamString::GetString() { diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 180c68f5bcd4..8fdb3109298d 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/SourceMgr.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" @@ -199,6 +200,11 @@ IRExecutionUnit::DisassembleFunction (Stream &stream, InstructionList &instruction_list = disassembler_sp->GetInstructionList(); const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize(); + const char *disassemble_format = "${addr-file-or-load}: "; + if (exe_ctx.HasTargetScope()) + { + disassemble_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat(); + } for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize(); instruction_index < num_instructions; @@ -209,7 +215,10 @@ IRExecutionUnit::DisassembleFunction (Stream &stream, max_opcode_byte_size, true, true, - &exe_ctx); + &exe_ctx, + NULL, + NULL, + disassemble_format); stream.PutChar('\n'); } // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp index c14371d0589c..2163814b0f6e 100644 --- a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -832,11 +832,19 @@ const char *DisassemblerLLVMC::SymbolLookup (uint64_t value, value_so_addr.Dump (&ss, target, - Address::DumpStyleResolvedDescriptionNoModule, + Address::DumpStyleResolvedDescriptionNoFunctionArguments, Address::DumpStyleSectionNameOffset); if (!ss.GetString().empty()) { + // If Address::Dump returned a multi-line description, most commonly seen when we + // have multiple levels of inlined functions at an address, only show the first line. + std::string &str(ss.GetString()); + size_t first_eol_char = str.find_first_of ("\r\n"); + if (first_eol_char != std::string::npos) + { + str.erase (first_eol_char); + } m_inst->AppendComment(ss.GetString()); } } diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp index b8d56d3909e9..7e484bd8dc73 100644 --- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -146,7 +146,8 @@ UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& if (log && log->GetVerbose ()) { StreamString strm; - inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL); + const char *disassemble_format = "${frame.pc}: "; + inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL, NULL, NULL, disassemble_format); log->PutCString (strm.GetData()); } diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 0e390dd08c5f..129f4def0067 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -128,7 +128,8 @@ SymbolContext::DumpStopContext const Address &addr, bool show_fullpaths, bool show_module, - bool show_inlined_frames + bool show_inlined_frames, + bool show_function_arguments ) const { bool dumped_something = false; @@ -146,7 +147,12 @@ SymbolContext::DumpStopContext { SymbolContext inline_parent_sc; Address inline_parent_addr; - if (function->GetMangled().GetName()) + if (show_function_arguments == false && function->GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments)) + { + dumped_something = true; + function->GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments).Dump(s); + } + else if (function->GetMangled().GetName()) { dumped_something = true; function->GetMangled().GetName().Dump(s); @@ -188,7 +194,7 @@ SymbolContext::DumpStopContext { s->EOL(); s->Indent(); - return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames); + return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames, show_function_arguments); } } else diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp index e6a9b027fc13..b1a60f6b4a24 100644 --- a/lldb/source/Symbol/Variable.cpp +++ b/lldb/source/Symbol/Variable.cpp @@ -174,13 +174,15 @@ Variable::DumpDeclaration (Stream *s, bool show_fullpaths, bool show_module) sc.block = nullptr; sc.line_entry.Clear(); bool show_inlined_frames = false; + const bool show_function_arguments = true; dumped_declaration_info = sc.DumpStopContext (s, nullptr, Address(), show_fullpaths, show_module, - show_inlined_frames); + show_inlined_frames, + show_function_arguments); if (sc.function) s->PutChar(':'); diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 0bea372b2e15..1197586f6658 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -1380,12 +1380,14 @@ StackFrame::Dump (Stream *strm, bool show_frame_index, bool show_fullpaths) GetSymbolContext(eSymbolContextEverything); const bool show_module = true; const bool show_inline = true; + const bool show_function_arguments = true; m_sc.DumpStopContext (strm, exe_ctx.GetBestExecutionContextScope(), GetFrameCodeAddress(), show_fullpaths, show_module, - show_inline); + show_inline, + show_function_arguments); } void diff --git a/lldb/source/Target/ThreadPlanTracer.cpp b/lldb/source/Target/ThreadPlanTracer.cpp index 5188df6d9193..d2b039d69f67 100644 --- a/lldb/source/Target/ThreadPlanTracer.cpp +++ b/lldb/source/Target/ThreadPlanTracer.cpp @@ -212,11 +212,15 @@ ThreadPlanAssemblyTracer::Log () const bool show_bytes = true; const bool show_address = true; Instruction *instruction = instruction_list.GetInstructionAtIndex(0).get(); + const char *disassemble_format = "${addr-file-or-load}: "; instruction->Dump (stream, max_opcode_byte_size, show_address, show_bytes, - NULL); + NULL, + NULL, + NULL, + disassemble_format); } } } diff --git a/lldb/test/functionalities/abbreviation/TestAbbreviations.py b/lldb/test/functionalities/abbreviation/TestAbbreviations.py index 4362fb79b3a9..48956352137c 100644 --- a/lldb/test/functionalities/abbreviation/TestAbbreviations.py +++ b/lldb/test/functionalities/abbreviation/TestAbbreviations.py @@ -150,8 +150,8 @@ class AbbreviationsTestCase(TestBase): # ARCH, if not specified, defaults to x86_64. if self.getArchitecture() in ["", 'x86_64', 'i386']: self.expect("dis -f", - startstr = "a.out`sum(int, int)", - substrs = [' mov', + substrs = [':', + ' mov', ' addl ', 'ret']) diff --git a/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py b/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py index 8f17a4e30d16..f8ed37249b99 100644 --- a/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py +++ b/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py @@ -176,8 +176,11 @@ class AssertingInferiorTestCase(TestBase): # of the function and in the next function. We also can't back the PC up # because we don't know how much to back it up by on targets with opcodes # that have differing sizes - self.expect("disassemble -a %s" % frame.GetPC(), - substrs = ['->']) + pc_backup_offset = 1 + if frame.GetFrameID() == 0: + pc_backup_offset = 0 + self.expect("disassemble -a %s" % (frame.GetPC() - pc_backup_offset), + substrs = ['<%s>:' % frame.GetFunctionName()]) def check_expr_in_main(self, thread): depth = thread.GetNumFrames()