• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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