• 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 #ifndef PANDA_RUNTIME_STACK_WALKER_H
16 #define PANDA_RUNTIME_STACK_WALKER_H
17 
18 #include <variant>
19 
20 #include "runtime/include/cframe.h"
21 #include "runtime/include/cframe_iterators.h"
22 #include "runtime/include/coretypes/tagged_value.h"
23 #include "runtime/interpreter/frame.h"
24 #include "compiler/code_info/code_info.h"
25 
26 namespace ark {
27 
28 enum class UnwindPolicy {
29     ALL,           // unwing all frames including inlined
30     SKIP_INLINED,  // unwing all frames excluding inlined
31     ONLY_INLINED,  // unwind all inlined frames within single cframe
32 };
33 
34 class FrameAccessor {
35 public:
36     using CFrameType = CFrame;
37     using FrameVariant = std::variant<Frame *, CFrame>;
38 
FrameAccessor(const FrameVariant & frame)39     explicit FrameAccessor(const FrameVariant &frame) : frame_(frame) {}
40 
IsValid()41     bool IsValid() const
42     {
43         return IsCFrame() || GetIFrame() != nullptr;
44     }
45 
IsCFrame()46     bool IsCFrame() const
47     {
48         return std::holds_alternative<CFrameType>(frame_);
49     }
50 
GetCFrame()51     CFrameType &GetCFrame()
52     {
53         ASSERT(IsCFrame());
54         return std::get<CFrameType>(frame_);
55     }
56 
GetCFrame()57     const CFrameType &GetCFrame() const
58     {
59         ASSERT(IsCFrame());
60         return std::get<CFrameType>(frame_);
61     }
62 
GetIFrame()63     Frame *GetIFrame()
64     {
65         return std::get<Frame *>(frame_);
66     }
67 
GetIFrame()68     const Frame *GetIFrame() const
69     {
70         return std::get<Frame *>(frame_);
71     }
72 
73 private:
74     FrameVariant frame_;
75 };
76 
77 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
78 class StackWalker {
79 public:
80     static constexpr size_t CALLEE_REGS_COUNT =
81         GetCalleeRegsCount(RUNTIME_ARCH, false) + GetCalleeRegsCount(RUNTIME_ARCH, true);
82 
83     using SlotType = std::conditional_t<ArchTraits<RUNTIME_ARCH>::IS_64_BITS, uint64_t, uint32_t>;
84     using FrameVariant = std::variant<Frame *, CFrame>;
85     using CFrameType = CFrame;
86     using CodeInfo = compiler::CodeInfo;
87     using VRegInfo = compiler::VRegInfo;
88     using CalleeRegsBuffer = std::array<SlotType, CALLEE_REGS_COUNT>;
89 
90     // Use static method to be able to ASSERT(thread->IsRuntimeCallEnabled()) before StackWalker construction to avoid
91     // crash in constructor, e.g. while GetTopFrameFromFp()
92     static PANDA_PUBLIC_API StackWalker Create(const ManagedThread *thread, UnwindPolicy policy = UnwindPolicy::ALL);
93 
94     StackWalker() = default;
95     StackWalker(void *fp, bool isFrameCompiled, uintptr_t npc, UnwindPolicy policy = UnwindPolicy::ALL);
96 
97     virtual ~StackWalker() = default;
98 
99     NO_COPY_SEMANTIC(StackWalker);
100     NO_MOVE_SEMANTIC(StackWalker);
101 
102     void Reset(const ManagedThread *thread);
103 
104     void Verify();
105 
106     PANDA_PUBLIC_API void NextFrame();
107 
108     PANDA_PUBLIC_API Method *GetMethod();
109 
GetMethod()110     const Method *GetMethod() const
111     {
112         return IsCFrame() ? GetCFrame().GetMethod() : GetIFrame()->GetMethod();
113     }
114 
GetBytecodePc()115     size_t GetBytecodePc() const
116     {
117         return IsCFrame() ? GetCFrameBytecodePc() : GetIFrame()->GetBytecodeOffset();
118     }
119 
GetNativePc()120     size_t GetNativePc() const
121     {
122         return IsCFrame() ? GetCFrameNativePc() : 0;
123     }
124 
GetFp()125     void *GetFp()
126     {
127         return IsCFrame() ? reinterpret_cast<void *>(GetCFrame().GetFrameOrigin())
128                           : reinterpret_cast<void *>(GetIFrame());
129     }
130 
HasFrame()131     bool HasFrame() const
132     {
133         return IsCFrame() || GetIFrame() != nullptr;
134     }
135 
136     template <typename Func>
IterateObjects(Func func)137     bool IterateObjects(Func func)
138     {
139         return IterateRegs<true, false>(func);
140     }
141 
142     template <typename Func>
IterateObjectsWithInfo(Func func)143     bool IterateObjectsWithInfo(Func func)
144     {
145         return IterateRegs<true, true>(func);
146     }
147 
148     template <typename Func>
IterateVRegsWithInfo(Func func)149     bool IterateVRegsWithInfo(Func func)
150     {
151         return IterateRegs<false, true>(func);
152     }
153 
IsCFrame()154     bool IsCFrame() const
155     {
156         return std::holds_alternative<CFrameType>(frame_);
157     }
158 
159     interpreter::VRegister GetVRegValue(size_t vregNum);
160 
161     template <bool IS_DYNAMIC = false, typename T>
162     void SetVRegValue(VRegInfo regInfo, T value);
163 
GetCFrame()164     CFrameType &GetCFrame()
165     {
166         ASSERT(IsCFrame());
167         return std::get<CFrameType>(frame_);
168     }
169 
GetCFrame()170     const CFrameType &GetCFrame() const
171     {
172         ASSERT(IsCFrame());
173         return std::get<CFrameType>(frame_);
174     }
175 
GetIFrame()176     Frame *GetIFrame()
177     {
178         return std::get<Frame *>(frame_);
179     }
180 
GetIFrame()181     const Frame *GetIFrame() const
182     {
183         return std::get<Frame *>(frame_);
184     }
185 
GetCompiledCodeEntry()186     auto GetCompiledCodeEntry() const
187     {
188         ASSERT(IsCFrame());
189         return codeInfo_.GetCode();
190     }
191 
192     Frame *ConvertToIFrame(FrameKind *prevFrameKind, uint32_t *numInlinedMethods);
193 
194     bool IsCompilerBoundFrame(SlotType *prev);
195 
196     FrameKind GetPreviousFrameKind() const;
197 
198     FrameAccessor GetNextFrame();
199 
GetCurrentFrame()200     FrameAccessor GetCurrentFrame()
201     {
202         return FrameAccessor(frame_);
203     }
204 
205     PANDA_PUBLIC_API bool IsDynamicMethod() const;
206 
207     void DumpFrame(std::ostream &os);
208 
209     template <FrameKind KIND>
GetPrevFromBoundary(void * ptr)210     static SlotType *GetPrevFromBoundary(void *ptr)
211     {
212         // In current implementation fp must point to previous fp
213         static_assert(BoundaryFrame<KIND>::FP_OFFSET == 0);
214         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
215         return *(reinterpret_cast<SlotType **>(ptr));
216     }
217 
218     template <FrameKind KIND>
GetBoundaryFrameMethod(const void * ptr)219     static uintptr_t GetBoundaryFrameMethod(const void *ptr)
220     {
221         auto frameMethod = reinterpret_cast<uintptr_t>(GetMethodFromBoundary<KIND>(ptr));
222         return frameMethod;
223     }
224 
225     template <FrameKind KIND>
IsBoundaryFrame(const void * ptr)226     static bool IsBoundaryFrame(const void *ptr)
227     {
228         if constexpr (KIND == FrameKind::INTERPRETER) {  // NOLINT
229             return GetBoundaryFrameMethod<KIND>(ptr) == COMPILED_CODE_TO_INTERPRETER;
230         } else {  // NOLINT
231             return GetBoundaryFrameMethod<KIND>(ptr) == INTERPRETER_TO_COMPILED_CODE;
232         }
233     }
234 
IsInlined()235     bool IsInlined() const
236     {
237         return inlineDepth_ != -1;
238     }
239 
240     // Dump modify walker state - you must call it only for rvalue object
241     void Dump(std::ostream &os, bool printVregs = false) &&;
242 
243     CalleeRegsBuffer &GetCalleeRegsForDeoptimize();
244 
245 private:
246     // These are shortenings for General-purpose register bank (scalar integer and pointer arithmetic)
FirstCalleeIntReg()247     inline constexpr size_t FirstCalleeIntReg()
248     {
249         return GetFirstCalleeReg(RUNTIME_ARCH, false);
250     }
LastCalleeIntReg()251     inline constexpr size_t LastCalleeIntReg()
252     {
253         return GetLastCalleeReg(RUNTIME_ARCH, false);
254     }
CalleeIntRegsCount()255     inline constexpr size_t CalleeIntRegsCount()
256     {
257         return GetCalleeRegsCount(RUNTIME_ARCH, false);
258     }
259 
260     // These are shortenings for SIMD and Floating-Point register bank
FirstCalleeFpReg()261     inline constexpr size_t FirstCalleeFpReg()
262     {
263         return GetFirstCalleeReg(RUNTIME_ARCH, true);
264     }
LastCalleeFpReg()265     inline constexpr size_t LastCalleeFpReg()
266     {
267         return GetLastCalleeReg(RUNTIME_ARCH, true);
268     }
CalleeFpRegsCount()269     inline constexpr size_t CalleeFpRegsCount()
270     {
271         return GetCalleeRegsCount(RUNTIME_ARCH, true);
272     }
273 
274     // Struct to keep pointers to stack slots holding callee-saved regs values
275     // and corresponding callee-saved regs masks.
276     struct CalleeStorage {
277         std::array<uintptr_t *, CALLEE_REGS_COUNT> stack = {nullptr};
278         RegMask intRegsMask {0};
279         RegMask fpRegsMask {0};
280     };
281 
282     void InitCalleeBuffer(SlotType *calleeSlots, CalleeStorage *prevCallees);
283     CFrameType CreateCFrame(SlotType *ptr, uintptr_t npc, SlotType *calleeSlots, CalleeStorage *prevCallees = nullptr);
284 
285     template <bool CREATE>
286     CFrameType CreateCFrameForC2IBridge(Frame *frame);
287 
288     template <bool OBJECTS, bool WITH_REG_INFO, typename Func>
289     bool IterateRegs(Func func);
290 
291     template <bool WITH_REG_INFO, typename Func>
292     bool IterateAllRegsForCFrame(Func func);
293 
294     template <bool OBJECTS, bool WITH_REG_INFO, typename Func>
295     bool IterateRegsForCFrameStatic(Func func);
296 
297     template <bool OBJECTS, bool WITH_REG_INFO, typename Func>
298     bool IterateRegsForCFrameDynamic(Func func);
299 
300     template <bool OBJECTS, bool WITH_REG_INFO, typename Func>
301     bool IterateRegsForIFrame(Func func);
302 
303     template <bool OBJECTS, bool WITH_REG_INFO, VRegInfo::Type OBJ_TYPE, VRegInfo::Type PRIMITIVE_TYPE, class F,
304               typename Func>
305     bool IterateRegsForIFrameInternal(F frameHandler, Func func);
306 
307     FrameVariant GetTopFrameFromFp(void *ptr, bool isFrameCompiled, uintptr_t npc);
308 
309     void NextFromCFrame();
310     void NextFromIFrame();
311 
GetMethodFromCBoundary(void * ptr)312     static Method *GetMethodFromCBoundary(void *ptr)
313     {
314         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
315         return *reinterpret_cast<Method **>(reinterpret_cast<SlotType *>(ptr) - CFrameLayout::MethodSlot::Start());
316     }
317 
318     template <FrameKind KIND>
GetMethodFromBoundary(void * ptr)319     static Method *GetMethodFromBoundary(void *ptr)
320     {
321         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
322         return *(reinterpret_cast<Method **>(ptr) + BoundaryFrame<KIND>::METHOD_OFFSET);
323     }
324 
325     template <FrameKind KIND>
GetMethodFromBoundary(const void * ptr)326     static const Method *GetMethodFromBoundary(const void *ptr)
327     {
328         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
329         return *(reinterpret_cast<Method *const *>(ptr) + BoundaryFrame<KIND>::METHOD_OFFSET);
330     }
331 
332     template <FrameKind KIND>
GetReturnAddressFromBoundary(const void * ptr)333     static uintptr_t GetReturnAddressFromBoundary(const void *ptr)
334     {
335         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
336         return *(reinterpret_cast<const uintptr_t *>(ptr) + BoundaryFrame<KIND>::RETURN_OFFSET);
337     }
338 
339     template <FrameKind KIND>
GetCalleeStackFromBoundary(void * ptr)340     static SlotType *GetCalleeStackFromBoundary(void *ptr)
341     {
342         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
343         return reinterpret_cast<SlotType *>(ptr) + BoundaryFrame<KIND>::CALLEES_OFFSET;
344     }
345 
GetCFrameBytecodePc()346     uintptr_t GetCFrameBytecodePc() const
347     {
348         if (GetCFrame().IsNative()) {
349             return 0;
350         }
351         if (IsInlined()) {
352             auto ii = codeInfo_.GetInlineInfo(stackmap_, inlineDepth_);
353             return ii.GetBytecodePc();
354         }
355         return stackmap_.GetBytecodePc();
356     }
GetCFrameNativePc()357     uintptr_t GetCFrameNativePc() const
358     {
359         return GetCFrame().IsNative() ? 0 : stackmap_.GetNativePcUnpacked();
360     }
361 
362     bool HandleAddingAsCFrame();
363 
364     bool HandleAddingAsIFrame();
365 
366     void SetPrevFrame(FrameKind *prevFrameKind, void **prevFrame, CFrameType *cframe);
367 
368     Frame *GetFrameFromPrevFrame(Frame *prevFrame);
369 
370     void DumpVRegs(std::ostream &os);
371 
372 #ifndef NDEBUG
373     void DebugSingleFrameVerify();
374 #endif
375 
376     void DumpVRegLocation(std::ostream &os, VRegInfo &regInfo);
377 
378 public:
379     class EnvData {
380         using EnvType = coretypes::TaggedType;
381 
382     public:
383         template <typename Allocator>
384         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
EnvData(const compiler::CodeInfo::VRegList<Allocator> & vregList,const CFrameType & cframe,const CodeInfo & codeInfo,SlotType ** calleeStack)385         explicit EnvData(const compiler::CodeInfo::VRegList<Allocator> &vregList, const CFrameType &cframe,
386                          const CodeInfo &codeInfo, SlotType **calleeStack)
387         {
388             for (size_t i = 0; i < VRegInfo::ENV_COUNT; i++) {
389                 auto vreg = vregList[vregList.size() - (VRegInfo::ENV_COUNT - i)];
390                 envData_[i] = static_cast<EnvType>(cframe.GetVRegValue<true>(vreg, codeInfo, calleeStack).GetValue());
391             }
392         }
393 
394         const EnvType &operator[](VRegInfo::VRegType type) const
395         {
396             return envData_[type - VRegInfo::VRegType::ENV_BEGIN];
397         };
398 
399     private:
400         std::array<EnvType, VRegInfo::ENV_COUNT> envData_;
401     };
402 
403 private:
404     FrameVariant frame_ {nullptr};
405     UnwindPolicy policy_ {UnwindPolicy::ALL};
406     CodeInfo codeInfo_;
407     compiler::StackMap stackmap_;
408     int inlineDepth_ {-1};
409     CalleeStorage calleeStack_;
410     CalleeStorage prevCalleeStack_;
411     CalleeRegsBuffer deoptCalleeRegs_ = {0};
412 };
413 
414 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::METHOD_OFFSET) * sizeof(uintptr_t) == Frame::GetMethodOffset());
415 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::FP_OFFSET) * sizeof(uintptr_t) == Frame::GetPrevFrameOffset());
416 
417 }  // namespace ark
418 
419 #endif  // PANDA_RUNTIME_STACK_WALKER_H
420