1 /**
2 * Copyright (c) 2022-2024 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 "runtime/tooling/inspector/debuggable_thread.h"
17
18 namespace ark::tooling::inspector {
DebuggableThread(ManagedThread * thread,SuspensionCallbacks && callbacks)19 DebuggableThread::DebuggableThread(ManagedThread *thread, SuspensionCallbacks &&callbacks)
20 : thread_(thread), callbacks_(std::move(callbacks))
21 {
22 ASSERT(thread);
23 }
24
Reset()25 void DebuggableThread::Reset()
26 {
27 os::memory::LockHolder lock(mutex_);
28 state_.Reset();
29 }
30
BreakOnStart()31 void DebuggableThread::BreakOnStart()
32 {
33 os::memory::LockHolder lock(mutex_);
34 state_.BreakOnStart();
35 }
36
Continue()37 void DebuggableThread::Continue()
38 {
39 os::memory::LockHolder lock(mutex_);
40 state_.Continue();
41 Resume();
42 }
43
ContinueTo(std::unordered_set<PtLocation,HashLocation> locations)44 void DebuggableThread::ContinueTo(std::unordered_set<PtLocation, HashLocation> locations)
45 {
46 os::memory::LockHolder lock(mutex_);
47 state_.ContinueTo(std::move(locations));
48 Resume();
49 }
50
StepInto(std::unordered_set<PtLocation,HashLocation> locations)51 void DebuggableThread::StepInto(std::unordered_set<PtLocation, HashLocation> locations)
52 {
53 os::memory::LockHolder lock(mutex_);
54 state_.StepInto(std::move(locations));
55 Resume();
56 }
57
StepOver(std::unordered_set<PtLocation,HashLocation> locations)58 void DebuggableThread::StepOver(std::unordered_set<PtLocation, HashLocation> locations)
59 {
60 os::memory::LockHolder lock(mutex_);
61 state_.StepOver(std::move(locations));
62 Resume();
63 }
64
StepOut()65 void DebuggableThread::StepOut()
66 {
67 os::memory::LockHolder lock(mutex_);
68 state_.StepOut();
69 Resume();
70 }
71
Touch()72 void DebuggableThread::Touch()
73 {
74 os::memory::LockHolder lock(mutex_);
75 Resume();
76 }
77
Pause()78 void DebuggableThread::Pause()
79 {
80 os::memory::LockHolder lock(mutex_);
81 state_.Pause();
82 }
83
SetBreakpointsActive(bool active)84 void DebuggableThread::SetBreakpointsActive(bool active)
85 {
86 os::memory::LockHolder lock(mutex_);
87 state_.SetBreakpointsActive(active);
88 }
89
SetBreakpoint(const std::vector<PtLocation> & locations)90 BreakpointId DebuggableThread::SetBreakpoint(const std::vector<PtLocation> &locations)
91 {
92 os::memory::LockHolder lock(mutex_);
93 return state_.SetBreakpoint(locations);
94 }
95
RemoveBreakpoint(BreakpointId id)96 void DebuggableThread::RemoveBreakpoint(BreakpointId id)
97 {
98 os::memory::LockHolder lock(mutex_);
99 state_.RemoveBreakpoint(id);
100 }
101
SetPauseOnExceptions(PauseOnExceptionsState state)102 void DebuggableThread::SetPauseOnExceptions(PauseOnExceptionsState state)
103 {
104 os::memory::LockHolder lock(mutex_);
105 state_.SetPauseOnExceptions(state);
106 }
107
RequestToObjectRepository(std::function<void (ObjectRepository &)> request)108 bool DebuggableThread::RequestToObjectRepository(std::function<void(ObjectRepository &)> request)
109 {
110 os::memory::LockHolder lock(mutex_);
111
112 ASSERT(!request_.has_value());
113
114 if (!suspended_) {
115 return false;
116 }
117
118 ASSERT(thread_->IsSuspended());
119
120 request_ = std::move(request);
121 thread_->Resume();
122
123 while (request_) {
124 requestDone_.Wait(&mutex_);
125 }
126
127 ASSERT(suspended_);
128 ASSERT(thread_->IsSuspended());
129
130 return true;
131 }
132
OnException(bool uncaught)133 void DebuggableThread::OnException(bool uncaught)
134 {
135 os::memory::LockHolder lock(mutex_);
136 state_.OnException(uncaught);
137 while (state_.IsPaused()) {
138 ObjectRepository objectRepository;
139 Suspend(objectRepository, {}, thread_->GetException());
140 }
141 }
142
OnFramePop()143 void DebuggableThread::OnFramePop()
144 {
145 os::memory::LockHolder lock(mutex_);
146 state_.OnFramePop();
147 }
148
OnMethodEntry()149 bool DebuggableThread::OnMethodEntry()
150 {
151 os::memory::LockHolder lock(mutex_);
152 return state_.OnMethodEntry();
153 }
154
OnSingleStep(const PtLocation & location)155 void DebuggableThread::OnSingleStep(const PtLocation &location)
156 {
157 os::memory::LockHolder lock(mutex_);
158 state_.OnSingleStep(location);
159 while (state_.IsPaused()) {
160 ObjectRepository objectRepository;
161 auto hitBreakpoints = state_.GetBreakpointsByLocation(location);
162 Suspend(objectRepository, hitBreakpoints, {});
163 }
164 }
165
OnConsoleCall(const PandaVector<TypedValue> & arguments)166 std::vector<RemoteObject> DebuggableThread::OnConsoleCall(const PandaVector<TypedValue> &arguments)
167 {
168 std::vector<RemoteObject> result;
169
170 ObjectRepository objectRepository;
171 std::transform(arguments.begin(), arguments.end(), std::back_inserter(result),
172 [&objectRepository](auto value) { return objectRepository.CreateObject(value); });
173
174 return result;
175 }
176
Suspend(ObjectRepository & objectRepository,const std::vector<BreakpointId> & hitBreakpoints,ObjectHeader * exception)177 void DebuggableThread::Suspend(ObjectRepository &objectRepository, const std::vector<BreakpointId> &hitBreakpoints,
178 ObjectHeader *exception)
179 {
180 ASSERT(ManagedThread::GetCurrent() == thread_);
181
182 ASSERT(!suspended_);
183 ASSERT(!request_.has_value());
184
185 callbacks_.preSuspend(objectRepository, hitBreakpoints, exception);
186
187 suspended_ = true;
188 thread_->Suspend();
189
190 callbacks_.postSuspend(objectRepository, hitBreakpoints, exception);
191
192 while (suspended_) {
193 mutex_.Unlock();
194
195 callbacks_.preWaitSuspension();
196 thread_->WaitSuspension();
197 callbacks_.postWaitSuspension();
198
199 mutex_.Lock();
200
201 // We have three levels of suspension:
202 // - state_.IsPaused() - tells if the thread is paused on breakpoint or step;
203 // - suspended_ - tells if the thread generally sleeps (but could execute object repository requests);
204 // - thread_->IsSuspended() - tells if the thread is actually sleeping
205 //
206 // If we are here, then the latter is false (thread waked up). The following variants are possible:
207 // - not paused and not suspended - e.g. continue / stepping action was performed;
208 // - not paused and suspended - invalid;
209 // - paused and not suspended - touch was performed, resume and sleep back
210 // (used to notify about the state on `runIfWaitingForDebugger`);
211 // - paused and suspended - object repository request was made.
212
213 ASSERT(suspended_ == request_.has_value());
214
215 if (request_) {
216 (*request_)(objectRepository);
217
218 request_.reset();
219 thread_->Suspend();
220
221 requestDone_.Signal();
222 }
223 }
224 }
225
Resume()226 void DebuggableThread::Resume()
227 {
228 ASSERT(!request_.has_value());
229
230 if (!suspended_) {
231 return;
232 }
233
234 ASSERT(thread_->IsSuspended());
235
236 callbacks_.preResume();
237
238 suspended_ = false;
239 thread_->Resume();
240
241 callbacks_.postResume();
242 }
243 } // namespace ark::tooling::inspector
244