1cfca06d7SDimitry Andric //===-- ThreadPlanShouldStopHere.cpp --------------------------------------===//
2f034231aSEd Maste //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f034231aSEd Maste //
7f034231aSEd Maste //===----------------------------------------------------------------------===//
8f034231aSEd Maste
974a628f7SDimitry Andric #include "lldb/Target/ThreadPlanShouldStopHere.h"
10f3fbd1c0SDimitry Andric #include "lldb/Symbol/Symbol.h"
11f034231aSEd Maste #include "lldb/Target/RegisterContext.h"
1274a628f7SDimitry Andric #include "lldb/Target/Thread.h"
13145449b1SDimitry Andric #include "lldb/Utility/LLDBLog.h"
1474a628f7SDimitry Andric #include "lldb/Utility/Log.h"
15f034231aSEd Maste
16f034231aSEd Maste using namespace lldb;
17f034231aSEd Maste using namespace lldb_private;
18f034231aSEd Maste
19f034231aSEd Maste // ThreadPlanShouldStopHere constructor
ThreadPlanShouldStopHere(ThreadPlan * owner)2014f1b3e8SDimitry Andric ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner)
2114f1b3e8SDimitry Andric : m_callbacks(), m_baton(nullptr), m_owner(owner),
2214f1b3e8SDimitry Andric m_flags(ThreadPlanShouldStopHere::eNone) {
2314f1b3e8SDimitry Andric m_callbacks.should_stop_here_callback =
2414f1b3e8SDimitry Andric ThreadPlanShouldStopHere::DefaultShouldStopHereCallback;
2514f1b3e8SDimitry Andric m_callbacks.step_from_here_callback =
2614f1b3e8SDimitry Andric ThreadPlanShouldStopHere::DefaultStepFromHereCallback;
270cac4ca3SEd Maste }
280cac4ca3SEd Maste
ThreadPlanShouldStopHere(ThreadPlan * owner,const ThreadPlanShouldStopHereCallbacks * callbacks,void * baton)2914f1b3e8SDimitry Andric ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(
3014f1b3e8SDimitry Andric ThreadPlan *owner, const ThreadPlanShouldStopHereCallbacks *callbacks,
3114f1b3e8SDimitry Andric void *baton)
3214f1b3e8SDimitry Andric : m_callbacks(), m_baton(), m_owner(owner),
3314f1b3e8SDimitry Andric m_flags(ThreadPlanShouldStopHere::eNone) {
340cac4ca3SEd Maste SetShouldStopHereCallbacks(callbacks, baton);
35f034231aSEd Maste }
36f034231aSEd Maste
37e81d9d49SDimitry Andric ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default;
38f034231aSEd Maste
InvokeShouldStopHereCallback(FrameComparison operation,Status & status)3914f1b3e8SDimitry Andric bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback(
4094994d37SDimitry Andric FrameComparison operation, Status &status) {
410cac4ca3SEd Maste bool should_stop_here = true;
4214f1b3e8SDimitry Andric if (m_callbacks.should_stop_here_callback) {
4314f1b3e8SDimitry Andric should_stop_here = m_callbacks.should_stop_here_callback(
4494994d37SDimitry Andric m_owner, m_flags, operation, status, m_baton);
45145449b1SDimitry Andric Log *log = GetLog(LLDBLog::Step);
4614f1b3e8SDimitry Andric if (log) {
4714f1b3e8SDimitry Andric lldb::addr_t current_addr =
4814f1b3e8SDimitry Andric m_owner->GetThread().GetRegisterContext()->GetPC(0);
49f034231aSEd Maste
50ead24645SDimitry Andric LLDB_LOGF(log, "ShouldStopHere callback returned %u from 0x%" PRIx64 ".",
5114f1b3e8SDimitry Andric should_stop_here, current_addr);
520cac4ca3SEd Maste }
530cac4ca3SEd Maste }
540cac4ca3SEd Maste
550cac4ca3SEd Maste return should_stop_here;
560cac4ca3SEd Maste }
570cac4ca3SEd Maste
DefaultShouldStopHereCallback(ThreadPlan * current_plan,Flags & flags,FrameComparison operation,Status & status,void * baton)5814f1b3e8SDimitry Andric bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback(
5914f1b3e8SDimitry Andric ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
6094994d37SDimitry Andric Status &status, void *baton) {
610cac4ca3SEd Maste bool should_stop_here = true;
620cac4ca3SEd Maste StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
630cac4ca3SEd Maste if (!frame)
640cac4ca3SEd Maste return true;
650cac4ca3SEd Maste
66145449b1SDimitry Andric Log *log = GetLog(LLDBLog::Step);
670cac4ca3SEd Maste
6814f1b3e8SDimitry Andric if ((operation == eFrameCompareOlder && flags.Test(eStepOutAvoidNoDebug)) ||
6914f1b3e8SDimitry Andric (operation == eFrameCompareYounger && flags.Test(eStepInAvoidNoDebug)) ||
7014f1b3e8SDimitry Andric (operation == eFrameCompareSameParent &&
7114f1b3e8SDimitry Andric flags.Test(eStepInAvoidNoDebug))) {
7214f1b3e8SDimitry Andric if (!frame->HasDebugInformation()) {
73ead24645SDimitry Andric LLDB_LOGF(log, "Stepping out of frame with no debug info");
740cac4ca3SEd Maste
750cac4ca3SEd Maste should_stop_here = false;
76f034231aSEd Maste }
77f034231aSEd Maste }
780cac4ca3SEd Maste
790cac4ca3SEd Maste // Always avoid code with line number 0.
8014f1b3e8SDimitry Andric // FIXME: At present the ShouldStop and the StepFromHere calculate this
8114f1b3e8SDimitry Andric // independently. If this ever
82f73363f1SDimitry Andric // becomes expensive (this one isn't) we can try to have this set a state
83f73363f1SDimitry Andric // that the StepFromHere can use.
8414f1b3e8SDimitry Andric if (frame) {
850cac4ca3SEd Maste SymbolContext sc;
860cac4ca3SEd Maste sc = frame->GetSymbolContext(eSymbolContextLineEntry);
870cac4ca3SEd Maste if (sc.line_entry.line == 0)
880cac4ca3SEd Maste should_stop_here = false;
890cac4ca3SEd Maste }
900cac4ca3SEd Maste
910cac4ca3SEd Maste return should_stop_here;
920cac4ca3SEd Maste }
930cac4ca3SEd Maste
DefaultStepFromHereCallback(ThreadPlan * current_plan,Flags & flags,FrameComparison operation,Status & status,void * baton)9414f1b3e8SDimitry Andric ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback(
9514f1b3e8SDimitry Andric ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
9694994d37SDimitry Andric Status &status, void *baton) {
970cac4ca3SEd Maste const bool stop_others = false;
980cac4ca3SEd Maste const size_t frame_index = 0;
990cac4ca3SEd Maste ThreadPlanSP return_plan_sp;
100f73363f1SDimitry Andric // If we are stepping through code at line number 0, then we need to step
101f73363f1SDimitry Andric // over this range. Otherwise we will step out.
102145449b1SDimitry Andric Log *log = GetLog(LLDBLog::Step);
103f3fbd1c0SDimitry Andric
1040cac4ca3SEd Maste StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
1050cac4ca3SEd Maste if (!frame)
1060cac4ca3SEd Maste return return_plan_sp;
1070cac4ca3SEd Maste SymbolContext sc;
108f3fbd1c0SDimitry Andric sc = frame->GetSymbolContext(eSymbolContextLineEntry | eSymbolContextSymbol);
109f3fbd1c0SDimitry Andric
11014f1b3e8SDimitry Andric if (sc.line_entry.line == 0) {
1110cac4ca3SEd Maste AddressRange range = sc.line_entry.range;
112f3fbd1c0SDimitry Andric
11314f1b3e8SDimitry Andric // If the whole function is marked line 0 just step out, that's easier &
114f73363f1SDimitry Andric // faster than continuing to step through it.
115f3fbd1c0SDimitry Andric bool just_step_out = false;
11614f1b3e8SDimitry Andric if (sc.symbol && sc.symbol->ValueIsAddress()) {
117f3fbd1c0SDimitry Andric Address symbol_end = sc.symbol->GetAddress();
118f3fbd1c0SDimitry Andric symbol_end.Slide(sc.symbol->GetByteSize() - 1);
11914f1b3e8SDimitry Andric if (range.ContainsFileAddress(sc.symbol->GetAddress()) &&
12014f1b3e8SDimitry Andric range.ContainsFileAddress(symbol_end)) {
121ead24645SDimitry Andric LLDB_LOGF(log, "Stopped in a function with only line 0 lines, just "
12214f1b3e8SDimitry Andric "stepping out.");
123f3fbd1c0SDimitry Andric just_step_out = true;
124f3fbd1c0SDimitry Andric }
125f3fbd1c0SDimitry Andric }
12614f1b3e8SDimitry Andric if (!just_step_out) {
127ead24645SDimitry Andric LLDB_LOGF(log, "ThreadPlanShouldStopHere::DefaultStepFromHereCallback "
12814f1b3e8SDimitry Andric "Queueing StepInRange plan to step through line 0 code.");
129f3fbd1c0SDimitry Andric
13014f1b3e8SDimitry Andric return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange(
1315f29bb8aSDimitry Andric false, range, sc, nullptr, eOnlyDuringStepping, status,
13294994d37SDimitry Andric eLazyBoolCalculate, eLazyBoolNo);
1330cac4ca3SEd Maste }
134f3fbd1c0SDimitry Andric }
1350cac4ca3SEd Maste
1360cac4ca3SEd Maste if (!return_plan_sp)
13714f1b3e8SDimitry Andric return_plan_sp =
13814f1b3e8SDimitry Andric current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop(
13914f1b3e8SDimitry Andric false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion,
14094994d37SDimitry Andric frame_index, status, true);
141f034231aSEd Maste return return_plan_sp;
142f034231aSEd Maste }
1430cac4ca3SEd Maste
QueueStepOutFromHerePlan(lldb_private::Flags & flags,lldb::FrameComparison operation,Status & status)14414f1b3e8SDimitry Andric ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan(
14594994d37SDimitry Andric lldb_private::Flags &flags, lldb::FrameComparison operation,
14694994d37SDimitry Andric Status &status) {
1470cac4ca3SEd Maste ThreadPlanSP return_plan_sp;
14814f1b3e8SDimitry Andric if (m_callbacks.step_from_here_callback) {
14994994d37SDimitry Andric return_plan_sp = m_callbacks.step_from_here_callback(
15094994d37SDimitry Andric m_owner, flags, operation, status, m_baton);
1510cac4ca3SEd Maste }
1520cac4ca3SEd Maste return return_plan_sp;
1530cac4ca3SEd Maste }
1540cac4ca3SEd Maste
CheckShouldStopHereAndQueueStepOut(lldb::FrameComparison operation,Status & status)15514f1b3e8SDimitry Andric lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut(
15694994d37SDimitry Andric lldb::FrameComparison operation, Status &status) {
15794994d37SDimitry Andric if (!InvokeShouldStopHereCallback(operation, status))
15894994d37SDimitry Andric return QueueStepOutFromHerePlan(m_flags, operation, status);
159f034231aSEd Maste else
160f034231aSEd Maste return ThreadPlanSP();
161f034231aSEd Maste }
162