• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #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 panda {
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 StackWalker Create(const ManagedThread *thread, UnwindPolicy policy = UnwindPolicy::ALL);
93 
94     StackWalker() = default;
95     StackWalker(void *fp, bool is_frame_compiled, 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     void NextFrame();
107 
108     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 vreg_num);
160 
161     template <bool is_dynamic = false, typename T>
162     void SetVRegValue(VRegInfo reg_info, 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 code_info_.GetCode();
190     }
191 
192     Frame *ConvertToIFrame(FrameKind *prev_frame_kind, uint32_t *num_inlined_methods);
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     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 frame_method = reinterpret_cast<uintptr_t>(GetMethodFromBoundary<kind>(ptr));
222         return frame_method;
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 inline_depth_ != -1;
238     }
239 
240     // Dump modify walker state - you must call it only for rvalue object
241     void Dump(std::ostream &os, bool print_vregs = 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 int_regs_mask {0};
279         RegMask fp_regs_mask {0};
280     };
281 
282     void InitCalleeBuffer(SlotType *callee_slots, CalleeStorage *prev_callees);
283     CFrameType CreateCFrame(SlotType *ptr, uintptr_t npc, SlotType *callee_slots,
284                             CalleeStorage *prev_callees = nullptr);
285 
286     template <bool create>
287     CFrameType CreateCFrameForC2IBridge(Frame *frame);
288 
289     template <bool objects, bool with_reg_info, typename Func>
290     bool IterateRegs(Func func);
291 
292     template <bool with_reg_info, typename Func>
293     bool IterateAllRegsForCFrame(Func func);
294 
295     template <bool objects, bool with_reg_info, typename Func>
296     bool IterateRegsForCFrameStatic(Func func);
297 
298     template <bool objects, bool with_reg_info, typename Func>
299     bool IterateRegsForCFrameDynamic(Func func);
300 
301     template <bool objects, bool with_reg_info, typename Func>
302     bool IterateRegsForIFrame(Func func);
303 
304     template <bool objects, bool with_reg_info, VRegInfo::Type obj_type, VRegInfo::Type primitive_type, class F,
305               typename Func>
306     bool IterateRegsForIFrameInternal(F frame_handler, Func func);
307 
308     FrameVariant GetTopFrameFromFp(void *ptr, bool is_frame_compiled, uintptr_t npc);
309 
310     void NextFromCFrame();
311     void NextFromIFrame();
312 
GetMethodFromCBoundary(void * ptr)313     static Method *GetMethodFromCBoundary(void *ptr)
314     {
315         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
316         return *reinterpret_cast<Method **>(reinterpret_cast<SlotType *>(ptr) - CFrameLayout::MethodSlot::Start());
317     }
318 
319     template <FrameKind kind>
GetMethodFromBoundary(void * ptr)320     static Method *GetMethodFromBoundary(void *ptr)
321     {
322         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
323         return *(reinterpret_cast<Method **>(ptr) + BoundaryFrame<kind>::METHOD_OFFSET);
324     }
325 
326     template <FrameKind kind>
GetMethodFromBoundary(const void * ptr)327     static const Method *GetMethodFromBoundary(const void *ptr)
328     {
329         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
330         return *(reinterpret_cast<Method *const *>(ptr) + BoundaryFrame<kind>::METHOD_OFFSET);
331     }
332 
333     template <FrameKind kind>
GetReturnAddressFromBoundary(const void * ptr)334     static uintptr_t GetReturnAddressFromBoundary(const void *ptr)
335     {
336         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
337         return *(reinterpret_cast<const uintptr_t *>(ptr) + BoundaryFrame<kind>::RETURN_OFFSET);
338     }
339 
340     template <FrameKind kind>
GetCalleeStackFromBoundary(void * ptr)341     static SlotType *GetCalleeStackFromBoundary(void *ptr)
342     {
343         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
344         return reinterpret_cast<SlotType *>(ptr) + BoundaryFrame<kind>::CALLEES_OFFSET;
345     }
346 
GetCFrameBytecodePc()347     uintptr_t GetCFrameBytecodePc() const
348     {
349         if (GetCFrame().IsNative()) {
350             return 0;
351         }
352         if (IsInlined()) {
353             auto ii = code_info_.GetInlineInfo(stackmap_, inline_depth_);
354             return ii.GetBytecodePc();
355         }
356         return stackmap_.GetBytecodePc();
357     }
GetCFrameNativePc()358     uintptr_t GetCFrameNativePc() const
359     {
360         return GetCFrame().IsNative() ? 0 : stackmap_.GetNativePcUnpacked();
361     }
362 
363     bool HandleAddingAsCFrame();
364 
365     bool HandleAddingAsIFrame();
366 
367     void SetPrevFrame(FrameKind *prev_frame_kind, void **prev_frame, CFrameType *cframe);
368 
369 private:
370     FrameVariant frame_ {nullptr};
371     UnwindPolicy policy_ {UnwindPolicy::ALL};
372     CodeInfo code_info_;
373     compiler::StackMap stackmap_;
374     int inline_depth_ {-1};
375     CalleeStorage callee_stack_;
376     CalleeStorage prev_callee_stack_;
377     CalleeRegsBuffer deopt_callee_regs_ = {0};
378 };
379 
380 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::METHOD_OFFSET) * sizeof(uintptr_t) == Frame::GetMethodOffset());
381 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::FP_OFFSET) * sizeof(uintptr_t) == Frame::GetPrevFrameOffset());
382 
383 }  // namespace panda
384 
385 #endif  // PANDA_RUNTIME_STACK_WALKER_H
386