1 /**
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "thread_state.h"
17
18 #include <memory>
19 #include <optional>
20 #include <sstream>
21
22 #include "include/tooling/pt_location.h"
23 #include "macros.h"
24 #include "breakpoint.h"
25 #include "error.h"
26 #include "types/numeric_id.h"
27
28 #include "../../hybrid_step/debug_step_flags.h"
29 namespace ark::tooling::inspector {
30
Reset()31 void ThreadState::Reset()
32 {
33 stepKind_ = StepKind::BREAK_ON_START;
34 stepLocations_.clear();
35 methodEntered_ = false;
36 skipAllPauses_ = false;
37 mixedDebugEnabled_ = false;
38 pauseOnExceptionsState_ = PauseOnExceptionsState::NONE;
39 }
40
BreakOnStart()41 void ThreadState::BreakOnStart()
42 {
43 if (!paused_) {
44 stepKind_ = StepKind::BREAK_ON_START;
45 }
46 breakOnStart_ = true;
47 }
48
Continue()49 void ThreadState::Continue()
50 {
51 stepKind_ = StepKind::NONE;
52 paused_ = false;
53 pauseReason_ = PauseReason::OTHER;
54 }
55
ContinueTo(std::unordered_set<PtLocation,HashLocation> locations)56 void ThreadState::ContinueTo(std::unordered_set<PtLocation, HashLocation> locations)
57 {
58 stepKind_ = StepKind::CONTINUE_TO;
59 stepLocations_ = std::move(locations);
60 paused_ = false;
61 pauseReason_ = PauseReason::OTHER;
62 }
63
StepInto(std::unordered_set<PtLocation,HashLocation> locations)64 void ThreadState::StepInto(std::unordered_set<PtLocation, HashLocation> locations)
65 {
66 stepKind_ = StepKind::STEP_INTO;
67 methodEntered_ = false;
68 stepLocations_ = std::move(locations);
69 paused_ = false;
70 pauseReason_ = PauseReason::STEP;
71 }
72
StepOver(std::unordered_set<PtLocation,HashLocation> locations)73 void ThreadState::StepOver(std::unordered_set<PtLocation, HashLocation> locations)
74 {
75 stepKind_ = StepKind::STEP_OVER;
76 methodEntered_ = false;
77 stepLocations_ = std::move(locations);
78 paused_ = false;
79 pauseReason_ = PauseReason::STEP;
80 }
81
StepOut()82 void ThreadState::StepOut()
83 {
84 stepKind_ = StepKind::STEP_OUT;
85 methodEntered_ = true;
86 paused_ = false;
87 pauseReason_ = PauseReason::STEP;
88 }
89
Pause()90 void ThreadState::Pause()
91 {
92 if (!paused_ && !skipAllPauses_) {
93 stepKind_ = StepKind::PAUSE;
94 }
95 }
96
SetSkipAllPauses(bool skip)97 void ThreadState::SetSkipAllPauses(bool skip)
98 {
99 skipAllPauses_ = skip;
100 }
101
102 // NOTE(fangting, #25108): implement "NativeOut" events when in mixed debug mode
SetMixedDebugEnabled(bool mixedDebugEnabled)103 void ThreadState::SetMixedDebugEnabled(bool mixedDebugEnabled)
104 {
105 mixedDebugEnabled_ = mixedDebugEnabled;
106 }
107
SetPauseOnExceptions(PauseOnExceptionsState state)108 void ThreadState::SetPauseOnExceptions(PauseOnExceptionsState state)
109 {
110 pauseOnExceptionsState_ = state;
111 }
112
OnException(bool uncaught)113 void ThreadState::OnException(bool uncaught)
114 {
115 ASSERT(!paused_);
116 if (skipAllPauses_) {
117 return;
118 }
119 switch (pauseOnExceptionsState_) {
120 case PauseOnExceptionsState::NONE:
121 break;
122 case PauseOnExceptionsState::CAUGHT:
123 paused_ = !uncaught;
124 break;
125 case PauseOnExceptionsState::UNCAUGHT:
126 paused_ = uncaught;
127 break;
128 case PauseOnExceptionsState::ALL:
129 paused_ = true;
130 break;
131 }
132 if (paused_) {
133 pauseReason_ = PauseReason::EXCEPTION;
134 }
135 }
136
OnFramePop()137 void ThreadState::OnFramePop()
138 {
139 ASSERT(!paused_);
140 switch (stepKind_) {
141 case StepKind::NONE:
142 case StepKind::BREAK_ON_START:
143 case StepKind::CONTINUE_TO:
144 case StepKind::PAUSE:
145 case StepKind::STEP_INTO: {
146 break;
147 }
148
149 case StepKind::STEP_OUT:
150 case StepKind::STEP_OVER: {
151 methodEntered_ = false;
152 break;
153 }
154 }
155 }
156
OnMethodEntry()157 bool ThreadState::OnMethodEntry()
158 {
159 ASSERT(!paused_);
160 switch (stepKind_) {
161 case StepKind::NONE:
162 case StepKind::BREAK_ON_START:
163 case StepKind::CONTINUE_TO:
164 case StepKind::PAUSE:
165 case StepKind::STEP_INTO: {
166 return false;
167 }
168
169 case StepKind::STEP_OUT:
170 case StepKind::STEP_OVER: {
171 return !std::exchange(methodEntered_, true);
172 }
173 }
174
175 UNREACHABLE();
176 }
177
OnSingleStep(const PtLocation & location,const char * sourceFile)178 void ThreadState::OnSingleStep(const PtLocation &location, const char *sourceFile)
179 {
180 ASSERT(!paused_);
181 DebugStepFlags::Get().SetStat2DynInto(true);
182 if (DebugStepFlags::Get().GetDyn2StatInto()) {
183 LOG(DEBUG, DEBUGGER) << "SingleStep from Dynamic";
184 paused_ = true;
185 DebugStepFlags::Get().SetDyn2StatInto(false);
186 return;
187 }
188
189 if (breakOnStart_) {
190 std::string_view file = sourceFile;
191 if (sourceFiles_.find(file) == sourceFiles_.end()) {
192 sourceFiles_.emplace(file);
193 stepKind_ = StepKind::BREAK_ON_START;
194 paused_ = true;
195 pauseReason_ = PauseReason::BREAK_ON_START;
196 return;
197 }
198 }
199
200 if (ShouldStopAtBreakpoint(location)) {
201 paused_ = true;
202 return;
203 }
204
205 switch (stepKind_) {
206 case StepKind::NONE: {
207 paused_ = false;
208 break;
209 }
210
211 case StepKind::BREAK_ON_START: {
212 paused_ = true;
213 break;
214 }
215
216 case StepKind::CONTINUE_TO: {
217 paused_ = stepLocations_.find(location) != stepLocations_.end();
218 break;
219 }
220
221 case StepKind::PAUSE: {
222 paused_ = true;
223 break;
224 }
225
226 case StepKind::STEP_INTO: {
227 paused_ = stepLocations_.find(location) == stepLocations_.end();
228 break;
229 }
230
231 case StepKind::STEP_OUT: {
232 paused_ = !methodEntered_;
233 break;
234 }
235
236 case StepKind::STEP_OVER: {
237 paused_ = !methodEntered_ && stepLocations_.find(location) == stepLocations_.end();
238 break;
239 }
240 }
241 }
242
ShouldStopAtBreakpoint(const PtLocation & location)243 bool ThreadState::ShouldStopAtBreakpoint(const PtLocation &location)
244 {
245 if (skipAllPauses_) {
246 return false;
247 }
248 return bpStorage_.ShouldStopAtBreakpoint(location, engine_);
249 }
250
GetBreakpointsByLocation(const PtLocation & location) const251 std::vector<BreakpointId> ThreadState::GetBreakpointsByLocation(const PtLocation &location) const
252 {
253 return bpStorage_.GetBreakpointsByLocation(location);
254 }
255 } // namespace ark::tooling::inspector
256