• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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_INCLUDE_STACK_WALKER_H_
17 #define PANDA_RUNTIME_INCLUDE_STACK_WALKER_H_
18 
19 #include <variant>
20 
21 #include "macros.h"
22 #include "runtime/include/cframe.h"
23 #include "runtime/include/cframe_iterators.h"
24 #include "runtime/interpreter/frame.h"
25 
26 namespace panda {
27 
28 enum class FrameKind { NONE, INTERPRETER, COMPILER };
29 
30 enum class UnwindPolicy {
31     ALL,           // unwing all frames including inlined
32     SKIP_INLINED,  // unwing all frames excluding inlined
33     ONLY_INLINED,  // unwind all inlined frames within single cframe
34 };
35 
36 template <FrameKind kind>
37 struct BoundaryFrame;
38 
39 template <>
40 struct BoundaryFrame<FrameKind::INTERPRETER> {
41     static constexpr ssize_t METHOD_OFFSET = 1;
42     static constexpr ssize_t FP_OFFSET = 0;
43     static constexpr ssize_t RETURN_OFFSET = 2;
44     static constexpr ssize_t CALLEES_OFFSET = -1;
45 };
46 
47 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::METHOD_OFFSET) * sizeof(uintptr_t) == Frame::GetMethodOffset());
48 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::FP_OFFSET) * sizeof(uintptr_t) == Frame::GetPrevFrameOffset());
49 
50 template <>
51 struct BoundaryFrame<FrameKind::COMPILER> {
52     static constexpr ssize_t METHOD_OFFSET = -1;
53     static constexpr ssize_t FP_OFFSET = 0;
54     static constexpr ssize_t RETURN_OFFSET = 1;
55     static constexpr ssize_t CALLEES_OFFSET = -2;
56 };
57 
58 class FrameAccessor {
59 public:
60     using CFrameType = CFrame;
61     using FrameVariant = std::variant<Frame *, CFrame>;
62 
63     explicit FrameAccessor(const FrameVariant &frame) : frame_(frame) {}
64 
65     bool IsValid() const
66     {
67         return IsCFrame() || GetIFrame() != nullptr;
68     }
69 
70     bool IsCFrame() const
71     {
72         return std::holds_alternative<CFrameType>(frame_);
73     }
74 
75     CFrameType &GetCFrame()
76     {
77         ASSERT(IsCFrame());
78         return std::get<CFrameType>(frame_);
79     }
80 
81     const CFrameType &GetCFrame() const
82     {
83         ASSERT(IsCFrame());
84         return std::get<CFrameType>(frame_);
85     }
86 
87     Frame *GetIFrame()
88     {
89         return std::get<Frame *>(frame_);
90     }
91 
92     const Frame *GetIFrame() const
93     {
94         return std::get<Frame *>(frame_);
95     }
96 
97 private:
98     FrameVariant frame_;
99 };
100 
101 struct CalleeStorage {
102     std::array<uintptr_t *, GetCalleeRegsCount(RUNTIME_ARCH, true) + GetCalleeRegsCount(RUNTIME_ARCH, false)> stack;
103     uint32_t callee_regs_mask;
104     uint32_t callee_fp_regs_mask;
105 };
106 
107 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
108 class StackWalker {
109 public:
110     static constexpr Arch ARCH = RUNTIME_ARCH;
111     using SlotType = std::conditional_t<ArchTraits<ARCH>::IS_64_BITS, uint64_t, uint32_t>;
112     using FrameVariant = std::variant<Frame *, CFrame>;
113     using CFrameType = CFrame;
114 
115     StackWalker() = default;
116     explicit StackWalker(ManagedThread *thread, UnwindPolicy policy = UnwindPolicy::ALL);
117     StackWalker(void *fp, bool is_frame_compiled, uintptr_t npc, UnwindPolicy policy = UnwindPolicy::ALL);
118 
119     virtual ~StackWalker() = default;
120 
121     NO_COPY_SEMANTIC(StackWalker);
122     NO_MOVE_SEMANTIC(StackWalker);
123 
124     void Reset(ManagedThread *thread);
125 
126     void Verify();
127 
128     void NextFrame();
129 
130     Method *GetMethod();
131 
132     const Method *GetMethod() const
133     {
134         return IsCFrame() ? GetCFrame().GetMethod() : GetIFrame()->GetMethod();
135     }
136 
137     size_t GetBytecodePc() const
138     {
139         return IsCFrame() ? GetCFrameBytecodePc() : GetIFrame()->GetBytecodeOffset();
140     }
141 
142     size_t GetNativePc() const
143     {
144         return IsCFrame() ? GetCFrameNativePc() : 0;
145     }
146 
147     void *GetFp()
148     {
149         return IsCFrame() ? reinterpret_cast<void *>(GetCFrame().GetFrameOrigin())
150                           : reinterpret_cast<void *>(GetIFrame());
151     }
152 
153     bool HasFrame() const
154     {
155         return IsCFrame() || GetIFrame() != nullptr;
156     }
157 
158     template <typename Func>
159     bool IterateObjects(Func func)
160     {
161         return IterateRegs<true, false>(func);
162     }
163 
164     template <typename Func>
165     bool IterateVRegs(Func func)
166     {
167         return IterateRegs<false, false>(func);
168     }
169 
170     template <typename Func>
171     bool IterateObjectsWithInfo(Func func)
172     {
173         return IterateRegs<true, true>(func);
174     }
175 
176     template <typename Func>
177     bool IterateVRegsWithInfo(Func func)
178     {
179         return IterateRegs<false, true>(func);
180     }
181 
182     bool IsCFrame() const
183     {
184         return std::holds_alternative<CFrameType>(frame_);
185     }
186 
187     Frame::VRegister GetVRegValue(size_t vreg_num);
188 
189     template <typename T>
190     void SetVRegValue(VRegInfo reg_info, T value);
191 
192     CFrameType &GetCFrame()
193     {
194         ASSERT(IsCFrame());
195         return std::get<CFrameType>(frame_);
196     }
197 
198     const CFrameType &GetCFrame() const
199     {
200         ASSERT(IsCFrame());
201         return std::get<CFrameType>(frame_);
202     }
203 
204     Frame *GetIFrame()
205     {
206         return std::get<Frame *>(frame_);
207     }
208 
209     const Frame *GetIFrame() const
210     {
211         return std::get<Frame *>(frame_);
212     }
213 
214     Frame *ConvertToIFrame(FrameKind *prev_frame_kind, uint32_t *num_inlined_methods);
215 
216     bool IsCompilerBoundFrame(SlotType *prev);
217 
218     FrameKind GetPreviousFrameKind() const;
219 
220     FrameAccessor GetNextFrame();
221 
222     FrameAccessor GetCurrentFrame()
223     {
224         return FrameAccessor(frame_);
225     }
226 
227     uint32_t GetCalleeRegsMask(bool is_fp) const
228     {
229         return is_fp ? callee_stack_.callee_fp_regs_mask : callee_stack_.callee_regs_mask;
230     }
231 
232     bool IsDynamicMethod() const;
233 
234     void DumpFrame(std::ostream &os);
235 
236     template <FrameKind kind>
237     static SlotType *GetPrevFromBoundary(void *ptr)
238     {
239         // In current implementation fp must point to previous fp
240         static_assert(BoundaryFrame<kind>::FP_OFFSET == 0);
241         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
242         return *(reinterpret_cast<SlotType **>(ptr));
243     }
244 
245     template <FrameKind kind>
246     static bool IsBoundaryFrame(const void *ptr)
247     {
248         if constexpr (kind == FrameKind::INTERPRETER) {  // NOLINT
249             return GetBoundaryFrameMethod<kind>(ptr) == COMPILED_CODE_TO_INTERPRETER;
250         } else {  // NOLINT
251             return GetBoundaryFrameMethod<kind>(ptr) == INTERPRETER_TO_COMPILED_CODE;
252         }
253     }
254 
255     // Dump modify walker state - you must call it only for rvalue object
256     void Dump(std::ostream &os, bool print_vregs = false) &&;
257 
258 private:
259     CFrameType CreateCFrame(void *ptr, uintptr_t npc, SlotType *callee_stack, CalleeStorage *prev_callees = nullptr);
260 
261     template <bool create>
262     CFrameType CreateCFrameForC2IBridge(Frame *frame);
263     void InitCalleeBuffer(SlotType *callee_stack, CalleeStorage *prev_callees);
264 
265     template <bool objects, bool with_reg_info, typename Func>
266     bool IterateRegs(Func func);
267 
268     template <bool objects, bool with_reg_info, typename Func>
269     bool IterateRegsForCFrame(Func func);
270 
271     template <bool objects, bool with_reg_info, typename Func>
272     bool IterateRegsForIFrame(Func func);
273 
274     FrameVariant GetTopFrameFromFp(void *ptr, bool is_frame_compiled, uintptr_t npc);
275 
276     void NextFromCFrame();
277     void NextFromIFrame();
278 
279     static Method *GetMethodFromCBoundary(void *ptr)
280     {
281         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
282         return *reinterpret_cast<Method **>(reinterpret_cast<SlotType *>(ptr) - CFrameLayout::MethodSlot::Start());
283     }
284 
285     template <FrameKind kind>
286     static Method *GetMethodFromBoundary(void *ptr)
287     {
288         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
289         return *(reinterpret_cast<Method **>(ptr) + BoundaryFrame<kind>::METHOD_OFFSET);
290     }
291 
292     template <FrameKind kind>
293     static const Method *GetMethodFromBoundary(const void *ptr)
294     {
295         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
296         return *(reinterpret_cast<Method *const *>(ptr) + BoundaryFrame<kind>::METHOD_OFFSET);
297     }
298 
299     template <FrameKind kind>
300     static uintptr_t GetReturnAddressFromBoundary(const void *ptr)
301     {
302         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
303         return *(reinterpret_cast<const uintptr_t *>(ptr) + BoundaryFrame<kind>::RETURN_OFFSET);
304     }
305 
306     template <FrameKind kind>
307     static SlotType *GetCalleeStackFromBoundary(void *ptr)
308     {
309         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
310         return reinterpret_cast<SlotType *>(ptr) + BoundaryFrame<kind>::CALLEES_OFFSET;
311     }
312 
313     template <FrameKind kind>
314     static uintptr_t GetBoundaryFrameMethod(const void *ptr)
315     {
316         auto frame_method = reinterpret_cast<uintptr_t>(GetMethodFromBoundary<kind>(ptr));
317         return frame_method;
318     }
319 
320     bool IsInlined() const
321     {
322         return inline_depth_ != -1;
323     }
324 
325     uintptr_t GetCFrameBytecodePc() const
326     {
327         return 0;
328     }
329     uintptr_t GetCFrameNativePc() const
330     {
331         return 0;
332     }
333 
334     bool HandleAddingAsCFrame();
335 
336     bool HandleAddingAsIFrame();
337 
338     void SetPrevFrame(FrameKind *prev_frame_kind, void **prev_frame, CFrameType *cframe);
339 
340 private:
341     FrameVariant frame_ {nullptr};
342     UnwindPolicy policy_ {UnwindPolicy::ALL};
343     int inline_depth_ {-1};
344     CalleeStorage callee_stack_;
345     CalleeStorage prev_callee_stack_;
346 };
347 
348 }  // namespace panda
349 
350 #endif  // PANDA_RUNTIME_INCLUDE_STACK_WALKER_H_
351