diff options
-rw-r--r-- | lldb/include/lldb/Target/Process.h | 28 | ||||
-rw-r--r-- | lldb/include/lldb/Target/Thread.h | 13 | ||||
-rw-r--r-- | lldb/include/lldb/Target/ThreadPlanStepOut.h | 3 | ||||
-rw-r--r-- | lldb/source/Target/Process.cpp | 62 | ||||
-rw-r--r-- | lldb/source/Target/Thread.cpp | 10 | ||||
-rw-r--r-- | lldb/source/Target/ThreadPlanShouldStopHere.cpp | 3 | ||||
-rw-r--r-- | lldb/source/Target/ThreadPlanStepOut.cpp | 29 | ||||
-rw-r--r-- | lldb/source/Target/ThreadPlanStepOverRange.cpp | 3 |
8 files changed, 140 insertions, 11 deletions
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 2e063c5bbcc..dfd332346e0 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -3149,6 +3149,34 @@ public: 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. + /// + /// @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); + protected: void SetState (lldb::EventSP &event_sp); diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 7aff77bd16f..56d86a5f4e1 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -888,6 +888,16 @@ public: /// @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 @@ public: 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 diff --git a/lldb/include/lldb/Target/ThreadPlanStepOut.h b/lldb/include/lldb/Target/ThreadPlanStepOut.h index ac5696357e9..ccf829f636d 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepOut.h +++ b/lldb/include/lldb/Target/ThreadPlanStepOut.h @@ -31,7 +31,8 @@ public: 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; diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 311c695860f..e4fe419660e 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6515,3 +6515,65 @@ Process::ResetImageToken(size_t token) if (token < m_image_tokens.size()) m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN; } + +Address +Process::AdvanceAddressToNextBranchInstruction (Address default_stop_addr, AddressRange range_bounds) +{ + Target &target = GetTarget(); + DisassemblerSP disassembler_sp; + InstructionList *insn_list = NULL; + + Address retval = default_stop_addr; + + if (target.GetUseFastStepping() == false) + return retval; + if (default_stop_addr.IsValid() == false) + return retval; + + 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; +} diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 9f9da972660..551e4800093 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1591,7 +1591,7 @@ Thread::QueueThreadPlanForStepOut(bool abort_other_plans, 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 @@ Thread::QueueThreadPlanForStepOut(bool abort_other_plans, 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 @@ Thread::QueueThreadPlanForStepOutNoShouldStop(bool abort_other_plans, 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 @@ Thread::QueueThreadPlanForStepOutNoShouldStop(bool abort_other_plans, stop_vote, run_vote, frame_idx, - eLazyBoolNo)); + eLazyBoolNo, + continue_to_next_branch)); ThreadPlanStepOut *new_plan = static_cast<ThreadPlanStepOut *>(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); diff --git a/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/lldb/source/Target/ThreadPlanShouldStopHere.cpp index 88f8db2bd7e..55aaaf00b56 100644 --- a/lldb/source/Target/ThreadPlanShouldStopHere.cpp +++ b/lldb/source/Target/ThreadPlanShouldStopHere.cpp @@ -135,7 +135,8 @@ ThreadPlanShouldStopHere::DefaultStepFromHereCallback (ThreadPlan *current_plan, stop_others, eVoteNo, eVoteNoOpinion, - frame_index); + frame_index, + true); return return_plan_sp; } diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp index 92403cb92ed..82b823be62e 100644 --- a/lldb/source/Target/ThreadPlanStepOut.cpp +++ b/lldb/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 @@ ThreadPlanStepOut::ThreadPlanStepOut 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 @@ ThreadPlanStepOut::ThreadPlanStepOut eVoteNoOpinion, eVoteNoOpinion, frame_idx - 1, - eLazyBoolNo)); + eLazyBoolNo, + continue_to_next_branch)); static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())->SetShouldStopHereCallbacks(nullptr, nullptr); m_step_out_to_inline_plan_sp->SetPrivate(true); } @@ -101,7 +104,27 @@ ThreadPlanStepOut::ThreadPlanStepOut // 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); + if (return_address_sc.line_entry.IsValid()) + { + range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(); + if (range.GetByteSize() > 0) + { + return_address = m_thread.GetProcess()->AdvanceAddressToNextBranchInstruction (return_address, + range); + } + } + } + m_return_addr = return_address.GetLoadAddress(&m_thread.GetProcess()->GetTarget()); if (m_return_addr == LLDB_INVALID_ADDRESS) return; diff --git a/lldb/source/Target/ThreadPlanStepOverRange.cpp b/lldb/source/Target/ThreadPlanStepOverRange.cpp index 08655be2439..2e731a84578 100644 --- a/lldb/source/Target/ThreadPlanStepOverRange.cpp +++ b/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -185,7 +185,8 @@ ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) stop_others, eVoteNo, eVoteNoOpinion, - 0); + 0, + true); break; } else |