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