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