Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -3149,6 +3149,43 @@ void ResetImageToken(size_t token); + //------------------------------------------------------------------ + /// Find the next branch instruction to set a breakpoint on + /// + /// When instruction stepping through a source line, instead of + /// stepping through each instruction, we can put a breakpoint on + /// the next branch instruction (within the range of instructions + /// we are stepping through) and continue the process to there, + /// yielding significant performance benefits over instruction + /// stepping. + /// + /// @param[in] default_stop_addr + /// The address of the instruction where lldb would put a + /// breakpoint normally. + /// + /// @param[in] range_bounds + /// The range which the breakpoint must be contained within. + /// Typically a source line. + /// + /// @param[in] insn_list + /// A pointer to an InstructionList for the instructions of + /// the range_bounds AddressRange. The caller may be able + /// to cache the InstructionList and re-use it across multiple + /// invocations. If this is NULL, this method will disassemble + /// the range_bounds AddressRange instructions and delete them + /// at the end of the method. + /// + /// @return + /// The address of the next branch instruction, or the end of + /// the range provided in range_bounds. If there are any + /// problems with the disassembly or getting the instructions, + /// the original default_stop_addr will be returned. + //------------------------------------------------------------------ + Address + AdvanceAddressToNextBranchInstruction (Address default_stop_addr, + AddressRange range_bounds, + InstructionList *insn_list); + protected: void SetState (lldb::EventSP &event_sp); Index: include/lldb/Target/Thread.h =================================================================== --- include/lldb/Target/Thread.h +++ include/lldb/Target/Thread.h @@ -888,6 +888,16 @@ /// @param[in] run_vote /// See standard meanings for the stop & run votes in ThreadPlan.h. /// + /// @param[in] continue_to_next_branch + /// Normally this will enqueue a plan that will put a breakpoint on the return address and continue + /// to there. If continue_to_next_branch is true, this is an operation not involving the user -- + /// e.g. stepping "next" in a source line and we instruction stepped into another function -- + /// so instead of putting a breakpoint on the return address, advance the breakpoint to the + /// end of the source line that is doing the call, or until the next flow control instruction. + /// If the return value from the function call is to be retrieved / displayed to the user, you must stop + /// on the return address. The return value may be stored in volatile registers which are overwritten + /// before the next branch instruction. + /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the plan could not be queued. //------------------------------------------------------------------ @@ -898,7 +908,8 @@ bool stop_other_threads, Vote stop_vote, // = eVoteYes, Vote run_vote, // = eVoteNoOpinion); - uint32_t frame_idx); + uint32_t frame_idx, + bool continue_to_next_branch = false); //------------------------------------------------------------------ /// Gets the plan used to step through the code that steps from a function Index: include/lldb/Target/ThreadPlanStepOut.h =================================================================== --- include/lldb/Target/ThreadPlanStepOut.h +++ include/lldb/Target/ThreadPlanStepOut.h @@ -31,7 +31,8 @@ Vote stop_vote, Vote run_vote, uint32_t frame_idx, - LazyBool step_out_avoids_code_without_debug_info); + LazyBool step_out_avoids_code_without_debug_info, + bool continue_to_next_branch = false); ~ThreadPlanStepOut() override; Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -6515,3 +6515,69 @@ if (token < m_image_tokens.size()) m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN; } + +Address +Process::AdvanceAddressToNextBranchInstruction (Address default_stop_addr, AddressRange range_bounds, + InstructionList *insn_list) +{ + Target &target = GetTarget(); + DisassemblerSP disassembler_sp; + + Address retval = default_stop_addr; + + if (target.GetUseFastStepping() == false) + return retval; + if (default_stop_addr.IsValid() == false) + return retval; + + + if (insn_list == NULL) + { + ExecutionContext exe_ctx (this); + const char *plugin_name = nullptr; + const char *flavor = nullptr; + const bool prefer_file_cache = true; + disassembler_sp = Disassembler::DisassembleRange(target.GetArchitecture(), + plugin_name, + flavor, + exe_ctx, + range_bounds, + prefer_file_cache); + if (disassembler_sp.get()) + insn_list = &disassembler_sp->GetInstructionList(); + } + + if (insn_list == NULL) + { + return retval; + } + + size_t insn_offset = insn_list->GetIndexOfInstructionAtAddress(default_stop_addr); + if (insn_offset == UINT32_MAX) + { + return retval; + } + + uint32_t branch_index = insn_list->GetIndexOfNextBranchInstruction (insn_offset, target); + if (branch_index == UINT32_MAX) + { + return retval; + } + + if (branch_index > insn_offset) + { + Address next_branch_insn_address = insn_list->GetInstructionAtIndex (branch_index)->GetAddress(); + if (next_branch_insn_address.IsValid() && range_bounds.ContainsFileAddress (next_branch_insn_address)) + { + retval = next_branch_insn_address; + } + } + + if (disassembler_sp.get()) + { + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + disassembler_sp->GetInstructionList().Clear(); + } + + return retval; +} Index: source/Target/Thread.cpp =================================================================== --- source/Target/Thread.cpp +++ source/Target/Thread.cpp @@ -1591,7 +1591,7 @@ Vote stop_vote, Vote run_vote, uint32_t frame_idx, - LazyBool step_out_avoids_code_withoug_debug_info) + LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp (new ThreadPlanStepOut (*this, addr_context, @@ -1600,7 +1600,7 @@ stop_vote, run_vote, frame_idx, - step_out_avoids_code_withoug_debug_info)); + step_out_avoids_code_without_debug_info)); if (thread_plan_sp->ValidatePlan(nullptr)) { @@ -1620,7 +1620,8 @@ bool stop_other_threads, Vote stop_vote, Vote run_vote, - uint32_t frame_idx) + uint32_t frame_idx, + bool continue_to_next_branch) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut (*this, addr_context, @@ -1629,7 +1630,8 @@ stop_vote, run_vote, frame_idx, - eLazyBoolNo)); + eLazyBoolNo, + continue_to_next_branch)); ThreadPlanStepOut *new_plan = static_cast(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); Index: source/Target/ThreadPlanShouldStopHere.cpp =================================================================== --- source/Target/ThreadPlanShouldStopHere.cpp +++ source/Target/ThreadPlanShouldStopHere.cpp @@ -135,7 +135,8 @@ stop_others, eVoteNo, eVoteNoOpinion, - frame_index); + frame_index, + true); return return_plan_sp; } Index: source/Target/ThreadPlanStepOut.cpp =================================================================== --- source/Target/ThreadPlanStepOut.cpp +++ source/Target/ThreadPlanStepOut.cpp @@ -18,6 +18,7 @@ #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/ABI.h" #include "lldb/Target/Process.h" @@ -44,7 +45,8 @@ Vote stop_vote, Vote run_vote, uint32_t frame_idx, - LazyBool step_out_avoids_code_without_debug_info + LazyBool step_out_avoids_code_without_debug_info, + bool continue_to_next_branch ) : ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote), ThreadPlanShouldStopHere (this), @@ -86,7 +88,8 @@ eVoteNoOpinion, eVoteNoOpinion, frame_idx - 1, - eLazyBoolNo)); + eLazyBoolNo, + continue_to_next_branch)); static_cast(m_step_out_to_inline_plan_sp.get())->SetShouldStopHereCallbacks(nullptr, nullptr); m_step_out_to_inline_plan_sp->SetPrivate(true); } @@ -101,7 +104,35 @@ // Find the return address and set a breakpoint there: // FIXME - can we do this more securely if we know first_insn? - m_return_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress(&m_thread.GetProcess()->GetTarget()); + Address return_address (return_frame_sp->GetFrameCodeAddress()); + if (continue_to_next_branch) + { + SymbolContext return_address_sc; + AddressRange range; + Address return_address_decr_pc = return_address; + if (return_address_decr_pc.GetOffset() > 0) + return_address_decr_pc.Slide (-1); + + return_address_decr_pc.CalculateSymbolContext (&return_address_sc, lldb::eSymbolContextLineEntry | lldb::eSymbolContextFunction | lldb::eSymbolContextSymbol); + if (return_address_sc.line_entry.IsValid()) + { + range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(); + } + else if (return_address_sc.function) + { + range = return_address_sc.function->GetAddressRange(); + } + else if (return_address_sc.symbol && return_address_sc.symbol->GetByteSizeIsValid()) + { + range.GetBaseAddress() = return_address_sc.symbol->GetAddress(); + range.SetByteSize (return_address_sc.symbol->GetByteSize()); + } + if (range.GetByteSize() > 0) + { + return_address = m_thread.GetProcess()->AdvanceAddressToNextBranchInstruction (return_address, range, nullptr); + } + } + m_return_addr = return_address.GetLoadAddress(&m_thread.GetProcess()->GetTarget()); if (m_return_addr == LLDB_INVALID_ADDRESS) return; Index: source/Target/ThreadPlanStepOverRange.cpp =================================================================== --- source/Target/ThreadPlanStepOverRange.cpp +++ source/Target/ThreadPlanStepOverRange.cpp @@ -185,7 +185,8 @@ stop_others, eVoteNo, eVoteNoOpinion, - 0); + 0, + true); break; } else