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