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