• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "debugger.h"
17 #include "include/stack_walker.h"
18 #include "include/thread_scopes.h"
19 #include "include/tooling/pt_location.h"
20 #include "include/tooling/pt_thread.h"
21 #include "pt_scoped_managed_code.h"
22 #include "interpreter/frame.h"
23 #include "include/mem/panda_smart_pointers.h"
24 #include "pt_thread_info.h"
25 
26 #include "libpandabase/macros.h"
27 #include "libpandabase/os/mem.h"
28 #include "libpandabase/utils/expected.h"
29 #include "libpandabase/utils/span.h"
30 #include "libpandafile/bytecode_instruction.h"
31 #include "runtime/include/mem/panda_smart_pointers.h"
32 #include "runtime/include/stack_walker-inl.h"
33 #include "runtime/include/stack_walker.h"
34 #include "runtime/include/thread-inl.h"
35 #include "runtime/interpreter/frame.h"
36 #include "runtime/handle_scope-inl.h"
37 
38 namespace ark::tooling {
39 // NOTE(maksenov): remove PtProperty class
FieldToPtProperty(Field * field)40 static PtProperty FieldToPtProperty(Field *field)
41 {
42     return PtProperty(field);
43 }
44 
PtPropertyToField(PtProperty property)45 static Field *PtPropertyToField(PtProperty property)
46 {
47     return reinterpret_cast<Field *>(property.GetData());
48 }
49 
SetNotification(PtThread thread,bool enable,PtHookType hookType)50 std::optional<Error> Debugger::SetNotification(PtThread thread, bool enable, PtHookType hookType)
51 {
52     if (thread == PtThread::NONE) {
53         if (enable) {
54             hooks_.EnableGlobalHook(hookType);
55         } else {
56             hooks_.DisableGlobalHook(hookType);
57         }
58     } else {
59         ManagedThread *managedThread = thread.GetManagedThread();
60         if (enable) {
61             managedThread->GetPtThreadInfo()->GetHookTypeInfo().Enable(hookType);
62         } else {
63             managedThread->GetPtThreadInfo()->GetHookTypeInfo().Disable(hookType);
64         }
65     }
66 
67     return {};
68 }
69 
CheckLocationInClass(const panda_file::File & pf,panda_file::File::EntityId classId,const PtLocation & location,std::optional<Error> & error)70 static bool CheckLocationInClass(const panda_file::File &pf, panda_file::File::EntityId classId,
71                                  const PtLocation &location, std::optional<Error> &error)
72 {
73     panda_file::ClassDataAccessor cda(pf, classId);
74     bool found = false;
75     cda.EnumerateMethods([&pf, &location, &error, &found](panda_file::MethodDataAccessor mda) {
76         if (mda.GetMethodId() == location.GetMethodId()) {
77             found = true;
78             auto codeId = mda.GetCodeId();
79             uint32_t codeSize = 0;
80             if (codeId.has_value()) {
81                 panda_file::CodeDataAccessor codeDa(pf, *codeId);
82                 codeSize = codeDa.GetCodeSize();
83             }
84             if (location.GetBytecodeOffset() >= codeSize) {
85                 error = Error(Error::Type::INVALID_BREAKPOINT,
86                               std::string("Invalid breakpoint location: bytecode offset (") +
87                                   std::to_string(location.GetBytecodeOffset()) + ") >= method code size (" +
88                                   std::to_string(codeSize) + ")");
89             }
90             return false;
91         }
92         return true;
93     });
94     return found;
95 }
96 
CheckLocation(const PtLocation & location)97 std::optional<Error> Debugger::CheckLocation(const PtLocation &location)
98 {
99     std::optional<Error> res;
100     runtime_->GetClassLinker()->EnumeratePandaFiles([&location, &res](const panda_file::File &pf) {
101         if (pf.GetFilename() != location.GetPandaFile()) {
102             return true;
103         }
104 
105         auto classes = pf.GetClasses();
106         bool found = false;
107         for (size_t i = 0; i < classes.Size(); i++) {
108             panda_file::File::EntityId id(classes[i]);
109             if (pf.IsExternal(id) || id.GetOffset() > location.GetMethodId().GetOffset()) {
110                 continue;
111             }
112 
113             found = CheckLocationInClass(pf, id, location, res);
114             if (found) {
115                 break;
116             }
117         }
118         if (!found) {
119             res =
120                 Error(Error::Type::METHOD_NOT_FOUND,
121                       std::string("Cannot find method with id ") + std::to_string(location.GetMethodId().GetOffset()) +
122                           " in panda file '" + std::string(location.GetPandaFile()) + "'");
123         }
124         return false;
125     });
126     return res;
127 }
128 
SetBreakpoint(const PtLocation & location)129 std::optional<Error> Debugger::SetBreakpoint(const PtLocation &location)
130 {
131     auto error = CheckLocation(location);
132     if (error.has_value()) {
133         return error;
134     }
135 
136     os::memory::WriteLockHolder wholder(rwlock_);
137     if (!breakpoints_.emplace(location).second) {
138         return Error(Error::Type::BREAKPOINT_ALREADY_EXISTS,
139                      std::string("Breakpoint already exists: bytecode offset ") +
140                          std::to_string(location.GetBytecodeOffset()));
141     }
142 
143     return {};
144 }
145 
RemoveBreakpoint(const PtLocation & location)146 std::optional<Error> Debugger::RemoveBreakpoint(const PtLocation &location)
147 {
148     if (!EraseBreakpoint(location)) {
149         return Error(Error::Type::BREAKPOINT_NOT_FOUND, "Breakpoint not found");
150     }
151 
152     return {};
153 }
154 
GetPandaFrame(StackWalker * pstack,uint32_t frameDepth,bool * outIsNative=nullptr)155 static ark::Frame *GetPandaFrame(StackWalker *pstack, uint32_t frameDepth, bool *outIsNative = nullptr)
156 {
157     ASSERT(pstack != nullptr);
158     StackWalker &stack = *pstack;
159 
160     while (stack.HasFrame() && frameDepth != 0) {
161         stack.NextFrame();
162         --frameDepth;
163     }
164 
165     bool isNative = false;
166     ark::Frame *frame = nullptr;
167     if (stack.HasFrame()) {
168         if (stack.IsCFrame()) {
169             isNative = true;
170         } else {
171             frame = stack.GetIFrame();
172         }
173     }
174 
175     if (outIsNative != nullptr) {
176         *outIsNative = isNative;
177     }
178 
179     return frame;
180 }
181 
GetPandaFrame(ManagedThread * thread,uint32_t frameDepth=0,bool * outIsNative=nullptr)182 static ark::Frame *GetPandaFrame(ManagedThread *thread, uint32_t frameDepth = 0, bool *outIsNative = nullptr)
183 {
184     auto stack = StackWalker::Create(thread);
185     return GetPandaFrame(&stack, frameDepth, outIsNative);
186 }
187 
GetThisAddrVRegByPandaFrame(ark::Frame * frame)188 static interpreter::StaticVRegisterRef GetThisAddrVRegByPandaFrame(ark::Frame *frame)
189 {
190     ASSERT(!frame->IsDynamic());
191     ASSERT(frame->GetMethod()->GetNumArgs() > 0);
192     uint32_t thisRegNum = frame->GetSize() - frame->GetMethod()->GetNumArgs();
193     return StaticFrameHandler(frame).GetVReg(thisRegNum);
194 }
195 
GetThisAddrVRegByPandaFrameDyn(ark::Frame * frame)196 static interpreter::DynamicVRegisterRef GetThisAddrVRegByPandaFrameDyn(ark::Frame *frame)
197 {
198     ASSERT(frame->IsDynamic());
199     ASSERT(frame->GetMethod()->GetNumArgs() > 0);
200     uint32_t thisRegNum = frame->GetSize() - frame->GetMethod()->GetNumArgs();
201     return DynamicFrameHandler(frame).GetVReg(thisRegNum);
202 }
203 
204 template <typename Callback>
GetPandaFrameByPtThread(PtThread thread,uint32_t frameDepth,Callback nativeFrameHandler)205 Expected<ark::Frame *, Error> GetPandaFrameByPtThread(PtThread thread, uint32_t frameDepth, Callback nativeFrameHandler)
206 {
207     ManagedThread *managedThread = thread.GetManagedThread();
208     ASSERT(managedThread != nullptr);
209 
210     if (MTManagedThread::ThreadIsMTManagedThread(managedThread)) {
211         // Check if thread is suspended
212         MTManagedThread *mtManagedThread = MTManagedThread::CastFromThread(managedThread);
213         if (MTManagedThread::GetCurrent() != mtManagedThread && !mtManagedThread->IsUserSuspended()) {
214             return Unexpected(Error(Error::Type::THREAD_NOT_SUSPENDED,
215                                     std::string("Thread " + std::to_string(thread.GetId()) + " is not suspended")));
216         }
217     }
218 
219     auto stack = StackWalker::Create(managedThread);
220     ark::Frame *frame = GetPandaFrame(&stack, frameDepth, nullptr);
221     if (frame == nullptr) {
222         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
223         if constexpr (!std::is_same_v<decltype(nativeFrameHandler), std::nullptr_t>) {
224             nativeFrameHandler(&stack);
225         }
226         return Unexpected(Error(Error::Type::FRAME_NOT_FOUND,
227                                 std::string("Frame not found or native, threadId=" + std::to_string(thread.GetId()) +
228                                             " frameDepth=" + std::to_string(frameDepth))));
229     }
230     return frame;
231 }
232 
GetVRegByPandaFrame(ark::Frame * frame,int32_t regNumber) const233 Expected<interpreter::StaticVRegisterRef, Error> Debugger::GetVRegByPandaFrame(ark::Frame *frame,
234                                                                                int32_t regNumber) const
235 {
236     if (regNumber == -1) {
237         return frame->GetAccAsVReg();
238     }
239 
240     if (regNumber >= 0 && uint32_t(regNumber) < frame->GetSize()) {
241         return StaticFrameHandler(frame).GetVReg(uint32_t(regNumber));
242     }
243 
244     return Unexpected(
245         Error(Error::Type::INVALID_REGISTER, std::string("Invalid register number: ") + std::to_string(regNumber)));
246 }
247 
GetVRegByPandaFrameDyn(ark::Frame * frame,int32_t regNumber) const248 Expected<interpreter::DynamicVRegisterRef, Error> Debugger::GetVRegByPandaFrameDyn(ark::Frame *frame,
249                                                                                    int32_t regNumber) const
250 {
251     if (regNumber == -1) {
252         return frame->template GetAccAsVReg<true>();
253     }
254 
255     if (regNumber >= 0 && uint32_t(regNumber) < frame->GetSize()) {
256         return DynamicFrameHandler(frame).GetVReg(uint32_t(regNumber));
257     }
258 
259     return Unexpected(
260         Error(Error::Type::INVALID_REGISTER, std::string("Invalid register number: ") + std::to_string(regNumber)));
261 }
262 
GetThisVariableByFrame(PtThread thread,uint32_t frameDepth,ObjectHeader ** thisPtr)263 std::optional<Error> Debugger::GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, ObjectHeader **thisPtr)
264 {
265     ASSERT_MANAGED_CODE();
266     *thisPtr = nullptr;
267 
268     std::optional<Error> nativeError;
269 
270     auto nativeFrameHandler = [thread, &nativeError, thisPtr](StackWalker *stack) {
271         if (!stack->GetCFrame().IsNative()) {
272             return;
273         }
274         if (stack->GetCFrame().GetMethod()->IsStatic()) {
275             nativeError =
276                 Error(Error::Type::INVALID_VALUE, std::string("Static native method, no this address slot, threadId=" +
277                                                               std::to_string(thread.GetId())));
278             return;
279         }
280         stack->IterateObjects([thisPtr](auto &vreg) {
281             // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
282             if constexpr (std::is_same_v<decltype(vreg), interpreter::StaticVRegisterRef &>) {
283                 ASSERT(vreg.HasObject());
284                 *thisPtr = vreg.GetReference();
285             }
286             return false;
287         });
288     };
289     auto ret = GetPandaFrameByPtThread(thread, frameDepth, nativeFrameHandler);
290     if (nativeError) {
291         return nativeError;
292     }
293     if (*thisPtr != nullptr) {
294         // The value was set by native frame handler
295         return {};
296     }
297     if (!ret) {
298         return ret.Error();
299     }
300     Frame *frame = ret.Value();
301     if (frame->GetMethod()->IsStatic()) {
302         return Error(Error::Type::INVALID_VALUE,
303                      std::string("Static method, no this address slot, threadId=" + std::to_string(thread.GetId())));
304     }
305 
306     if (frame->IsDynamic()) {
307         auto reg = GetThisAddrVRegByPandaFrameDyn(frame);
308         *thisPtr = reg.GetReference();
309     } else {
310         auto reg = GetThisAddrVRegByPandaFrame(ret.Value());
311         *thisPtr = reg.GetReference();
312     }
313     return {};
314 }
315 
GetVariable(PtThread thread,uint32_t frameDepth,int32_t regNumber,VRegValue * result) const316 std::optional<Error> Debugger::GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
317                                            VRegValue *result) const
318 {
319     ASSERT_MANAGED_CODE();
320     auto ret = GetPandaFrameByPtThread(thread, frameDepth, nullptr);
321     if (!ret) {
322         return ret.Error();
323     }
324 
325     Frame *frame = ret.Value();
326     if (frame->IsDynamic()) {
327         auto reg = GetVRegByPandaFrameDyn(frame, regNumber);
328         if (!reg) {
329             return reg.Error();
330         }
331 
332         auto vreg = reg.Value();
333         *result = VRegValue(vreg.GetValue());
334         return {};
335     }
336 
337     auto reg = GetVRegByPandaFrame(ret.Value(), regNumber);
338     if (!reg) {
339         return reg.Error();
340     }
341 
342     auto vreg = reg.Value();
343     *result = VRegValue(vreg.GetValue());
344     return {};
345 }
346 
SetVariable(PtThread thread,uint32_t frameDepth,int32_t regNumber,const VRegValue & value) const347 std::optional<Error> Debugger::SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
348                                            const VRegValue &value) const
349 {
350     ASSERT_MANAGED_CODE();
351     auto ret = GetPandaFrameByPtThread(thread, frameDepth, nullptr);
352     if (!ret) {
353         return ret.Error();
354     }
355 
356     if (ret.Value()->IsDynamic()) {
357         auto reg = GetVRegByPandaFrameDyn(ret.Value(), regNumber);
358         if (!reg) {
359             return reg.Error();
360         }
361 
362         auto vreg = reg.Value();
363         vreg.SetValue(value.GetValue());
364         return {};
365     }
366 
367     auto reg = GetVRegByPandaFrame(ret.Value(), regNumber);
368     if (!reg) {
369         return reg.Error();
370     }
371 
372     auto vreg = reg.Value();
373     vreg.SetValue(value.GetValue());
374     return {};
375 }
376 
GetCurrentFrame(PtThread thread) const377 Expected<std::unique_ptr<PtFrame>, Error> Debugger::GetCurrentFrame(PtThread thread) const
378 {
379     ManagedThread *managedThread = thread.GetManagedThread();
380     ASSERT(managedThread != nullptr);
381 
382     auto stack = StackWalker::Create(managedThread);
383     Method *method = stack.GetMethod();
384 
385     Frame *interpreterFrame = nullptr;
386     if (!stack.IsCFrame()) {
387         interpreterFrame = stack.GetIFrame();
388     }
389 
390     return {std::make_unique<PtDebugFrame>(method, interpreterFrame)};
391 }
392 
EnumerateFrames(PtThread thread,std::function<bool (const PtFrame &)> callback) const393 std::optional<Error> Debugger::EnumerateFrames(PtThread thread, std::function<bool(const PtFrame &)> callback) const
394 {
395     ManagedThread *managedThread = thread.GetManagedThread();
396     ASSERT(managedThread != nullptr);
397 
398     auto stack = StackWalker::Create(managedThread);
399     while (stack.HasFrame()) {
400         Method *method = stack.GetMethod();
401         Frame *frame = stack.IsCFrame() ? nullptr : stack.GetIFrame();
402         PtDebugFrame debugFrame(method, frame);
403         if (!callback(debugFrame)) {
404             break;
405         }
406         stack.NextFrame();
407     }
408 
409     return {};
410 }
411 
SuspendThread(PtThread thread) const412 std::optional<Error> Debugger::SuspendThread(PtThread thread) const
413 {
414     ManagedThread *managedThread = thread.GetManagedThread();
415     ASSERT(managedThread != nullptr);
416 
417     if (!MTManagedThread::ThreadIsMTManagedThread(managedThread)) {
418         return Error(Error::Type::THREAD_NOT_FOUND,
419                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
420     }
421     MTManagedThread *mtManagedThread = MTManagedThread::CastFromThread(managedThread);
422     mtManagedThread->Suspend();
423 
424     return {};
425 }
426 
ResumeThread(PtThread thread) const427 std::optional<Error> Debugger::ResumeThread(PtThread thread) const
428 {
429     ManagedThread *managedThread = thread.GetManagedThread();
430     ASSERT(managedThread != nullptr);
431 
432     if (!MTManagedThread::ThreadIsMTManagedThread(managedThread)) {
433         return Error(Error::Type::THREAD_NOT_FOUND,
434                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
435     }
436     MTManagedThread *mtManagedThread = MTManagedThread::CastFromThread(managedThread);
437     mtManagedThread->Resume();
438 
439     return {};
440 }
441 
RestartFrame(PtThread thread,uint32_t frameNumber) const442 std::optional<Error> Debugger::RestartFrame(PtThread thread, uint32_t frameNumber) const
443 {
444     ManagedThread *managedThread = thread.GetManagedThread();
445     ASSERT(managedThread != nullptr);
446 
447     if (!managedThread->IsUserSuspended()) {
448         return Error(Error::Type::THREAD_NOT_SUSPENDED,
449                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
450     }
451 
452     auto stack = StackWalker::Create(managedThread);
453     ark::Frame *popFrame = nullptr;
454     ark::Frame *retryFrame = nullptr;
455     uint32_t currentFrameNumber = 0;
456 
457     while (stack.HasFrame()) {
458         if (stack.IsCFrame()) {
459             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
460                                                         ", frame at depth is executing a native method");
461         }
462         if (currentFrameNumber == frameNumber) {
463             popFrame = stack.GetIFrame();
464         } else if (currentFrameNumber == (frameNumber + 1)) {
465             retryFrame = stack.GetIFrame();
466             break;
467         }
468         ++currentFrameNumber;
469         stack.NextFrame();
470     }
471 
472     if (popFrame == nullptr) {
473         return Error(Error::Type::FRAME_NOT_FOUND, std::string("Thread ") + std::to_string(thread.GetId()) +
474                                                        " doesn't have managed frame with number " +
475                                                        std::to_string(frameNumber));
476     }
477 
478     if (retryFrame == nullptr) {
479         return Error(Error::Type::NO_MORE_FRAMES, std::string("Thread ") + std::to_string(thread.GetId()) +
480                                                       " does not have more than one frame on the call stack");
481     }
482 
483     // Set force pop frames from top to target
484     stack.Reset(managedThread);
485     while (stack.HasFrame()) {
486         ark::Frame *frame = stack.GetIFrame();
487         frame->SetForcePop();
488         if (frame == popFrame) {
489             break;
490         }
491         stack.NextFrame();
492     }
493     retryFrame->SetRetryInstruction();
494 
495     return {};
496 }
497 
NotifyFramePop(PtThread thread,uint32_t depth) const498 std::optional<Error> Debugger::NotifyFramePop(PtThread thread, uint32_t depth) const
499 {
500     ManagedThread *managedThread = thread.GetManagedThread();
501     ASSERT(managedThread != nullptr);
502 
503     /* NOTE: (cmd) the second NotifyFramePop is error. use one debugger instance to resolve this.
504     if (!mt_managed_thread->IsUserSuspended()) {
505         return Error(Error::Type::THREAD_NOT_SUSPENDED,
506                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
507     }
508     */
509 
510     bool isNative = false;
511     ark::Frame *popFrame = GetPandaFrame(managedThread, depth, &isNative);
512     if (popFrame == nullptr) {
513         if (isNative) {
514             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
515                                                         ", frame at depth is executing a native method");
516         }
517 
518         return Error(Error::Type::NO_MORE_FRAMES,
519                      std::string("Thread ") + std::to_string(thread.GetId()) +
520                          ", are no stack frames at the specified depth: " + std::to_string(depth));
521     }
522 
523     popFrame->SetNotifyPop();
524     return {};
525 }
526 
BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bcOffset)527 void Debugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset)
528 {
529     ASSERT(bcOffset < method->GetCodeSize() && "code size of current method less then bc_offset");
530     PtLocation location(method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset);
531 
532     // Step event is reported before breakpoint, according to the spec.
533     HandleStep(thread, method, location);
534     HandleBreakpoint(thread, method, location);
535 
536     if (IsPropertyWatchActive()) {
537         if (!HandlePropertyAccess(thread, method, location)) {
538             HandlePropertyModify(thread, method, location);
539         }
540     }
541 }
542 
ObjectAlloc(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)543 void Debugger::ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size)
544 {
545     if (!vmStarted_) {
546         return;
547     }
548     if (thread == nullptr) {
549         thread = ManagedThread::GetCurrent();
550         if (thread == nullptr) {
551             return;
552         }
553     }
554 
555     hooks_.ObjectAlloc(klass, object, PtThread(thread), size);
556 }
557 
MethodEntry(ManagedThread * managedThread,Method * method)558 void Debugger::MethodEntry(ManagedThread *managedThread, Method *method)
559 {
560     hooks_.MethodEntry(PtThread(managedThread), method);
561 }
562 
MethodExit(ManagedThread * managedThread,Method * method)563 void Debugger::MethodExit(ManagedThread *managedThread, Method *method)
564 {
565     bool isExceptionTriggered = managedThread->HasPendingException();
566     VRegValue retValue(managedThread->GetCurrentFrame()->GetAcc().GetValue());
567     hooks_.MethodExit(PtThread(managedThread), method, isExceptionTriggered, retValue);
568 
569     HandleNotifyFramePop(managedThread, method, isExceptionTriggered);
570 }
571 
ClassLoad(Class * klass)572 void Debugger::ClassLoad(Class *klass)
573 {
574     auto *thread = Thread::GetCurrent();
575     if (!vmStarted_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
576         return;
577     }
578 
579     hooks_.ClassLoad(PtThread(ManagedThread::CastFromThread(thread)), klass);
580 }
581 
ClassPrepare(Class * klass)582 void Debugger::ClassPrepare(Class *klass)
583 {
584     auto *thread = Thread::GetCurrent();
585     if (!vmStarted_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
586         return;
587     }
588 
589     hooks_.ClassPrepare(PtThread(ManagedThread::CastFromThread(thread)), klass);
590 }
591 
MonitorWait(ObjectHeader * object,int64_t timeout)592 void Debugger::MonitorWait(ObjectHeader *object, int64_t timeout)
593 {
594     hooks_.MonitorWait(PtThread(ManagedThread::GetCurrent()), object, timeout);
595 }
596 
MonitorWaited(ObjectHeader * object,bool timedOut)597 void Debugger::MonitorWaited(ObjectHeader *object, bool timedOut)
598 {
599     hooks_.MonitorWaited(PtThread(ManagedThread::GetCurrent()), object, timedOut);
600 }
601 
MonitorContendedEnter(ObjectHeader * object)602 void Debugger::MonitorContendedEnter(ObjectHeader *object)
603 {
604     hooks_.MonitorContendedEnter(PtThread(ManagedThread::GetCurrent()), object);
605 }
606 
MonitorContendedEntered(ObjectHeader * object)607 void Debugger::MonitorContendedEntered(ObjectHeader *object)
608 {
609     hooks_.MonitorContendedEntered(PtThread(ManagedThread::GetCurrent()), object);
610 }
611 
HandleBreakpoint(ManagedThread * managedThread,Method * method,const PtLocation & location)612 bool Debugger::HandleBreakpoint(ManagedThread *managedThread, Method *method, const PtLocation &location)
613 {
614     {
615         os::memory::ReadLockHolder rholder(rwlock_);
616         if (!IsBreakpoint(location)) {
617             return false;
618         }
619     }
620 
621     hooks_.Breakpoint(PtThread(managedThread), method, location);
622     return true;
623 }
624 
ExceptionThrow(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)625 void Debugger::ExceptionThrow(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
626 {
627     ASSERT(thread->HasPendingException());
628     HandleScope<ObjectHeader *> scope(thread);
629     VMHandle<ObjectHeader> handle(thread, exceptionObject);
630 
631     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*method);
632     std::pair<Method *, uint32_t> res = ctx.GetCatchMethodAndOffset(method, thread);
633     auto *catchMethodFile = res.first->GetPandaFile();
634 
635     PtLocation throwLocation {method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset};
636     PtLocation catchLocation {catchMethodFile->GetFilename().c_str(), res.first->GetFileId(), res.second};
637 
638     hooks_.Exception(PtThread(thread), method, throwLocation, handle.GetPtr(), res.first, catchLocation);
639 }
640 
ExceptionCatch(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)641 void Debugger::ExceptionCatch(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
642 {
643     ASSERT(!thread->HasPendingException());
644 
645     auto *pf = method->GetPandaFile();
646     PtLocation catchLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
647 
648     hooks_.ExceptionCatch(PtThread(thread), method, catchLocation, exceptionObject);
649 }
650 
HandleStep(ManagedThread * managedThread,Method * method,const PtLocation & location)651 bool Debugger::HandleStep(ManagedThread *managedThread, Method *method, const PtLocation &location)
652 {
653     hooks_.SingleStep(PtThread(managedThread), method, location);
654     return true;
655 }
656 
HandleNotifyFramePop(ManagedThread * managedThread,Method * method,bool wasPoppedByException)657 void Debugger::HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException)
658 {
659     ark::Frame *frame = GetPandaFrame(managedThread);
660     if (frame != nullptr && frame->IsNotifyPop()) {
661         hooks_.FramePop(PtThread(managedThread), method, wasPoppedByException);
662         frame->ClearNotifyPop();
663     }
664 }
665 
ResolveField(ManagedThread * thread,const Method * caller,const BytecodeInstruction & inst)666 static Field *ResolveField(ManagedThread *thread, const Method *caller, const BytecodeInstruction &inst)
667 {
668     auto propertyIndex = inst.GetId().AsIndex();
669     auto propertyId = caller->GetClass()->ResolveFieldIndex(propertyIndex);
670     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
671     ASSERT(classLinker);
672     ASSERT(!thread->HasPendingException());
673     auto *field = classLinker->GetField(*caller, propertyId);
674     if (UNLIKELY(field == nullptr)) {
675         // Field might be nullptr if a class was not found
676         thread->ClearException();
677     }
678     return field;
679 }
680 
HandlePropertyAccess(ManagedThread * thread,Method * method,const PtLocation & location)681 bool Debugger::HandlePropertyAccess(ManagedThread *thread, Method *method, const PtLocation &location)
682 {
683     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
684     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
685     auto opcode = inst.GetOpcode();
686     bool isStatic = false;
687 
688     switch (opcode) {
689         case BytecodeInstruction::Opcode::LDOBJ_V8_ID16:
690         case BytecodeInstruction::Opcode::LDOBJ_64_V8_ID16:
691         case BytecodeInstruction::Opcode::LDOBJ_OBJ_V8_ID16:
692             break;
693         case BytecodeInstruction::Opcode::LDSTATIC_ID16:
694         case BytecodeInstruction::Opcode::LDSTATIC_64_ID16:
695         case BytecodeInstruction::Opcode::LDSTATIC_OBJ_ID16:
696             isStatic = true;
697             break;
698         default:
699             return false;
700     }
701 
702     Field *field = ResolveField(thread, method, inst);
703     if (field == nullptr) {
704         return false;
705     }
706     Class *klass = field->GetClass();
707     ASSERT(klass);
708 
709     {
710         os::memory::ReadLockHolder rholder(rwlock_);
711         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::ACCESS) == nullptr) {
712             return false;
713         }
714     }
715 
716     PtProperty ptProperty = FieldToPtProperty(field);
717 
718     if (isStatic) {
719         hooks_.PropertyAccess(PtThread(thread), method, location, nullptr, ptProperty);
720     } else {
721         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
722         hooks_.PropertyAccess(PtThread(thread), method, location, reg.GetReference(), ptProperty);
723     }
724 
725     return true;
726 }
727 
HandlePropertyModify(ManagedThread * thread,Method * method,const PtLocation & location)728 bool Debugger::HandlePropertyModify(ManagedThread *thread, Method *method, const PtLocation &location)
729 {
730     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
731     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
732     auto opcode = inst.GetOpcode();
733     bool isStatic = false;
734 
735     switch (opcode) {
736         case BytecodeInstruction::Opcode::STOBJ_V8_ID16:
737         case BytecodeInstruction::Opcode::STOBJ_64_V8_ID16:
738         case BytecodeInstruction::Opcode::STOBJ_OBJ_V8_ID16:
739             break;
740         case BytecodeInstruction::Opcode::STSTATIC_ID16:
741         case BytecodeInstruction::Opcode::STSTATIC_64_ID16:
742         case BytecodeInstruction::Opcode::STSTATIC_OBJ_ID16:
743             isStatic = true;
744             break;
745         default:
746             return false;
747     }
748 
749     Field *field = ResolveField(thread, method, inst);
750     if (field == nullptr) {
751         return false;
752     }
753     Class *klass = field->GetClass();
754     ASSERT(klass);
755 
756     {
757         os::memory::ReadLockHolder rholder(rwlock_);
758         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::MODIFY) == nullptr) {
759             return false;
760         }
761     }
762 
763     PtProperty ptProperty = FieldToPtProperty(field);
764 
765     VRegValue value(thread->GetCurrentFrame()->GetAcc().GetValue());
766     if (isStatic) {
767         hooks_.PropertyModification(PtThread(thread), method, location, nullptr, ptProperty, value);
768     } else {
769         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
770         hooks_.PropertyModification(PtThread(thread), method, location, reg.GetReference(), ptProperty, value);
771     }
772 
773     return true;
774 }
775 
SetPropertyAccessWatch(BaseClass * klass,PtProperty property)776 std::optional<Error> Debugger::SetPropertyAccessWatch(BaseClass *klass, PtProperty property)
777 {
778     os::memory::WriteLockHolder wholder(rwlock_);
779     ASSERT(!klass->IsDynamicClass());
780     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
781     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
782     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS) != nullptr) {
783         return Error(Error::Type::INVALID_PROPERTY_ACCESS_WATCH,
784                      std::string("Invalid property access watch, already exist, ClassID: ") +
785                          std::to_string(classId.GetOffset()) +
786                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
787     }
788     propertyWatches_.emplace_back(classId, propertyId, PropertyWatch::Type::ACCESS);
789     return {};
790 }
791 
ClearPropertyAccessWatch(BaseClass * klass,PtProperty property)792 std::optional<Error> Debugger::ClearPropertyAccessWatch(BaseClass *klass, PtProperty property)
793 {
794     ASSERT(!klass->IsDynamicClass());
795     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
796     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
797     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS)) {
798         return Error(Error::Type::PROPERTY_ACCESS_WATCH_NOT_FOUND,
799                      std::string("Property access watch not found, ClassID: ") + std::to_string(classId.GetOffset()) +
800                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
801     }
802     return {};
803 }
804 
SetPropertyModificationWatch(BaseClass * klass,PtProperty property)805 std::optional<Error> Debugger::SetPropertyModificationWatch(BaseClass *klass, PtProperty property)
806 {
807     os::memory::WriteLockHolder wholder(rwlock_);
808     ASSERT(!klass->IsDynamicClass());
809     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
810     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
811     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY) != nullptr) {
812         return Error(Error::Type::INVALID_PROPERTY_MODIFY_WATCH,
813                      std::string("Invalid property modification watch, already exist, ClassID: ") +
814                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
815     }
816     propertyWatches_.emplace_back(classId, propertyId, PropertyWatch::Type::MODIFY);
817     return {};
818 }
819 
ClearPropertyModificationWatch(BaseClass * klass,PtProperty property)820 std::optional<Error> Debugger::ClearPropertyModificationWatch(BaseClass *klass, PtProperty property)
821 {
822     ASSERT(!klass->IsDynamicClass());
823     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
824     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
825     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY)) {
826         return Error(Error::Type::PROPERTY_MODIFY_WATCH_NOT_FOUND,
827                      std::string("Property modification watch not found, ClassID: ") +
828                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
829     }
830     return {};
831 }
832 
IsBreakpoint(const PtLocation & location) const833 bool Debugger::IsBreakpoint(const PtLocation &location) const REQUIRES_SHARED(rwlock_)
834 {
835     auto it = breakpoints_.find(location);
836     return it != breakpoints_.end();
837 }
838 
EraseBreakpoint(const PtLocation & location)839 bool Debugger::EraseBreakpoint(const PtLocation &location)
840 {
841     os::memory::WriteLockHolder wholder(rwlock_);
842     auto it = breakpoints_.find(location);
843     if (it != breakpoints_.end()) {
844         breakpoints_.erase(it);
845         return true;
846     }
847     return false;
848 }
849 
FindPropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type) const850 const tooling::PropertyWatch *Debugger::FindPropertyWatch(panda_file::File::EntityId classId,
851                                                           panda_file::File::EntityId fieldId,
852                                                           tooling::PropertyWatch::Type type) const
853     REQUIRES_SHARED(rwlock_)
854 {
855     for (const auto &pw : propertyWatches_) {
856         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
857             return &pw;
858         }
859     }
860 
861     return nullptr;
862 }
863 
RemovePropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type)864 bool Debugger::RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId,
865                                    tooling::PropertyWatch::Type type)
866 {
867     os::memory::WriteLockHolder wholder(rwlock_);
868     auto it = propertyWatches_.begin();
869     while (it != propertyWatches_.end()) {
870         const auto &pw = *it;
871         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
872             propertyWatches_.erase(it);
873             return true;
874         }
875 
876         it++;
877     }
878 
879     return false;
880 }
881 
882 template <class VRegRef>
GetVRegValue(VRegRef reg)883 static uint64_t GetVRegValue(VRegRef reg)
884 {
885     return reg.HasObject() ? reinterpret_cast<uintptr_t>(reg.GetReference()) : reg.GetLong();
886 }
887 
888 template <class VRegRef>
GetVRegKind(VRegRef reg)889 static PtFrame::RegisterKind GetVRegKind([[maybe_unused]] VRegRef reg)
890 {
891     if constexpr (std::is_same<VRegRef, interpreter::DynamicVRegisterRef>::value) {
892         return PtFrame::RegisterKind::TAGGED;
893     } else {
894         return reg.HasObject() ? PtFrame::RegisterKind::REFERENCE : PtFrame::RegisterKind::PRIMITIVE;
895     }
896 }
897 
898 template <class FrameHandler>
FillRegisters(Frame * interpreterFrame,PandaVector<uint64_t> & vregs,PandaVector<PtFrame::RegisterKind> & vregKinds,size_t nregs,PandaVector<uint64_t> & args,PandaVector<PtFrame::RegisterKind> & argKinds,size_t nargs,uint64_t & acc,PtFrame::RegisterKind & accKind)899 static void FillRegisters(Frame *interpreterFrame, PandaVector<uint64_t> &vregs,
900                           PandaVector<PtFrame::RegisterKind> &vregKinds, size_t nregs, PandaVector<uint64_t> &args,
901                           PandaVector<PtFrame::RegisterKind> &argKinds, size_t nargs, uint64_t &acc,
902                           PtFrame::RegisterKind &accKind)
903 {
904     FrameHandler frameHandler(interpreterFrame);
905 
906     vregs.reserve(nregs);
907     vregKinds.reserve(nregs);
908     for (size_t i = 0; i < nregs; i++) {
909         auto vregReg = frameHandler.GetVReg(i);
910         vregs.push_back(GetVRegValue(vregReg));
911         vregKinds.push_back(GetVRegKind(vregReg));
912     }
913 
914     args.reserve(nargs);
915     argKinds.reserve(nargs);
916     for (size_t i = 0; i < nargs; i++) {
917         auto argReg = frameHandler.GetVReg(i + nregs);
918         args.push_back(GetVRegValue(argReg));
919         argKinds.push_back(GetVRegKind(argReg));
920     }
921 
922     auto accReg = frameHandler.GetAccAsVReg();
923     acc = GetVRegValue(accReg);
924     accKind = GetVRegKind(accReg);
925 }
926 
927 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
PtDebugFrame(Method * method,Frame * interpreterFrame)928 PtDebugFrame::PtDebugFrame(Method *method, Frame *interpreterFrame)
929     : method_(method), methodId_(method->GetFileId()), pandaFile_(method->GetPandaFile()->GetFilename())
930 {
931     isInterpreterFrame_ = interpreterFrame != nullptr;
932     if (!isInterpreterFrame_) {
933         return;
934     }
935 
936     size_t nregs = method->GetNumVregs();
937     size_t nargs = method->GetNumArgs();
938     if (interpreterFrame->IsDynamic()) {
939         FillRegisters<DynamicFrameHandler>(interpreterFrame, vregs_, vregKinds_, nregs, args_, argKinds_, nargs, acc_,
940                                            accKind_);
941     } else {
942         FillRegisters<StaticFrameHandler>(interpreterFrame, vregs_, vregKinds_, nregs, args_, argKinds_, nargs, acc_,
943                                           accKind_);
944     }
945 
946     bcOffset_ = interpreterFrame->GetBytecodeOffset();
947 }
948 
949 }  // namespace ark::tooling
950