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 "inspector.h"
17
18 #include "error.h"
19
20 #include "macros.h"
21 #include "plugins.h"
22 #include "runtime.h"
23 #include "tooling/inspector/types/remote_object.h"
24 #include "tooling/inspector/types/scope.h"
25 #include "utils/logger.h"
26
27 #include <deque>
28 #include <functional>
29 #include <string>
30 #include <utility>
31 #include <vector>
32
33 using namespace std::placeholders; // NOLINT(google-build-using-namespace)
34
35 namespace ark::tooling::inspector {
Inspector(Server & server,DebugInterface & debugger,bool breakOnStart)36 Inspector::Inspector(Server &server, DebugInterface &debugger, bool breakOnStart)
37 : breakOnStart_(breakOnStart), inspectorServer_(server), debugger_(debugger)
38 {
39 if (!HandleError(debugger_.RegisterHooks(this))) {
40 return;
41 }
42
43 // acquire lock to later release it either in `OnOpen` or `OnFail` callbacks
44 inspectorServer_.OnValidate([this]() NO_THREAD_SAFETY_ANALYSIS {
45 ASSERT(!connecting_); // NOLINT(bugprone-lambda-function-name)
46 debuggerEventsLock_.WriteLock();
47 connecting_ = true;
48 });
49 inspectorServer_.OnOpen([this]() NO_THREAD_SAFETY_ANALYSIS {
50 ASSERT(connecting_); // NOLINT(bugprone-lambda-function-name)
51
52 for (auto &[thread, dbg_thread] : threads_) {
53 (void)thread;
54 dbg_thread.Reset();
55 }
56
57 connecting_ = false;
58 debuggerEventsLock_.Unlock();
59 });
60 inspectorServer_.OnFail([this]() NO_THREAD_SAFETY_ANALYSIS {
61 if (connecting_) {
62 connecting_ = false;
63 debuggerEventsLock_.Unlock();
64 }
65 });
66
67 // NOLINTBEGIN(modernize-avoid-bind)
68 inspectorServer_.OnCallDebuggerContinueToLocation(std::bind(&Inspector::ContinueToLocation, this, _1, _2, _3));
69 inspectorServer_.OnCallDebuggerGetPossibleBreakpoints(
70 std::bind(&Inspector::GetPossibleBreakpoints, this, _1, _2, _3, _4));
71 inspectorServer_.OnCallDebuggerGetScriptSource(std::bind(&Inspector::GetSourceCode, this, _1));
72 inspectorServer_.OnCallDebuggerPause(std::bind(&Inspector::Pause, this, _1));
73 inspectorServer_.OnCallDebuggerRemoveBreakpoint(std::bind(&Inspector::RemoveBreakpoint, this, _1, _2));
74 inspectorServer_.OnCallDebuggerRestartFrame(std::bind(&Inspector::RestartFrame, this, _1, _2));
75 inspectorServer_.OnCallDebuggerResume(std::bind(&Inspector::Continue, this, _1));
76 inspectorServer_.OnCallDebuggerSetBreakpoint(std::bind(&Inspector::SetBreakpoint, this, _1, _2, _3, _4));
77 inspectorServer_.OnCallDebuggerSetBreakpointByUrl(std::bind(&Inspector::SetBreakpoint, this, _1, _2, _3, _4));
78 inspectorServer_.OnCallDebuggerSetBreakpointsActive(std::bind(&Inspector::SetBreakpointsActive, this, _1, _2));
79 inspectorServer_.OnCallDebuggerSetPauseOnExceptions(std::bind(&Inspector::SetPauseOnExceptions, this, _1, _2));
80 inspectorServer_.OnCallDebuggerStepInto(std::bind(&Inspector::StepInto, this, _1));
81 inspectorServer_.OnCallDebuggerStepOut(std::bind(&Inspector::StepOut, this, _1));
82 inspectorServer_.OnCallDebuggerStepOver(std::bind(&Inspector::StepOver, this, _1));
83 inspectorServer_.OnCallRuntimeEnable(std::bind(&Inspector::RuntimeEnable, this, _1));
84 inspectorServer_.OnCallRuntimeGetProperties(std::bind(&Inspector::GetProperties, this, _1, _2, _3));
85 inspectorServer_.OnCallRuntimeRunIfWaitingForDebugger(std::bind(&Inspector::RunIfWaitingForDebugger, this, _1));
86 // NOLINTEND(modernize-avoid-bind)
87
88 serverThread_ = std::thread(&InspectorServer::Run, &inspectorServer_);
89 os::thread::SetThreadName(serverThread_.native_handle(), "InspectorServer");
90 }
91
~Inspector()92 Inspector::~Inspector()
93 {
94 NotifyExecutionEnded();
95 inspectorServer_.Kill();
96 serverThread_.join();
97 HandleError(debugger_.UnregisterHooks());
98 }
99
ConsoleCall(PtThread thread,ConsoleCallType type,uint64_t timestamp,const PandaVector<TypedValue> & arguments)100 void Inspector::ConsoleCall(PtThread thread, ConsoleCallType type, uint64_t timestamp,
101 const PandaVector<TypedValue> &arguments)
102 {
103 os::memory::ReadLockHolder lock(debuggerEventsLock_);
104
105 auto it = threads_.find(thread);
106 ASSERT(it != threads_.end());
107
108 inspectorServer_.CallRuntimeConsoleApiCalled(thread, type, timestamp, it->second.OnConsoleCall(arguments));
109 }
110
Exception(PtThread thread,Method *,const PtLocation &,ObjectHeader *,Method *,const PtLocation & catchLocation)111 void Inspector::Exception(PtThread thread, Method * /* method */, const PtLocation & /* location */,
112 ObjectHeader * /* exception */, Method * /* catch_method */, const PtLocation &catchLocation)
113 {
114 os::memory::ReadLockHolder lock(debuggerEventsLock_);
115
116 auto it = threads_.find(thread);
117 ASSERT(it != threads_.end());
118 it->second.OnException(catchLocation.GetBytecodeOffset() == panda_file::INVALID_OFFSET);
119 }
120
FramePop(PtThread thread,Method *,bool)121 void Inspector::FramePop(PtThread thread, Method * /* method */, bool /* was_popped_by_exception */)
122 {
123 os::memory::ReadLockHolder lock(debuggerEventsLock_);
124
125 auto it = threads_.find(thread);
126 ASSERT(it != threads_.end());
127 it->second.OnFramePop();
128 }
129
MethodEntry(PtThread thread,Method *)130 void Inspector::MethodEntry(PtThread thread, Method * /* method */)
131 {
132 os::memory::ReadLockHolder lock(debuggerEventsLock_);
133
134 auto it = threads_.find(thread);
135 ASSERT(it != threads_.end());
136 if (it->second.OnMethodEntry()) {
137 HandleError(debugger_.NotifyFramePop(thread, 0));
138 }
139 }
140
LoadModule(std::string_view fileName)141 void Inspector::LoadModule(std::string_view fileName)
142 {
143 os::memory::ReadLockHolder lock(debuggerEventsLock_);
144
145 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles(
146 [this, fileName](auto &file) {
147 if (file.GetFilename() == fileName) {
148 debugInfoCache_.AddPandaFile(file);
149 }
150
151 return true;
152 },
153 !fileName.empty());
154 }
155
SingleStep(PtThread thread,Method *,const PtLocation & location)156 void Inspector::SingleStep(PtThread thread, Method * /* method */, const PtLocation &location)
157 {
158 os::memory::ReadLockHolder lock(debuggerEventsLock_);
159
160 auto it = threads_.find(thread);
161 ASSERT(it != threads_.end());
162 it->second.OnSingleStep(location);
163 }
164
ThreadStart(PtThread thread)165 void Inspector::ThreadStart(PtThread thread)
166 {
167 os::memory::ReadLockHolder lock(debuggerEventsLock_);
168
169 if (thread != PtThread::NONE) {
170 inspectorServer_.CallTargetAttachedToTarget(thread);
171 }
172
173 // NOLINTBEGIN(modernize-avoid-bind)
174 auto callbacks = DebuggableThread::SuspensionCallbacks {
175 [](auto &, auto &, auto) {},
176 std::bind(&Inspector::DebuggableThreadPostSuspend, this, thread, _1, _2, _3),
177 [this]() NO_THREAD_SAFETY_ANALYSIS { debuggerEventsLock_.Unlock(); },
178 [this]() NO_THREAD_SAFETY_ANALYSIS { debuggerEventsLock_.ReadLock(); },
179 []() {},
180 [this, thread]() { inspectorServer_.CallDebuggerResumed(thread); }};
181 // NOLINTEND(modernize-avoid-bind)
182 auto [it, inserted] = threads_.emplace(std::piecewise_construct, std::forward_as_tuple(thread),
183 std::forward_as_tuple(thread.GetManagedThread(), std::move(callbacks)));
184 (void)inserted;
185 ASSERT(inserted);
186
187 if (breakOnStart_) {
188 it->second.BreakOnStart();
189 }
190 }
191
ThreadEnd(PtThread thread)192 void Inspector::ThreadEnd(PtThread thread)
193 {
194 os::memory::ReadLockHolder lock(debuggerEventsLock_);
195
196 if (thread != PtThread::NONE) {
197 inspectorServer_.CallTargetDetachedFromTarget(thread);
198 }
199
200 threads_.erase(thread);
201 }
202
RuntimeEnable(PtThread thread)203 void Inspector::RuntimeEnable(PtThread thread)
204 {
205 inspectorServer_.CallRuntimeExecutionContextCreated(thread);
206 }
207
RunIfWaitingForDebugger(PtThread thread)208 void Inspector::RunIfWaitingForDebugger(PtThread thread)
209 {
210 auto it = threads_.find(thread);
211 if (it != threads_.end()) {
212 it->second.Touch();
213 }
214 }
215
Pause(PtThread thread)216 void Inspector::Pause(PtThread thread)
217 {
218 auto it = threads_.find(thread);
219 if (it != threads_.end()) {
220 it->second.Pause();
221 }
222 }
223
Continue(PtThread thread)224 void Inspector::Continue(PtThread thread)
225 {
226 auto it = threads_.find(thread);
227 if (it != threads_.end()) {
228 it->second.Continue();
229 }
230 }
231
SetBreakpointsActive(PtThread thread,bool active)232 void Inspector::SetBreakpointsActive(PtThread thread, bool active)
233 {
234 auto it = threads_.find(thread);
235 if (it != threads_.end()) {
236 it->second.SetBreakpointsActive(active);
237 }
238 }
239
GetPossibleBreakpoints(std::string_view sourceFile,size_t startLine,size_t endLine,bool restrictToFunction)240 std::set<size_t> Inspector::GetPossibleBreakpoints(std::string_view sourceFile, size_t startLine, size_t endLine,
241 bool restrictToFunction)
242 {
243 return debugInfoCache_.GetValidLineNumbers(sourceFile, startLine, endLine, restrictToFunction);
244 }
245
SetBreakpoint(PtThread thread,const std::function<bool (std::string_view)> & sourceFilesFilter,size_t lineNumber,std::set<std::string_view> & sourceFiles)246 std::optional<BreakpointId> Inspector::SetBreakpoint(PtThread thread,
247 const std::function<bool(std::string_view)> &sourceFilesFilter,
248 size_t lineNumber, std::set<std::string_view> &sourceFiles)
249 {
250 if (auto it = threads_.find(thread); it != threads_.end()) {
251 auto locations = debugInfoCache_.GetBreakpointLocations(sourceFilesFilter, lineNumber, sourceFiles);
252 return it->second.SetBreakpoint(locations);
253 }
254
255 return {};
256 }
257
RemoveBreakpoint(PtThread thread,BreakpointId id)258 void Inspector::RemoveBreakpoint(PtThread thread, BreakpointId id)
259 {
260 auto it = threads_.find(thread);
261 if (it != threads_.end()) {
262 it->second.RemoveBreakpoint(id);
263 }
264 }
265
SetPauseOnExceptions(PtThread thread,PauseOnExceptionsState state)266 void Inspector::SetPauseOnExceptions(PtThread thread, PauseOnExceptionsState state)
267 {
268 auto it = threads_.find(thread);
269 if (it != threads_.end()) {
270 it->second.SetPauseOnExceptions(state);
271 }
272 }
273
StepInto(PtThread thread)274 void Inspector::StepInto(PtThread thread)
275 {
276 auto it = threads_.find(thread);
277 if (it != threads_.end()) {
278 auto frame = debugger_.GetCurrentFrame(thread);
279 if (!frame) {
280 HandleError(frame.Error());
281 return;
282 }
283
284 it->second.StepInto(debugInfoCache_.GetCurrentLineLocations(*frame.Value()));
285 }
286 }
287
StepOver(PtThread thread)288 void Inspector::StepOver(PtThread thread)
289 {
290 auto it = threads_.find(thread);
291 if (it != threads_.end()) {
292 auto frame = debugger_.GetCurrentFrame(thread);
293 if (!frame) {
294 HandleError(frame.Error());
295 return;
296 }
297
298 it->second.StepOver(debugInfoCache_.GetCurrentLineLocations(*frame.Value()));
299 }
300 }
301
StepOut(PtThread thread)302 void Inspector::StepOut(PtThread thread)
303 {
304 auto it = threads_.find(thread);
305 if (it != threads_.end()) {
306 HandleError(debugger_.NotifyFramePop(thread, 0));
307 it->second.StepOut();
308 }
309 }
310
ContinueToLocation(PtThread thread,std::string_view sourceFile,size_t lineNumber)311 void Inspector::ContinueToLocation(PtThread thread, std::string_view sourceFile, size_t lineNumber)
312 {
313 auto it = threads_.find(thread);
314 if (it != threads_.end()) {
315 it->second.ContinueTo(debugInfoCache_.GetContinueToLocations(sourceFile, lineNumber));
316 }
317 }
318
RestartFrame(PtThread thread,FrameId frameId)319 void Inspector::RestartFrame(PtThread thread, FrameId frameId)
320 {
321 auto it = threads_.find(thread);
322 if (it != threads_.end()) {
323 if (auto error = debugger_.RestartFrame(thread, frameId)) {
324 HandleError(*error);
325 return;
326 }
327
328 it->second.StepInto({});
329 }
330 }
331
GetProperties(PtThread thread,RemoteObjectId objectId,bool generatePreview)332 std::vector<PropertyDescriptor> Inspector::GetProperties(PtThread thread, RemoteObjectId objectId, bool generatePreview)
333 {
334 std::optional<std::vector<PropertyDescriptor>> properties;
335
336 auto it = threads_.find(thread);
337 if (it != threads_.end()) {
338 it->second.RequestToObjectRepository([objectId, generatePreview, &properties](auto &objectRepository) {
339 properties = objectRepository.GetProperties(objectId, generatePreview);
340 });
341 }
342
343 if (!properties) {
344 LOG(INFO, DEBUGGER) << "Failed to resolve object id: " << objectId;
345 return {};
346 }
347
348 return *properties;
349 }
350
GetSourceCode(std::string_view sourceFile)351 std::string Inspector::GetSourceCode(std::string_view sourceFile)
352 {
353 return debugInfoCache_.GetSourceCode(sourceFile);
354 }
355
DebuggableThreadPostSuspend(PtThread thread,ObjectRepository & objectRepository,const std::vector<BreakpointId> & hitBreakpoints,ObjectHeader * exception)356 void Inspector::DebuggableThreadPostSuspend(PtThread thread, ObjectRepository &objectRepository,
357 const std::vector<BreakpointId> &hitBreakpoints, ObjectHeader *exception)
358 {
359 auto exceptionRemoteObject = exception != nullptr ? objectRepository.CreateObject(TypedValue::Reference(exception))
360 : std::optional<RemoteObject>();
361
362 inspectorServer_.CallDebuggerPaused(
363 thread, hitBreakpoints, exceptionRemoteObject, [this, thread, &objectRepository](auto &handler) {
364 FrameId frameId = 0;
365 HandleError(debugger_.EnumerateFrames(thread, [this, &objectRepository, &handler,
366 &frameId](const PtFrame &frame) {
367 std::string_view sourceFile;
368 std::string_view methodName;
369 size_t lineNumber;
370 debugInfoCache_.GetSourceLocation(frame, sourceFile, methodName, lineNumber);
371
372 std::optional<RemoteObject> objThis;
373 auto frameObject = objectRepository.CreateFrameObject(frame, debugInfoCache_.GetLocals(frame), objThis);
374 auto scopeChain = std::vector {Scope(Scope::Type::LOCAL, std::move(frameObject)),
375 Scope(Scope::Type::GLOBAL, objectRepository.CreateGlobalObject())};
376
377 handler(frameId++, methodName, sourceFile, lineNumber, scopeChain, objThis);
378
379 return true;
380 }));
381 });
382 }
383
NotifyExecutionEnded()384 void Inspector::NotifyExecutionEnded()
385 {
386 inspectorServer_.CallRuntimeExecutionContextsCleared();
387 }
388 } // namespace ark::tooling::inspector
389