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 ® = 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 ® = 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