• 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 // NOTE(maksenov): remove PtProperty class
FieldToPtProperty(Field * field)40 static PtProperty FieldToPtProperty(Field *field)
41 {
42     return PtProperty(field);
43 }
44 
PtPropertyToField(PtProperty property)45 static Field *PtPropertyToField(PtProperty property)
46 {
47     return reinterpret_cast<Field *>(property.GetData());
48 }
49 
SetNotification(PtThread thread,bool enable,PtHookType hookType)50 std::optional<Error> Debugger::SetNotification(PtThread thread, bool enable, PtHookType hookType)
51 {
52     if (thread == PtThread::NONE) {
53         if (enable) {
54             hooks_.EnableGlobalHook(hookType);
55         } else {
56             hooks_.DisableGlobalHook(hookType);
57         }
58     } else {
59         ManagedThread *managedThread = thread.GetManagedThread();
60         if (enable) {
61             managedThread->GetPtThreadInfo()->GetHookTypeInfo().Enable(hookType);
62         } else {
63             managedThread->GetPtThreadInfo()->GetHookTypeInfo().Disable(hookType);
64         }
65     }
66 
67     return {};
68 }
69 
CheckLocationInClass(const panda_file::File & pf,panda_file::File::EntityId classId,const PtLocation & location,std::optional<Error> & error)70 static bool CheckLocationInClass(const panda_file::File &pf, panda_file::File::EntityId classId,
71                                  const PtLocation &location, std::optional<Error> &error)
72 {
73     panda_file::ClassDataAccessor cda(pf, classId);
74     bool found = false;
75     cda.EnumerateMethods([&pf, &location, &error, &found](panda_file::MethodDataAccessor mda) {
76         if (mda.GetMethodId() == location.GetMethodId()) {
77             found = true;
78             auto codeId = mda.GetCodeId();
79             uint32_t codeSize = 0;
80             if (codeId.has_value()) {
81                 panda_file::CodeDataAccessor codeDa(pf, *codeId);
82                 codeSize = codeDa.GetCodeSize();
83             }
84             if (location.GetBytecodeOffset() >= codeSize) {
85                 error = Error(Error::Type::INVALID_BREAKPOINT,
86                               std::string("Invalid breakpoint location: bytecode offset (") +
87                                   std::to_string(location.GetBytecodeOffset()) + ") >= method code size (" +
88                                   std::to_string(codeSize) + ")");
89             }
90             return false;
91         }
92         return true;
93     });
94     return found;
95 }
96 
CheckLocation(const PtLocation & location)97 std::optional<Error> Debugger::CheckLocation(const PtLocation &location)
98 {
99     std::optional<Error> res;
100     runtime_->GetClassLinker()->EnumeratePandaFiles([&location, &res](const panda_file::File &pf) {
101         if (pf.GetFilename() != location.GetPandaFile()) {
102             return true;
103         }
104 
105         auto classes = pf.GetClasses();
106         bool found = false;
107         for (size_t i = 0; i < classes.Size(); i++) {
108             panda_file::File::EntityId id(classes[i]);
109             if (pf.IsExternal(id) || id.GetOffset() > location.GetMethodId().GetOffset()) {
110                 continue;
111             }
112 
113             found = CheckLocationInClass(pf, id, location, res);
114             if (found) {
115                 break;
116             }
117         }
118         if (!found) {
119             res =
120                 Error(Error::Type::METHOD_NOT_FOUND,
121                       std::string("Cannot find method with id ") + std::to_string(location.GetMethodId().GetOffset()) +
122                           " in panda file '" + std::string(location.GetPandaFile()) + "'");
123         }
124         return false;
125     });
126     return res;
127 }
128 
SetBreakpoint(const PtLocation & location)129 std::optional<Error> Debugger::SetBreakpoint(const PtLocation &location)
130 {
131     auto error = CheckLocation(location);
132     if (error.has_value()) {
133         return error;
134     }
135 
136     os::memory::WriteLockHolder wholder(rwlock_);
137     if (!breakpoints_.emplace(location).second) {
138         return Error(Error::Type::BREAKPOINT_ALREADY_EXISTS,
139                      std::string("Breakpoint already exists: bytecode offset ") +
140                          std::to_string(location.GetBytecodeOffset()));
141     }
142 
143     return {};
144 }
145 
RemoveBreakpoint(const PtLocation & location)146 std::optional<Error> Debugger::RemoveBreakpoint(const PtLocation &location)
147 {
148     if (!EraseBreakpoint(location)) {
149         return Error(Error::Type::BREAKPOINT_NOT_FOUND, "Breakpoint not found");
150     }
151 
152     return {};
153 }
154 
GetPandaFrame(StackWalker * pstack,uint32_t frameDepth,bool * outIsNative=nullptr)155 static 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 *managedThread = thread.GetManagedThread();
209     ASSERT(managedThread != nullptr);
210 
211     if (MTManagedThread::ThreadIsMTManagedThread(managedThread)) {
212         // Check if thread is suspended
213         MTManagedThread *mtManagedThread = MTManagedThread::CastFromThread(managedThread);
214         if (MTManagedThread::GetCurrent() != mtManagedThread && !mtManagedThread->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(managedThread);
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 *managedThread = thread.GetManagedThread();
381     ASSERT(managedThread != nullptr);
382 
383     auto stack = StackWalker::Create(managedThread);
384     Method *method = stack.GetMethod();
385 
386     Frame *interpreterFrame = nullptr;
387     if (!stack.IsCFrame()) {
388         interpreterFrame = stack.GetIFrame();
389     }
390 
391     return {std::make_unique<PtDebugFrame>(method, interpreterFrame)};
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 *managedThread = thread.GetManagedThread();
397     ASSERT(managedThread != nullptr);
398 
399     auto stack = StackWalker::Create(managedThread);
400     while (stack.HasFrame()) {
401         Method *method = stack.GetMethod();
402         Frame *frame = stack.IsCFrame() ? nullptr : stack.GetIFrame();
403         PtDebugFrame debugFrame(method, frame);
404         if (!callback(debugFrame)) {
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 *managedThread = thread.GetManagedThread();
416     ASSERT(managedThread != nullptr);
417 
418     if (!MTManagedThread::ThreadIsMTManagedThread(managedThread)) {
419         return Error(Error::Type::THREAD_NOT_FOUND,
420                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
421     }
422     MTManagedThread *mtManagedThread = MTManagedThread::CastFromThread(managedThread);
423     mtManagedThread->Suspend();
424 
425     return {};
426 }
427 
ResumeThread(PtThread thread) const428 std::optional<Error> Debugger::ResumeThread(PtThread thread) const
429 {
430     ManagedThread *managedThread = thread.GetManagedThread();
431     ASSERT(managedThread != nullptr);
432 
433     if (!MTManagedThread::ThreadIsMTManagedThread(managedThread)) {
434         return Error(Error::Type::THREAD_NOT_FOUND,
435                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not MT Thread");
436     }
437     MTManagedThread *mtManagedThread = MTManagedThread::CastFromThread(managedThread);
438     mtManagedThread->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 *managedThread = thread.GetManagedThread();
446     ASSERT(managedThread != nullptr);
447 
448     if (!managedThread->IsUserSuspended()) {
449         return Error(Error::Type::THREAD_NOT_SUSPENDED,
450                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
451     }
452 
453     auto stack = StackWalker::Create(managedThread);
454     panda::Frame *popFrame = nullptr;
455     panda::Frame *retryFrame = nullptr;
456     uint32_t currentFrameNumber = 0;
457 
458     while (stack.HasFrame()) {
459         if (stack.IsCFrame()) {
460             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
461                                                         ", frame at depth is executing a native method");
462         }
463         if (currentFrameNumber == frameNumber) {
464             popFrame = stack.GetIFrame();
465         } else if (currentFrameNumber == (frameNumber + 1)) {
466             retryFrame = stack.GetIFrame();
467             break;
468         }
469         ++currentFrameNumber;
470         stack.NextFrame();
471     }
472 
473     if (popFrame == nullptr) {
474         return Error(Error::Type::FRAME_NOT_FOUND, std::string("Thread ") + std::to_string(thread.GetId()) +
475                                                        " doesn't have managed frame with number " +
476                                                        std::to_string(frameNumber));
477     }
478 
479     if (retryFrame == nullptr) {
480         return Error(Error::Type::NO_MORE_FRAMES, std::string("Thread ") + std::to_string(thread.GetId()) +
481                                                       " does not have more than one frame on the call stack");
482     }
483 
484     // Set force pop frames from top to target
485     stack.Reset(managedThread);
486     while (stack.HasFrame()) {
487         panda::Frame *frame = stack.GetIFrame();
488         frame->SetForcePop();
489         if (frame == popFrame) {
490             break;
491         }
492         stack.NextFrame();
493     }
494     retryFrame->SetRetryInstruction();
495 
496     return {};
497 }
498 
NotifyFramePop(PtThread thread,uint32_t depth) const499 std::optional<Error> Debugger::NotifyFramePop(PtThread thread, uint32_t depth) const
500 {
501     ManagedThread *managedThread = thread.GetManagedThread();
502     ASSERT(managedThread != nullptr);
503 
504     /* NOTE: (cmd) the second NotifyFramePop is error. use one debugger instance to resolve this.
505     if (!mt_managed_thread->IsUserSuspended()) {
506         return Error(Error::Type::THREAD_NOT_SUSPENDED,
507                      std::string("Thread ") + std::to_string(thread.GetId()) + " is not suspended");
508     }
509     */
510 
511     bool isNative = false;
512     panda::Frame *popFrame = GetPandaFrame(managedThread, depth, &isNative);
513     if (popFrame == nullptr) {
514         if (isNative) {
515             return Error(Error::Type::OPAQUE_FRAME, std::string("Thread ") + std::to_string(thread.GetId()) +
516                                                         ", frame at depth is executing a native method");
517         }
518 
519         return Error(Error::Type::NO_MORE_FRAMES,
520                      std::string("Thread ") + std::to_string(thread.GetId()) +
521                          ", are no stack frames at the specified depth: " + std::to_string(depth));
522     }
523 
524     popFrame->SetNotifyPop();
525     return {};
526 }
527 
BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bcOffset)528 void Debugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset)
529 {
530     ASSERT(bcOffset < method->GetCodeSize() && "code size of current method less then bc_offset");
531     PtLocation location(method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset);
532 
533     // Step event is reported before breakpoint, according to the spec.
534     HandleStep(thread, method, location);
535     HandleBreakpoint(thread, method, location);
536 
537     if (IsPropertyWatchActive()) {
538         if (!HandlePropertyAccess(thread, method, location)) {
539             HandlePropertyModify(thread, method, location);
540         }
541     }
542 }
543 
ObjectAlloc(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)544 void Debugger::ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size)
545 {
546     if (!vmStarted_) {
547         return;
548     }
549     if (thread == nullptr) {
550         thread = ManagedThread::GetCurrent();
551         if (thread == nullptr) {
552             return;
553         }
554     }
555 
556     hooks_.ObjectAlloc(klass, object, PtThread(thread), size);
557 }
558 
MethodEntry(ManagedThread * managedThread,Method * method)559 void Debugger::MethodEntry(ManagedThread *managedThread, Method *method)
560 {
561     hooks_.MethodEntry(PtThread(managedThread), method);
562 }
563 
MethodExit(ManagedThread * managedThread,Method * method)564 void Debugger::MethodExit(ManagedThread *managedThread, Method *method)
565 {
566     bool isExceptionTriggered = managedThread->HasPendingException();
567     VRegValue retValue(managedThread->GetCurrentFrame()->GetAcc().GetValue());
568     hooks_.MethodExit(PtThread(managedThread), method, isExceptionTriggered, retValue);
569 
570     HandleNotifyFramePop(managedThread, method, isExceptionTriggered);
571 }
572 
ClassLoad(Class * klass)573 void Debugger::ClassLoad(Class *klass)
574 {
575     auto *thread = Thread::GetCurrent();
576     if (!vmStarted_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
577         return;
578     }
579 
580     hooks_.ClassLoad(PtThread(ManagedThread::CastFromThread(thread)), klass);
581 }
582 
ClassPrepare(Class * klass)583 void Debugger::ClassPrepare(Class *klass)
584 {
585     auto *thread = Thread::GetCurrent();
586     if (!vmStarted_ || thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_COMPILER) {
587         return;
588     }
589 
590     hooks_.ClassPrepare(PtThread(ManagedThread::CastFromThread(thread)), klass);
591 }
592 
MonitorWait(ObjectHeader * object,int64_t timeout)593 void Debugger::MonitorWait(ObjectHeader *object, int64_t timeout)
594 {
595     hooks_.MonitorWait(PtThread(ManagedThread::GetCurrent()), object, timeout);
596 }
597 
MonitorWaited(ObjectHeader * object,bool timedOut)598 void Debugger::MonitorWaited(ObjectHeader *object, bool timedOut)
599 {
600     hooks_.MonitorWaited(PtThread(ManagedThread::GetCurrent()), object, timedOut);
601 }
602 
MonitorContendedEnter(ObjectHeader * object)603 void Debugger::MonitorContendedEnter(ObjectHeader *object)
604 {
605     hooks_.MonitorContendedEnter(PtThread(ManagedThread::GetCurrent()), object);
606 }
607 
MonitorContendedEntered(ObjectHeader * object)608 void Debugger::MonitorContendedEntered(ObjectHeader *object)
609 {
610     hooks_.MonitorContendedEntered(PtThread(ManagedThread::GetCurrent()), object);
611 }
612 
HandleBreakpoint(ManagedThread * managedThread,Method * method,const PtLocation & location)613 bool Debugger::HandleBreakpoint(ManagedThread *managedThread, Method *method, const PtLocation &location)
614 {
615     {
616         os::memory::ReadLockHolder rholder(rwlock_);
617         if (!IsBreakpoint(location)) {
618             return false;
619         }
620     }
621 
622     hooks_.Breakpoint(PtThread(managedThread), method, location);
623     return true;
624 }
625 
ExceptionThrow(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)626 void Debugger::ExceptionThrow(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
627 {
628     ASSERT(thread->HasPendingException());
629     HandleScope<ObjectHeader *> scope(thread);
630     VMHandle<ObjectHeader> handle(thread, exceptionObject);
631 
632     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*method);
633     std::pair<Method *, uint32_t> res = ctx.GetCatchMethodAndOffset(method, thread);
634     auto *catchMethodFile = res.first->GetPandaFile();
635 
636     PtLocation throwLocation {method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), bcOffset};
637     PtLocation catchLocation {catchMethodFile->GetFilename().c_str(), res.first->GetFileId(), res.second};
638 
639     hooks_.Exception(PtThread(thread), method, throwLocation, handle.GetPtr(), res.first, catchLocation);
640 }
641 
ExceptionCatch(ManagedThread * thread,Method * method,ObjectHeader * exceptionObject,uint32_t bcOffset)642 void Debugger::ExceptionCatch(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset)
643 {
644     ASSERT(!thread->HasPendingException());
645 
646     auto *pf = method->GetPandaFile();
647     PtLocation catchLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset};
648 
649     hooks_.ExceptionCatch(PtThread(thread), method, catchLocation, exceptionObject);
650 }
651 
HandleStep(ManagedThread * managedThread,Method * method,const PtLocation & location)652 bool Debugger::HandleStep(ManagedThread *managedThread, Method *method, const PtLocation &location)
653 {
654     hooks_.SingleStep(PtThread(managedThread), method, location);
655     return true;
656 }
657 
HandleNotifyFramePop(ManagedThread * managedThread,Method * method,bool wasPoppedByException)658 void Debugger::HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException)
659 {
660     panda::Frame *frame = GetPandaFrame(managedThread);
661     if (frame != nullptr && frame->IsNotifyPop()) {
662         hooks_.FramePop(PtThread(managedThread), method, wasPoppedByException);
663         frame->ClearNotifyPop();
664     }
665 }
666 
ResolveField(ManagedThread * thread,const Method * caller,const BytecodeInstruction & inst)667 static Field *ResolveField(ManagedThread *thread, const Method *caller, const BytecodeInstruction &inst)
668 {
669     auto propertyIndex = inst.GetId().AsIndex();
670     auto propertyId = caller->GetClass()->ResolveFieldIndex(propertyIndex);
671     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
672     ASSERT(classLinker);
673     ASSERT(!thread->HasPendingException());
674     auto *field = classLinker->GetField(*caller, propertyId);
675     if (UNLIKELY(field == nullptr)) {
676         // Field might be nullptr if a class was not found
677         thread->ClearException();
678     }
679     return field;
680 }
681 
HandlePropertyAccess(ManagedThread * thread,Method * method,const PtLocation & location)682 bool Debugger::HandlePropertyAccess(ManagedThread *thread, Method *method, const PtLocation &location)
683 {
684     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
685     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
686     auto opcode = inst.GetOpcode();
687     bool isStatic = false;
688 
689     switch (opcode) {
690         case BytecodeInstruction::Opcode::LDOBJ_V8_ID16:
691         case BytecodeInstruction::Opcode::LDOBJ_64_V8_ID16:
692         case BytecodeInstruction::Opcode::LDOBJ_OBJ_V8_ID16:
693             break;
694         case BytecodeInstruction::Opcode::LDSTATIC_ID16:
695         case BytecodeInstruction::Opcode::LDSTATIC_64_ID16:
696         case BytecodeInstruction::Opcode::LDSTATIC_OBJ_ID16:
697             isStatic = true;
698             break;
699         default:
700             return false;
701     }
702 
703     Field *field = ResolveField(thread, method, inst);
704     if (field == nullptr) {
705         return false;
706     }
707     Class *klass = field->GetClass();
708     ASSERT(klass);
709 
710     {
711         os::memory::ReadLockHolder rholder(rwlock_);
712         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::ACCESS) == nullptr) {
713             return false;
714         }
715     }
716 
717     PtProperty ptProperty = FieldToPtProperty(field);
718 
719     if (isStatic) {
720         hooks_.PropertyAccess(PtThread(thread), method, location, nullptr, ptProperty);
721     } else {
722         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
723         hooks_.PropertyAccess(PtThread(thread), method, location, reg.GetReference(), ptProperty);
724     }
725 
726     return true;
727 }
728 
HandlePropertyModify(ManagedThread * thread,Method * method,const PtLocation & location)729 bool Debugger::HandlePropertyModify(ManagedThread *thread, Method *method, const PtLocation &location)
730 {
731     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
732     BytecodeInstruction inst(method->GetInstructions() + location.GetBytecodeOffset());
733     auto opcode = inst.GetOpcode();
734     bool isStatic = false;
735 
736     switch (opcode) {
737         case BytecodeInstruction::Opcode::STOBJ_V8_ID16:
738         case BytecodeInstruction::Opcode::STOBJ_64_V8_ID16:
739         case BytecodeInstruction::Opcode::STOBJ_OBJ_V8_ID16:
740             break;
741         case BytecodeInstruction::Opcode::STSTATIC_ID16:
742         case BytecodeInstruction::Opcode::STSTATIC_64_ID16:
743         case BytecodeInstruction::Opcode::STSTATIC_OBJ_ID16:
744             isStatic = true;
745             break;
746         default:
747             return false;
748     }
749 
750     Field *field = ResolveField(thread, method, inst);
751     if (field == nullptr) {
752         return false;
753     }
754     Class *klass = field->GetClass();
755     ASSERT(klass);
756 
757     {
758         os::memory::ReadLockHolder rholder(rwlock_);
759         if (FindPropertyWatch(klass->GetFileId(), field->GetFileId(), PropertyWatch::Type::MODIFY) == nullptr) {
760             return false;
761         }
762     }
763 
764     PtProperty ptProperty = FieldToPtProperty(field);
765 
766     VRegValue value(thread->GetCurrentFrame()->GetAcc().GetValue());
767     if (isStatic) {
768         hooks_.PropertyModification(PtThread(thread), method, location, nullptr, ptProperty, value);
769     } else {
770         interpreter::VRegister &reg = thread->GetCurrentFrame()->GetVReg(inst.GetVReg());
771         hooks_.PropertyModification(PtThread(thread), method, location, reg.GetReference(), ptProperty, value);
772     }
773 
774     return true;
775 }
776 
SetPropertyAccessWatch(BaseClass * klass,PtProperty property)777 std::optional<Error> Debugger::SetPropertyAccessWatch(BaseClass *klass, PtProperty property)
778 {
779     os::memory::WriteLockHolder wholder(rwlock_);
780     ASSERT(!klass->IsDynamicClass());
781     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
782     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
783     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS) != nullptr) {
784         return Error(Error::Type::INVALID_PROPERTY_ACCESS_WATCH,
785                      std::string("Invalid property access watch, already exist, ClassID: ") +
786                          std::to_string(classId.GetOffset()) +
787                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
788     }
789     propertyWatches_.emplace_back(classId, propertyId, PropertyWatch::Type::ACCESS);
790     return {};
791 }
792 
ClearPropertyAccessWatch(BaseClass * klass,PtProperty property)793 std::optional<Error> Debugger::ClearPropertyAccessWatch(BaseClass *klass, PtProperty property)
794 {
795     ASSERT(!klass->IsDynamicClass());
796     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
797     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
798     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::ACCESS)) {
799         return Error(Error::Type::PROPERTY_ACCESS_WATCH_NOT_FOUND,
800                      std::string("Property access watch not found, ClassID: ") + std::to_string(classId.GetOffset()) +
801                          ", PropertyID: " + std::to_string(propertyId.GetOffset()));
802     }
803     return {};
804 }
805 
SetPropertyModificationWatch(BaseClass * klass,PtProperty property)806 std::optional<Error> Debugger::SetPropertyModificationWatch(BaseClass *klass, PtProperty property)
807 {
808     os::memory::WriteLockHolder wholder(rwlock_);
809     ASSERT(!klass->IsDynamicClass());
810     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
811     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
812     if (FindPropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY) != nullptr) {
813         return Error(Error::Type::INVALID_PROPERTY_MODIFY_WATCH,
814                      std::string("Invalid property modification watch, already exist, ClassID: ") +
815                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
816     }
817     propertyWatches_.emplace_back(classId, propertyId, PropertyWatch::Type::MODIFY);
818     return {};
819 }
820 
ClearPropertyModificationWatch(BaseClass * klass,PtProperty property)821 std::optional<Error> Debugger::ClearPropertyModificationWatch(BaseClass *klass, PtProperty property)
822 {
823     ASSERT(!klass->IsDynamicClass());
824     panda_file::File::EntityId classId = static_cast<Class *>(klass)->GetFileId();
825     panda_file::File::EntityId propertyId = PtPropertyToField(property)->GetFileId();
826     if (!RemovePropertyWatch(classId, propertyId, PropertyWatch::Type::MODIFY)) {
827         return Error(Error::Type::PROPERTY_MODIFY_WATCH_NOT_FOUND,
828                      std::string("Property modification watch not found, ClassID: ") +
829                          std::to_string(classId.GetOffset()) + ", PropertyID" + std::to_string(propertyId.GetOffset()));
830     }
831     return {};
832 }
833 
IsBreakpoint(const PtLocation & location) const834 bool Debugger::IsBreakpoint(const PtLocation &location) const REQUIRES_SHARED(rwlock_)
835 {
836     auto it = breakpoints_.find(location);
837     return it != breakpoints_.end();
838 }
839 
EraseBreakpoint(const PtLocation & location)840 bool Debugger::EraseBreakpoint(const PtLocation &location)
841 {
842     os::memory::WriteLockHolder wholder(rwlock_);
843     auto it = breakpoints_.find(location);
844     if (it != breakpoints_.end()) {
845         breakpoints_.erase(it);
846         return true;
847     }
848     return false;
849 }
850 
FindPropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type) const851 const tooling::PropertyWatch *Debugger::FindPropertyWatch(panda_file::File::EntityId classId,
852                                                           panda_file::File::EntityId fieldId,
853                                                           tooling::PropertyWatch::Type type) const
854     REQUIRES_SHARED(rwlock_)
855 {
856     for (const auto &pw : propertyWatches_) {
857         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
858             return &pw;
859         }
860     }
861 
862     return nullptr;
863 }
864 
RemovePropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,tooling::PropertyWatch::Type type)865 bool Debugger::RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId,
866                                    tooling::PropertyWatch::Type type)
867 {
868     os::memory::WriteLockHolder wholder(rwlock_);
869     auto it = propertyWatches_.begin();
870     while (it != propertyWatches_.end()) {
871         const auto &pw = *it;
872         if (pw.GetClassId() == classId && pw.GetFieldId() == fieldId && pw.GetType() == type) {
873             propertyWatches_.erase(it);
874             return true;
875         }
876 
877         it++;
878     }
879 
880     return false;
881 }
882 
883 template <class VRegRef>
GetVRegValue(VRegRef reg)884 static uint64_t GetVRegValue(VRegRef reg)
885 {
886     return reg.HasObject() ? reinterpret_cast<uintptr_t>(reg.GetReference()) : reg.GetLong();
887 }
888 
889 template <class VRegRef>
GetVRegKind(VRegRef reg)890 static PtFrame::RegisterKind GetVRegKind([[maybe_unused]] VRegRef reg)
891 {
892     if constexpr (std::is_same<VRegRef, interpreter::DynamicVRegisterRef>::value) {
893         return PtFrame::RegisterKind::TAGGED;
894     } else {
895         return reg.HasObject() ? PtFrame::RegisterKind::REFERENCE : PtFrame::RegisterKind::PRIMITIVE;
896     }
897 }
898 
899 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)900 static void FillRegisters(Frame *interpreterFrame, PandaVector<uint64_t> &vregs,
901                           PandaVector<PtFrame::RegisterKind> &vregKinds, size_t nregs, PandaVector<uint64_t> &args,
902                           PandaVector<PtFrame::RegisterKind> &argKinds, size_t nargs, uint64_t &acc,
903                           PtFrame::RegisterKind &accKind)
904 {
905     FrameHandler frameHandler(interpreterFrame);
906 
907     vregs.reserve(nregs);
908     vregKinds.reserve(nregs);
909     for (size_t i = 0; i < nregs; i++) {
910         auto vregReg = frameHandler.GetVReg(i);
911         vregs.push_back(GetVRegValue(vregReg));
912         vregKinds.push_back(GetVRegKind(vregReg));
913     }
914 
915     args.reserve(nargs);
916     argKinds.reserve(nargs);
917     for (size_t i = 0; i < nargs; i++) {
918         auto argReg = frameHandler.GetVReg(i + nregs);
919         args.push_back(GetVRegValue(argReg));
920         argKinds.push_back(GetVRegKind(argReg));
921     }
922 
923     auto accReg = frameHandler.GetAccAsVReg();
924     acc = GetVRegValue(accReg);
925     accKind = GetVRegKind(accReg);
926 }
927 
928 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
PtDebugFrame(Method * method,Frame * interpreterFrame)929 PtDebugFrame::PtDebugFrame(Method *method, Frame *interpreterFrame) : method_(method)
930 {
931     pandaFile_ = method->GetPandaFile()->GetFilename();
932     methodId_ = method->GetFileId();
933 
934     isInterpreterFrame_ = interpreterFrame != nullptr;
935     if (!isInterpreterFrame_) {
936         return;
937     }
938 
939     size_t nregs = method->GetNumVregs();
940     size_t nargs = method->GetNumArgs();
941     if (interpreterFrame->IsDynamic()) {
942         FillRegisters<DynamicFrameHandler>(interpreterFrame, vregs_, vregKinds_, nregs, args_, argKinds_, nargs, acc_,
943                                            accKind_);
944     } else {
945         FillRegisters<StaticFrameHandler>(interpreterFrame, vregs_, vregKinds_, nregs, args_, argKinds_, nargs, acc_,
946                                           accKind_);
947     }
948 
949     bcOffset_ = interpreterFrame->GetBytecodeOffset();
950 }
951 
952 }  // namespace panda::tooling
953