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