• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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     // NOTE(dslynko, #22497): must select first non-boot context as target
456     auto *ctx = ret.Value()->GetMethod()->GetClass()->GetLoadContext();
457     auto optMethod = LoadExpressionBytecode(ctx, expr);
458     if (!optMethod) {
459         return optMethod.Error();
460     }
461 
462     *method = optMethod.Value();
463     return EvaluateExpression(thread, frameNumber, *method, result);
464 }
465 
EvaluateExpression(PtThread thread,uint32_t frameNumber,Method * method,VRegValue * result) const466 std::optional<Error> Debugger::EvaluateExpression(PtThread thread, [[maybe_unused]] uint32_t frameNumber,
467                                                   Method *method, VRegValue *result) const
468 {
469     ASSERT_MANAGED_CODE();
470     ASSERT(method != nullptr);
471     ASSERT(result != nullptr);
472     ASSERT(!thread.GetManagedThread()->HasPendingException());
473     // NOTE(dslynko): support custom frame depth
474     ASSERT(frameNumber == 0);
475 
476     auto value = method->Invoke(thread.GetManagedThread(), nullptr);
477     *result = VRegValue(value.GetAsLong());
478     return {};
479 }
480 
RestartFrame(PtThread thread,uint32_t frameNumber) const481 std::optional<Error> Debugger::RestartFrame(PtThread thread, uint32_t frameNumber) const
482 {
483     ManagedThread *managedThread = thread.GetManagedThread();
484     ASSERT(managedThread != nullptr);
485 
486     if (!managedThread->IsUserSuspended()) {
487         return Error(Error::Type::THREAD_NOT_SUSPENDED,
488                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
489     }
490 
491     auto stack = StackWalker::Create(managedThread);
492     ark::Frame *popFrame = nullptr;
493     ark::Frame *retryFrame = nullptr;
494     uint32_t currentFrameNumber = 0;
495 
496     while (stack.HasFrame()) {
497         if (stack.IsCFrame()) {
498             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
499                                                         ", frame at depth is executing a native method");
500         }
501         if (currentFrameNumber == frameNumber) {
502             popFrame = stack.GetIFrame();
503         } else if (currentFrameNumber == (frameNumber + 1)) {
504             retryFrame = stack.GetIFrame();
505             break;
506         }
507         ++currentFrameNumber;
508         stack.NextFrame();
509     }
510 
511     if (popFrame == nullptr) {
512         return Error(Error::Type::FRAME_NOT_FOUND, std::string("Thread ") + std::to_string(thread.GetId()) +
513                                                        " doesn't have managed frame with number " +
514                                                        std::to_string(frameNumber));
515     }
516 
517     if (retryFrame == nullptr) {
518         return Error(Error::Type::NO_MORE_FRAMES, std::string("Thread ") + std::to_string(thread.GetId()) +
519                                                       " does not have more than one frame on the call stack");
520     }
521 
522     // Set force pop frames from top to target
523     stack.Reset(managedThread);
524     while (stack.HasFrame()) {
525         ark::Frame *frame = stack.GetIFrame();
526         frame->SetForcePop();
527         if (frame == popFrame) {
528             break;
529         }
530         stack.NextFrame();
531     }
532     retryFrame->SetRetryInstruction();
533 
534     return {};
535 }
536 
NotifyFramePop(PtThread thread,uint32_t depth) const537 std::optional<Error> Debugger::NotifyFramePop(PtThread thread, uint32_t depth) const
538 {
539     ManagedThread *managedThread = thread.GetManagedThread();
540     ASSERT(managedThread != nullptr);
541 
542     /* NOTE: (cmd) the second NotifyFramePop is error. use one debugger instance to resolve this.
543     if (!mt_managed_thread->IsUserSuspended()) {
544         return Error(Error::Type::THREAD_NOT_SUSPENDED,
545                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
546     }
547     */
548 
549     bool isNative = false;
550     ark::Frame *popFrame = GetPandaFrame(managedThread, depth, &isNative);
551     if (popFrame == nullptr) {
552         if (isNative) {
553             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
554                                                         ", frame at depth is executing a native method");
555         }
556 
557         return Error(Error::Type::NO_MORE_FRAMES,
558                      std::string("Thread ") + std::to_string(thread.GetId()) +
559                          ", are no stack frames at the specified depth: " + std::to_string(depth));
560     }
561 
562     popFrame->SetNotifyPop();
563     return {};
564 }
565 
BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bcOffset)566 void Debugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset)
567 {
568     ASSERT(bcOffset < method->GetCodeSize() && "code size of current method less then bc_offset");
569     PtLocation location(method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset);
570 
571     // Step event is reported before breakpoint, according to the spec.
572     HandleStep(thread, method, location);
573     HandleBreakpoint(thread, method, location);
574 
575     if (IsPropertyWatchActive()) {
576         if (!HandlePropertyAccess(thread, method, location)) {
577             HandlePropertyModify(thread, method, location);
578         }
579     }
580 }
581 
ObjectAlloc(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)582 void Debugger::ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size)
583 {
584     if (!vmStarted_) {
585         return;
586     }
587     if (thread == nullptr) {
588         thread = ManagedThread::GetCurrent();
589         if (thread == nullptr) {
590             return;
591         }
592     }
593 
594     hooks_.ObjectAlloc(klass, object, PtThread(thread), size);
595 }
596 
MethodEntry(ManagedThread * managedThread,Method * method)597 void Debugger::MethodEntry(ManagedThread *managedThread, Method *method)
598 {
599     hooks_.MethodEntry(PtThread(managedThread), method);
600 }
601 
MethodExit(ManagedThread * managedThread,Method * method)602 void Debugger::MethodExit(ManagedThread *managedThread, Method *method)
603 {
604     bool isExceptionTriggered = managedThread->HasPendingException();
605     VRegValue retValue(managedThread->GetCurrentFrame()->GetAcc().GetValue());
606     hooks_.MethodExit(PtThread(managedThread), method, isExceptionTriggered, retValue);
607 
608     HandleNotifyFramePop(managedThread, method, isExceptionTriggered);
609 }
610 
ClassLoad(Class * klass)611 void Debugger::ClassLoad(Class *klass)
612 {
613     auto *thread = Thread::GetCurrent();
614     if (!vmStarted_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
615         return;
616     }
617 
618     hooks_.ClassLoad(PtThread(ManagedThread::CastFromThread(thread)), klass);
619 }
620 
ClassPrepare(Class * klass)621 void Debugger::ClassPrepare(Class *klass)
622 {
623     auto *thread = Thread::GetCurrent();
624     if (!vmStarted_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
625         return;
626     }
627 
628     hooks_.ClassPrepare(PtThread(ManagedThread::CastFromThread(thread)), klass);
629 }
630 
MonitorWait(ObjectHeader * object,int64_t timeout)631 void Debugger::MonitorWait(ObjectHeader *object, int64_t timeout)
632 {
633     hooks_.MonitorWait(PtThread(ManagedThread::GetCurrent()), object, timeout);
634 }
635 
MonitorWaited(ObjectHeader * object,bool timedOut)636 void Debugger::MonitorWaited(ObjectHeader *object, bool timedOut)
637 {
638     hooks_.MonitorWaited(PtThread(ManagedThread::GetCurrent()), object, timedOut);
639 }
640 
MonitorContendedEnter(ObjectHeader * object)641 void Debugger::MonitorContendedEnter(ObjectHeader *object)
642 {
643     hooks_.MonitorContendedEnter(PtThread(ManagedThread::GetCurrent()), object);
644 }
645 
MonitorContendedEntered(ObjectHeader * object)646 void Debugger::MonitorContendedEntered(ObjectHeader *object)
647 {
648     hooks_.MonitorContendedEntered(PtThread(ManagedThread::GetCurrent()), object);
649 }
650 
HandleBreakpoint(ManagedThread * managedThread,Method * method,const PtLocation & location)651 bool Debugger::HandleBreakpoint(ManagedThread *managedThread, Method *method, const PtLocation &location)
652 {
653     {
654         os::memory::ReadLockHolder rholder(rwlock_);
655         if (!IsBreakpoint(location)) {
656             return false;
657         }
658     }
659 
660     hooks_.Breakpoint(PtThread(managedThread), method, location);
661     return true;
662 }
663 
ExceptionThrow(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)664 void Debugger::ExceptionThrow(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
665 {
666     ASSERT(thread->HasPendingException());
667     HandleScope<ObjectHeader *> scope(thread);
668     VMHandle<ObjectHeader> handle(thread, exceptionObject);
669 
670     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*method);
671     std::pair<Method *, uint32_t> res = ctx.GetCatchMethodAndOffset(method, thread);
672     auto *catchMethodFile = res.first->GetPandaFile();
673 
674     PtLocation throwLocation {method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset};
675     PtLocation catchLocation {catchMethodFile->GetFilename().c_str(), res.first->GetFileId(), res.second};
676 
677     hooks_.Exception(PtThread(thread), method, throwLocation, handle.GetPtr(), res.first, catchLocation);
678 }
679 
ExceptionCatch(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)680 void Debugger::ExceptionCatch(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
681 {
682     ASSERT(!thread->HasPendingException());
683 
684     auto *pf = method->GetPandaFile();
685     PtLocation catchLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
686 
687     hooks_.ExceptionCatch(PtThread(thread), method, catchLocation, exceptionObject);
688 }
689 
HandleStep(ManagedThread * managedThread,Method * method,const PtLocation & location)690 bool Debugger::HandleStep(ManagedThread *managedThread, Method *method, const PtLocation &location)
691 {
692     hooks_.SingleStep(PtThread(managedThread), method, location);
693     return true;
694 }
695 
HandleNotifyFramePop(ManagedThread * managedThread,Method * method,bool wasPoppedByException)696 void Debugger::HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException)
697 {
698     ark::Frame *frame = GetPandaFrame(managedThread);
699     if (frame != nullptr && frame->IsNotifyPop()) {
700         hooks_.FramePop(PtThread(managedThread), method, wasPoppedByException);
701         frame->ClearNotifyPop();
702     }
703 }
704 
ResolveField(ManagedThread * thread,const Method * caller,const BytecodeInstruction & inst,bool isStatic)705 static Field *ResolveField(ManagedThread *thread, const Method *caller, const BytecodeInstruction &inst, bool isStatic)
706 {
707     auto propertyIndex = inst.GetId().AsIndex();
708     auto propertyId = caller->GetClass()->ResolveFieldIndex(propertyIndex);
709     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
710     ASSERT(classLinker);
711     ASSERT(!thread->HasPendingException());
712     auto *field = classLinker->GetField(*caller, propertyId, isStatic);
713     if (UNLIKELY(field == nullptr)) {
714         // Field might be nullptr if a class was not found
715         thread->ClearException();
716     }
717     return field;
718 }
719 
HandlePropertyAccess(ManagedThread * thread,Method * method,const PtLocation & location)720 bool Debugger::HandlePropertyAccess(ManagedThread *thread, Method *method, const PtLocation &location)
721 {
722     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
723     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
724     auto opcode = inst.GetOpcode();
725     bool isStatic = false;
726 
727     switch (opcode) {
728         case BytecodeInstruction::Opcode::LDOBJ_V8_ID16:
729         case BytecodeInstruction::Opcode::LDOBJ_64_V8_ID16:
730         case BytecodeInstruction::Opcode::LDOBJ_OBJ_V8_ID16:
731             break;
732         case BytecodeInstruction::Opcode::LDSTATIC_ID16:
733         case BytecodeInstruction::Opcode::LDSTATIC_64_ID16:
734         case BytecodeInstruction::Opcode::LDSTATIC_OBJ_ID16:
735             isStatic = true;
736             break;
737         default:
738             return false;
739     }
740 
741     Field *field = ResolveField(thread, method, inst, isStatic);
742     if (field == nullptr) {
743         return false;
744     }
745     Class *klass = field->GetClass();
746     ASSERT(klass);
747 
748     {
749         os::memory::ReadLockHolder rholder(rwlock_);
750         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::ACCESS) == nullptr) {
751             return false;
752         }
753     }
754 
755     PtProperty ptProperty = FieldToPtProperty(field);
756 
757     if (isStatic) {
758         hooks_.PropertyAccess(PtThread(thread), method, location, nullptr, ptProperty);
759     } else {
760         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
761         hooks_.PropertyAccess(PtThread(thread), method, location, reg.GetReference(), ptProperty);
762     }
763 
764     return true;
765 }
766 
HandlePropertyModify(ManagedThread * thread,Method * method,const PtLocation & location)767 bool Debugger::HandlePropertyModify(ManagedThread *thread, Method *method, const PtLocation &location)
768 {
769     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
770     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
771     auto opcode = inst.GetOpcode();
772     bool isStatic = false;
773 
774     switch (opcode) {
775         case BytecodeInstruction::Opcode::STOBJ_V8_ID16:
776         case BytecodeInstruction::Opcode::STOBJ_64_V8_ID16:
777         case BytecodeInstruction::Opcode::STOBJ_OBJ_V8_ID16:
778             break;
779         case BytecodeInstruction::Opcode::STSTATIC_ID16:
780         case BytecodeInstruction::Opcode::STSTATIC_64_ID16:
781         case BytecodeInstruction::Opcode::STSTATIC_OBJ_ID16:
782             isStatic = true;
783             break;
784         default:
785             return false;
786     }
787 
788     Field *field = ResolveField(thread, method, inst, isStatic);
789     if (field == nullptr) {
790         return false;
791     }
792     Class *klass = field->GetClass();
793     ASSERT(klass);
794 
795     {
796         os::memory::ReadLockHolder rholder(rwlock_);
797         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::MODIFY) == nullptr) {
798             return false;
799         }
800     }
801 
802     PtProperty ptProperty = FieldToPtProperty(field);
803 
804     VRegValue value(thread->GetCurrentFrame()->GetAcc().GetValue());
805     if (isStatic) {
806         hooks_.PropertyModification(PtThread(thread), method, location, nullptr, ptProperty, value);
807     } else {
808         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
809         hooks_.PropertyModification(PtThread(thread), method, location, reg.GetReference(), ptProperty, value);
810     }
811 
812     return true;
813 }
814 
SetPropertyAccessWatch(BaseClass * klass,PtProperty property)815 std::optional<Error> Debugger::SetPropertyAccessWatch(BaseClass *klass, PtProperty property)
816 {
817     os::memory::WriteLockHolder wholder(rwlock_);
818     ASSERT(!klass->IsDynamicClass());
819     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
820     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
821     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS) != nullptr) {
822         return Error(Error::Type::INVALID_PROPERTY_ACCESS_WATCH,
823                      std::string("Invalid property access watch, already exist, ClassID: ") +
824                          std::to_string(classId.GetOffset()) +
825                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
826     }
827     propertyWatches_.emplace_back(classId, propertyId, PropertyWatch::Type::ACCESS);
828     return {};
829 }
830 
ClearPropertyAccessWatch(BaseClass * klass,PtProperty property)831 std::optional<Error> Debugger::ClearPropertyAccessWatch(BaseClass *klass, PtProperty property)
832 {
833     ASSERT(!klass->IsDynamicClass());
834     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
835     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
836     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS)) {
837         return Error(Error::Type::PROPERTY_ACCESS_WATCH_NOT_FOUND,
838                      std::string("Property access watch not found, ClassID: ") + std::to_string(classId.GetOffset()) +
839                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
840     }
841     return {};
842 }
843 
SetPropertyModificationWatch(BaseClass * klass,PtProperty property)844 std::optional<Error> Debugger::SetPropertyModificationWatch(BaseClass *klass, PtProperty property)
845 {
846     os::memory::WriteLockHolder wholder(rwlock_);
847     ASSERT(!klass->IsDynamicClass());
848     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
849     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
850     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY) != nullptr) {
851         return Error(Error::Type::INVALID_PROPERTY_MODIFY_WATCH,
852                      std::string("Invalid property modification watch, already exist, ClassID: ") +
853                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
854     }
855     propertyWatches_.emplace_back(classId, propertyId, PropertyWatch::Type::MODIFY);
856     return {};
857 }
858 
ClearPropertyModificationWatch(BaseClass * klass,PtProperty property)859 std::optional<Error> Debugger::ClearPropertyModificationWatch(BaseClass *klass, PtProperty property)
860 {
861     ASSERT(!klass->IsDynamicClass());
862     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
863     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
864     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY)) {
865         return Error(Error::Type::PROPERTY_MODIFY_WATCH_NOT_FOUND,
866                      std::string("Property modification watch not found, ClassID: ") +
867                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
868     }
869     return {};
870 }
871 
IsBreakpoint(const PtLocation & location) const872 bool Debugger::IsBreakpoint(const PtLocation &location) const REQUIRES_SHARED(rwlock_)
873 {
874     auto it = breakpoints_.find(location);
875     return it != breakpoints_.end();
876 }
877 
EraseBreakpoint(const PtLocation & location)878 bool Debugger::EraseBreakpoint(const PtLocation &location)
879 {
880     os::memory::WriteLockHolder wholder(rwlock_);
881     auto it = breakpoints_.find(location);
882     if (it != breakpoints_.end()) {
883         breakpoints_.erase(it);
884         return true;
885     }
886     return false;
887 }
888 
FindPropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type) const889 const tooling::PropertyWatch *Debugger::FindPropertyWatch(panda_file::File::EntityId classId,
890                                                           panda_file::File::EntityId fieldId,
891                                                           tooling::PropertyWatch::Type type) const
892     REQUIRES_SHARED(rwlock_)
893 {
894     for (const auto &pw : propertyWatches_) {
895         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
896             return &pw;
897         }
898     }
899 
900     return nullptr;
901 }
902 
RemovePropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type)903 bool Debugger::RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId,
904                                    tooling::PropertyWatch::Type type)
905 {
906     os::memory::WriteLockHolder wholder(rwlock_);
907     auto it = propertyWatches_.begin();
908     while (it != propertyWatches_.end()) {
909         const auto &pw = *it;
910         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
911             propertyWatches_.erase(it);
912             return true;
913         }
914 
915         it++;
916     }
917 
918     return false;
919 }
920 
921 template <class VRegRef>
GetVRegValue(VRegRef reg)922 static uint64_t GetVRegValue(VRegRef reg)
923 {
924     return reg.HasObject() ? reinterpret_cast<uintptr_t>(reg.GetReference()) : reg.GetLong();
925 }
926 
927 template <class VRegRef>
GetVRegKind(VRegRef reg)928 static PtFrame::RegisterKind GetVRegKind([[maybe_unused]] VRegRef reg)
929 {
930     if constexpr (std::is_same<VRegRef, interpreter::DynamicVRegisterRef>::value) {
931         return PtFrame::RegisterKind::TAGGED;
932     } else {
933         return reg.HasObject() ? PtFrame::RegisterKind::REFERENCE : PtFrame::RegisterKind::PRIMITIVE;
934     }
935 }
936 
937 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)938 static void FillRegisters(Frame *interpreterFrame, PandaVector<uint64_t> &vregs,
939                           PandaVector<PtFrame::RegisterKind> &vregKinds, size_t nregs, PandaVector<uint64_t> &args,
940                           PandaVector<PtFrame::RegisterKind> &argKinds, size_t nargs, uint64_t &acc,
941                           PtFrame::RegisterKind &accKind)
942 {
943     FrameHandler frameHandler(interpreterFrame);
944 
945     vregs.reserve(nregs);
946     vregKinds.reserve(nregs);
947     for (size_t i = 0; i < nregs; i++) {
948         auto vregReg = frameHandler.GetVReg(i);
949         vregs.push_back(GetVRegValue(vregReg));
950         vregKinds.push_back(GetVRegKind(vregReg));
951     }
952 
953     args.reserve(nargs);
954     argKinds.reserve(nargs);
955     for (size_t i = 0; i < nargs; i++) {
956         auto argReg = frameHandler.GetVReg(i + nregs);
957         args.push_back(GetVRegValue(argReg));
958         argKinds.push_back(GetVRegKind(argReg));
959     }
960 
961     auto accReg = frameHandler.GetAccAsVReg();
962     acc = GetVRegValue(accReg);
963     accKind = GetVRegKind(accReg);
964 }
965 
966 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
PtDebugFrame(Method * method,Frame * interpreterFrame)967 PtDebugFrame::PtDebugFrame(Method *method, Frame *interpreterFrame)
968     : method_(method), methodId_(method->GetFileId()), pandaFile_(method->GetPandaFile()->GetFilename())
969 {
970     isInterpreterFrame_ = interpreterFrame != nullptr;
971     if (!isInterpreterFrame_) {
972         return;
973     }
974 
975     size_t nregs = method->GetNumVregs();
976     size_t nargs = method->GetNumArgs();
977     if (interpreterFrame->IsDynamic()) {
978         FillRegisters<DynamicFrameHandler>(interpreterFrame, vregs_, vregKinds_, nregs, args_, argKinds_, nargs, acc_,
979                                            accKind_);
980     } else {
981         FillRegisters<StaticFrameHandler>(interpreterFrame, vregs_, vregKinds_, nregs, args_, argKinds_, nargs, acc_,
982                                           accKind_);
983     }
984 
985     bcOffset_ = interpreterFrame->GetBytecodeOffset();
986 }
987 
988 }  // namespace ark::tooling
989