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 ®Info, 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 ®Info)
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 ®Info, 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