• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 #ifndef PANDA_RUNTIME_TOOLING_DEBUGGER_H
17 #define PANDA_RUNTIME_TOOLING_DEBUGGER_H
18 
19 #include <atomic>
20 #include <functional>
21 #include <memory>
22 #include <string_view>
23 
24 #include "include/mem/panda_containers.h"
25 #include "include/mem/panda_smart_pointers.h"
26 #include "include/method.h"
27 #include "include/panda_vm.h"
28 #include "include/runtime.h"
29 #include "include/runtime_notification.h"
30 #include "include/tooling/debug_interface.h"
31 #include "libpandabase/os/mutex.h"
32 #include "libpandabase/utils/span.h"
33 #include "pt_hooks_wrapper.h"
34 #include "runtime/thread_manager.h"
35 
36 namespace ark::tooling {
37 // Deprecated API
38 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
39 class Breakpoint {
40 public:
41     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
Breakpoint(Method * method,uint32_t bcOffset)42     Breakpoint(Method *method, uint32_t bcOffset) : method_(method), bcOffset_(bcOffset) {}
43     ~Breakpoint() = default;
44 
GetMethod()45     Method *GetMethod() const
46     {
47         return method_;
48     }
49 
GetBytecodeOffset()50     uint32_t GetBytecodeOffset() const
51     {
52         return bcOffset_;
53     }
54 
55     bool operator==(const Breakpoint &bpoint) const
56     {
57         return GetMethod() == bpoint.GetMethod() && GetBytecodeOffset() == bpoint.GetBytecodeOffset();
58     }
59 
60     DEFAULT_COPY_SEMANTIC(Breakpoint);
61     DEFAULT_MOVE_SEMANTIC(Breakpoint);
62 
63 private:
64     Method *method_;
65     uint32_t bcOffset_;
66 };
67 
68 // Deprecated API
69 class HashBreakpoint {
70 public:
operator()71     size_t operator()(const Breakpoint &bpoint) const
72     {
73         return (std::hash<Method *>()(bpoint.GetMethod())) ^ (std::hash<uint32_t>()(bpoint.GetBytecodeOffset()));
74     }
75 };
76 
77 class HashLocation {
78 public:
operator()79     size_t operator()(const PtLocation &location) const
80     {
81         return std::hash<std::string>()(location.GetPandaFile()) ^
82                std::hash<uint32_t>()(location.GetMethodId().GetOffset()) ^  // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
83                std::hash<uint32_t>()(location.GetBytecodeOffset());         // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
84     }
85 };
86 
87 class PropertyWatch {
88 public:
89     enum class Type { ACCESS, MODIFY };
90 
91     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
PropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,Type type)92     PropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, Type type)
93         : classId_(classId), fieldId_(fieldId), type_(type)
94     {
95     }
96 
97     ~PropertyWatch() = default;
98 
GetClassId()99     panda_file::File::EntityId GetClassId() const
100     {
101         return classId_;
102     }
103 
GetFieldId()104     panda_file::File::EntityId GetFieldId() const
105     {
106         return fieldId_;
107     }
108 
GetType()109     Type GetType() const
110     {
111         return type_;
112     }
113 
114 private:
115     NO_COPY_SEMANTIC(PropertyWatch);
116     NO_MOVE_SEMANTIC(PropertyWatch);
117 
118     panda_file::File::EntityId classId_;
119     panda_file::File::EntityId fieldId_;
120     Type type_;
121 };
122 
123 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
124 class Debugger : public DebugInterface, RuntimeListener {
125 public:
126     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
Debugger(const Runtime * runtime)127     explicit Debugger(const Runtime *runtime)
128         : runtime_(runtime),
129           breakpoints_(GetInternalAllocatorAdapter(runtime)),
130           propertyWatches_(GetInternalAllocatorAdapter(runtime)),
131           vmStarted_(runtime->IsInitialized())
132     {
133         runtime_->GetNotificationManager()->AddListener(this, DEBUG_EVENT_MASK);
134     }
135 
~Debugger()136     ~Debugger() override
137     {
138         runtime_->GetNotificationManager()->RemoveListener(this, DEBUG_EVENT_MASK);
139     }
140 
RegisterHooks(PtHooks * hooks)141     std::optional<Error> RegisterHooks(PtHooks *hooks) override
142     {
143         hooks_.SetHooks(hooks);
144         return {};
145     }
146 
UnregisterHooks()147     std::optional<Error> UnregisterHooks() override
148     {
149         hooks_.SetHooks(nullptr);
150         return {};
151     }
152 
EnableAllGlobalHook()153     std::optional<Error> EnableAllGlobalHook() override
154     {
155         hooks_.EnableAllGlobalHook();
156         return {};
157     }
158 
DisableAllGlobalHook()159     std::optional<Error> DisableAllGlobalHook() override
160     {
161         hooks_.DisableAllGlobalHook();
162         return {};
163     }
164 
165     std::optional<Error> SetNotification(PtThread thread, bool enable, PtHookType hookType) override;
166     std::optional<Error> SetBreakpoint(const PtLocation &location) override;
167 
168     std::optional<Error> RemoveBreakpoint(const PtLocation &location) override;
169 
170     Expected<std::unique_ptr<PtFrame>, Error> GetCurrentFrame(PtThread thread) const override;
171 
172     std::optional<Error> EnumerateFrames(PtThread thread, std::function<bool(const PtFrame &)> callback) const override;
173 
174     // RuntimeListener methods
175 
LoadModule(std::string_view filename)176     void LoadModule(std::string_view filename) override
177     {
178         hooks_.LoadModule(filename);
179     }
180 
ThreadStart(ManagedThread * managedThread)181     void ThreadStart(ManagedThread *managedThread) override
182     {
183         hooks_.ThreadStart(PtThread(managedThread));
184     }
185 
ThreadEnd(ManagedThread * managedThread)186     void ThreadEnd(ManagedThread *managedThread) override
187     {
188         hooks_.ThreadEnd(PtThread(managedThread));
189     }
190 
191     void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override;
192 
VmStart()193     void VmStart() override
194     {
195         vmStarted_ = true;
196         hooks_.VmStart();
197     }
198 
VmInitialization(ManagedThread * managedThread)199     void VmInitialization(ManagedThread *managedThread) override
200     {
201         hooks_.VmInitialization(PtThread(managedThread));
202     }
203 
VmDeath()204     void VmDeath() override
205     {
206         hooks_.VmDeath();
207     }
208 
GarbageCollectorStart()209     void GarbageCollectorStart() override
210     {
211         hooks_.GarbageCollectionStart();
212     }
213 
GarbageCollectorFinish()214     void GarbageCollectorFinish() override
215     {
216         ark::MTManagedThread *self = ark::MTManagedThread::GetCurrent();
217         if (self == nullptr) {
218             return;
219         }
220         hooks_.GarbageCollectionFinish();
221     }
222 
223     void ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) override;
224 
225     void ExceptionThrow(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject,
226                         uint32_t bcOffset) override;
227     void ExceptionCatch(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject,
228                         uint32_t bcOffset) override;
229 
ConsoleCall(ManagedThread * thread,ConsoleCallType type,uint64_t timestamp,const PandaVector<TypedValue> & arguments)230     void ConsoleCall(ManagedThread *thread, ConsoleCallType type, uint64_t timestamp,
231                      const PandaVector<TypedValue> &arguments) override
232     {
233         hooks_.ConsoleCall(PtThread(thread), type, timestamp, arguments);
234     }
235 
236     void MethodEntry(ManagedThread *managedThread, Method *method) override;
237     void MethodExit(ManagedThread *managedThread, Method *method) override;
238 
239     void ClassLoad(Class *klass) override;
240     void ClassPrepare(Class *klass) override;
241 
242     void MonitorWait(ObjectHeader *object, int64_t timeout) override;
243     void MonitorWaited(ObjectHeader *object, bool timedOut) override;
244     void MonitorContendedEnter(ObjectHeader *object) override;
245     void MonitorContendedEntered(ObjectHeader *object) override;
246 
247     /*
248      * Mock API for debug interphase starts:
249      *
250      * API's function should be revorked and input parameters should be added
251      */
GetThreadList(PandaVector<PtThread> * threadList)252     std::optional<Error> GetThreadList(PandaVector<PtThread> *threadList) const override
253     {
254         runtime_->GetPandaVM()->GetThreadManager()->EnumerateThreads(
255             [threadList](ManagedThread *managedThread) {
256                 ASSERT(managedThread && "thread is null");
257                 threadList->push_back(PtThread(managedThread));
258                 return true;
259             },
260             static_cast<unsigned int>(ark::EnumerationFlag::ALL),
261             static_cast<unsigned int>(ark::EnumerationFlag::VM_THREAD));
262 
263         return {};
264     }
265 
GetThreadInfo(PtThread thread,ThreadInfo * infoPtr)266     std::optional<Error> GetThreadInfo([[maybe_unused]] PtThread thread,
267                                        [[maybe_unused]] ThreadInfo *infoPtr) const override
268     {
269         PT_UNIMPLEMENTED();
270         return {};
271     }
272 
273     std::optional<Error> SuspendThread(PtThread thread) const override;
274 
275     std::optional<Error> ResumeThread(PtThread thread) const override;
276 
277     std::optional<Error> SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
278                                      const VRegValue &value) const override;
279 
280     std::optional<Error> GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber,
281                                      VRegValue *result) const override;
282 
283     /**
284      * @brief Loads provided bytecode and executes evaluation method.
285      * File must contain public class with equally named static method taking no arguments.
286      * Name of both class and method must equal source code file name, path to which must be included into binary.
287      * @param thread in which to load and execute method.
288      * @param frameNumber call stack depth in which context to evaluate.
289      * @param expr bytecode with expression.
290      * @param method pointer to save the loaded method in.
291      * @param result pointer to save result in.
292      */
293     std::optional<Error> EvaluateExpression(PtThread thread, uint32_t frameNumber, const ExpressionWrapper &expr,
294                                             Method **method, VRegValue *result) const override;
295 
296     /**
297      * @brief Executes the provided evaluation method.
298      * @param thread in which to execute method.
299      * @param frameNumber call stack depth in which context to evaluate.
300      * @param method pointer to the method.
301      * @param result pointer to save result in.
302      */
303     std::optional<Error> EvaluateExpression(PtThread thread, uint32_t frameNumber, Method *method,
304                                             VRegValue *result) const override;
305 
306     std::optional<Error> RestartFrame([[maybe_unused]] PtThread thread,
307                                       [[maybe_unused]] uint32_t frameNumber) const override;
308 
SetAsyncCallStackDepth(uint32_t maxDepth)309     std::optional<Error> SetAsyncCallStackDepth([[maybe_unused]] uint32_t maxDepth) const override
310     {
311         PT_UNIMPLEMENTED();
312         return {};
313     }
314 
GetProperties(uint32_t * countPtr,char *** propertyPtr)315     std::optional<Error> GetProperties([[maybe_unused]] uint32_t *countPtr,
316                                        [[maybe_unused]] char ***propertyPtr) const override
317     {
318         PT_UNIMPLEMENTED();
319         return {};
320     }
321 
322     std::optional<Error> NotifyFramePop(PtThread thread, uint32_t depth) const override;
323 
324     std::optional<Error> GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, ObjectHeader **thisPtr) override;
325 
326     std::optional<Error> SetPropertyAccessWatch(BaseClass *klass, PtProperty property) override;
327 
328     std::optional<Error> ClearPropertyAccessWatch(BaseClass *klass, PtProperty property) override;
329 
330     std::optional<Error> SetPropertyModificationWatch(BaseClass *klass, PtProperty property) override;
331 
332     std::optional<Error> ClearPropertyModificationWatch(BaseClass *klass, PtProperty property) override;
333 
334 private:
335     Expected<interpreter::StaticVRegisterRef, Error> GetVRegByPandaFrame(ark::Frame *frame, int32_t regNumber) const;
336     Expected<interpreter::DynamicVRegisterRef, Error> GetVRegByPandaFrameDyn(ark::Frame *frame,
337                                                                              int32_t regNumber) const;
338     std::optional<Error> CheckLocation(const PtLocation &location);
339     bool IsBreakpoint(const PtLocation &location) const REQUIRES_SHARED(rwlock_);
340     bool EraseBreakpoint(const PtLocation &location);
341 
IsPropertyWatchActive()342     bool IsPropertyWatchActive() const
343     {
344         os::memory::ReadLockHolder rholder(rwlock_);
345         return !propertyWatches_.empty();
346     }
347     const tooling::PropertyWatch *FindPropertyWatch(panda_file::File::EntityId classId,
348                                                     panda_file::File::EntityId fieldId,
349                                                     tooling::PropertyWatch::Type type) const REQUIRES_SHARED(rwlock_);
350     bool RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId,
351                              tooling::PropertyWatch::Type type);
352 
353     bool HandleBreakpoint(ManagedThread *managedThread, Method *method, const PtLocation &location);
354     void HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException);
355     bool HandleStep(ManagedThread *managedThread, Method *method, const PtLocation &location);
356 
357     bool HandlePropertyAccess(ManagedThread *thread, Method *method, const PtLocation &location);
358     bool HandlePropertyModify(ManagedThread *thread, Method *method, const PtLocation &location);
359 
360     static constexpr uint32_t DEBUG_EVENT_MASK =
361         RuntimeNotificationManager::Event::LOAD_MODULE | RuntimeNotificationManager::Event::THREAD_EVENTS |
362         RuntimeNotificationManager::Event::BYTECODE_PC_CHANGED | RuntimeNotificationManager::Event::EXCEPTION_EVENTS |
363         RuntimeNotificationManager::Event::VM_EVENTS | RuntimeNotificationManager::Event::GARBAGE_COLLECTOR_EVENTS |
364         RuntimeNotificationManager::Event::METHOD_EVENTS | RuntimeNotificationManager::Event::CLASS_EVENTS |
365         RuntimeNotificationManager::Event::MONITOR_EVENTS | RuntimeNotificationManager::Event::ALLOCATION_EVENTS |
366         RuntimeNotificationManager::Event::CONSOLE_EVENTS;
367 
368     const Runtime *runtime_;
369     PtHooksWrapper hooks_;
370 
371     mutable os::memory::RWLock rwlock_;
372     PandaUnorderedSet<PtLocation, HashLocation> breakpoints_ GUARDED_BY(rwlock_);
373     PandaList<PropertyWatch> propertyWatches_ GUARDED_BY(rwlock_);
374     // NOTE(m.strizhak): research how to rework VM start to avoid atomic
375     std::atomic_bool vmStarted_ {false};
376 
377     NO_COPY_SEMANTIC(Debugger);
378     NO_MOVE_SEMANTIC(Debugger);
379 };
380 
381 class PtDebugFrame : public PtFrame {
382 public:
383     explicit PANDA_PUBLIC_API PtDebugFrame(Method *method, Frame *interpreterFrame);
384     ~PtDebugFrame() override = default;
385 
IsInterpreterFrame()386     bool IsInterpreterFrame() const override
387     {
388         return isInterpreterFrame_;
389     }
390 
GetMethod()391     Method *GetMethod() const override
392     {
393         return method_;
394     }
395 
GetVReg(size_t i)396     uint64_t GetVReg(size_t i) const override
397     {
398         if (!isInterpreterFrame_) {
399             return 0;
400         }
401         return vregs_[i];
402     }
403 
GetVRegKind(size_t i)404     RegisterKind GetVRegKind(size_t i) const override
405     {
406         if (!isInterpreterFrame_) {
407             return PtFrame::RegisterKind::PRIMITIVE;
408         }
409         return vregKinds_[i];
410     }
411 
GetVRegNum()412     size_t GetVRegNum() const override
413     {
414         return vregs_.size();
415     }
416 
GetArgument(size_t i)417     uint64_t GetArgument(size_t i) const override
418     {
419         if (!isInterpreterFrame_) {
420             return 0;
421         }
422         return args_[i];
423     }
424 
GetArgumentKind(size_t i)425     RegisterKind GetArgumentKind(size_t i) const override
426     {
427         if (!isInterpreterFrame_) {
428             return PtFrame::RegisterKind::PRIMITIVE;
429         }
430         return argKinds_[i];
431     }
432 
GetArgumentNum()433     size_t GetArgumentNum() const override
434     {
435         return args_.size();
436     }
437 
GetAccumulator()438     uint64_t GetAccumulator() const override
439     {
440         return acc_;
441     }
442 
GetAccumulatorKind()443     RegisterKind GetAccumulatorKind() const override
444     {
445         return accKind_;
446     }
447 
GetMethodId()448     panda_file::File::EntityId GetMethodId() const override
449     {
450         return methodId_;
451     }
452 
GetBytecodeOffset()453     uint32_t GetBytecodeOffset() const override
454     {
455         return bcOffset_;
456     }
457 
GetPandaFile()458     std::string GetPandaFile() const override
459     {
460         return pandaFile_;
461     }
462 
463     // mock API
GetFrameId()464     uint32_t GetFrameId() const override
465     {
466         return 0;
467     }
468 
469 private:
470     NO_COPY_SEMANTIC(PtDebugFrame);
471     NO_MOVE_SEMANTIC(PtDebugFrame);
472 
473     bool isInterpreterFrame_;
474     Method *method_;
475     uint64_t acc_ {0};
476     RegisterKind accKind_ {PtFrame::RegisterKind::PRIMITIVE};
477     PandaVector<uint64_t> vregs_;
478     PandaVector<RegisterKind> vregKinds_;
479     PandaVector<uint64_t> args_;
480     PandaVector<RegisterKind> argKinds_;
481     panda_file::File::EntityId methodId_;
482     uint32_t bcOffset_ {0};
483     std::string pandaFile_;
484 };
485 }  // namespace ark::tooling
486 
487 #endif  // PANDA_RUNTIME_TOOLING_DEBUGGER_H
488