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