• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "debuggable_thread.h"
17 
18 #include "breakpoint_storage.h"
19 #include "error.h"
20 #include "include/method.h"
21 #include "types/numeric_id.h"
22 
23 namespace ark::tooling::inspector {
DebuggableThread(ManagedThread * thread,DebugInterface * debugger,SuspensionCallbacks && callbacks,BreakpointStorage & bpStorage)24 DebuggableThread::DebuggableThread(ManagedThread *thread, DebugInterface *debugger, SuspensionCallbacks &&callbacks,
25                                    BreakpointStorage &bpStorage)
26     : PtThreadEvaluationEngine(debugger, thread), callbacks_(std::move(callbacks)), state_(*this, bpStorage)
27 {
28 }
29 
Reset()30 void DebuggableThread::Reset()
31 {
32     os::memory::LockHolder lock(mutex_);
33     state_.Reset();
34 }
35 
BreakOnStart()36 void DebuggableThread::BreakOnStart()
37 {
38     os::memory::LockHolder lock(mutex_);
39     state_.BreakOnStart();
40 }
41 
Continue()42 void DebuggableThread::Continue()
43 {
44     os::memory::LockHolder lock(mutex_);
45     state_.Continue();
46     Resume();
47 }
48 
ContinueTo(std::unordered_set<PtLocation,HashLocation> locations)49 void DebuggableThread::ContinueTo(std::unordered_set<PtLocation, HashLocation> locations)
50 {
51     os::memory::LockHolder lock(mutex_);
52     state_.ContinueTo(std::move(locations));
53     Resume();
54 }
55 
StepInto(std::unordered_set<PtLocation,HashLocation> locations)56 void DebuggableThread::StepInto(std::unordered_set<PtLocation, HashLocation> locations)
57 {
58     os::memory::LockHolder lock(mutex_);
59     state_.StepInto(std::move(locations));
60     Resume();
61 }
62 
StepOver(std::unordered_set<PtLocation,HashLocation> locations)63 void DebuggableThread::StepOver(std::unordered_set<PtLocation, HashLocation> locations)
64 {
65     os::memory::LockHolder lock(mutex_);
66     state_.StepOver(std::move(locations));
67     Resume();
68 }
69 
StepOut()70 void DebuggableThread::StepOut()
71 {
72     os::memory::LockHolder lock(mutex_);
73     state_.StepOut();
74     Resume();
75 }
76 
IsPaused()77 bool DebuggableThread::IsPaused()
78 {
79     os::memory::LockHolder lock(mutex_);
80     return suspended_;
81 }
82 
Touch()83 void DebuggableThread::Touch()
84 {
85     os::memory::LockHolder lock(mutex_);
86     Resume();
87 }
88 
Pause()89 void DebuggableThread::Pause()
90 {
91     os::memory::LockHolder lock(mutex_);
92     state_.Pause();
93 }
94 
SetSkipAllPauses(bool skip)95 void DebuggableThread::SetSkipAllPauses(bool skip)
96 {
97     os::memory::LockHolder lock(mutex_);
98     state_.SetSkipAllPauses(skip);
99 }
100 
SetMixedDebugEnabled(bool mixedDebugEnabled)101 void DebuggableThread::SetMixedDebugEnabled(bool mixedDebugEnabled)
102 {
103     os::memory::LockHolder lock(mutex_);
104     state_.SetMixedDebugEnabled(mixedDebugEnabled);
105 }
106 
SetPauseOnExceptions(PauseOnExceptionsState state)107 void DebuggableThread::SetPauseOnExceptions(PauseOnExceptionsState state)
108 {
109     os::memory::LockHolder lock(mutex_);
110     state_.SetPauseOnExceptions(state);
111 }
112 
RequestToObjectRepository(std::function<void (ObjectRepository &)> request)113 bool DebuggableThread::RequestToObjectRepository(std::function<void(ObjectRepository &)> request)
114 {
115     os::memory::LockHolder lock(mutex_);
116 
117     ASSERT(!request_.has_value());
118 
119     if (!suspended_) {
120         return false;
121     }
122 
123     ASSERT(GetManagedThread()->IsSuspended());
124 
125     request_ = std::move(request);
126     GetManagedThread()->Resume();
127 
128     while (request_) {
129         requestDone_.Wait(&mutex_);
130     }
131 
132     ASSERT(suspended_);
133     ASSERT(GetManagedThread()->IsSuspended());
134 
135     return true;
136 }
137 
EvaluateExpression(uint32_t frameNumber,const ExpressionWrapper & bytecode)138 Expected<std::pair<RemoteObject, std::optional<RemoteObject>>, std::string> DebuggableThread::EvaluateExpression(
139     uint32_t frameNumber, const ExpressionWrapper &bytecode)
140 {
141     std::optional<RemoteObject> optResult;
142     std::optional<RemoteObject> optException;
143     std::optional<Error> optError;
144     RequestToObjectRepository(
145         [this, frameNumber, &bytecode, &optResult, &optException, &optError](ObjectRepository &objectRepo) {
146             Method *method = nullptr;
147             auto res = EvaluateExpression(frameNumber, bytecode, &method);
148             if (!res) {
149                 HandleError(res.Error());
150                 optError = res.Error();
151                 return;
152             }
153 
154             auto [result, exc] = res.Value();
155             optResult.emplace(objectRepo.CreateObject(result));
156             if (exc != nullptr) {
157                 optException.emplace(objectRepo.CreateObject(TypedValue::Reference(exc)));
158             }
159         });
160     if (optError) {
161         ASSERT(!optResult);
162         return Unexpected(optError->GetMessage());
163     }
164     return std::make_pair(*optResult, optException);
165 }
166 
OnException(bool uncaught)167 void DebuggableThread::OnException(bool uncaught)
168 {
169     if (IsEvaluating()) {
170         return;
171     }
172     os::memory::LockHolder lock(mutex_);
173     state_.OnException(uncaught);
174     while (state_.IsPaused()) {
175         ObjectRepository objectRepository;
176         Suspend(objectRepository, {}, GetManagedThread()->GetException(), state_.GetPauseReason());
177     }
178 }
179 
OnFramePop()180 void DebuggableThread::OnFramePop()
181 {
182     if (IsEvaluating()) {
183         return;
184     }
185     os::memory::LockHolder lock(mutex_);
186     state_.OnFramePop();
187 }
188 
OnMethodEntry()189 bool DebuggableThread::OnMethodEntry()
190 {
191     if (IsEvaluating()) {
192         return false;
193     }
194     os::memory::LockHolder lock(mutex_);
195     return state_.OnMethodEntry();
196 }
197 
OnSingleStep(const PtLocation & location,const char * sourceFile)198 void DebuggableThread::OnSingleStep(const PtLocation &location, const char *sourceFile)
199 {
200     if (IsEvaluating()) {
201         return;
202     }
203     os::memory::LockHolder lock(mutex_);
204     state_.OnSingleStep(location, sourceFile);
205     while (state_.IsPaused()) {
206         ObjectRepository objectRepository;
207         auto hitBreakpoints = state_.GetBreakpointsByLocation(location);
208         Suspend(objectRepository, hitBreakpoints, {}, state_.GetPauseReason());
209     }
210 }
211 
OnConsoleCall(const PandaVector<TypedValue> & arguments)212 std::vector<RemoteObject> DebuggableThread::OnConsoleCall(const PandaVector<TypedValue> &arguments)
213 {
214     std::vector<RemoteObject> result;
215 
216     ObjectRepository objectRepository;
217     std::transform(arguments.begin(), arguments.end(), std::back_inserter(result),
218                    [&objectRepository](auto value) { return objectRepository.CreateObject(value); });
219 
220     return result;
221 }
222 
Suspend(ObjectRepository & objectRepository,const std::vector<BreakpointId> & hitBreakpoints,ObjectHeader * exception,PauseReason pauseReason)223 void DebuggableThread::Suspend(ObjectRepository &objectRepository, const std::vector<BreakpointId> &hitBreakpoints,
224                                ObjectHeader *exception, PauseReason pauseReason)
225 {
226     ASSERT(ManagedThread::GetCurrent() == GetManagedThread());
227 
228     ASSERT(!suspended_);
229     ASSERT(!request_.has_value());
230 
231     callbacks_.preSuspend(objectRepository, hitBreakpoints, exception);
232 
233     suspended_ = true;
234     GetManagedThread()->Suspend();
235 
236     callbacks_.postSuspend(objectRepository, hitBreakpoints, exception, pauseReason);
237 
238     while (suspended_) {
239         mutex_.Unlock();
240 
241         callbacks_.preWaitSuspension();
242         GetManagedThread()->WaitSuspension();
243         callbacks_.postWaitSuspension();
244 
245         mutex_.Lock();
246 
247         // We have three levels of suspension:
248         // - state_.IsPaused() - tells if the thread is paused on breakpoint or step;
249         // - suspended_ - tells if the thread generally sleeps (but could execute object repository requests);
250         // - GetManagedThread()->IsSuspended() - tells if the thread is actually sleeping
251         //
252         // If we are here, then the latter is false (thread waked up). The following variants are possible:
253         // - not paused and not suspended - e.g. continue / stepping action was performed;
254         // - not paused and suspended - invalid;
255         // - paused and not suspended - touch was performed, resume and sleep back
256         //     (used to notify about the state on `runIfWaitingForDebugger`);
257         // - paused and suspended - object repository request was made.
258 
259         ASSERT(suspended_ == request_.has_value());
260 
261         if (request_) {
262             (*request_)(objectRepository);
263 
264             request_.reset();
265             GetManagedThread()->Suspend();
266 
267             requestDone_.Signal();
268         }
269     }
270 }
271 
Resume()272 void DebuggableThread::Resume()
273 {
274     ASSERT(!request_.has_value());
275 
276     if (!suspended_) {
277         return;
278     }
279 
280     ASSERT(GetManagedThread()->IsSuspended());
281 
282     callbacks_.preResume();
283 
284     suspended_ = false;
285     GetManagedThread()->Resume();
286 
287     callbacks_.postResume();
288 }
289 }  // namespace ark::tooling::inspector
290