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 ®Info, 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 ®Info)705 void StackWalker::DumpVRegLocation(std::ostream &os, VRegInfo ®Info)
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 ®Info, 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