diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h --- a/lldb/source/Commands/CommandObjectDisassemble.h +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -62,6 +62,7 @@ // "at_pc". This should be set // in SetOptionValue if anything the selects a location is set. lldb::addr_t symbol_containing_addr; + bool force = false; }; CommandObjectDisassemble(CommandInterpreter &interpreter); @@ -73,15 +74,19 @@ protected: bool DoExecute(Args &command, CommandReturnObject &result) override; - llvm::Expected> GetRangesForSelectedMode(); + llvm::Expected> + GetRangesForSelectedMode(CommandReturnObject &result); llvm::Expected> GetContainingAddressRanges(); llvm::Expected> GetCurrentFunctionRanges(); llvm::Expected> GetCurrentLineRanges(); - llvm::Expected> GetNameRanges(); + llvm::Expected> + GetNameRanges(CommandReturnObject &result); llvm::Expected> GetPCRanges(); llvm::Expected> GetStartEndAddressRanges(); + llvm::Error CheckRangeSize(const AddressRange &range, llvm::StringRef what); + CommandOptions m_options; }; diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp --- a/lldb/source/Commands/CommandObjectDisassemble.cpp +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -21,8 +21,9 @@ #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" -#define DEFAULT_DISASM_BYTE_SIZE 32 -#define DEFAULT_DISASM_NUM_INS 4 +static constexpr unsigned default_disasm_byte_size = 32; +static constexpr unsigned default_disasm_num_ins = 4; +static constexpr unsigned large_function_threshold = 4000; using namespace lldb; using namespace lldb_private; @@ -143,6 +144,10 @@ } } break; + case '\x01': + force = true; + break; + default: llvm_unreachable("Unimplemented option"); } @@ -186,6 +191,7 @@ arch.Clear(); some_location_specified = false; + force = false; } Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished( @@ -214,6 +220,21 @@ CommandObjectDisassemble::~CommandObjectDisassemble() = default; +llvm::Error CommandObjectDisassemble::CheckRangeSize(const AddressRange &range, + llvm::StringRef what) { + if (m_options.num_instructions > 0 || m_options.force || + range.GetByteSize() < large_function_threshold) + return llvm::Error::success(); + StreamString msg; + msg << "Not disassembling " << what << " because it is very large "; + range.Dump(&msg, &GetSelectedTarget(), Address::DumpStyleLoadAddress, + Address::DumpStyleFileAddress); + msg << ". To disassemble specify an instruction count limit, start/stop " + "addresses or use the --force option."; + return llvm::createStringError(llvm::inconvertibleErrorCode(), + msg.GetString()); +} + llvm::Expected> CommandObjectDisassemble::GetContainingAddressRanges() { std::vector ranges; @@ -254,6 +275,9 @@ "Could not find function bounds for address 0x%" PRIx64, m_options.symbol_containing_addr); } + + if (llvm::Error err = CheckRangeSize(ranges[0], "the function")) + return std::move(err); return ranges; } @@ -273,8 +297,10 @@ else if (sc.symbol && sc.symbol->ValueIsAddress()) { range = {sc.symbol->GetAddress(), sc.symbol->GetByteSize()}; } else - range = {frame->GetFrameCodeAddress(), DEFAULT_DISASM_BYTE_SIZE}; + range = {frame->GetFrameCodeAddress(), default_disasm_byte_size}; + if (llvm::Error err = CheckRangeSize(range, "the current function")) + return std::move(err); return std::vector{range}; } @@ -298,7 +324,7 @@ } llvm::Expected> -CommandObjectDisassemble::GetNameRanges() { +CommandObjectDisassemble::GetNameRanges(CommandReturnObject &result) { ConstString name(m_options.func_name.c_str()); const bool include_symbols = true; const bool include_inlines = true; @@ -309,6 +335,7 @@ name, eFunctionNameTypeAuto, include_symbols, include_inlines, sc_list); std::vector ranges; + llvm::Error range_errs = llvm::Error::success(); AddressRange range; const uint32_t scope = eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol; @@ -317,14 +344,21 @@ for (uint32_t range_idx = 0; sc.GetAddressRange(scope, range_idx, use_inline_block_range, range); ++range_idx) { - ranges.push_back(range); + if (llvm::Error err = CheckRangeSize(range, "a range")) + range_errs = joinErrors(std::move(range_errs), std::move(err)); + else + ranges.push_back(range); } } if (ranges.empty()) { + if (range_errs) + return std::move(range_errs); return llvm::createStringError(llvm::inconvertibleErrorCode(), "Unable to find symbol with name '%s'.\n", name.GetCString()); } + if (range_errs) + result.AppendWarning(toString(std::move(range_errs))); return ranges; } @@ -340,7 +374,7 @@ if (m_options.num_instructions == 0) { // Disassembling at the PC always disassembles some number of // instructions (not the whole function). - m_options.num_instructions = DEFAULT_DISASM_NUM_INS; + m_options.num_instructions = default_disasm_num_ins; } return std::vector{{frame->GetFrameCodeAddress(), 0}}; } @@ -359,7 +393,8 @@ } llvm::Expected> -CommandObjectDisassemble::GetRangesForSelectedMode() { +CommandObjectDisassemble::GetRangesForSelectedMode( + CommandReturnObject &result) { if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) return CommandObjectDisassemble::GetContainingAddressRanges(); if (m_options.current_function) @@ -367,7 +402,7 @@ if (m_options.frame_line) return CommandObjectDisassemble::GetCurrentLineRanges(); if (!m_options.func_name.empty()) - return CommandObjectDisassemble::GetNameRanges(); + return CommandObjectDisassemble::GetNameRanges(result); if (m_options.start_addr != LLDB_INVALID_ADDRESS) return CommandObjectDisassemble::GetStartEndAddressRanges(); return CommandObjectDisassemble::GetPCRanges(); @@ -440,7 +475,8 @@ if (m_options.raw) options |= Disassembler::eOptionRawOuput; - llvm::Expected> ranges = GetRangesForSelectedMode(); + llvm::Expected> ranges = + GetRangesForSelectedMode(result); if (!ranges) { result.AppendError(toString(ranges.takeError())); result.SetStatus(eReturnStatusFailed); @@ -453,7 +489,7 @@ if (m_options.num_instructions == 0) { limit = {Disassembler::Limit::Bytes, cur_range.GetByteSize()}; if (limit.value == 0) - limit.value = DEFAULT_DISASM_BYTE_SIZE; + limit.value = default_disasm_byte_size; } else { limit = {Disassembler::Limit::Instructions, m_options.num_instructions}; } @@ -476,7 +512,7 @@ result.SetStatus(eReturnStatusFailed); } if (print_sc_header) - result.AppendMessage("\n"); + result.GetOutputStream() << "\n"; } return result.Succeeded(); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -311,7 +311,7 @@ Desc<"Address at which to start disassembling.">; def disassemble_options_end_address : Option<"end-address", "e">, Group<1>, Arg<"AddressOrExpression">, Desc<"Address at which to end disassembling.">; - def disassemble_options_count : Option<"count", "c">, Groups<[2,3,4,5]>, + def disassemble_options_count : Option<"count", "c">, Groups<[2,3,4,5,7]>, Arg<"NumLines">, Desc<"Number of instructions to display.">; def disassemble_options_name : Option<"name", "n">, Group<3>, Arg<"FunctionName">, Completion<"Symbol">, @@ -326,6 +326,8 @@ def disassemble_options_address : Option<"address", "a">, Group<7>, Arg<"AddressOrExpression">, Desc<"Disassemble function containing this address.">; + def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>, + Desc<"Force dissasembly of large functions.">; } let Command = "expression" in { diff --git a/lldb/test/Shell/Commands/Inputs/command-disassemble.lldbinit b/lldb/test/Shell/Commands/Inputs/command-disassemble.lldbinit --- a/lldb/test/Shell/Commands/Inputs/command-disassemble.lldbinit +++ b/lldb/test/Shell/Commands/Inputs/command-disassemble.lldbinit @@ -6,7 +6,13 @@ disassemble --start-address 0x4 --end-address 0x8 disassemble --start-address 0x8 --end-address 0x4 disassemble --address 0x0 -disassemble --address 0xdead +disassemble --address 0xdeadb +disassemble --address 0x100 +disassemble --address 0x100 --count 3 +disassemble --address 0x100 --force disassemble --start-address 0x0 --count 7 disassemble --start-address 0x0 --end-address 0x20 --count 7 -disassemble --address 0x0 --count 7 +disassemble --name case1 +disassemble --name case2 +disassemble --name case3 +disassemble --name case3 --count 3 diff --git a/lldb/test/Shell/Commands/command-disassemble-process.yaml b/lldb/test/Shell/Commands/command-disassemble-process.yaml --- a/lldb/test/Shell/Commands/command-disassemble-process.yaml +++ b/lldb/test/Shell/Commands/command-disassemble-process.yaml @@ -1,6 +1,7 @@ # REQUIRES: x86 -# RUN: yaml2obj --docnum=1 %s > %T/command-disassemble-process.exe +# RUN: yaml2obj --docnum=1 -DMAIN_SIZE=8 %s > %T/command-disassemble-process.exe +# RUN: yaml2obj --docnum=1 -DMAIN_SIZE=4000 %s > %T/command-disassemble-process.big.exe # RUN: yaml2obj --docnum=2 %s > %t # RUN: %lldb -c %t %T/command-disassemble-process.exe \ @@ -8,6 +9,9 @@ # RUN: -s %S/Inputs/command-disassemble-process.lldbinit -o exit 2>&1 \ # RUN: | FileCheck %s +# RUN: %lldb -c %t %T/command-disassemble-process.big.exe \ +# RUN: -o disassemble -o exit 2>&1 | FileCheck %s --check-prefix=BIG + # CHECK: (lldb) disassemble # CHECK-NEXT: command-disassemble-process.exe`main: # CHECK-NEXT: 0x4002 <+0>: addb %al, (%rcx) @@ -59,6 +63,8 @@ # CHECK-NEXT: 0x400e: addb %cl, (%rcx) # CHECK-NEXT: 0x4010: addb %cl, (%rdx) +# BIG: error: Not disassembling the current function because it is very large [0x0000000000004002-0x0000000000004fa2). To disassemble specify an instruction count limit, start/stop addresses or use the --force option. + --- !ELF FileHeader: Class: ELFCLASS64 @@ -72,6 +78,7 @@ Address: 0x0000000000004000 AddressAlign: 0x0000000000001000 Content: 00000001000200030006000700080009000A000B000E000F00100011001200130016001700180019001A001B001E001F00200021002200230026002700280029002A002B002E002F + Size: 0x10000 - Name: .note.gnu.build-id Type: SHT_NOTE Flags: [ SHF_ALLOC ] @@ -83,7 +90,7 @@ Type: STT_FUNC Section: .text Value: 0x0000000000004002 - Size: 0x0000000000000008 + Size: [[MAIN_SIZE]] ProgramHeaders: - Type: PT_LOAD Flags: [ PF_X, PF_R ] diff --git a/lldb/test/Shell/Commands/command-disassemble.s b/lldb/test/Shell/Commands/command-disassemble.s --- a/lldb/test/Shell/Commands/command-disassemble.s +++ b/lldb/test/Shell/Commands/command-disassemble.s @@ -51,8 +51,19 @@ # CHECK-NEXT: command-disassemble.s.tmp[0x8] <+8>: int $0x14 # CHECK-NEXT: command-disassemble.s.tmp[0xa] <+10>: int $0x15 # CHECK-NEXT: command-disassemble.s.tmp[0xc] <+12>: int $0x16 -# CHECK-NEXT: (lldb) disassemble --address 0xdead -# CHECK-NEXT: error: Could not find function bounds for address 0xdead +# CHECK-NEXT: (lldb) disassemble --address 0xdeadb +# CHECK-NEXT: error: Could not find function bounds for address 0xdeadb +# CHECK-NEXT: (lldb) disassemble --address 0x100 +# CHECK-NEXT: error: Not disassembling the function because it is very large [0x0000000000000040-0x0000000000002040). To disassemble specify an instruction count limit, start/stop addresses or use the --force option. +# CHECK-NEXT: (lldb) disassemble --address 0x100 --count 3 +# CHECK-NEXT: command-disassemble.s.tmp`very_long: +# CHECK-NEXT: command-disassemble.s.tmp[0x40] <+0>: int $0x2a +# CHECK-NEXT: command-disassemble.s.tmp[0x42] <+2>: int $0x2a +# CHECK-NEXT: command-disassemble.s.tmp[0x44] <+4>: int $0x2a +# CHECK-NEXT: (lldb) disassemble --address 0x100 --force +# CHECK-NEXT: command-disassemble.s.tmp`very_long: +# CHECK-NEXT: command-disassemble.s.tmp[0x40] <+0>: int $0x2a +# CHECK: command-disassemble.s.tmp[0x203e] <+8190>: int $0x2a # CHECK-NEXT: (lldb) disassemble --start-address 0x0 --count 7 # CHECK-NEXT: command-disassemble.s.tmp`foo: # CHECK-NEXT: command-disassemble.s.tmp[0x0] <+0>: int $0x10 @@ -64,8 +75,32 @@ # CHECK-NEXT: command-disassemble.s.tmp[0xc] <+12>: int $0x16 # CHECK-NEXT: (lldb) disassemble --start-address 0x0 --end-address 0x20 --count 7 # CHECK-NEXT: error: invalid combination of options for the given command -# CHECK-NEXT: (lldb) disassemble --address 0x0 --count 7 -# CHECK-NEXT: error: invalid combination of options for the given command +# CHECK-NEXT: (lldb) disassemble --name case1 +# CHECK-NEXT: command-disassemble.s.tmp`n1::case1: +# CHECK-NEXT: command-disassemble.s.tmp[0x2040] <+0>: int $0x30 +# CHECK-EMPTY: +# CHECK-NEXT: command-disassemble.s.tmp`n2::case1: +# CHECK-NEXT: command-disassemble.s.tmp[0x2042] <+0>: int $0x31 +# CHECK-EMPTY: +# CHECK-NEXT: (lldb) disassemble --name case2 +# CHECK-NEXT: command-disassemble.s.tmp`n1::case2: +# CHECK-NEXT: command-disassemble.s.tmp[0x2044] <+0>: int $0x32 +# CHECK-NEXT: warning: Not disassembling a range because it is very large [0x0000000000002046-0x0000000000004046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option. +# CHECK-NEXT: (lldb) disassemble --name case3 +# CHECK-NEXT: error: Not disassembling a range because it is very large [0x0000000000004046-0x0000000000006046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option. +# CHECK-NEXT: Not disassembling a range because it is very large [0x0000000000006046-0x0000000000008046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option. +# CHECK-NEXT: (lldb) disassemble --name case3 --count 3 +# CHECK-NEXT: command-disassemble.s.tmp`n1::case3: +# CHECK-NEXT: command-disassemble.s.tmp[0x4046] <+0>: int $0x2a +# CHECK-NEXT: command-disassemble.s.tmp[0x4048] <+2>: int $0x2a +# CHECK-NEXT: command-disassemble.s.tmp[0x404a] <+4>: int $0x2a +# CHECK-EMPTY: +# CHECK-NEXT: command-disassemble.s.tmp`n2::case3: +# CHECK-NEXT: command-disassemble.s.tmp[0x6046] <+0>: int $0x2a +# CHECK-NEXT: command-disassemble.s.tmp[0x6048] <+2>: int $0x2a +# CHECK-NEXT: command-disassemble.s.tmp[0x604a] <+4>: int $0x2a +# CHECK-EMPTY: + .text foo: @@ -102,3 +137,32 @@ int $0x2d int $0x2e int $0x2f + +very_long: + .rept 0x1000 + int $42 + .endr + +_ZN2n15case1Ev: + int $0x30 + +_ZN2n25case1Ev: + int $0x31 + +_ZN2n15case2Ev: + int $0x32 + +_ZN2n25case2Ev: + .rept 0x1000 + int $42 + .endr + +_ZN2n15case3Ev: + .rept 0x1000 + int $42 + .endr + +_ZN2n25case3Ev: + .rept 0x1000 + int $42 + .endr