• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 #include "compiler/code_info/code_info.h"
17 #include "runtime/include/stack_walker-inl.h"
18 #include "runtime/include/runtime.h"
19 #include "runtime/include/thread.h"
20 #include "runtime/include/panda_vm.h"
21 #include "libpandabase/mem/mem.h"
22 #include "libpandafile/bytecode_instruction.h"
23 #include "runtime/interpreter/runtime_interface.h"
24 
25 #include <iomanip>
26 
27 namespace ark {
28 
Create(const ManagedThread * thread,UnwindPolicy policy)29 StackWalker StackWalker::Create(const ManagedThread *thread, UnwindPolicy policy)
30 {
31     ASSERT(thread != nullptr);
32 #ifndef NDEBUG
33     ASSERT(thread->IsRuntimeCallEnabled());
34     if (Runtime::GetOptions().IsVerifyCallStack()) {
35         StackWalker(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc(), policy)
36             .Verify();
37     }
38 #endif
39     return StackWalker(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc(), policy);
40 }
41 
42 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
StackWalker(void * fp,bool isFrameCompiled,uintptr_t npc,UnwindPolicy policy)43 StackWalker::StackWalker(void *fp, bool isFrameCompiled, uintptr_t npc, UnwindPolicy policy)
44 {
45     frame_ = GetTopFrameFromFp(fp, isFrameCompiled, npc);
46     if (policy == UnwindPolicy::SKIP_INLINED) {
47         inlineDepth_ = -1;
48     }
49 }
50 
Reset(const ManagedThread * thread)51 void StackWalker::Reset(const ManagedThread *thread)
52 {
53     ASSERT(thread != nullptr);
54     frame_ = GetTopFrameFromFp(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc());
55 }
56 
57 /* static */
GetTopFrameFromFp(void * ptr,bool isFrameCompiled,uintptr_t npc)58 typename StackWalker::FrameVariant StackWalker::GetTopFrameFromFp(void *ptr, bool isFrameCompiled, uintptr_t npc)
59 {
60     if (isFrameCompiled) {
61         if (IsBoundaryFrame<FrameKind::INTERPRETER>(ptr)) {
62             auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(ptr);
63             if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
64                 return CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
65                                     GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
66                                     GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp));
67             }
68             return CreateCFrame(GetPrevFromBoundary<FrameKind::INTERPRETER>(ptr),
69                                 GetReturnAddressFromBoundary<FrameKind::INTERPRETER>(ptr),
70                                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
71                                 reinterpret_cast<SlotType *>(ptr) +
72                                     BoundaryFrame<FrameKind::INTERPRETER>::CALLEES_OFFSET);  // NOLINT
73         }
74         return CreateCFrame(reinterpret_cast<SlotType *>(ptr), npc, nullptr);
75     }
76     return reinterpret_cast<Frame *>(ptr);
77 }
78 
GetMethod()79 Method *StackWalker::GetMethod()
80 {
81     ASSERT(HasFrame());
82     if (!IsCFrame()) {
83         return GetIFrame()->GetMethod();
84     }
85     auto &cframe = GetCFrame();
86     if (!cframe.IsNative()) {
87         // NOTE(m.strizhak): replace this condition with assert after fixing JIT trampolines for sampler
88         if (!stackmap_.IsValid()) {
89             return nullptr;
90         }
91         if (IsInlined()) {
92             auto methodVariant = codeInfo_.GetMethod(stackmap_, inlineDepth_);
93             if (std::holds_alternative<uint32_t>(methodVariant)) {
94                 return Runtime::GetCurrent()->GetClassLinker()->GetMethod(
95                     *cframe.GetMethod(), panda_file::File::EntityId(std::get<uint32_t>(methodVariant)));
96             }
97             return reinterpret_cast<Method *>(std::get<void *>(methodVariant));
98         }
99     }
100     return cframe.GetMethod();
101 }
102 
103 template <bool CREATE>
CreateCFrameForC2IBridge(Frame * frame)104 StackWalker::CFrameType StackWalker::CreateCFrameForC2IBridge(Frame *frame)
105 {
106     auto prev = GetPrevFromBoundary<FrameKind::INTERPRETER>(frame);
107     ASSERT(GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) != FrameBridgeKind::BYPASS);
108     // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
109     if constexpr (CREATE) {
110         return CreateCFrame(reinterpret_cast<SlotType *>(prev),
111                             GetReturnAddressFromBoundary<FrameKind::INTERPRETER>(frame),
112                             GetCalleeStackFromBoundary<FrameKind::INTERPRETER>(frame));
113     }
114     return CFrameType(prev);
115 }
116 
CreateCFrame(SlotType * ptr,uintptr_t npc,SlotType * calleeSlots,CalleeStorage * prevCallees)117 StackWalker::CFrameType StackWalker::CreateCFrame(SlotType *ptr, uintptr_t npc, SlotType *calleeSlots,
118                                                   CalleeStorage *prevCallees)
119 {
120     CFrameType cframe(ptr);
121     // NOTE(m.strizhak): replace this condition with assert after fixing JIT trampolines for sampler
122     if (cframe.GetMethod() == nullptr) {
123         return cframe;
124     }
125     if (cframe.IsNativeMethod()) {
126         return cframe;
127     }
128     const void *codeEntry;
129     if (cframe.ShouldDeoptimize()) {
130         // When method was deoptimized due to speculation failure, regular code entry become invalid,
131         // so we read entry from special backup field in the frame.
132         codeEntry = cframe.GetDeoptCodeEntry();
133     } else if (cframe.IsOsr()) {
134         codeEntry = Thread::GetCurrent()->GetVM()->GetCompiler()->GetOsrCode(cframe.GetMethod());
135     } else {
136         codeEntry = cframe.GetMethod()->GetCompiledEntryPoint();
137     }
138     new (&codeInfo_) CodeInfo(CodeInfo::GetCodeOriginFromEntryPoint(codeEntry));
139     // StackOverflow stackmap has zero address
140     if (npc == 0) {
141         stackmap_ = codeInfo_.FindStackMapForNativePc(npc);
142     } else {
143         auto code = reinterpret_cast<uintptr_t>(codeInfo_.GetCode());
144         CHECK_GT(npc, code);
145         CHECK_LT(npc - code, std::numeric_limits<uint32_t>::max());
146         stackmap_ = codeInfo_.FindStackMapForNativePc(npc - code);
147     }
148 
149     ASSERT_PRINT(stackmap_.IsValid(),
150                  "Stackmap not found " << cframe.GetMethod()->GetFullName() << ": npc=0x" << std::hex << npc
151                                        << ", code=[" << reinterpret_cast<const void *>(codeInfo_.GetCode())
152                                        << ".."
153                                        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
154                                        << reinterpret_cast<const void *>(codeInfo_.GetCode() + codeInfo_.GetCodeSize())
155                                        << "]" << std::dec);
156     calleeStack_.intRegsMask = codeInfo_.GetHeader().GetCalleeRegMask();
157     calleeStack_.fpRegsMask = codeInfo_.GetHeader().GetCalleeFpRegMask();
158     inlineDepth_ = codeInfo_.GetInlineDepth(stackmap_);
159 
160     InitCalleeBuffer(calleeSlots, prevCallees);
161 
162     return cframe;
163 }
164 
165 /**
166  * If all callees are saved then callee-saved regs are placed on the stack as follows:
167  *
168  * ---------------------  <-- callee_slots
169  * LastCalleeReg   (x28)
170  * ---------------------  <-- callee_slots - 1
171  *                  ...
172  * ---------------------
173  * FirstCalleeReg  (x19)
174  * ---------------------  <-- callee_slots - CalleeRegsCount()
175  * LastCalleeFpReg (d15)
176  * ---------------------
177  *                  ...
178  * ---------------------
179  * FirstCalleeFpReg (d8)
180  * ---------------------  <-- callee_slots - CalleeRegsCount() - CalleeFpRegsCount()
181  *
182  * If only used callees are saved, then the space is reserved for all callee registers,
183  * but only umasked regs are saved and there are no gaps between them.
184  *
185  * Suppose that regs masks are as follows:
186  *
187  * int_regs_mask = 0x00980000 (1001 1000 0000 0000 0000 0000, i.e. x19, x20 and x23 must be saved)
188  * fp_regs_mask  = 0x0,
189  *
190  * then we have the following layout:
191  *
192  * --------------------  <-- callee_slots
193  *                (x23)
194  * --------------------  <-- callee_slots - 1
195  *                (x20)
196  * --------------------  <-- callee_slots - 2
197  *                (x19)
198  * --------------------  <-- callee_slots - 3
199  *                 ...
200  * --------------------
201  *                (---)
202  * --------------------  <-- callee_slots - CalleeIntRegsCount()
203  *                 ...
204  * --------------------
205  *                (---)
206  * --------------------  <-- callee_slots - CalleeIntRegsCount() - CalleeFpRegsCount()
207  */
InitCalleeBuffer(SlotType * calleeSlots,CalleeStorage * prevCallees)208 void StackWalker::InitCalleeBuffer(SlotType *calleeSlots, CalleeStorage *prevCallees)
209 {
210     constexpr RegMask ARCH_INT_REGS_MASK(ark::GetCalleeRegsMask(RUNTIME_ARCH, false));
211     constexpr RegMask ARCH_FP_REGS_MASK(ark::GetCalleeRegsMask(RUNTIME_ARCH, true));
212 
213     bool prevIsNative = IsCFrame() ? GetCFrame().IsNative() : false;
214     if (calleeSlots != nullptr || prevCallees != nullptr) {
215         // Process scalar integer callee registers
216         for (size_t reg = FirstCalleeIntReg(); reg <= LastCalleeIntReg(); reg++) {
217             size_t offset = reg - FirstCalleeIntReg();
218             if (prevCallees == nullptr || prevIsNative) {
219                 size_t slot = ARCH_INT_REGS_MASK.GetDistanceFromHead(reg);
220                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
221                 calleeStack_.stack[offset] = calleeSlots - slot - 1;
222             } else if (prevCallees->intRegsMask.Test(reg)) {
223                 size_t slot = prevCallees->intRegsMask.GetDistanceFromHead(reg);
224                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
225                 calleeStack_.stack[offset] = calleeSlots - slot - 1;
226             } else {
227                 ASSERT(nullptr != prevCallees->stack[offset]);
228                 calleeStack_.stack[offset] = prevCallees->stack[offset];
229             }
230         }
231         // Process SIMD and Floating-Point callee registers
232         for (size_t reg = FirstCalleeFpReg(); reg <= LastCalleeFpReg(); reg++) {
233             size_t offset = CalleeIntRegsCount() + reg - FirstCalleeFpReg();
234             if (prevCallees == nullptr || prevIsNative) {
235                 size_t slot = ARCH_FP_REGS_MASK.GetDistanceFromHead(reg);
236                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
237                 calleeStack_.stack[offset] = calleeSlots - CalleeIntRegsCount() - slot - 1;
238             } else if (prevCallees->fpRegsMask.Test(reg)) {
239                 size_t slot = prevCallees->fpRegsMask.GetDistanceFromHead(reg);
240                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
241                 calleeStack_.stack[offset] = calleeSlots - CalleeIntRegsCount() - slot - 1;
242             } else {
243                 ASSERT(nullptr != prevCallees->stack[offset]);
244                 calleeStack_.stack[offset] = prevCallees->stack[offset];
245             }
246         }
247     }
248 }
249 
GetCalleeRegsForDeoptimize()250 StackWalker::CalleeRegsBuffer &StackWalker::GetCalleeRegsForDeoptimize()
251 {
252     // Process scalar integer callee registers
253     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
254     SlotType *calleeSrcSlots = GetCFrame().GetCalleeSaveStack() - 1;
255     SlotType *calleeDstSlots = &deoptCalleeRegs_[CalleeFpRegsCount()];
256     for (size_t reg = FirstCalleeIntReg(); reg <= LastCalleeIntReg(); reg++) {
257         size_t offset = reg - FirstCalleeIntReg();
258         if (calleeStack_.intRegsMask.Test(reg)) {
259             size_t slot = calleeStack_.intRegsMask.GetDistanceFromHead(reg);
260             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
261             calleeDstSlots[offset] = *(calleeSrcSlots - slot);
262         } else {
263             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
264             calleeDstSlots[offset] = *calleeStack_.stack[offset];
265         }
266     }
267     // Process SIMD and Floating-Point callee registers
268     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
269     calleeSrcSlots = GetCFrame().GetCalleeSaveStack() - CalleeIntRegsCount() - 1;
270     calleeDstSlots = deoptCalleeRegs_.begin();
271     for (size_t reg = FirstCalleeFpReg(); reg <= LastCalleeFpReg(); reg++) {
272         size_t offset = reg - FirstCalleeFpReg();
273         if (calleeStack_.fpRegsMask.Test(reg)) {
274             size_t slot = calleeStack_.fpRegsMask.GetDistanceFromHead(reg);
275             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
276             calleeDstSlots[offset] = *(calleeSrcSlots - slot);
277         } else {
278             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
279             calleeDstSlots[offset] = *calleeStack_.stack[CalleeIntRegsCount() + offset];
280         }
281     }
282 
283     return deoptCalleeRegs_;
284 }
285 
GetVRegValue(size_t vregNum)286 interpreter::VRegister StackWalker::GetVRegValue(size_t vregNum)
287 {
288     if (IsCFrame()) {
289         // NOTE(msherstennikov): we need to cache vregs_list within single cframe
290         auto vregsList =
291             codeInfo_.GetVRegList(stackmap_, inlineDepth_, mem::InternalAllocator<>::GetInternalAllocatorFromRuntime());
292         ASSERT(vregsList[vregNum].GetIndex() == vregNum);
293         interpreter::VRegister vreg0;
294         [[maybe_unused]] interpreter::VRegister vreg1;
295         GetCFrame().GetVRegValue(vregsList[vregNum], codeInfo_, calleeStack_.stack.data(),
296                                  interpreter::StaticVRegisterRef(&vreg0, &vreg1));
297         return vreg0;
298     }
299     ASSERT(vregNum < GetIFrame()->GetSize());
300     return GetIFrame()->GetVReg(vregNum);
301 }
302 
303 template <bool IS_DYNAMIC, typename T>
SetVRegValue(VRegInfo regInfo,T value)304 void StackWalker::SetVRegValue(VRegInfo regInfo, T value)
305 {
306     if (IsCFrame()) {
307         auto &cframe = GetCFrame();
308         if (IsDynamicMethod()) {
309             if constexpr (sizeof(T) == sizeof(uint64_t)) {  // NOLINT
310                 cframe.SetVRegValue<true>(regInfo, bit_cast<uint64_t>(value), calleeStack_.stack.data());
311             } else {  // NOLINT
312                 static_assert(sizeof(T) == sizeof(uint32_t));
313                 cframe.SetVRegValue<true>(regInfo, static_cast<uint64_t>(bit_cast<uint32_t>(value)),
314                                           calleeStack_.stack.data());
315             }
316         } else {
317             if constexpr (sizeof(T) == sizeof(uint64_t)) {  // NOLINT
318                 cframe.SetVRegValue(regInfo, bit_cast<uint64_t>(value), calleeStack_.stack.data());
319             } else {  // NOLINT
320                 static_assert(sizeof(T) == sizeof(uint32_t));
321                 cframe.SetVRegValue(regInfo, static_cast<uint64_t>(bit_cast<uint32_t>(value)),
322                                     calleeStack_.stack.data());
323             }
324         }
325     } else {
326         auto vreg = GetFrameHandler<IS_DYNAMIC>(GetIFrame()).GetVReg(regInfo.GetIndex());
327         if constexpr (std::is_same_v<T, ObjectHeader *>) {  // NOLINT
328             ASSERT(vreg.HasObject() && "Trying to change object variable by scalar value");
329             vreg.SetReference(value);
330         } else {  // NOLINT
331             ASSERT(!vreg.HasObject() && "Trying to change object variable by scalar value");
332             vreg.Set(value);
333         }
334     }
335 }
336 
337 template void StackWalker::SetVRegValue(VRegInfo reg_info, uint32_t value);
338 template void StackWalker::SetVRegValue(VRegInfo reg_info, int32_t value);
339 template void StackWalker::SetVRegValue(VRegInfo reg_info, uint64_t value);
340 template void StackWalker::SetVRegValue(VRegInfo reg_info, int64_t value);
341 template void StackWalker::SetVRegValue(VRegInfo reg_info, float value);
342 template void StackWalker::SetVRegValue(VRegInfo reg_info, double value);
343 template void StackWalker::SetVRegValue(VRegInfo reg_info, ObjectHeader *value);
344 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, uint32_t value);
345 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, int32_t value);
346 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, uint64_t value);
347 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, int64_t value);
348 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, float value);
349 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, double value);
350 template void StackWalker::SetVRegValue<true>(VRegInfo reg_info, ObjectHeader *value);
351 
NextFrame()352 void StackWalker::NextFrame()
353 {
354     if (IsCFrame()) {
355         NextFromCFrame();
356     } else {
357         NextFromIFrame();
358     }
359 }
360 
NextFromCFrame()361 void StackWalker::NextFromCFrame()
362 {
363     if (IsInlined()) {
364         if (policy_ != UnwindPolicy::SKIP_INLINED) {
365             inlineDepth_--;
366             return;
367         }
368         inlineDepth_ = -1;
369     }
370     if (policy_ == UnwindPolicy::ONLY_INLINED) {
371         frame_ = nullptr;
372         return;
373     }
374     auto prev = GetCFrame().GetPrevFrame();
375     if (prev == nullptr) {
376         frame_ = nullptr;
377         return;
378     }
379     auto frameMethod = GetBoundaryFrameMethod<FrameKind::COMPILER>(prev);
380     switch (frameMethod) {
381         case FrameBridgeKind::INTERPRETER_TO_COMPILED_CODE: {
382             auto prevFrame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
383             if (prevFrame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prevFrame)) {
384                 frame_ = CreateCFrameForC2IBridge<true>(prevFrame);
385                 break;
386             }
387 
388             frame_ = reinterpret_cast<Frame *>(prevFrame);
389             break;
390         }
391         case FrameBridgeKind::BYPASS: {
392             auto prevFrame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
393             if (prevFrame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prevFrame)) {
394                 frame_ = CreateCFrameForC2IBridge<true>(prevFrame);
395                 break;
396             }
397             frame_ = CreateCFrame(reinterpret_cast<SlotType *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev)),
398                                   GetReturnAddressFromBoundary<FrameKind::COMPILER>(prev),
399                                   GetCalleeStackFromBoundary<FrameKind::COMPILER>(prev));
400             break;
401         }
402         default:
403             prevCalleeStack_ = calleeStack_;
404             frame_ = CreateCFrame(reinterpret_cast<SlotType *>(prev), GetCFrame().GetLr(),
405                                   GetCFrame().GetCalleeSaveStack(), &prevCalleeStack_);
406             break;
407     }
408 }
409 
NextFromIFrame()410 void StackWalker::NextFromIFrame()
411 {
412     if (policy_ == UnwindPolicy::ONLY_INLINED) {
413         frame_ = nullptr;
414         return;
415     }
416     auto prev = GetIFrame()->GetPrevFrame();
417     if (prev == nullptr) {
418         frame_ = nullptr;
419         return;
420     }
421     if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
422         auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(prev);
423         if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
424             frame_ = CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
425                                   GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
426                                   GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp));
427         } else {
428             frame_ = CreateCFrameForC2IBridge<true>(prev);
429         }
430     } else {
431         frame_ = reinterpret_cast<Frame *>(prev);
432     }
433 }
434 
GetNextFrame()435 FrameAccessor StackWalker::GetNextFrame()
436 {
437     if (IsCFrame()) {
438         if (IsInlined()) {
439             return FrameAccessor(frame_);
440         }
441         auto prev = GetCFrame().GetPrevFrame();
442         if (prev == nullptr) {
443             return FrameAccessor(nullptr);
444         }
445         auto frameMethod = GetBoundaryFrameMethod<FrameKind::COMPILER>(prev);
446         switch (frameMethod) {
447             case FrameBridgeKind::INTERPRETER_TO_COMPILED_CODE: {
448                 auto prevFrame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
449                 if (prevFrame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prevFrame)) {
450                     return FrameAccessor(CreateCFrameForC2IBridge<false>(prevFrame));
451                 }
452                 return FrameAccessor(prevFrame);
453             }
454             case FrameBridgeKind::BYPASS: {
455                 auto prevFrame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
456                 if (prevFrame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prevFrame)) {
457                     return FrameAccessor(CreateCFrameForC2IBridge<false>(prevFrame));
458                 }
459                 return FrameAccessor(
460                     CFrameType(reinterpret_cast<SlotType *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev))));
461             }
462             default:
463                 return FrameAccessor(CFrameType(reinterpret_cast<SlotType *>(prev)));
464         }
465     } else {
466         auto prev = GetIFrame()->GetPrevFrame();
467         if (prev == nullptr) {
468             return FrameAccessor(nullptr);
469         }
470         if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
471             auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(prev);
472             if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
473                 return FrameAccessor(CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
474                                                   GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
475                                                   GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp)));
476             }
477             return FrameAccessor(CreateCFrameForC2IBridge<false>(prev));
478         }
479         return FrameAccessor(reinterpret_cast<Frame *>(prev));
480     }
481 }
482 
GetPreviousFrameKind() const483 FrameKind StackWalker::GetPreviousFrameKind() const
484 {
485     if (IsCFrame()) {
486         auto prev = GetCFrame().GetPrevFrame();
487         if (prev == nullptr) {
488             return FrameKind::NONE;
489         }
490         if (IsBoundaryFrame<FrameKind::COMPILER>(prev)) {
491             return FrameKind::INTERPRETER;
492         }
493         return FrameKind::COMPILER;
494     }
495     auto prev = GetIFrame()->GetPrevFrame();
496     if (prev == nullptr) {
497         return FrameKind::NONE;
498     }
499     if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
500         return FrameKind::COMPILER;
501     }
502     return FrameKind::INTERPRETER;
503 }
504 
IsCompilerBoundFrame(SlotType * prev)505 bool StackWalker::IsCompilerBoundFrame(SlotType *prev)
506 {
507     if (IsBoundaryFrame<FrameKind::COMPILER>(prev)) {
508         return true;
509     }
510     if (GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) == FrameBridgeKind::BYPASS) {
511         auto prevFrame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
512         // Case for clinit:
513         // Compiled code -> C2I -> InitializeClass -> call clinit -> I2C -> compiled code for clinit
514         if (prevFrame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prevFrame)) {
515             return true;
516         }
517     }
518 
519     return false;
520 }
521 
GetFrameFromPrevFrame(Frame * prevFrame)522 Frame *StackWalker::GetFrameFromPrevFrame(Frame *prevFrame)
523 {
524     auto vregList =
525         codeInfo_.GetVRegList(stackmap_, inlineDepth_, mem::InternalAllocator<>::GetInternalAllocatorFromRuntime());
526     auto *method = GetMethod();
527     ASSERT(method != nullptr);
528     Frame *frame;
529     if (IsDynamicMethod()) {
530         /* If there is a usage of rest arguments in dynamic function, then a managed object to contain actual arguments
531          * is constructed in prologue. Thus there is no need to reconstruct rest arguments here
532          */
533         auto numActualArgs = method->GetNumArgs();
534         /* If there are no arguments-keeping object construction in execution path, the number of actual args may be
535          * retreived from cframe
536          */
537 
538         size_t frameNumVregs = method->GetNumVregs() + numActualArgs;
539         frame = interpreter::RuntimeInterface::CreateFrameWithActualArgs<true>(frameNumVregs, numActualArgs, method,
540                                                                                prevFrame);
541         ASSERT(frame != nullptr);
542         frame->SetDynamic();
543         DynamicFrameHandler frameHandler(frame);
544         static constexpr uint8_t ACC_OFFSET = VRegInfo::ENV_COUNT + 1;
545         for (size_t i = 0; i < vregList.size() - ACC_OFFSET; i++) {
546             auto vreg = vregList[i];
547             if (!vreg.IsLive()) {
548                 continue;
549             }
550             auto regRef = frameHandler.GetVReg(i);
551             GetCFrame().GetPackVRegValue(vreg, codeInfo_, calleeStack_.stack.data(), regRef);
552         }
553         {
554             auto vreg = vregList[vregList.size() - ACC_OFFSET];
555             if (vreg.IsLive()) {
556                 auto regRef = frameHandler.GetAccAsVReg();
557                 GetCFrame().GetPackVRegValue(vreg, codeInfo_, calleeStack_.stack.data(), regRef);
558             }
559         }
560         EnvData envData {vregList, GetCFrame(), codeInfo_, calleeStack_.stack.data()};
561         Thread::GetCurrent()->GetVM()->GetLanguageContext().RestoreEnv(frame, envData);
562     } else {
563         auto frameNumVregs = method->GetNumVregs() + method->GetNumArgs();
564         ASSERT((frameNumVregs + 1) >= vregList.size());
565         frame = interpreter::RuntimeInterface::CreateFrame(frameNumVregs, method, prevFrame);
566         StaticFrameHandler frameHandler(frame);
567         for (size_t i = 0; i < vregList.size(); i++) {
568             auto vreg = vregList[i];
569             if (!vreg.IsLive()) {
570                 continue;
571             }
572 
573             bool isAcc = i == (vregList.size() - 1);
574             auto regRef = isAcc ? frame->GetAccAsVReg() : frameHandler.GetVReg(i);
575             GetCFrame().GetVRegValue(vreg, codeInfo_, calleeStack_.stack.data(), regRef);
576         }
577     }
578     return frame;
579 }
580 
ConvertToIFrame(FrameKind * prevFrameKind,uint32_t * numInlinedMethods)581 Frame *StackWalker::ConvertToIFrame(FrameKind *prevFrameKind, uint32_t *numInlinedMethods)
582 {
583     if (!IsCFrame()) {
584         return GetIFrame();
585     }
586     auto &cframe = GetCFrame();
587 
588     auto inlineDepth = inlineDepth_;
589     bool isInvoke = false;
590 
591     void *prevFrame;
592     bool isInit = false;
593     if (IsInlined()) {
594         inlineDepth_--;
595         *numInlinedMethods = *numInlinedMethods + 1;
596         prevFrame = ConvertToIFrame(prevFrameKind, numInlinedMethods);
597         auto iframe = static_cast<Frame *>(prevFrame);
598         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
599         auto pc = iframe->GetMethod()->GetInstructions() + iframe->GetBytecodeOffset();
600         if (BytecodeInstruction(pc).HasFlag(BytecodeInstruction::INIT_OBJ)) {
601             isInit = true;
602         }
603     } else {
604         auto prev = cframe.GetPrevFrame();
605         if (prev == nullptr) {
606             *prevFrameKind = FrameKind::NONE;
607             prevFrame = nullptr;
608         } else if (IsCompilerBoundFrame(prev)) {
609             isInvoke = true;
610             prevFrame =
611                 reinterpret_cast<Frame *>(StackWalker::GetPrevFromBoundary<FrameKind::COMPILER>(cframe.GetPrevFrame()));
612             if (prevFrameKind != nullptr) {
613                 *prevFrameKind = FrameKind::INTERPRETER;
614             }
615         } else {
616             prevFrame = cframe.GetPrevFrame();
617             if (prevFrameKind != nullptr) {
618                 *prevFrameKind = FrameKind::COMPILER;
619             }
620         }
621     }
622     inlineDepth_ = inlineDepth;
623     Frame *frame = GetFrameFromPrevFrame(reinterpret_cast<Frame *>(prevFrame));
624 
625     frame->SetDeoptimized();
626     frame->SetBytecodeOffset(GetBytecodePc());
627     if (isInit) {
628         frame->SetInitobj();
629     }
630     if (isInvoke) {
631         frame->SetInvoke();
632     }
633     return frame;
634 }
635 
IsDynamicMethod() const636 bool StackWalker::IsDynamicMethod() const
637 {
638     // Dynamic method may have no class
639     return GetMethod()->GetClass() == nullptr ||
640            ark::panda_file::IsDynamicLanguage(Runtime::GetCurrent()->GetLanguageContext(*GetMethod()).GetLanguage());
641 }
642 
643 #ifndef NDEBUG
DebugSingleFrameVerify()644 void StackWalker::DebugSingleFrameVerify()
645 {
646     ASSERT(GetMethod() != nullptr);
647     IterateVRegsWithInfo([this]([[maybe_unused]] const auto &regInfo, const auto &vreg) {
648         if (regInfo.GetType() == compiler::VRegInfo::Type::ANY) {
649             ASSERT(IsDynamicMethod());
650             return true;
651         }
652 
653         if (!vreg.HasObject()) {
654             ASSERT(!regInfo.IsObject());
655             vreg.GetLong();
656             return true;
657         }
658         // Use Frame::VRegister::HasObject() to detect objects
659         ASSERT(regInfo.IsObject());
660         if (ObjectHeader *object = vreg.GetReference(); object != nullptr) {
661             auto *cls = object->ClassAddr<Class>();
662             if (!IsAddressInObjectsHeap(cls)) {
663                 StackWalker::Create(ManagedThread::GetCurrent()).Dump(std::cerr, true);
664                 // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
665                 LOG(FATAL, INTEROP) << "Wrong class " << cls << " for object " << object << "\n";
666             } else {
667                 cls->GetDescriptor();
668             }
669         }
670         return true;
671     });
672 
673     if (IsCFrame()) {
674         IterateObjects([this](const auto &vreg) {
675             if (IsDynamicMethod()) {
676                 ASSERT(vreg.HasObject());
677                 return true;
678             }
679 
680             ASSERT(vreg.HasObject());
681             ObjectHeader *object = vreg.GetReference();
682             if (object == nullptr) {
683                 return true;
684             }
685             ASSERT(IsAddressInObjectsHeap(object));
686             auto *cls = object->ClassAddr<Class>();
687             if (!IsAddressInObjectsHeap(cls)) {
688                 StackWalker::Create(ManagedThread::GetCurrent()).Dump(std::cerr, true);
689                 // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
690                 LOG(FATAL, INTEROP) << "Wrong class " << cls << " for object " << object << "\n";
691             } else {
692                 cls->GetDescriptor();
693             }
694             return true;
695         });
696     }
697 }
698 #endif  // ifndef NDEBUG
699 
Verify()700 void StackWalker::Verify()
701 {
702     for (; HasFrame(); NextFrame()) {
703 #ifndef NDEBUG
704         DebugSingleFrameVerify();
705 #endif  // ifndef NDEBUG
706     }
707 }
708 
DumpVRegLocation(std::ostream & os,VRegInfo & regInfo)709 void StackWalker::DumpVRegLocation(std::ostream &os, VRegInfo &regInfo)
710 {
711     [[maybe_unused]] static constexpr size_t WIDTH_LOCATION = 12;
712     os << std::setw(WIDTH_LOCATION) << std::setfill(' ') << regInfo.GetTypeString();  // NOLINT
713     if (IsCFrame()) {
714         os << regInfo.GetLocationString() << ":" << std::dec << helpers::ToSigned(regInfo.GetValue());
715     } else {
716         os << '-';
717     }
718 }
719 
DumpVRegs(std::ostream & os)720 void StackWalker::DumpVRegs(std::ostream &os)
721 {
722     [[maybe_unused]] static constexpr size_t WIDTH_REG = 10;
723     [[maybe_unused]] static constexpr size_t WIDTH_TYPE = 20;
724 
725     IterateVRegsWithInfo([this, &os](auto regInfo, const auto &vreg) {
726         os << "     " << std::setw(WIDTH_REG) << std::setfill(' ') << std::right
727            << (regInfo.IsSpecialVReg() ? VRegInfo::VRegTypeToString(regInfo.GetVRegType())
728                                        : (std::string("v") + std::to_string(regInfo.GetIndex())));
729         os << " = ";
730         if (regInfo.GetType() == compiler::VRegInfo::Type::ANY) {
731             os << "0x";
732         }
733         os << std::left;
734         os << std::setw(WIDTH_TYPE) << std::setfill(' ');
735         switch (regInfo.GetType()) {
736             case compiler::VRegInfo::Type::INT64:
737             case compiler::VRegInfo::Type::INT32:
738                 os << std::dec << vreg.GetLong();
739                 break;
740             case compiler::VRegInfo::Type::FLOAT64:
741                 os << vreg.GetDouble();
742                 break;
743             case compiler::VRegInfo::Type::FLOAT32:
744                 os << vreg.GetFloat();
745                 break;
746             case compiler::VRegInfo::Type::BOOL:
747                 os << (vreg.Get() ? "true" : "false");
748                 break;
749             case compiler::VRegInfo::Type::OBJECT:
750                 os << vreg.GetReference();
751                 break;
752             case compiler::VRegInfo::Type::ANY: {
753                 os << std::hex << static_cast<uint64_t>(vreg.GetValue());
754                 break;
755             }
756             case compiler::VRegInfo::Type::UNDEFINED:
757                 os << "undfined";
758                 break;
759             default:
760                 os << "unknown";
761                 break;
762         }
763         DumpVRegLocation(os, regInfo);
764         os << std::endl;
765         return true;
766     });
767 }
768 
769 // Dump function change StackWalker object-state, that's why it may be called only
770 // with rvalue reference.
Dump(std::ostream & os,bool printVregs)771 void StackWalker::Dump(std::ostream &os, bool printVregs /* = false */) &&
772 {
773     [[maybe_unused]] static constexpr size_t WIDTH_INDEX = 4;
774     [[maybe_unused]] static constexpr size_t WIDTH_FRAME = 8;
775 
776     size_t frameIndex = 0;
777     os << "Panda call stack:\n";
778     for (; HasFrame(); NextFrame()) {
779         os << std::setw(WIDTH_INDEX) << std::setfill(' ') << std::right << std::dec << frameIndex << ": "
780            << std::setfill('0');
781         os << std::setw(WIDTH_FRAME) << std::hex;
782         os << (IsCFrame() ? reinterpret_cast<Frame *>(GetCFrame().GetFrameOrigin()) : GetIFrame()) << " in ";
783         DumpFrame(os);
784         os << std::endl;
785         if (printVregs) {
786             DumpVRegs(os);
787         }
788         if (IsCFrame() && printVregs) {
789             os << "roots:";
790             IterateObjectsWithInfo([&os](auto &regInfo, const auto &vreg) {
791                 ASSERT(vreg.HasObject());
792                 os << " " << regInfo.GetLocationString() << "[" << std::dec << regInfo.GetValue() << "]=" << std::hex
793                    << vreg.GetReference();
794                 return true;
795             });
796             os << std::endl;
797         }
798         frameIndex++;
799     }
800 }
801 
DumpFrame(std::ostream & os)802 void StackWalker::DumpFrame(std::ostream &os)
803 {
804     ASSERT(GetMethod() != nullptr);
805     os << GetMethod()->GetFullName();
806     if (IsCFrame()) {
807         if (GetCFrame().IsNative()) {
808             os << " (native)";
809         } else {
810             os << " (compiled" << (GetCFrame().IsOsr() ? "/osr" : "") << ": npc=" << GetNativePc()
811                << (IsInlined() ? ", inlined) " : ") ");
812             if (IsInlined()) {
813                 codeInfo_.DumpInlineInfo(os, stackmap_, inlineDepth_);
814             } else {
815                 codeInfo_.Dump(os, stackmap_);
816             }
817         }
818     } else {
819         os << " (managed)";
820     }
821 }
822 
823 }  // namespace ark
824