• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 panda {
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(panda::GetCalleeRegsMask(RUNTIME_ARCH, false));
209     constexpr RegMask ARCH_FP_REGS_MASK(panda::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 
ConvertToIFrame(FrameKind * prevFrameKind,uint32_t * numInlinedMethods)520 Frame *StackWalker::ConvertToIFrame(FrameKind *prevFrameKind, uint32_t *numInlinedMethods)
521 {
522     if (!IsCFrame()) {
523         return GetIFrame();
524     }
525     auto &cframe = GetCFrame();
526 
527     auto inlineDepth = inlineDepth_;
528     bool isInvoke = false;
529 
530     void *prevFrame;
531     bool isInit = false;
532     if (IsInlined()) {
533         inlineDepth_--;
534         *numInlinedMethods = *numInlinedMethods + 1;
535         prevFrame = ConvertToIFrame(prevFrameKind, numInlinedMethods);
536         auto iframe = static_cast<Frame *>(prevFrame);
537         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
538         auto pc = iframe->GetMethod()->GetInstructions() + iframe->GetBytecodeOffset();
539         if (BytecodeInstruction(pc).HasFlag(BytecodeInstruction::INIT_OBJ)) {
540             isInit = true;
541         }
542     } else {
543         auto prev = cframe.GetPrevFrame();
544         if (prev == nullptr) {
545             *prevFrameKind = FrameKind::NONE;
546             prevFrame = nullptr;
547         } else {
548             if (IsCompilerBoundFrame(prev)) {
549                 isInvoke = true;
550                 prevFrame = reinterpret_cast<Frame *>(
551                     StackWalker::GetPrevFromBoundary<FrameKind::COMPILER>(cframe.GetPrevFrame()));
552                 if (prevFrameKind != nullptr) {
553                     *prevFrameKind = FrameKind::INTERPRETER;
554                 }
555             } else {
556                 prevFrame = cframe.GetPrevFrame();
557                 if (prevFrameKind != nullptr) {
558                     *prevFrameKind = FrameKind::COMPILER;
559                 }
560             }
561         }
562     }
563     inlineDepth_ = inlineDepth;
564     auto vregList =
565         codeInfo_.GetVRegList(stackmap_, inlineDepth_, mem::InternalAllocator<>::GetInternalAllocatorFromRuntime());
566     auto method = GetMethod();
567     Frame *frame;
568 
569     if (IsDynamicMethod()) {
570         /* If there is a usage of rest arguments in dynamic function, then a managed object to contain actual arguments
571          * is constructed in prologue. Thus there is no need to reconstruct rest arguments here
572          */
573         auto numActualArgs = method->GetNumArgs();
574         /* If there are no arguments-keeping object construction in execution path, the number of actual args may be
575          * retreived from cframe
576          */
577 
578         size_t frameNumVregs = method->GetNumVregs() + numActualArgs;
579         frame = interpreter::RuntimeInterface::CreateFrameWithActualArgs<true>(frameNumVregs, numActualArgs, method,
580                                                                                reinterpret_cast<Frame *>(prevFrame));
581         frame->SetDynamic();
582         DynamicFrameHandler frameHandler(frame);
583         static constexpr uint8_t ACC_OFFSET = VRegInfo::ENV_COUNT + 1;
584         for (size_t i = 0; i < vregList.size() - ACC_OFFSET; i++) {
585             auto vreg = vregList[i];
586             if (!vreg.IsLive()) {
587                 continue;
588             }
589             auto regRef = frameHandler.GetVReg(i);
590             GetCFrame().GetPackVRegValue(vreg, codeInfo_, calleeStack_.stack.data(), regRef);
591         }
592         {
593             auto vreg = vregList[vregList.size() - ACC_OFFSET];
594             if (vreg.IsLive()) {
595                 auto regRef = frameHandler.GetAccAsVReg();
596                 GetCFrame().GetPackVRegValue(vreg, codeInfo_, calleeStack_.stack.data(), regRef);
597             }
598         }
599         EnvData envData {vregList, GetCFrame(), codeInfo_, calleeStack_.stack.data()};
600         Thread::GetCurrent()->GetVM()->GetLanguageContext().RestoreEnv(frame, envData);
601     } else {
602         auto frameNumVregs = method->GetNumVregs() + method->GetNumArgs();
603         ASSERT((frameNumVregs + 1) >= vregList.size());
604         frame = interpreter::RuntimeInterface::CreateFrame(frameNumVregs, method, reinterpret_cast<Frame *>(prevFrame));
605         StaticFrameHandler frameHandler(frame);
606         for (size_t i = 0; i < vregList.size(); i++) {
607             auto vreg = vregList[i];
608             if (!vreg.IsLive()) {
609                 continue;
610             }
611 
612             bool isAcc = i == (vregList.size() - 1);
613             auto regRef = isAcc ? frame->GetAccAsVReg() : frameHandler.GetVReg(i);
614             GetCFrame().GetVRegValue(vreg, codeInfo_, calleeStack_.stack.data(), regRef);
615         }
616     }
617 
618     frame->SetDeoptimized();
619     frame->SetBytecodeOffset(GetBytecodePc());
620     if (isInit) {
621         frame->SetInitobj();
622     }
623     if (isInvoke) {
624         frame->SetInvoke();
625     }
626     return frame;
627 }
628 
IsDynamicMethod() const629 bool StackWalker::IsDynamicMethod() const
630 {
631     // Dynamic method may have no class
632     return GetMethod()->GetClass() == nullptr ||
633            panda::panda_file::IsDynamicLanguage(Runtime::GetCurrent()->GetLanguageContext(*GetMethod()).GetLanguage());
634 }
635 
Verify()636 void StackWalker::Verify()
637 {
638     for (; HasFrame(); NextFrame()) {
639 #ifndef NDEBUG
640         ASSERT(GetMethod() != nullptr);
641         IterateVRegsWithInfo([this]([[maybe_unused]] const auto &regInfo, const auto &vreg) {
642             if (regInfo.GetType() == compiler::VRegInfo::Type::ANY) {
643                 ASSERT(IsDynamicMethod());
644                 return true;
645             }
646 
647             if (vreg.HasObject()) {
648                 // Use Frame::VRegister::HasObject() to detect objects
649                 ASSERT(regInfo.IsObject());
650                 if (ObjectHeader *object = vreg.GetReference(); object != nullptr) {
651                     auto *cls = object->ClassAddr<Class>();
652                     if (!IsAddressInObjectsHeap(cls)) {
653                         StackWalker::Create(ManagedThread::GetCurrent()).Dump(std::cerr, true);
654                         // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
655                         LOG(FATAL, INTEROP) << "Wrong class " << cls << " for object " << object << "\n";
656                     } else {
657                         cls->GetDescriptor();
658                     }
659                 }
660             } else {
661                 ASSERT(!regInfo.IsObject());
662                 vreg.GetLong();
663             }
664             return true;
665         });
666 
667         if (IsCFrame()) {
668             IterateObjects([this](const auto &vreg) {
669                 if (IsDynamicMethod()) {
670                     ASSERT(vreg.HasObject());
671                     return true;
672                 }
673 
674                 ASSERT(vreg.HasObject());
675                 if (ObjectHeader *object = vreg.GetReference(); object != nullptr) {
676                     ASSERT(IsAddressInObjectsHeap(object));
677                     auto *cls = object->ClassAddr<Class>();
678                     if (!IsAddressInObjectsHeap(cls)) {
679                         StackWalker::Create(ManagedThread::GetCurrent()).Dump(std::cerr, true);
680                         // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
681                         LOG(FATAL, INTEROP) << "Wrong class " << cls << " for object " << object << "\n";
682                     } else {
683                         cls->GetDescriptor();
684                     }
685                 }
686                 return true;
687             });
688         }
689 #endif  // ifndef NDEBUG
690     }
691 }
692 
693 // Dump function change StackWalker object-state, that's why it may be called only
694 // with rvalue reference.
Dump(std::ostream & os,bool printVregs)695 void StackWalker::Dump(std::ostream &os, bool printVregs /* = false */) &&
696 {
697     [[maybe_unused]] static constexpr size_t WIDTH_INDEX = 4;
698     [[maybe_unused]] static constexpr size_t WIDTH_REG = 10;
699     [[maybe_unused]] static constexpr size_t WIDTH_FRAME = 8;
700     [[maybe_unused]] static constexpr size_t WIDTH_LOCATION = 12;
701     [[maybe_unused]] static constexpr size_t WIDTH_TYPE = 20;
702 
703     size_t frameIndex = 0;
704     os << "Panda call stack:\n";
705     for (; HasFrame(); NextFrame()) {
706         os << std::setw(WIDTH_INDEX) << std::setfill(' ') << std::right << std::dec << frameIndex << ": "
707            << std::setfill('0');
708         os << std::setw(WIDTH_FRAME) << std::hex;
709         os << (IsCFrame() ? reinterpret_cast<Frame *>(GetCFrame().GetFrameOrigin()) : GetIFrame()) << " in ";
710         DumpFrame(os);
711         os << std::endl;
712         if (printVregs) {
713             IterateVRegsWithInfo([this, &os](auto regInfo, const auto &vreg) {
714                 os << "     " << std::setw(WIDTH_REG) << std::setfill(' ') << std::right
715                    << (regInfo.IsSpecialVReg() ? VRegInfo::VRegTypeToString(regInfo.GetVRegType())
716                                                : (std::string("v") + std::to_string(regInfo.GetIndex())));
717                 os << " = ";
718                 if (regInfo.GetType() == compiler::VRegInfo::Type::ANY) {
719                     os << "0x";
720                 }
721                 os << std::left;
722                 os << std::setw(WIDTH_TYPE) << std::setfill(' ');
723                 switch (regInfo.GetType()) {
724                     case compiler::VRegInfo::Type::INT64:
725                     case compiler::VRegInfo::Type::INT32:
726                         os << std::dec << vreg.GetLong();
727                         break;
728                     case compiler::VRegInfo::Type::FLOAT64:
729                         os << vreg.GetDouble();
730                         break;
731                     case compiler::VRegInfo::Type::FLOAT32:
732                         os << vreg.GetFloat();
733                         break;
734                     case compiler::VRegInfo::Type::BOOL:
735                         os << (vreg.Get() ? "true" : "false");
736                         break;
737                     case compiler::VRegInfo::Type::OBJECT:
738                         os << vreg.GetReference();
739                         break;
740                     case compiler::VRegInfo::Type::ANY: {
741                         os << std::hex << static_cast<uint64_t>(vreg.GetValue());
742                         break;
743                     }
744                     case compiler::VRegInfo::Type::UNDEFINED:
745                         os << "undfined";
746                         break;
747                     default:
748                         os << "unknown";
749                         break;
750                 }
751                 os << std::setw(WIDTH_LOCATION) << std::setfill(' ') << regInfo.GetTypeString();  // NOLINT
752                 if (IsCFrame()) {
753                     os << regInfo.GetLocationString() << ":" << std::dec << helpers::ToSigned(regInfo.GetValue());
754                 } else {
755                     os << '-';
756                 }
757                 os << std::endl;
758                 return true;
759             });
760         }
761         if (IsCFrame() && printVregs) {
762             os << "roots:";
763             IterateObjectsWithInfo([&os](auto &regInfo, const auto &vreg) {
764                 ASSERT(vreg.HasObject());
765                 os << " " << regInfo.GetLocationString() << "[" << std::dec << regInfo.GetValue() << "]=" << std::hex
766                    << vreg.GetReference();
767                 return true;
768             });
769             os << std::endl;
770         }
771         frameIndex++;
772     }
773 }
774 
DumpFrame(std::ostream & os)775 void StackWalker::DumpFrame(std::ostream &os)
776 {
777     os << GetMethod()->GetFullName();
778     if (IsCFrame()) {
779         if (GetCFrame().IsNative()) {
780             os << " (native)";
781         } else {
782             os << " (compiled" << (GetCFrame().IsOsr() ? "/osr" : "") << ": npc=" << GetNativePc()
783                << (IsInlined() ? ", inlined) " : ") ");
784             if (IsInlined()) {
785                 codeInfo_.DumpInlineInfo(os, stackmap_, inlineDepth_);
786             } else {
787                 codeInfo_.Dump(os, stackmap_);
788             }
789         }
790     } else {
791         os << " (managed)";
792     }
793 }
794 
795 }  // namespace panda
796