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