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