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