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