• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 panda::tooling {
39 // TODO(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 *managed_thread = thread.GetManagedThread();
60         if (enable) {
61             managed_thread->GetPtThreadInfo()->GetHookTypeInfo().Enable(hookType);
62         } else {
63             managed_thread->GetPtThreadInfo()->GetHookTypeInfo().Disable(hookType);
64         }
65     }
66 
67     return {};
68 }
69 
CheckLocationInClass(const panda_file::File & pf,panda_file::File::EntityId class_id,const PtLocation & location,std::optional<Error> & error)70 static bool CheckLocationInClass(const panda_file::File &pf, panda_file::File::EntityId class_id,
71                                  const PtLocation &location, std::optional<Error> &error)
72 {
73     panda_file::ClassDataAccessor cda(pf, class_id);
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 code_id = mda.GetCodeId();
79             uint32_t code_size = 0;
80             if (code_id.has_value()) {
81                 panda_file::CodeDataAccessor code_da(pf, *code_id);
82                 code_size = code_da.GetCodeSize();
83             }
84             if (location.GetBytecodeOffset() >= code_size) {
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(code_size) + ")");
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 panda::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     panda::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 panda::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(panda::Frame * frame)188 static interpreter::StaticVRegisterRef GetThisAddrVRegByPandaFrame(panda::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(panda::Frame * frame)196 static interpreter::DynamicVRegisterRef GetThisAddrVRegByPandaFrameDyn(panda::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<panda::Frame *, Error> GetPandaFrameByPtThread(PtThread thread, uint32_t frameDepth,
206                                                         Callback nativeFrameHandler)
207 {
208     ManagedThread *managed_thread = thread.GetManagedThread();
209     ASSERT(managed_thread != nullptr);
210 
211     if (MTManagedThread::ThreadIsMTManagedThread(managed_thread)) {
212         // Check if thread is suspended
213         MTManagedThread *mt_managed_thread = MTManagedThread::CastFromThread(managed_thread);
214         if (MTManagedThread::GetCurrent() != mt_managed_thread && !mt_managed_thread->IsUserSuspended()) {
215             return Unexpected(Error(Error::Type::THREAD_NOT_SUSPENDED,
216                                     std::string("Thread " + std::to_string(thread.GetId()) + " is not suspended")));
217         }
218     }
219 
220     auto stack = StackWalker::Create(managed_thread);
221     panda::Frame *frame = GetPandaFrame(&stack, frameDepth, nullptr);
222     if (frame == nullptr) {
223         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
224         if constexpr (!std::is_same_v<decltype(nativeFrameHandler), std::nullptr_t>) {
225             nativeFrameHandler(&stack);
226         }
227         return Unexpected(Error(Error::Type::FRAME_NOT_FOUND,
228                                 std::string("Frame not found or native, threadId=" + std::to_string(thread.GetId()) +
229                                             " frameDepth=" + std::to_string(frameDepth))));
230     }
231     return frame;
232 }
233 
GetVRegByPandaFrame(panda::Frame * frame,int32_t regNumber) const234 Expected<interpreter::StaticVRegisterRef, Error> Debugger::GetVRegByPandaFrame(panda::Frame *frame,
235                                                                                int32_t regNumber) const
236 {
237     if (regNumber == -1) {
238         return frame->GetAccAsVReg();
239     }
240 
241     if (regNumber >= 0 && uint32_t(regNumber) < frame->GetSize()) {
242         return StaticFrameHandler(frame).GetVReg(uint32_t(regNumber));
243     }
244 
245     return Unexpected(
246         Error(Error::Type::INVALID_REGISTER, std::string("Invalid register number: ") + std::to_string(regNumber)));
247 }
248 
GetVRegByPandaFrameDyn(panda::Frame * frame,int32_t regNumber) const249 Expected<interpreter::DynamicVRegisterRef, Error> Debugger::GetVRegByPandaFrameDyn(panda::Frame *frame,
250                                                                                    int32_t regNumber) const
251 {
252     if (regNumber == -1) {
253         return frame->template GetAccAsVReg<true>();
254     }
255 
256     if (regNumber >= 0 && uint32_t(regNumber) < frame->GetSize()) {
257         return DynamicFrameHandler(frame).GetVReg(uint32_t(regNumber));
258     }
259 
260     return Unexpected(
261         Error(Error::Type::INVALID_REGISTER, std::string("Invalid register number: ") + std::to_string(regNumber)));
262 }
263 
GetThisVariableByFrame(PtThread thread,uint32_t frameDepth,ObjectHeader ** thisPtr)264 std::optional<Error> Debugger::GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, ObjectHeader **thisPtr)
265 {
266     ASSERT_MANAGED_CODE();
267     *thisPtr = nullptr;
268 
269     std::optional<Error> nativeError;
270 
271     auto nativeFrameHandler = [thread, &nativeError, thisPtr](StackWalker *stack) {
272         if (!stack->GetCFrame().IsNative()) {
273             return;
274         }
275         if (stack->GetCFrame().GetMethod()->IsStatic()) {
276             nativeError =
277                 Error(Error::Type::INVALID_VALUE, std::string("Static native method, no this address slot, threadId=" +
278                                                               std::to_string(thread.GetId())));
279             return;
280         }
281         stack->IterateObjects([thisPtr](auto &vreg) {
282             // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
283             if constexpr (std::is_same_v<decltype(vreg), interpreter::StaticVRegisterRef &>) {
284                 ASSERT(vreg.HasObject());
285                 *thisPtr = vreg.GetReference();
286             }
287             return false;
288         });
289     };
290     auto ret = GetPandaFrameByPtThread(thread, frameDepth, nativeFrameHandler);
291     if (nativeError) {
292         return nativeError;
293     }
294     if (*thisPtr != nullptr) {
295         // The value was set by native frame handler
296         return {};
297     }
298     if (!ret) {
299         return ret.Error();
300     }
301     Frame *frame = ret.Value();
302     if (frame->GetMethod()->IsStatic()) {
303         return Error(Error::Type::INVALID_VALUE,
304                      std::string("Static method, no this address slot, threadId=" + std::to_string(thread.GetId())));
305     }
306 
307     if (frame->IsDynamic()) {
308         auto reg = GetThisAddrVRegByPandaFrameDyn(frame);
309         *thisPtr = reg.GetReference();
310     } else {
311         auto reg = GetThisAddrVRegByPandaFrame(ret.Value());
312         *thisPtr = reg.GetReference();
313     }
314     return {};
315 }
316 
GetVariable(PtThread thread,uint32_t frameDepth,int32_t regNumber,VRegValue * result) const317 std::optional<Error> Debugger::GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
318                                            VRegValue *result) const
319 {
320     ASSERT_MANAGED_CODE();
321     auto ret = GetPandaFrameByPtThread(thread, frameDepth, nullptr);
322     if (!ret) {
323         return ret.Error();
324     }
325 
326     Frame *frame = ret.Value();
327     if (frame->IsDynamic()) {
328         auto reg = GetVRegByPandaFrameDyn(frame, regNumber);
329         if (!reg) {
330             return reg.Error();
331         }
332 
333         auto vreg = reg.Value();
334         *result = VRegValue(vreg.GetValue());
335         return {};
336     }
337 
338     auto reg = GetVRegByPandaFrame(ret.Value(), regNumber);
339     if (!reg) {
340         return reg.Error();
341     }
342 
343     auto vreg = reg.Value();
344     *result = VRegValue(vreg.GetValue());
345     return {};
346 }
347 
SetVariable(PtThread thread,uint32_t frameDepth,int32_t regNumber,const VRegValue & value) const348 std::optional<Error> Debugger::SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
349                                            const VRegValue &value) const
350 {
351     ASSERT_MANAGED_CODE();
352     auto ret = GetPandaFrameByPtThread(thread, frameDepth, nullptr);
353     if (!ret) {
354         return ret.Error();
355     }
356 
357     if (ret.Value()->IsDynamic()) {
358         auto reg = GetVRegByPandaFrameDyn(ret.Value(), regNumber);
359         if (!reg) {
360             return reg.Error();
361         }
362 
363         auto vreg = reg.Value();
364         vreg.SetValue(value.GetValue());
365         return {};
366     }
367 
368     auto reg = GetVRegByPandaFrame(ret.Value(), regNumber);
369     if (!reg) {
370         return reg.Error();
371     }
372 
373     auto vreg = reg.Value();
374     vreg.SetValue(value.GetValue());
375     return {};
376 }
377 
GetCurrentFrame(PtThread thread) const378 Expected<std::unique_ptr<PtFrame>, Error> Debugger::GetCurrentFrame(PtThread thread) const
379 {
380     ManagedThread *managed_thread = thread.GetManagedThread();
381     ASSERT(managed_thread != nullptr);
382 
383     auto stack = StackWalker::Create(managed_thread);
384     Method *method = stack.GetMethod();
385 
386     Frame *interpreter_frame = nullptr;
387     if (!stack.IsCFrame()) {
388         interpreter_frame = stack.GetIFrame();
389     }
390 
391     return {std::make_unique<PtDebugFrame>(method, interpreter_frame)};
392 }
393 
EnumerateFrames(PtThread thread,std::function<bool (const PtFrame &)> callback) const394 std::optional<Error> Debugger::EnumerateFrames(PtThread thread, std::function<bool(const PtFrame &)> callback) const
395 {
396     ManagedThread *managed_thread = thread.GetManagedThread();
397     ASSERT(managed_thread != nullptr);
398 
399     auto stack = StackWalker::Create(managed_thread);
400     while (stack.HasFrame()) {
401         Method *method = stack.GetMethod();
402         Frame *frame = stack.IsCFrame() ? nullptr : stack.GetIFrame();
403         PtDebugFrame debug_frame(method, frame);
404         if (!callback(debug_frame)) {
405             break;
406         }
407         stack.NextFrame();
408     }
409 
410     return {};
411 }
412 
SuspendThread(PtThread thread) const413 std::optional<Error> Debugger::SuspendThread(PtThread thread) const
414 {
415     ManagedThread *managed_thread = thread.GetManagedThread();
416     ASSERT(managed_thread != nullptr);
417 
418     if (!MTManagedThread::ThreadIsMTManagedThread(managed_thread)) {
419         return Error(Error::Type::THREAD_NOT_FOUND,
420                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
421     }
422     MTManagedThread *mt_managed_thread = MTManagedThread::CastFromThread(managed_thread);
423     mt_managed_thread->Suspend();
424 
425     return {};
426 }
427 
ResumeThread(PtThread thread) const428 std::optional<Error> Debugger::ResumeThread(PtThread thread) const
429 {
430     ManagedThread *managed_thread = thread.GetManagedThread();
431     ASSERT(managed_thread != nullptr);
432 
433     if (!MTManagedThread::ThreadIsMTManagedThread(managed_thread)) {
434         return Error(Error::Type::THREAD_NOT_FOUND,
435                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
436     }
437     MTManagedThread *mt_managed_thread = MTManagedThread::CastFromThread(managed_thread);
438     mt_managed_thread->Resume();
439 
440     return {};
441 }
442 
RestartFrame(PtThread thread,uint32_t frameNumber) const443 std::optional<Error> Debugger::RestartFrame(PtThread thread, uint32_t frameNumber) const
444 {
445     ManagedThread *managed_thread = thread.GetManagedThread();
446     ASSERT(managed_thread != nullptr);
447 
448     if (!MTManagedThread::ThreadIsMTManagedThread(managed_thread)) {
449         // TODO(maksenov): Support it for non-MT thread
450         return Error(Error::Type::THREAD_NOT_FOUND,
451                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
452     }
453 
454     MTManagedThread *mt_managed_thread = MTManagedThread::CastFromThread(managed_thread);
455     if (!mt_managed_thread->IsUserSuspended()) {
456         return Error(Error::Type::THREAD_NOT_SUSPENDED,
457                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
458     }
459 
460     auto stack = StackWalker::Create(managed_thread);
461     panda::Frame *popFrame = nullptr;
462     panda::Frame *retryFrame = nullptr;
463     uint32_t currentFrameNumber = 0;
464 
465     while (stack.HasFrame()) {
466         if (stack.IsCFrame()) {
467             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
468                                                         ", frame at depth is executing a native method");
469         }
470         if (currentFrameNumber == frameNumber) {
471             popFrame = stack.GetIFrame();
472         } else if (currentFrameNumber == (frameNumber + 1)) {
473             retryFrame = stack.GetIFrame();
474             break;
475         }
476         ++currentFrameNumber;
477         stack.NextFrame();
478     }
479 
480     if (popFrame == nullptr) {
481         return Error(Error::Type::FRAME_NOT_FOUND, std::string("Thread ") + std::to_string(thread.GetId()) +
482                                                        " doesn't have managed frame with number " +
483                                                        std::to_string(frameNumber));
484     }
485 
486     if (retryFrame == nullptr) {
487         return Error(Error::Type::NO_MORE_FRAMES, std::string("Thread ") + std::to_string(thread.GetId()) +
488                                                       " does not have more than one frame on the call stack");
489     }
490 
491     // Set force pop frames from top to target
492     stack.Reset(managed_thread);
493     while (stack.HasFrame()) {
494         panda::Frame *frame = stack.GetIFrame();
495         frame->SetForcePop();
496         if (frame == popFrame) {
497             break;
498         }
499         stack.NextFrame();
500     }
501     retryFrame->SetRetryInstruction();
502 
503     return {};
504 }
505 
NotifyFramePop(PtThread thread,uint32_t depth) const506 std::optional<Error> Debugger::NotifyFramePop(PtThread thread, uint32_t depth) const
507 {
508     ManagedThread *managed_thread = thread.GetManagedThread();
509     ASSERT(managed_thread != nullptr);
510 
511     /* TODO: (cmd) the second NotifyFramePop is error. use one debugger instance to resolve this.
512     if (!mt_managed_thread->IsUserSuspended()) {
513         return Error(Error::Type::THREAD_NOT_SUSPENDED,
514                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
515     }
516     */
517 
518     bool isNative = false;
519     panda::Frame *popFrame = GetPandaFrame(managed_thread, depth, &isNative);
520     if (popFrame == nullptr) {
521         if (isNative) {
522             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
523                                                         ", frame at depth is executing a native method");
524         }
525 
526         return Error(Error::Type::NO_MORE_FRAMES,
527                      std::string("Thread ") + std::to_string(thread.GetId()) +
528                          ", are no stack frames at the specified depth: " + std::to_string(depth));
529     }
530 
531     popFrame->SetNotifyPop();
532     return {};
533 }
534 
BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bcOffset)535 void Debugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset)
536 {
537     ASSERT(bcOffset < method->GetCodeSize() && "code size of current method less then bcOffset");
538     PtLocation location(method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset);
539 
540     // Step event is reported before breakpoint, according to the spec.
541     HandleStep(thread, method, location);
542     HandleBreakpoint(thread, method, location);
543 
544     if (IsPropertyWatchActive()) {
545         if (!HandlePropertyAccess(thread, method, location)) {
546             HandlePropertyModify(thread, method, location);
547         }
548     }
549 }
550 
ObjectAlloc(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)551 void Debugger::ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size)
552 {
553     if (!vm_started_) {
554         return;
555     }
556     if (thread == nullptr) {
557         thread = ManagedThread::GetCurrent();
558         if (thread == nullptr) {
559             return;
560         }
561     }
562 
563     hooks_.ObjectAlloc(klass, object, PtThread(thread), size);
564 }
565 
MethodEntry(ManagedThread * managedThread,Method * method)566 void Debugger::MethodEntry(ManagedThread *managedThread, Method *method)
567 {
568     hooks_.MethodEntry(PtThread(managedThread), method);
569 }
570 
MethodExit(ManagedThread * managedThread,Method * method)571 void Debugger::MethodExit(ManagedThread *managedThread, Method *method)
572 {
573     bool isExceptionTriggered = managedThread->HasPendingException();
574     VRegValue retValue(managedThread->GetCurrentFrame()->GetAcc().GetValue());
575     hooks_.MethodExit(PtThread(managedThread), method, isExceptionTriggered, retValue);
576 
577     HandleNotifyFramePop(managedThread, method, isExceptionTriggered);
578 }
579 
ClassLoad(Class * klass)580 void Debugger::ClassLoad(Class *klass)
581 {
582     auto *thread = Thread::GetCurrent();
583     if (!vm_started_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
584         return;
585     }
586 
587     hooks_.ClassLoad(PtThread(ManagedThread::CastFromThread(thread)), klass);
588 }
589 
ClassPrepare(Class * klass)590 void Debugger::ClassPrepare(Class *klass)
591 {
592     auto *thread = Thread::GetCurrent();
593     if (!vm_started_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
594         return;
595     }
596 
597     hooks_.ClassPrepare(PtThread(ManagedThread::CastFromThread(thread)), klass);
598 }
599 
MonitorWait(ObjectHeader * object,int64_t timeout)600 void Debugger::MonitorWait(ObjectHeader *object, int64_t timeout)
601 {
602     hooks_.MonitorWait(PtThread(ManagedThread::GetCurrent()), object, timeout);
603 }
604 
MonitorWaited(ObjectHeader * object,bool timedOut)605 void Debugger::MonitorWaited(ObjectHeader *object, bool timedOut)
606 {
607     hooks_.MonitorWaited(PtThread(ManagedThread::GetCurrent()), object, timedOut);
608 }
609 
MonitorContendedEnter(ObjectHeader * object)610 void Debugger::MonitorContendedEnter(ObjectHeader *object)
611 {
612     hooks_.MonitorContendedEnter(PtThread(ManagedThread::GetCurrent()), object);
613 }
614 
MonitorContendedEntered(ObjectHeader * object)615 void Debugger::MonitorContendedEntered(ObjectHeader *object)
616 {
617     hooks_.MonitorContendedEntered(PtThread(ManagedThread::GetCurrent()), object);
618 }
619 
HandleBreakpoint(ManagedThread * managedThread,Method * method,const PtLocation & location)620 bool Debugger::HandleBreakpoint(ManagedThread *managedThread, Method *method, const PtLocation &location)
621 {
622     {
623         os::memory::ReadLockHolder rholder(rwlock_);
624         if (!IsBreakpoint(location)) {
625             return false;
626         }
627     }
628 
629     hooks_.Breakpoint(PtThread(managedThread), method, location);
630     return true;
631 }
632 
ExceptionThrow(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)633 void Debugger::ExceptionThrow(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
634 {
635     ASSERT(thread->HasPendingException());
636     HandleScope<ObjectHeader *> scope(thread);
637     VMHandle<ObjectHeader> handle(thread, exceptionObject);
638 
639     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*method);
640     std::pair<Method *, uint32_t> res = ctx.GetCatchMethodAndOffset(method, thread);
641     auto *catchMethodFile = res.first->GetPandaFile();
642 
643     PtLocation throwLocation {method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset};
644     PtLocation catchLocation {catchMethodFile->GetFilename().c_str(), res.first->GetFileId(), res.second};
645 
646     hooks_.Exception(PtThread(thread), method, throwLocation, handle.GetPtr(), res.first, catchLocation);
647 }
648 
ExceptionCatch(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)649 void Debugger::ExceptionCatch(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
650 {
651     ASSERT(!thread->HasPendingException());
652 
653     auto *pf = method->GetPandaFile();
654     PtLocation catchLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
655 
656     hooks_.ExceptionCatch(PtThread(thread), method, catchLocation, exceptionObject);
657 }
658 
HandleStep(ManagedThread * managedThread,Method * method,const PtLocation & location)659 bool Debugger::HandleStep(ManagedThread *managedThread, Method *method, const PtLocation &location)
660 {
661     hooks_.SingleStep(PtThread(managedThread), method, location);
662     return true;
663 }
664 
HandleNotifyFramePop(ManagedThread * managedThread,Method * method,bool wasPoppedByException)665 void Debugger::HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException)
666 {
667     panda::Frame *frame = GetPandaFrame(managedThread);
668     if (frame != nullptr && frame->IsNotifyPop()) {
669         hooks_.FramePop(PtThread(managedThread), method, wasPoppedByException);
670         frame->ClearNotifyPop();
671     }
672 }
673 
ResolveField(ManagedThread * thread,const Method * caller,const BytecodeInstruction & inst)674 static Field *ResolveField(ManagedThread *thread, const Method *caller, const BytecodeInstruction &inst)
675 {
676     auto propertyIndex = inst.GetId().AsIndex();
677     auto propertyId = caller->GetClass()->ResolveFieldIndex(propertyIndex);
678     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
679     ASSERT(classLinker);
680     ASSERT(!thread->HasPendingException());
681     auto *field = classLinker->GetField(*caller, propertyId);
682     if (UNLIKELY(field == nullptr)) {
683         // Field might be nullptr if a class was not found
684         thread->ClearException();
685     }
686     return field;
687 }
688 
HandlePropertyAccess(ManagedThread * thread,Method * method,const PtLocation & location)689 bool Debugger::HandlePropertyAccess(ManagedThread *thread, Method *method, const PtLocation &location)
690 {
691     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
692     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
693     auto opcode = inst.GetOpcode();
694     bool isStatic = false;
695 
696     switch (opcode) {
697         case BytecodeInstruction::Opcode::LDOBJ_V8_ID16:
698         case BytecodeInstruction::Opcode::LDOBJ_64_V8_ID16:
699         case BytecodeInstruction::Opcode::LDOBJ_OBJ_V8_ID16:
700             break;
701         case BytecodeInstruction::Opcode::LDSTATIC_ID16:
702         case BytecodeInstruction::Opcode::LDSTATIC_64_ID16:
703         case BytecodeInstruction::Opcode::LDSTATIC_OBJ_ID16:
704             isStatic = true;
705             break;
706         default:
707             return false;
708     }
709 
710     Field *field = ResolveField(thread, method, inst);
711     if (field == nullptr) {
712         return false;
713     }
714     Class *klass = field->GetClass();
715     ASSERT(klass);
716 
717     {
718         os::memory::ReadLockHolder rholder(rwlock_);
719         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::ACCESS) == nullptr) {
720             return false;
721         }
722     }
723 
724     PtProperty ptProperty = FieldToPtProperty(field);
725 
726     if (isStatic) {
727         hooks_.PropertyAccess(PtThread(thread), method, location, nullptr, ptProperty);
728     } else {
729         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
730         hooks_.PropertyAccess(PtThread(thread), method, location, reg.GetReference(), ptProperty);
731     }
732 
733     return true;
734 }
735 
HandlePropertyModify(ManagedThread * thread,Method * method,const PtLocation & location)736 bool Debugger::HandlePropertyModify(ManagedThread *thread, Method *method, const PtLocation &location)
737 {
738     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
739     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
740     auto opcode = inst.GetOpcode();
741     bool isStatic = false;
742 
743     switch (opcode) {
744         case BytecodeInstruction::Opcode::STOBJ_V8_ID16:
745         case BytecodeInstruction::Opcode::STOBJ_64_V8_ID16:
746         case BytecodeInstruction::Opcode::STOBJ_OBJ_V8_ID16:
747             break;
748         case BytecodeInstruction::Opcode::STSTATIC_ID16:
749         case BytecodeInstruction::Opcode::STSTATIC_64_ID16:
750         case BytecodeInstruction::Opcode::STSTATIC_OBJ_ID16:
751             isStatic = true;
752             break;
753         default:
754             return false;
755     }
756 
757     Field *field = ResolveField(thread, method, inst);
758     if (field == nullptr) {
759         return false;
760     }
761     Class *klass = field->GetClass();
762     ASSERT(klass);
763 
764     {
765         os::memory::ReadLockHolder rholder(rwlock_);
766         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::MODIFY) == nullptr) {
767             return false;
768         }
769     }
770 
771     PtProperty ptProperty = FieldToPtProperty(field);
772 
773     VRegValue value(thread->GetCurrentFrame()->GetAcc().GetValue());
774     if (isStatic) {
775         hooks_.PropertyModification(PtThread(thread), method, location, nullptr, ptProperty, value);
776     } else {
777         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
778         hooks_.PropertyModification(PtThread(thread), method, location, reg.GetReference(), ptProperty, value);
779     }
780 
781     return true;
782 }
783 
SetPropertyAccessWatch(BaseClass * klass,PtProperty property)784 std::optional<Error> Debugger::SetPropertyAccessWatch(BaseClass *klass, PtProperty property)
785 {
786     os::memory::WriteLockHolder wholder(rwlock_);
787     ASSERT(!klass->IsDynamicClass());
788     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
789     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
790     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS) != nullptr) {
791         return Error(Error::Type::INVALID_PROPERTY_ACCESS_WATCH,
792                      std::string("Invalid property access watch, already exist, ClassID: ") +
793                          std::to_string(classId.GetOffset()) +
794                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
795     }
796     property_watches_.emplace_back(classId, propertyId, PropertyWatch::Type::ACCESS);
797     return {};
798 }
799 
ClearPropertyAccessWatch(BaseClass * klass,PtProperty property)800 std::optional<Error> Debugger::ClearPropertyAccessWatch(BaseClass *klass, PtProperty property)
801 {
802     ASSERT(!klass->IsDynamicClass());
803     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
804     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
805     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS)) {
806         return Error(Error::Type::PROPERTY_ACCESS_WATCH_NOT_FOUND,
807                      std::string("Property access watch not found, ClassID: ") + std::to_string(classId.GetOffset()) +
808                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
809     }
810     return {};
811 }
812 
SetPropertyModificationWatch(BaseClass * klass,PtProperty property)813 std::optional<Error> Debugger::SetPropertyModificationWatch(BaseClass *klass, PtProperty property)
814 {
815     os::memory::WriteLockHolder wholder(rwlock_);
816     ASSERT(!klass->IsDynamicClass());
817     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
818     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
819     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY) != nullptr) {
820         return Error(Error::Type::INVALID_PROPERTY_MODIFY_WATCH,
821                      std::string("Invalid property modification watch, already exist, ClassID: ") +
822                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
823     }
824     property_watches_.emplace_back(classId, propertyId, PropertyWatch::Type::MODIFY);
825     return {};
826 }
827 
ClearPropertyModificationWatch(BaseClass * klass,PtProperty property)828 std::optional<Error> Debugger::ClearPropertyModificationWatch(BaseClass *klass, PtProperty property)
829 {
830     ASSERT(!klass->IsDynamicClass());
831     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
832     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
833     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY)) {
834         return Error(Error::Type::PROPERTY_MODIFY_WATCH_NOT_FOUND,
835                      std::string("Property modification watch not found, ClassID: ") +
836                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
837     }
838     return {};
839 }
840 
IsBreakpoint(const PtLocation & location) const841 bool Debugger::IsBreakpoint(const PtLocation &location) const REQUIRES_SHARED(rwlock_)
842 {
843     auto it = breakpoints_.find(location);
844     return it != breakpoints_.end();
845 }
846 
EraseBreakpoint(const PtLocation & location)847 bool Debugger::EraseBreakpoint(const PtLocation &location)
848 {
849     os::memory::WriteLockHolder wholder(rwlock_);
850     auto it = breakpoints_.find(location);
851     if (it != breakpoints_.end()) {
852         breakpoints_.erase(it);
853         return true;
854     }
855     return false;
856 }
857 
FindPropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type) const858 const tooling::PropertyWatch *Debugger::FindPropertyWatch(panda_file::File::EntityId classId,
859                                                           panda_file::File::EntityId fieldId,
860                                                           tooling::PropertyWatch::Type type) const
861     REQUIRES_SHARED(rwlock_)
862 {
863     for (const auto &pw : property_watches_) {
864         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
865             return &pw;
866         }
867     }
868 
869     return nullptr;
870 }
871 
RemovePropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type)872 bool Debugger::RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId,
873                                    tooling::PropertyWatch::Type type)
874 {
875     os::memory::WriteLockHolder wholder(rwlock_);
876     auto it = property_watches_.begin();
877     while (it != property_watches_.end()) {
878         const auto &pw = *it;
879         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
880             property_watches_.erase(it);
881             return true;
882         }
883 
884         it++;
885     }
886 
887     return false;
888 }
889 
890 template <class VRegRef>
GetVRegValue(VRegRef reg)891 static inline uint64_t GetVRegValue(VRegRef reg)
892 {
893     return reg.HasObject() ? reinterpret_cast<uintptr_t>(reg.GetReference()) : reg.GetLong();
894 }
895 
896 template <class FrameHandler>
GetAccValue(PandaVector<uint64_t> & vregs,size_t nregs,PandaVector<uint64_t> & args,size_t nargs,FrameHandler frame_handler)897 static inline uint64_t GetAccValue(PandaVector<uint64_t> &vregs, size_t nregs, PandaVector<uint64_t> &args,
898                                    size_t nargs, FrameHandler frame_handler)
899 {
900     for (size_t i = 0; i < nregs; i++) {
901         vregs.push_back(GetVRegValue(frame_handler.GetVReg(i)));
902     }
903 
904     for (size_t i = 0; i < nargs; i++) {
905         args.push_back(GetVRegValue(frame_handler.GetVReg(i + nregs)));
906     }
907 
908     return GetVRegValue(frame_handler.GetAccAsVReg());
909 }
910 
911 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
PtDebugFrame(Method * method,Frame * interpreterFrame)912 PtDebugFrame::PtDebugFrame(Method *method, Frame *interpreterFrame) : method_(method)
913 {
914     panda_file_ = method->GetPandaFile()->GetFilename();
915     method_id_ = method->GetFileId();
916 
917     is_interpreter_frame_ = interpreterFrame != nullptr;
918     if (!is_interpreter_frame_) {
919         return;
920     }
921 
922     size_t nregs = method->GetNumVregs();
923     size_t nargs = method->GetNumArgs();
924     if (interpreterFrame->IsDynamic()) {
925         acc_ = GetAccValue(vregs_, nregs, args_, nargs, DynamicFrameHandler(interpreterFrame));
926     } else {
927         acc_ = GetAccValue(vregs_, nregs, args_, nargs, StaticFrameHandler(interpreterFrame));
928     }
929 
930     bc_offset_ = interpreterFrame->GetBytecodeOffset();
931 }
932 
933 }  // namespace panda::tooling
934