1 /*
2 * Copyright (c) 2021 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 "debugger.h"
17 #include "include/stack_walker.h"
18 #include "include/tooling/pt_location.h"
19 #include "include/tooling/pt_thread.h"
20 #include "pt_method_private.h"
21 #include "pt_scoped_managed_code.h"
22 #include "pt_lang_ext_private.h"
23 #include "pt_object_private.h"
24 #include "interpreter/frame.h"
25 #include "include/mem/panda_smart_pointers.h"
26 #include "tooling/pt_object_private.h"
27 #include "tooling/pt_reference_private.h"
28 #include "pt_thread_info.h"
29
30 #include "libpandabase/macros.h"
31 #include "libpandabase/os/mem.h"
32 #include "libpandabase/utils/expected.h"
33 #include "libpandabase/utils/span.h"
34 #include "libpandafile/bytecode_instruction.h"
35 #include "runtime/include/mem/panda_smart_pointers.h"
36 #include "runtime/include/stack_walker.h"
37 #include "runtime/interpreter/frame.h"
38 #include "runtime/tooling/pt_method_private.h"
39 #include "runtime/tooling/pt_value_private.h"
40
41 namespace panda::tooling {
GetPtLangExtPrivate()42 static PtLangExtPrivate *GetPtLangExtPrivate()
43 {
44 return reinterpret_cast<PtLangExtPrivate *>(Runtime::GetCurrent()->GetPtLangExt());
45 }
46
SetNotification(PtThread thread,bool enable,PtHookType hookType)47 std::optional<Error> Debugger::SetNotification(PtThread thread, bool enable, PtHookType hookType)
48 {
49 if (thread == PtThread::NONE) {
50 if (enable) {
51 hooks_.EnableGlobalHook(hookType);
52 } else {
53 hooks_.DisableGlobalHook(hookType);
54 }
55 } else {
56 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread);
57 if (enable) {
58 mt_managed_thread->GetPtThreadInfo()->GetHookTypeInfo().Enable(hookType);
59 } else {
60 mt_managed_thread->GetPtThreadInfo()->GetHookTypeInfo().Disable(hookType);
61 }
62 }
63
64 return {};
65 }
66
SetBreakpoint(const PtLocation & location)67 std::optional<Error> Debugger::SetBreakpoint(const PtLocation &location)
68 {
69 Method *method = runtime_->GetClassLinker()->GetMethod(location.GetPandaFile(), location.GetMethodId());
70 if (method == nullptr) {
71 return Error(Error::Type::METHOD_NOT_FOUND,
72 std::string("Cannot find method with id ") + std::to_string(location.GetMethodId().GetOffset()) +
73 " in panda file '" + std::string(location.GetPandaFile()) + "'");
74 }
75
76 if (location.GetBytecodeOffset() >= method->GetCodeSize()) {
77 return Error(Error::Type::INVALID_BREAKPOINT, std::string("Invalid breakpoint location: bytecode offset (") +
78 std::to_string(location.GetBytecodeOffset()) +
79 ") >= method code size (" +
80 std::to_string(method->GetCodeSize()) + ")");
81 }
82
83 if (!breakpoints_.emplace(method, location.GetBytecodeOffset()).second) {
84 return Error(Error::Type::BREAKPOINT_ALREADY_EXISTS,
85 std::string("Breakpoint already exists: bytecode offset ") +
86 std::to_string(location.GetBytecodeOffset()));
87 }
88
89 return {};
90 }
91
RemoveBreakpoint(const PtLocation & location)92 std::optional<Error> Debugger::RemoveBreakpoint(const PtLocation &location)
93 {
94 Method *method = runtime_->GetClassLinker()->GetMethod(location.GetPandaFile(), location.GetMethodId());
95 if (method == nullptr) {
96 return Error(Error::Type::METHOD_NOT_FOUND,
97 std::string("Cannot find method with id ") + std::to_string(location.GetMethodId().GetOffset()) +
98 " in panda file '" + std::string(location.GetPandaFile()) + "'");
99 }
100
101 if (!RemoveBreakpoint(method, location.GetBytecodeOffset())) {
102 return Error(Error::Type::BREAKPOINT_NOT_FOUND, "Breakpoint not found");
103 }
104
105 return {};
106 }
107
GetPandaFrame(ManagedThread * thread,uint32_t frameDepth=0,bool * outIsNative=nullptr)108 static panda::Frame *GetPandaFrame(ManagedThread *thread, uint32_t frameDepth = 0, bool *outIsNative = nullptr)
109 {
110 StackWalker stack(thread);
111
112 while (stack.HasFrame() && frameDepth != 0) {
113 stack.NextFrame();
114 --frameDepth;
115 }
116
117 bool isNative = false;
118 panda::Frame *frame = nullptr;
119 if (stack.HasFrame()) {
120 if (!stack.IsCFrame()) {
121 frame = stack.GetIFrame();
122 } else {
123 isNative = true;
124 }
125 }
126
127 if (outIsNative != nullptr) {
128 *outIsNative = isNative;
129 }
130
131 return frame;
132 }
133
GetVRegByPtThread(PtThread thread,uint32_t frameDepth,int32_t regNumber) const134 Expected<panda::Frame::VRegister *, Error> Debugger::GetVRegByPtThread(PtThread thread, uint32_t frameDepth,
135 int32_t regNumber) const
136 {
137 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread);
138 if (MTManagedThread::GetCurrent() != mt_managed_thread && !mt_managed_thread->IsUserSuspended()) {
139 return Unexpected(Error(Error::Type::THREAD_NOT_SUSPENDED,
140 std::string("Thread " + std::to_string(thread.GetId()) + " is not suspended")));
141 }
142
143 bool isNative = false;
144 panda::Frame *frame = GetPandaFrame(mt_managed_thread, frameDepth, &isNative);
145 if (frame == nullptr) {
146 if (isNative) {
147 return Unexpected(Error(Error::Type::OPAQUE_FRAME,
148 std::string("Frame is native, threadId=" + std::to_string(thread.GetId()) +
149 " frameDepth=" + std::to_string(frameDepth))));
150 }
151
152 return Unexpected(Error(Error::Type::FRAME_NOT_FOUND,
153 std::string("Frame not found or native, threadId=" + std::to_string(thread.GetId()) +
154 " frameDepth=" + std::to_string(frameDepth))));
155 }
156
157 if (regNumber == -1) {
158 return &frame->GetAcc();
159 }
160
161 if (regNumber >= 0 && uint32_t(regNumber) < frame->GetSize()) {
162 return &frame->GetVReg(uint32_t(regNumber));
163 }
164
165 return Unexpected(
166 Error(Error::Type::INVALID_REGISTER, std::string("Invalid register number: ") + std::to_string(regNumber)));
167 }
168
GetThisVariableByFrame(PtThread thread,uint32_t frameDepth,PtValue * result)169 std::optional<Error> Debugger::GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, PtValue *result)
170 {
171 return {};
172 }
173
GetVariable(PtThread thread,uint32_t frameDepth,int32_t regNumber,PtValue * result) const174 std::optional<Error> Debugger::GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
175 PtValue *result) const
176 {
177 ASSERT_NATIVE_CODE();
178 auto ret = GetVRegByPtThread(thread, frameDepth, regNumber);
179 if (!ret) {
180 return ret.Error();
181 }
182
183 Frame::VRegister *reg = ret.Value();
184 PtScopedManagedCode smc;
185 return GetPtLangExtPrivate()->GetPtValueFromManaged(*reg, result);
186 }
187
SetVariable(PtThread thread,uint32_t frameDepth,int32_t regNumber,const PtValue & value) const188 std::optional<Error> Debugger::SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
189 const PtValue &value) const
190 {
191 ASSERT_NATIVE_CODE();
192 auto ret = GetVRegByPtThread(thread, frameDepth, regNumber);
193 if (!ret) {
194 return ret.Error();
195 }
196
197 Frame::VRegister *reg = ret.Value();
198 PtScopedManagedCode smc;
199 return GetPtLangExtPrivate()->StorePtValueFromManaged(value, reg);
200 }
201
GetCurrentFrame(PtThread thread) const202 Expected<std::unique_ptr<PtFrame>, Error> Debugger::GetCurrentFrame(PtThread thread) const
203 {
204 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread);
205 if (mt_managed_thread == nullptr) {
206 return Unexpected(Error(Error::Type::THREAD_NOT_FOUND,
207 std::string("Thread ") + std::to_string(thread.GetId()) + " not found"));
208 }
209
210 StackWalker stack(mt_managed_thread);
211
212 Method *method = stack.GetMethod();
213 Frame *interpreterFrame = nullptr;
214
215 if (!stack.IsCFrame()) {
216 interpreterFrame = stack.GetIFrame();
217 }
218
219 return {std::make_unique<PtDebugFrame>(method, interpreterFrame)};
220 }
221
EnumerateFrames(PtThread thread,std::function<bool (const PtFrame &)> callback) const222 std::optional<Error> Debugger::EnumerateFrames(PtThread thread, std::function<bool(const PtFrame &)> callback) const
223 {
224 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread);
225 if (mt_managed_thread == nullptr) {
226 return Error(Error::Type::THREAD_NOT_FOUND,
227 std::string("Thread ") + std::to_string(thread.GetId()) + " not found");
228 }
229
230 StackWalker stack(mt_managed_thread);
231 while (stack.HasFrame()) {
232 Method *method = stack.GetMethod();
233 Frame *frame = stack.IsCFrame() ? nullptr : stack.GetIFrame();
234 PtDebugFrame debug_frame(method, frame);
235 if (!callback(debug_frame)) {
236 break;
237 }
238 stack.NextFrame();
239 }
240
241 return {};
242 }
243
SuspendThread(PtThread thread) const244 std::optional<Error> Debugger::SuspendThread(PtThread thread) const
245 {
246 auto managedThread = GetManagedThreadByPtThread(thread);
247 if (managedThread == nullptr) {
248 return Error(Error::Type::THREAD_NOT_FOUND,
249 std::string("MT thread ") + std::to_string(thread.GetId()) + " not found");
250 }
251 managedThread->SuspendImpl();
252
253 return {};
254 }
255
ResumeThread(PtThread thread) const256 std::optional<Error> Debugger::ResumeThread(PtThread thread) const
257 {
258 auto managedThread = GetManagedThreadByPtThread(thread);
259 if (managedThread == nullptr) {
260 return Error(Error::Type::THREAD_NOT_FOUND,
261 std::string("MT thread ") + std::to_string(thread.GetId()) + " not found");
262 }
263 managedThread->ResumeImpl();
264
265 return {};
266 }
267
GetPtMethod(const PtLocation & location) const268 Expected<PtMethod, Error> Debugger::GetPtMethod(const PtLocation &location) const
269 {
270 panda_file::File::EntityId methodId = location.GetMethodId();
271 const char *pandaFile = location.GetPandaFile();
272 Method *method = runtime_->GetClassLinker()->GetMethod(pandaFile, methodId);
273 if (method == nullptr) {
274 return Unexpected(Error(Error::Type::METHOD_NOT_FOUND, std::string("Cannot find method with id ") +
275 std::to_string(methodId.GetOffset()) +
276 " in panda file '" + std::string(pandaFile) + "'"));
277 }
278 return MethodToPtMethod(method);
279 }
280
RestartFrame(PtThread thread,uint32_t frameNumber) const281 std::optional<Error> Debugger::RestartFrame(PtThread thread, uint32_t frameNumber) const
282 {
283 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread);
284 if (mt_managed_thread == nullptr) {
285 return Error(Error::Type::THREAD_NOT_FOUND,
286 std::string("Thread ") + std::to_string(thread.GetId()) + " not found");
287 }
288 if (!mt_managed_thread->IsUserSuspended() && mt_managed_thread->IsJavaThread()) {
289 return Error(Error::Type::THREAD_NOT_SUSPENDED,
290 std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
291 }
292
293 StackWalker stack(mt_managed_thread);
294 panda::Frame *popFrame = nullptr;
295 panda::Frame *retryFrame = nullptr;
296 uint32_t currentFrameNumber = 0;
297
298 while (stack.HasFrame()) {
299 if (stack.IsCFrame()) {
300 return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
301 ", frame at depth is executing a native method");
302 }
303 if (currentFrameNumber == frameNumber) {
304 popFrame = stack.GetIFrame();
305 } else if (currentFrameNumber == (frameNumber + 1)) {
306 retryFrame = stack.GetIFrame();
307 break;
308 }
309 ++currentFrameNumber;
310 stack.NextFrame();
311 }
312
313 if (popFrame == nullptr) {
314 return Error(Error::Type::FRAME_NOT_FOUND, std::string("Thread ") + std::to_string(thread.GetId()) +
315 " doesn't have managed frame with number " +
316 std::to_string(frameNumber));
317 }
318
319 if (retryFrame == nullptr) {
320 return Error(Error::Type::NO_MORE_FRAMES, std::string("Thread ") + std::to_string(thread.GetId()) +
321 " does not have more than one frame on the call stack");
322 }
323
324 // Set force pop frames from top to target
325 stack.Reset(mt_managed_thread);
326 while (stack.HasFrame()) {
327 panda::Frame *frame = stack.GetIFrame();
328 frame->SetForcePop();
329 if (frame == popFrame) {
330 break;
331 }
332 stack.NextFrame();
333 }
334 retryFrame->SetRetryInstruction();
335
336 return {};
337 }
338
NotifyFramePop(PtThread thread,uint32_t depth) const339 std::optional<Error> Debugger::NotifyFramePop(PtThread thread, uint32_t depth) const
340 {
341 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread);
342 if (mt_managed_thread == nullptr) {
343 return Error(Error::Type::THREAD_NOT_FOUND,
344 std::string("Thread ") + std::to_string(thread.GetId()) + " not found");
345 }
346
347 bool isNative = false;
348 panda::Frame *popFrame = GetPandaFrame(mt_managed_thread, depth, &isNative);
349 if (popFrame == nullptr) {
350 if (isNative) {
351 return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
352 ", frame at depth is executing a native method");
353 }
354
355 return Error(Error::Type::NO_MORE_FRAMES,
356 std::string("Thread ") + std::to_string(thread.GetId()) +
357 ", are no stack frames at the specified depth: " + std::to_string(depth));
358 }
359
360 popFrame->SetNotifyPop();
361 return {};
362 }
363
BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bcOffset)364 void Debugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset)
365 {
366 ASSERT(bcOffset < method->GetCodeSize() && "code size of current method less then bcOffset");
367
368 HandleExceptionThrowEvent(thread, method, bcOffset);
369
370 // Step event is reported before breakpoint, according to the spec.
371 HandleStep(thread, method, bcOffset);
372 HandleBreakpoint(thread, method, bcOffset);
373
374 if (IsPropertyWatchActive()) {
375 if (!HandlePropertyAccess(thread, method, bcOffset)) {
376 HandlePropertyModify(thread, method, bcOffset);
377 }
378 }
379 }
380
ObjectAlloc(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)381 void Debugger::ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size)
382 {
383 if (!vm_started_) {
384 return;
385 }
386 if (thread == nullptr) {
387 thread = ManagedThread::GetCurrent();
388 }
389 if (thread == nullptr) {
390 return;
391 }
392
393 PtThread ptThread(thread->GetId());
394 PtLangExtPrivate *ext = GetPtLangExtPrivate();
395 PtClass ptClass = ext->ClassToPtClass(klass);
396 PtScopedObjectPrivate scopedObject(object);
397 hooks_.ObjectAlloc(ptClass, scopedObject.GetObject(), ptThread, size);
398 }
399
MethodEntry(ManagedThread * managedThread,Method * method)400 void Debugger::MethodEntry(ManagedThread *managedThread, Method *method)
401 {
402 uint32_t threadId = managedThread->GetId();
403 PtThread ptThread(threadId);
404
405 hooks_.MethodEntry(ptThread, MethodToPtMethod(method));
406 }
407
MethodExit(ManagedThread * managedThread,Method * method)408 void Debugger::MethodExit(ManagedThread *managedThread, Method *method)
409 {
410 bool isExceptionTriggered = managedThread->HasPendingException();
411 PtThread ptThread(managedThread->GetId());
412 PtValue retValue(managedThread->GetCurrentFrame()->GetAcc().GetValue());
413 hooks_.MethodExit(ptThread, MethodToPtMethod(method), isExceptionTriggered, retValue);
414
415 HandleNotifyFramePop(managedThread, method, isExceptionTriggered);
416 }
417
IsSkipClassEvent()418 static bool IsSkipClassEvent()
419 {
420 auto *thread = ManagedThread::GetCurrent();
421 return (thread == nullptr || thread->IsJSThread());
422 }
423
ClassLoad(Class * klass)424 void Debugger::ClassLoad(Class *klass)
425 {
426 if (!vm_started_ || IsSkipClassEvent()) {
427 return;
428 }
429
430 PtLangExtPrivate *ext = GetPtLangExtPrivate();
431 PtThread ptThread(ManagedThread::GetCurrent()->GetId());
432 PtClass ptClass = ext->ClassToPtClass(klass);
433
434 hooks_.ClassLoad(ptThread, ptClass);
435 }
436
ClassPrepare(Class * klass)437 void Debugger::ClassPrepare(Class *klass)
438 {
439 if (!vm_started_ || IsSkipClassEvent()) {
440 return;
441 }
442
443 PtLangExtPrivate *ext = GetPtLangExtPrivate();
444 PtThread ptThread(ManagedThread::GetCurrent()->GetId());
445 PtClass ptClass = ext->ClassToPtClass(klass);
446
447 hooks_.ClassPrepare(ptThread, ptClass);
448 }
449
MonitorWait(ObjectHeader * object,int64_t timeout)450 void Debugger::MonitorWait(ObjectHeader *object, int64_t timeout)
451 {
452 PtThread ptThread(ManagedThread::GetCurrent()->GetId());
453 PtScopedObjectPrivate ptScopedObj(object);
454
455 hooks_.MonitorWait(ptThread, ptScopedObj.GetObject(), timeout);
456 }
457
MonitorWaited(ObjectHeader * object,bool timedOut)458 void Debugger::MonitorWaited(ObjectHeader *object, bool timedOut)
459 {
460 PtThread ptThread(ManagedThread::GetCurrent()->GetId());
461 PtScopedObjectPrivate ptScopedObj(object);
462
463 hooks_.MonitorWaited(ptThread, ptScopedObj.GetObject(), timedOut);
464 }
465
MonitorContendedEnter(ObjectHeader * object)466 void Debugger::MonitorContendedEnter(ObjectHeader *object)
467 {
468 PtThread ptThread(ManagedThread::GetCurrent()->GetId());
469 PtScopedObjectPrivate ptScopedObj(object);
470
471 hooks_.MonitorContendedEnter(ptThread, ptScopedObj.GetObject());
472 }
473
MonitorContendedEntered(ObjectHeader * object)474 void Debugger::MonitorContendedEntered(ObjectHeader *object)
475 {
476 PtThread ptThread(ManagedThread::GetCurrent()->GetId());
477 PtScopedObjectPrivate ptScopedObj(object);
478
479 hooks_.MonitorContendedEntered(ptThread, ptScopedObj.GetObject());
480 }
481
HandleBreakpoint(const ManagedThread * managedThread,const Method * method,uint32_t bcOffset)482 bool Debugger::HandleBreakpoint(const ManagedThread *managedThread, const Method *method, uint32_t bcOffset)
483 {
484 if (FindBreakpoint(method, bcOffset) == nullptr) {
485 return false;
486 }
487
488 auto *pf = method->GetPandaFile();
489 PtLocation location {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
490 hooks_.Breakpoint(PtThread(managedThread->GetId()), location);
491
492 return true;
493 }
494
HandleExceptionThrowEvent(ManagedThread * thread,Method * method,uint32_t bcOffset)495 void Debugger::HandleExceptionThrowEvent(ManagedThread *thread, Method *method, uint32_t bcOffset)
496 {
497 if (!thread->HasPendingException() || thread->GetPtThreadInfo()->GetPtActiveExceptionThrown()) {
498 return;
499 }
500
501 thread->GetPtThreadInfo()->SetPtActiveExceptionThrown(true);
502
503 auto *pf = method->GetPandaFile();
504 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*method);
505 std::pair<Method *, uint32_t> res = ctx.GetCatchMethodAndOffset(method, thread);
506 auto *catchMethodFile = res.first->GetPandaFile();
507
508 PtLocation throwLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
509 PtLocation catchLocation {catchMethodFile->GetFilename().c_str(), res.first->GetFileId(), res.second};
510
511 ObjectHeader *exceptionObject = thread->GetException();
512 PtScopedObjectPrivate ptScopedExObj(exceptionObject);
513
514 thread->GetPtThreadInfo()->SetCurrentException(ptScopedExObj.GetObject());
515
516 hooks_.Exception(PtThread(thread->GetId()), throwLocation, ptScopedExObj.GetObject(), catchLocation);
517 }
518
ExceptionCatch(const ManagedThread * thread,const Method * method,uint32_t bcOffset)519 void Debugger::ExceptionCatch(const ManagedThread *thread, const Method *method, uint32_t bcOffset)
520 {
521 ASSERT(!thread->HasPendingException() && thread->GetPtThreadInfo()->GetPtActiveExceptionThrown());
522
523 thread->GetPtThreadInfo()->SetPtActiveExceptionThrown(false);
524
525 auto *pf = method->GetPandaFile();
526 PtLocation catchLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
527
528 PtObject exceptionObject = thread->GetPtThreadInfo()->GetCurrentException();
529 hooks_.ExceptionCatch(PtThread(thread->GetId()), catchLocation, exceptionObject);
530 thread->GetPtThreadInfo()->ResetCurrentException();
531 }
532
HandleStep(const ManagedThread * managedThread,const Method * method,uint32_t bcOffset)533 bool Debugger::HandleStep(const ManagedThread *managedThread, const Method *method, uint32_t bcOffset)
534 {
535 auto *pf = method->GetPandaFile();
536 PtLocation location {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
537 hooks_.SingleStep(PtThread(managedThread->GetId()), location);
538 return true;
539 }
540
HandleNotifyFramePop(ManagedThread * managedThread,Method * method,bool wasPoppedByException)541 void Debugger::HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException)
542 {
543 panda::Frame *frame = GetPandaFrame(managedThread);
544 if (frame != nullptr && frame->IsNotifyPop()) {
545 hooks_.FramePop(PtThread(managedThread->GetId()), MethodToPtMethod(method), wasPoppedByException);
546 frame->ClearNotifyPop();
547 }
548 }
549
HandlePropertyAccess(const ManagedThread * thread,const Method * method,uint32_t bcOffset)550 bool Debugger::HandlePropertyAccess(const ManagedThread *thread, const Method *method, uint32_t bcOffset)
551 {
552 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
553 BytecodeInstruction inst(method->GetInstructions() + bcOffset);
554 auto opcode = inst.GetOpcode();
555 bool isStatic = false;
556
557 switch (opcode) {
558 case BytecodeInstruction::Opcode::LDOBJ_V8_ID16:
559 case BytecodeInstruction::Opcode::LDOBJ_64_V8_ID16:
560 case BytecodeInstruction::Opcode::LDOBJ_OBJ_V8_ID16:
561 break;
562 case BytecodeInstruction::Opcode::LDSTATIC_ID16:
563 case BytecodeInstruction::Opcode::LDSTATIC_64_ID16:
564 case BytecodeInstruction::Opcode::LDSTATIC_OBJ_ID16:
565 isStatic = true;
566 break;
567 default:
568 return false;
569 }
570
571 auto propertyIndex = inst.GetId().AsIndex();
572 auto propertyId = method->GetClass()->ResolveFieldIndex(propertyIndex);
573 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
574 ASSERT(classLinker);
575 auto *field = classLinker->GetField(*method, propertyId);
576 ASSERT(field);
577 auto *klass = field->GetClass();
578 ASSERT(klass);
579
580 if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::ACCESS) == nullptr) {
581 return false;
582 }
583
584 PtLocation location {method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset};
585 PtThread ptThread(thread->GetId());
586 PtLangExtPrivate *ext = GetPtLangExtPrivate();
587 PtProperty ptProperty = ext->FieldToPtProperty(field);
588
589 if (isStatic) {
590 hooks_.PropertyAccess(ptThread, location, PtObject(), ptProperty);
591 } else {
592 Frame::VRegister ® = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
593 ASSERT(reg.HasObject());
594 PtScopedObjectPrivate slo(reg.GetReference());
595 hooks_.PropertyAccess(ptThread, location, slo.GetObject(), ptProperty);
596 }
597
598 return true;
599 }
600
HandlePropertyModify(const ManagedThread * thread,const Method * method,uint32_t bcOffset)601 bool Debugger::HandlePropertyModify(const ManagedThread *thread, const Method *method, uint32_t bcOffset)
602 {
603 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
604 BytecodeInstruction inst(method->GetInstructions() + bcOffset);
605 auto opcode = inst.GetOpcode();
606 bool isStatic = false;
607
608 switch (opcode) {
609 case BytecodeInstruction::Opcode::STOBJ_V8_ID16:
610 case BytecodeInstruction::Opcode::STOBJ_64_V8_ID16:
611 case BytecodeInstruction::Opcode::STOBJ_OBJ_V8_ID16:
612 break;
613 case BytecodeInstruction::Opcode::STSTATIC_ID16:
614 case BytecodeInstruction::Opcode::STSTATIC_64_ID16:
615 case BytecodeInstruction::Opcode::STSTATIC_OBJ_ID16:
616 isStatic = true;
617 break;
618 default:
619 return false;
620 }
621
622 auto propertyIdx = inst.GetId().AsIndex();
623 auto propertyId = method->GetClass()->ResolveFieldIndex(propertyIdx);
624 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
625 ASSERT(classLinker);
626 auto *field = classLinker->GetField(*method, propertyId);
627 ASSERT(field);
628 auto *klass = field->GetClass();
629 ASSERT(klass);
630
631 if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::MODIFY) == nullptr) {
632 return false;
633 }
634
635 PtThread ptThread(thread->GetId());
636 PtLangExtPrivate *ext = GetPtLangExtPrivate();
637 PtLocation location {method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset};
638 PtProperty ptProperty = ext->FieldToPtProperty(field);
639
640 PtValuePrivate svfm(ext, &thread->GetCurrentFrame()->GetAcc());
641 if (isStatic) {
642 hooks_.PropertyModification(ptThread, location, PtObject(), ptProperty, svfm.GetValue());
643 } else {
644 Frame::VRegister ® = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
645 ASSERT(reg.HasObject());
646 PtScopedObjectPrivate slo(reg.GetReference());
647 hooks_.PropertyModification(ptThread, location, slo.GetObject(), ptProperty, svfm.GetValue());
648 }
649
650 return true;
651 }
652
SetPropertyAccessWatch(PtClass klass,PtProperty property)653 std::optional<Error> Debugger::SetPropertyAccessWatch(PtClass klass, PtProperty property)
654 {
655 PtLangExtPrivate *langExt = GetPtLangExtPrivate();
656 panda_file::File::EntityId classId = langExt->PtClassToClass(klass)->GetFileId();
657 panda_file::File::EntityId propertyId = langExt->PtPropertyToField(property)->GetFileId();
658 if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS) != nullptr) {
659 return Error(Error::Type::INVALID_PROPERTY_ACCESS_WATCH,
660 std::string("Invalid property access watch, already exist, ClassID: ") +
661 std::to_string(classId.GetOffset()) +
662 ", PropertyID: " + std::to_string(propertyId.GetOffset()));
663 }
664 property_watches_.emplace_back(classId, propertyId, PropertyWatch::Type::ACCESS);
665 return {};
666 }
667
ClearPropertyAccessWatch(PtClass klass,PtProperty property)668 std::optional<Error> Debugger::ClearPropertyAccessWatch(PtClass klass, PtProperty property)
669 {
670 PtLangExtPrivate *langExt = GetPtLangExtPrivate();
671 panda_file::File::EntityId classId = langExt->PtClassToClass(klass)->GetFileId();
672 panda_file::File::EntityId propertyId = langExt->PtPropertyToField(property)->GetFileId();
673 if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS)) {
674 return Error(Error::Type::PROPERTY_ACCESS_WATCH_NOT_FOUND,
675 std::string("Property access watch not found, ClassID: ") + std::to_string(classId.GetOffset()) +
676 ", PropertyID: " + std::to_string(propertyId.GetOffset()));
677 }
678 return {};
679 }
680
SetPropertyModificationWatch(PtClass klass,PtProperty property)681 std::optional<Error> Debugger::SetPropertyModificationWatch(PtClass klass, PtProperty property)
682 {
683 PtLangExtPrivate *langExt = GetPtLangExtPrivate();
684 panda_file::File::EntityId classId = langExt->PtClassToClass(klass)->GetFileId();
685 panda_file::File::EntityId propertyId = langExt->PtPropertyToField(property)->GetFileId();
686 if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY) != nullptr) {
687 return Error(Error::Type::INVALID_PROPERTY_MODIFY_WATCH,
688 std::string("Invalid property modification watch, already exist, ClassID: ") +
689 std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
690 }
691 property_watches_.emplace_back(classId, propertyId, PropertyWatch::Type::MODIFY);
692 return {};
693 }
694
ClearPropertyModificationWatch(PtClass klass,PtProperty property)695 std::optional<Error> Debugger::ClearPropertyModificationWatch(PtClass klass, PtProperty property)
696 {
697 PtLangExtPrivate *langExt = GetPtLangExtPrivate();
698 panda_file::File::EntityId classId = langExt->PtClassToClass(klass)->GetFileId();
699 panda_file::File::EntityId propertyId = langExt->PtPropertyToField(property)->GetFileId();
700 if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY)) {
701 return Error(Error::Type::PROPERTY_MODIFY_WATCH_NOT_FOUND,
702 std::string("Property modification watch not found, ClassID: ") +
703 std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
704 }
705 return {};
706 }
707
FindBreakpoint(const Method * method,uint32_t bcOffset) const708 const tooling::Breakpoint *Debugger::FindBreakpoint(const Method *method, uint32_t bcOffset) const
709 {
710 for (const auto &bp : breakpoints_) {
711 if (bp.GetBytecodeOffset() == bcOffset && bp.GetMethod()->GetPandaFile() == method->GetPandaFile() &&
712 bp.GetMethod()->GetFileId() == method->GetFileId()) {
713 return &bp;
714 }
715 }
716
717 return nullptr;
718 }
719
RemoveBreakpoint(Method * method,uint32_t bcOffset)720 bool Debugger::RemoveBreakpoint(Method *method, uint32_t bcOffset)
721 {
722 auto it = breakpoints_.begin();
723 while (it != breakpoints_.end()) {
724 const auto &bp = *it;
725 if (bp.GetBytecodeOffset() == bcOffset && bp.GetMethod() == method) {
726 it = breakpoints_.erase(it);
727 return true;
728 }
729
730 it++;
731 }
732
733 return false;
734 }
735
FindPropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type) const736 const tooling::PropertyWatch *Debugger::FindPropertyWatch(panda_file::File::EntityId classId,
737 panda_file::File::EntityId fieldId,
738 tooling::PropertyWatch::Type type) const
739 {
740 for (const auto &pw : property_watches_) {
741 if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
742 return &pw;
743 }
744 }
745
746 return nullptr;
747 }
748
RemovePropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type)749 bool Debugger::RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId,
750 tooling::PropertyWatch::Type type)
751 {
752 auto it = property_watches_.begin();
753 while (it != property_watches_.end()) {
754 const auto &pw = *it;
755 if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
756 property_watches_.erase(it);
757 return true;
758 }
759
760 it++;
761 }
762
763 return false;
764 }
765
GetManagedThreadByPtThread(PtThread thread) const766 MTManagedThread *Debugger::GetManagedThreadByPtThread(PtThread thread) const
767 {
768 if (thread.GetId() == 0) {
769 MTManagedThread *curr_thread = MTManagedThread::GetCurrent();
770 ASSERT(curr_thread && "Current thread is nullptr!");
771 if (curr_thread->IsJSThread()) {
772 return curr_thread;
773 }
774 }
775
776 MTManagedThread *res = nullptr;
777 runtime_->GetPandaVM()->GetThreadManager()->EnumerateThreads(
778 [&res, thread](MTManagedThread *mt_managed_thread) {
779 if (mt_managed_thread->GetId() == thread.GetId()) {
780 res = mt_managed_thread;
781 return false;
782 }
783
784 return true;
785 },
786 static_cast<unsigned int>(panda::EnumerationFlag::ALL),
787 static_cast<unsigned int>(panda::EnumerationFlag::VM_THREAD));
788
789 return res;
790 }
791
GetVRegValue(const Frame::VRegister & reg)792 static uint64_t GetVRegValue(const Frame::VRegister ®)
793 {
794 return reg.HasObject() ? reinterpret_cast<uintptr_t>(reg.GetReference()) : reg.GetLong();
795 }
796
797 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
PtDebugFrame(Method * method,const Frame * interpreterFrame)798 PtDebugFrame::PtDebugFrame(Method *method, const Frame *interpreterFrame)
799 : method_(MethodToPtMethod(method)),
800 method_id_(method->GetFileId()),
801 panda_file_(method->GetPandaFile()->GetFilename())
802 {
803 is_interpreter_frame_ = interpreterFrame != nullptr;
804 if (!is_interpreter_frame_) {
805 return;
806 }
807
808 size_t nregs = method->GetNumVregs();
809 size_t nargs = method->GetNumArgs();
810
811 for (size_t i = 0; i < nregs; i++) {
812 vregs_.push_back(GetVRegValue(interpreterFrame->GetVReg(i)));
813 }
814
815 for (size_t i = 0; i < nargs; i++) {
816 args_.push_back(GetVRegValue(interpreterFrame->GetVReg(i + nregs)));
817 }
818
819 acc_ = GetVRegValue(interpreterFrame->GetAcc());
820 bc_offset_ = interpreterFrame->GetBytecodeOffset();
821 }
822 } // namespace panda::tooling
823