diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Target/ThreadPlanStepOverRange.cpp')
-rw-r--r-- | contrib/llvm/tools/lldb/source/Target/ThreadPlanStepOverRange.cpp | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Target/ThreadPlanStepOverRange.cpp b/contrib/llvm/tools/lldb/source/Target/ThreadPlanStepOverRange.cpp new file mode 100644 index 0000000..aba8922 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -0,0 +1,450 @@ +//===-- ThreadPlanStepOverRange.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/ThreadPlanStepOverRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" + +using namespace lldb_private; +using namespace lldb; + +uint32_t ThreadPlanStepOverRange::s_default_flag_values = 0; + +//---------------------------------------------------------------------- +// ThreadPlanStepOverRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepOverRange::ThreadPlanStepOverRange +( + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others, + LazyBool step_out_avoids_code_without_debug_info +) : + ThreadPlanStepRange (ThreadPlan::eKindStepOverRange, "Step range stepping over", thread, range, addr_context, stop_others), + ThreadPlanShouldStopHere (this), + m_first_resume(true) +{ + SetFlagsToDefault(); + SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); +} + +ThreadPlanStepOverRange::~ThreadPlanStepOverRange () +{ +} + +void +ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf("step over"); + return; + } + s->Printf ("Stepping over"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) + { + s->Printf (" line "); + m_addr_context.line_entry.DumpStopContext (s, false); + printed_line_info = true; + } + + if (!printed_line_info || level == eDescriptionLevelVerbose) + { + s->Printf (" using ranges: "); + DumpRanges(s); + } + + s->PutChar('.'); +} + +void +ThreadPlanStepOverRange::SetupAvoidNoDebug(LazyBool step_out_avoids_code_without_debug_info) +{ + bool avoid_nodebug = true; + switch (step_out_avoids_code_without_debug_info) + { + case eLazyBoolYes: + avoid_nodebug = true; + break; + case eLazyBoolNo: + avoid_nodebug = false; + break; + case eLazyBoolCalculate: + avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug(); + break; + } + if (avoid_nodebug) + GetFlags().Set (ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); + else + GetFlags().Clear (ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); + // Step Over plans should always avoid no-debug on step in. Seems like you shouldn't + // have to say this, but a tail call looks more like a step in that a step out, so + // we want to catch this case. + GetFlags().Set (ThreadPlanShouldStopHere::eStepInAvoidNoDebug); +} + +bool +ThreadPlanStepOverRange::IsEquivalentContext(const SymbolContext &context) +{ + + // Match as much as is specified in the m_addr_context: + // This is a fairly loose sanity check. Note, sometimes the target doesn't get filled + // in so I left out the target check. And sometimes the module comes in as the .o file from the + // inlined range, so I left that out too... + if (m_addr_context.comp_unit) + { + if (m_addr_context.comp_unit == context.comp_unit) + { + if (m_addr_context.function && m_addr_context.function == context.function) + { + if (m_addr_context.block && m_addr_context.block == context.block) + return true; + } + } + } + else if (m_addr_context.symbol && m_addr_context.symbol == context.symbol) + { + return true; + } + return false; +} + +bool +ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + + if (log) + { + StreamString s; + s.Address (m_thread.GetRegisterContext()->GetPC(), + m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize()); + log->Printf("ThreadPlanStepOverRange reached %s.", s.GetData()); + } + + // If we're out of the range but in the same frame or in our caller's frame + // then we should stop. + // When stepping out we only stop others if we are forcing running one thread. + bool stop_others; + if (m_stop_others == lldb::eOnlyThisThread) + stop_others = true; + else + stop_others = false; + + ThreadPlanSP new_plan_sp; + + FrameComparison frame_order = CompareCurrentFrameToStartFrame(); + + if (frame_order == eFrameCompareOlder) + { + // If we're in an older frame then we should stop. + // + // A caveat to this is if we think the frame is older but we're actually in a trampoline. + // I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are + // in a trampoline we think the frame is older because the trampoline confused the backtracer. + // As below, we step through first, and then try to figure out how to get back out again. + + new_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others); + + if (new_plan_sp && log) + log->Printf("Thought I stepped out, but in fact arrived at a trampoline."); + } + else if (frame_order == eFrameCompareYounger) + { + // Make sure we really are in a new frame. Do that by unwinding and seeing if the + // start function really is our start function... + for(uint32_t i = 1;; ++i) + { + StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(i); + if (!older_frame_sp) { + // We can't unwind the next frame we should just get out of here & stop... + break; + } + + const SymbolContext &older_context = older_frame_sp->GetSymbolContext(eSymbolContextEverything); + if (IsEquivalentContext(older_context)) + { + new_plan_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop (false, + NULL, + true, + stop_others, + eVoteNo, + eVoteNoOpinion, + 0); + break; + } + else + { + new_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others); + // If we found a way through, then we should stop recursing. + if (new_plan_sp) + break; + } + } + } + else + { + // If we're still in the range, keep going. + if (InRange()) + { + SetNextBranchBreakpoint(); + return false; + } + + + if (!InSymbol()) + { + // This one is a little tricky. Sometimes we may be in a stub or something similar, + // in which case we need to get out of there. But if we are in a stub then it's + // likely going to be hard to get out from here. It is probably easiest to step into the + // stub, and then it will be straight-forward to step out. + new_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others); + } + else + { + // The current clang (at least through 424) doesn't always get the address range for the + // DW_TAG_inlined_subroutines right, so that when you leave the inlined range the line table says + // you are still in the source file of the inlining function. This is bad, because now you are missing + // the stack frame for the function containing the inlining, and if you sensibly do "finish" to get + // out of this function you will instead exit the containing function. + // To work around this, we check whether we are still in the source file we started in, and if not assume + // it is an error, and push a plan to get us out of this line and back to the containing file. + + if (m_addr_context.line_entry.IsValid()) + { + SymbolContext sc; + StackFrameSP frame_sp = m_thread.GetStackFrameAtIndex(0); + sc = frame_sp->GetSymbolContext (eSymbolContextEverything); + if (sc.line_entry.IsValid()) + { + if (sc.line_entry.file != m_addr_context.line_entry.file + && sc.comp_unit == m_addr_context.comp_unit + && sc.function == m_addr_context.function) + { + // Okay, find the next occurrence of this file in the line table: + LineTable *line_table = m_addr_context.comp_unit->GetLineTable(); + if (line_table) + { + Address cur_address = frame_sp->GetFrameCodeAddress(); + uint32_t entry_idx; + LineEntry line_entry; + if (line_table->FindLineEntryByAddress (cur_address, line_entry, &entry_idx)) + { + LineEntry next_line_entry; + bool step_past_remaining_inline = false; + if (entry_idx > 0) + { + // We require the previous line entry and the current line entry come + // from the same file. + // The other requirement is that the previous line table entry be part of an + // inlined block, we don't want to step past cases where people have inlined + // some code fragment by using #include <source-fragment.c> directly. + LineEntry prev_line_entry; + if (line_table->GetLineEntryAtIndex(entry_idx - 1, prev_line_entry) + && prev_line_entry.file == line_entry.file) + { + SymbolContext prev_sc; + Address prev_address = prev_line_entry.range.GetBaseAddress(); + prev_address.CalculateSymbolContext(&prev_sc); + if (prev_sc.block) + { + Block *inlined_block = prev_sc.block->GetContainingInlinedBlock(); + if (inlined_block) + { + AddressRange inline_range; + inlined_block->GetRangeContainingAddress(prev_address, inline_range); + if (!inline_range.ContainsFileAddress(cur_address)) + { + + step_past_remaining_inline = true; + } + + } + } + } + } + + if (step_past_remaining_inline) + { + uint32_t look_ahead_step = 1; + while (line_table->GetLineEntryAtIndex(entry_idx + look_ahead_step, next_line_entry)) + { + // Make sure we haven't wandered out of the function we started from... + Address next_line_address = next_line_entry.range.GetBaseAddress(); + Function *next_line_function = next_line_address.CalculateSymbolContextFunction(); + if (next_line_function != m_addr_context.function) + break; + + if (next_line_entry.file == m_addr_context.line_entry.file) + { + const bool abort_other_plans = false; + const bool stop_other_threads = false; + new_plan_sp = m_thread.QueueThreadPlanForRunToAddress(abort_other_plans, + next_line_address, + stop_other_threads); + break; + } + look_ahead_step++; + } + } + } + } + } + } + } + } + } + + // If we get to this point, we're not going to use a previously set "next branch" breakpoint, so delete it: + ClearNextBranchBreakpoint(); + + + // If we haven't figured out something to do yet, then ask the ShouldStopHere callback: + if (!new_plan_sp) + { + new_plan_sp = CheckShouldStopHereAndQueueStepOut (frame_order); + } + + if (!new_plan_sp) + m_no_more_plans = true; + else + { + // Any new plan will be an implementation plan, so mark it private: + new_plan_sp->SetPrivate(true); + m_no_more_plans = false; + } + + if (!new_plan_sp) + { + // For efficiencies sake, we know we're done here so we don't have to do this + // calculation again in MischiefManaged. + SetPlanComplete(); + return true; + } + else + return false; +} + +bool +ThreadPlanStepOverRange::DoPlanExplainsStop (Event *event_ptr) +{ + // For crashes, breakpoint hits, signals, etc, let the base plan (or some plan above us) + // handle the stop. That way the user can see the stop, step around, and then when they + // are done, continue and have their step complete. The exception is if we've hit our + // "run to next branch" breakpoint. + // Note, unlike the step in range plan, we don't mark ourselves complete if we hit an + // unexplained breakpoint/crash. + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); + bool return_value; + + if (stop_info_sp) + { + StopReason reason = stop_info_sp->GetStopReason(); + + switch (reason) + { + case eStopReasonTrace: + return_value = true; + break; + case eStopReasonBreakpoint: + if (NextRangeBreakpointExplainsStop(stop_info_sp)) + return_value = true; + else + return_value = false; + break; + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + default: + if (log) + log->PutCString ("ThreadPlanStepInRange got asked if it explains the stop for some reason other than step."); + return_value = false; + break; + } + } + else + return_value = true; + + return return_value; +} + +bool +ThreadPlanStepOverRange::DoWillResume (lldb::StateType resume_state, bool current_plan) +{ + if (resume_state != eStateSuspended && m_first_resume) + { + m_first_resume = false; + if (resume_state == eStateStepping && current_plan) + { + // See if we are about to step over an inlined call in the middle of the inlined stack, if so figure + // out its extents and reset our range to step over that. + bool in_inlined_stack = m_thread.DecrementCurrentInlinedDepth(); + if (in_inlined_stack) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("ThreadPlanStepInRange::DoWillResume: adjusting range to the frame at inlined depth %d.", + m_thread.GetCurrentInlinedDepth()); + StackFrameSP stack_sp = m_thread.GetStackFrameAtIndex(0); + if (stack_sp) + { + Block *frame_block = stack_sp->GetFrameBlock(); + lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); + AddressRange my_range; + if (frame_block->GetRangeContainingLoadAddress(curr_pc, m_thread.GetProcess()->GetTarget(), my_range)) + { + m_address_ranges.clear(); + m_address_ranges.push_back(my_range); + if (log) + { + StreamString s; + const InlineFunctionInfo *inline_info = frame_block->GetInlinedFunctionInfo(); + const char *name; + if (inline_info) + name = inline_info->GetName(frame_block->CalculateSymbolContextFunction()->GetLanguage()).AsCString(); + else + name = "<unknown-notinlined>"; + + s.Printf ("Stepping over inlined function \"%s\" in inlined stack: ", name); + DumpRanges(&s); + log->PutCString(s.GetData()); + } + } + + } + } + } + } + + return true; +} + |