1 /*
2 * Copyright (c) 2021 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 "runtime/include/stack_walker-inl.h"
17 #include "runtime/entrypoints/entrypoints.h"
18 #include "runtime/include/runtime.h"
19 #include "runtime/include/thread.h"
20 #include "runtime/include/panda_vm.h"
21
22 #include <iomanip>
23
24 namespace panda {
25
StackWalker(ManagedThread * thread,UnwindPolicy policy)26 StackWalker::StackWalker(ManagedThread *thread, UnwindPolicy policy)
27 : StackWalker(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc(), policy)
28 {
29 #ifndef NDEBUG
30 if (Runtime::GetOptions().IsVerifyCallStack()) {
31 StackWalker(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc(), policy)
32 .Verify();
33 }
34 #endif
35 }
36
37 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
StackWalker(void * fp,bool is_frame_compiled,uintptr_t npc,UnwindPolicy policy)38 StackWalker::StackWalker(void *fp, bool is_frame_compiled, uintptr_t npc, UnwindPolicy policy)
39 {
40 frame_ = GetTopFrameFromFp(fp, is_frame_compiled, npc);
41 if (policy == UnwindPolicy::SKIP_INLINED) {
42 inline_depth_ = -1;
43 }
44 }
45
Reset(ManagedThread * thread)46 void StackWalker::Reset(ManagedThread *thread)
47 {
48 frame_ = GetTopFrameFromFp(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc());
49 }
50
51 /* static */
GetTopFrameFromFp(void * ptr,bool is_frame_compiled,uintptr_t npc)52 typename StackWalker::FrameVariant StackWalker::GetTopFrameFromFp(void *ptr, bool is_frame_compiled, uintptr_t npc)
53 {
54 if (!is_frame_compiled) {
55 return reinterpret_cast<Frame *>(ptr);
56 }
57
58 if (IsBoundaryFrame<FrameKind::INTERPRETER>(ptr)) {
59 auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(ptr);
60 if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
61 return CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
62 GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
63 GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp));
64 }
65 return CreateCFrame(
66 GetPrevFromBoundary<FrameKind::INTERPRETER>(ptr), GetReturnAddressFromBoundary<FrameKind::INTERPRETER>(ptr),
67 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
68 reinterpret_cast<SlotType *>(ptr) + BoundaryFrame<FrameKind::INTERPRETER>::CALLEES_OFFSET); // NOLINT
69 }
70 return CreateCFrame(reinterpret_cast<SlotType *>(ptr), npc, nullptr);
71 }
72
GetMethod()73 Method *StackWalker::GetMethod()
74 {
75 ASSERT(HasFrame());
76 if (!IsCFrame()) {
77 return GetIFrame()->GetMethod();
78 }
79 auto &cframe = GetCFrame();
80 ASSERT(cframe.IsJni());
81 return cframe.GetMethod();
82 }
83
84 template <bool create>
CreateCFrameForC2IBridge(Frame * frame)85 StackWalker::CFrameType StackWalker::CreateCFrameForC2IBridge(Frame *frame)
86 {
87 auto prev = GetPrevFromBoundary<FrameKind::INTERPRETER>(frame);
88 ASSERT(GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) != FrameBridgeKind::BYPASS);
89 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
90 if constexpr (create) {
91 return CreateCFrame(reinterpret_cast<SlotType *>(prev),
92 GetReturnAddressFromBoundary<FrameKind::INTERPRETER>(frame),
93 GetCalleeStackFromBoundary<FrameKind::INTERPRETER>(frame));
94 }
95 return CFrameType(prev);
96 }
97
CreateCFrame(void * ptr,uintptr_t npc,SlotType * callee_stack,CalleeStorage * prev_callees)98 StackWalker::CFrameType StackWalker::CreateCFrame(void *ptr, [[maybe_unused]] uintptr_t npc,
99 [[maybe_unused]] SlotType *callee_stack,
100 [[maybe_unused]] CalleeStorage *prev_callees)
101 {
102 CFrameType cframe(ptr);
103 ASSERT(cframe.IsNativeMethod());
104 return cframe;
105 }
106
InitCalleeBuffer(SlotType * callee_stack,CalleeStorage * prev_callees)107 void StackWalker::InitCalleeBuffer(SlotType *callee_stack, CalleeStorage *prev_callees)
108 {
109 if (callee_stack == nullptr && prev_callees == nullptr) {
110 return;
111 }
112
113 bool prev_is_jni = IsCFrame() ? GetCFrame().IsJni() : false;
114 size_t callee_regs_count = GetCalleeRegsCount(ARCH, false);
115 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
116 auto start_slot = callee_stack - callee_regs_count;
117 for (size_t reg = GetFirstCalleeReg(ARCH, false); reg <= GetLastCalleeReg(ARCH, false); reg++) {
118 size_t offset = reg - GetFirstCalleeReg(ARCH, false);
119 // if it's a top cframe or previous frame has saved the register, then copy it from previous frame's stack
120 if (prev_callees == nullptr || prev_is_jni || (prev_callees->callee_regs_mask & (1U << reg)) != 0) {
121 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
122 callee_stack_.stack[offset] =
123 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
124 start_slot + (callee_regs_count - Popcount(callee_stack_.callee_regs_mask >> reg));
125 } else {
126 callee_stack_.stack[offset] = prev_callees->stack[offset];
127 }
128 }
129 size_t callee_vregs_count = GetCalleeRegsCount(ARCH, true);
130 start_slot -= callee_vregs_count; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
131 for (size_t reg = GetFirstCalleeReg(ARCH, true); reg <= GetLastCalleeReg(ARCH, true); reg++) {
132 size_t offset = callee_regs_count + reg - GetFirstCalleeReg(ARCH, true);
133 // if it's a top cframe or previous frame has saved the register, then copy it from previous frame's stack
134 if (prev_callees == nullptr || prev_is_jni || (prev_callees->callee_fp_regs_mask & (1U << reg)) != 0) {
135 callee_stack_.stack[offset] =
136 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
137 start_slot + (callee_vregs_count - Popcount(callee_stack_.callee_fp_regs_mask >> reg));
138 } else {
139 callee_stack_.stack[offset] = prev_callees->stack[offset];
140 }
141 }
142 }
143
GetVRegValue(size_t vreg_num)144 Frame::VRegister StackWalker::GetVRegValue(size_t vreg_num)
145 {
146 ASSERT(!IsCFrame());
147 ASSERT(vreg_num < GetIFrame()->GetSize());
148 return GetIFrame()->GetVReg(vreg_num);
149 }
150
151 template <typename T>
SetVRegValue(VRegInfo reg_info,T value)152 void StackWalker::SetVRegValue(VRegInfo reg_info, T value)
153 {
154 if (IsCFrame()) {
155 auto &cframe = GetCFrame();
156 if constexpr (sizeof(T) == sizeof(uint64_t)) { // NOLINT
157 cframe.SetVRegValue(reg_info, bit_cast<uint64_t>(value), callee_stack_.stack.data());
158 } else { // NOLINT
159 static_assert(sizeof(T) == sizeof(uint32_t));
160 cframe.SetVRegValue(reg_info, static_cast<uint64_t>(bit_cast<uint32_t>(value)), callee_stack_.stack.data());
161 }
162 } else {
163 auto &vreg = GetIFrame()->GetVReg(reg_info.GetIndex());
164 if constexpr (std::is_same_v<T, ObjectHeader *>) { // NOLINT
165 ASSERT(vreg.HasObject() && "Trying to change object variable by scalar value");
166 vreg.SetReference(value);
167 } else { // NOLINT
168 ASSERT(!vreg.HasObject() && "Trying to change object variable by scalar value");
169 vreg.Set(value);
170 }
171 }
172 }
173
174 template void StackWalker::SetVRegValue(VRegInfo reg_info, uint32_t value);
175 template void StackWalker::SetVRegValue(VRegInfo reg_info, int32_t value);
176 template void StackWalker::SetVRegValue(VRegInfo reg_info, uint64_t value);
177 template void StackWalker::SetVRegValue(VRegInfo reg_info, int64_t value);
178 template void StackWalker::SetVRegValue(VRegInfo reg_info, float value);
179 template void StackWalker::SetVRegValue(VRegInfo reg_info, double value);
180 template void StackWalker::SetVRegValue(VRegInfo reg_info, ObjectHeader *value);
181
NextFrame()182 void StackWalker::NextFrame()
183 {
184 if (IsCFrame()) {
185 NextFromCFrame();
186 } else {
187 NextFromIFrame();
188 }
189 }
190
NextFromCFrame()191 void StackWalker::NextFromCFrame()
192 {
193 if (IsInlined()) {
194 if (policy_ != UnwindPolicy::SKIP_INLINED) {
195 inline_depth_--;
196 return;
197 }
198 inline_depth_ = -1;
199 }
200 if (policy_ == UnwindPolicy::ONLY_INLINED) {
201 frame_ = nullptr;
202 return;
203 }
204 auto prev = GetCFrame().GetPrevFrame();
205 if (prev == nullptr) {
206 frame_ = nullptr;
207 return;
208 }
209 auto frame_method = GetBoundaryFrameMethod<FrameKind::COMPILER>(prev);
210 switch (frame_method) {
211 case FrameBridgeKind::INTERPRETER_TO_COMPILED_CODE: {
212 auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
213 if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
214 frame_ = CreateCFrameForC2IBridge<true>(prev_frame);
215 break;
216 }
217
218 frame_ = reinterpret_cast<Frame *>(prev_frame);
219 break;
220 }
221 case FrameBridgeKind::BYPASS: {
222 auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
223 if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
224 frame_ = CreateCFrameForC2IBridge<true>(prev_frame);
225 break;
226 }
227 frame_ = CreateCFrame(reinterpret_cast<SlotType *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev)),
228 GetReturnAddressFromBoundary<FrameKind::COMPILER>(prev),
229 GetCalleeStackFromBoundary<FrameKind::COMPILER>(prev));
230 break;
231 }
232 default:
233 prev_callee_stack_ = callee_stack_;
234 frame_ = CreateCFrame(reinterpret_cast<SlotType *>(prev), GetCFrame().GetLr(),
235 GetCFrame().GetCalleeSaveStack(), &prev_callee_stack_);
236 break;
237 }
238 }
239
NextFromIFrame()240 void StackWalker::NextFromIFrame()
241 {
242 if (policy_ == UnwindPolicy::ONLY_INLINED) {
243 frame_ = nullptr;
244 return;
245 }
246 auto prev = GetIFrame()->GetPrevFrame();
247 if (prev == nullptr) {
248 frame_ = nullptr;
249 return;
250 }
251 if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
252 auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(prev);
253 if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
254 frame_ = CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
255 GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
256 GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp));
257 } else {
258 frame_ = CreateCFrameForC2IBridge<true>(prev);
259 }
260 } else {
261 frame_ = reinterpret_cast<Frame *>(prev);
262 }
263 }
264
GetNextFrame()265 FrameAccessor StackWalker::GetNextFrame()
266 {
267 if (IsCFrame()) {
268 if (IsInlined()) {
269 return FrameAccessor(frame_);
270 }
271 auto prev = GetCFrame().GetPrevFrame();
272 if (prev == nullptr) {
273 return FrameAccessor(nullptr);
274 }
275 auto frame_method = GetBoundaryFrameMethod<FrameKind::COMPILER>(prev);
276 switch (frame_method) {
277 case FrameBridgeKind::INTERPRETER_TO_COMPILED_CODE: {
278 auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
279 if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
280 return FrameAccessor(CreateCFrameForC2IBridge<false>(prev_frame));
281 }
282 return FrameAccessor(prev_frame);
283 }
284 case FrameBridgeKind::BYPASS: {
285 auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
286 if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
287 return FrameAccessor(CreateCFrameForC2IBridge<false>(prev_frame));
288 }
289 return FrameAccessor(
290 CFrameType(reinterpret_cast<SlotType *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev))));
291 }
292 default:
293 return FrameAccessor(CFrameType(reinterpret_cast<SlotType *>(prev)));
294 }
295 } else {
296 auto prev = GetIFrame()->GetPrevFrame();
297 if (prev == nullptr) {
298 return FrameAccessor(nullptr);
299 }
300 if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
301 auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(prev);
302 if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
303 return FrameAccessor(CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
304 GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
305 GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp)));
306 }
307 return FrameAccessor(CreateCFrameForC2IBridge<false>(prev));
308 }
309 return FrameAccessor(reinterpret_cast<Frame *>(prev));
310 }
311 }
312
GetPreviousFrameKind() const313 FrameKind StackWalker::GetPreviousFrameKind() const
314 {
315 if (IsCFrame()) {
316 auto prev = GetCFrame().GetPrevFrame();
317 if (prev == nullptr) {
318 return FrameKind::NONE;
319 }
320 if (IsBoundaryFrame<FrameKind::COMPILER>(prev)) {
321 return FrameKind::INTERPRETER;
322 }
323 return FrameKind::COMPILER;
324 }
325 auto prev = GetIFrame()->GetPrevFrame();
326 if (prev == nullptr) {
327 return FrameKind::NONE;
328 }
329 if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
330 return FrameKind::COMPILER;
331 }
332 return FrameKind::INTERPRETER;
333 }
334
IsCompilerBoundFrame(SlotType * prev)335 bool StackWalker::IsCompilerBoundFrame(SlotType *prev)
336 {
337 if (IsBoundaryFrame<FrameKind::COMPILER>(prev)) {
338 return true;
339 }
340 if (GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) == FrameBridgeKind::BYPASS) {
341 auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
342 // Case for clinit:
343 // Compiled code -> C2I -> InitializeClass -> call clinit -> I2C -> compiled code for clinit
344 if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
345 return true;
346 }
347 }
348
349 return false;
350 }
351
ConvertToIFrame(FrameKind * prev_frame_kind,uint32_t * num_inlined_methods)352 Frame *StackWalker::ConvertToIFrame([[maybe_unused]] FrameKind *prev_frame_kind,
353 [[maybe_unused]] uint32_t *num_inlined_methods)
354 {
355 if (!IsCFrame()) {
356 return GetIFrame();
357 }
358
359 UNREACHABLE();
360 }
361
IsDynamicMethod() const362 bool StackWalker::IsDynamicMethod() const
363 {
364 // Dynamic method may have no class
365 return GetMethod()->GetClass() == nullptr ||
366 Runtime::GetCurrent()->GetLanguageContext(*GetMethod()).IsDynamicLanguage();
367 }
368
Verify()369 void StackWalker::Verify()
370 {
371 #ifndef NDEBUG
372 for (; HasFrame(); NextFrame()) {
373 ASSERT(GetMethod() != nullptr);
374 [[maybe_unused]] bool is_dynamic = IsDynamicMethod();
375 IterateVRegsWithInfo([this, is_dynamic]([[maybe_unused]] const auto ®_info, const auto &vreg) {
376 if (vreg.HasObject()) {
377 // In dynamic methods all reg_infos are generic values.
378 // Use Frame::VRegister::HasObject() to detect objects
379 ASSERT(is_dynamic || reg_info.IsObject());
380 if (ObjectHeader *object = vreg.GetReference(); object != nullptr && IsCFrame()) {
381 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_NESTING_LEVEL)
382 if (auto *bcls = object->ClassAddr<BaseClass>(); bcls != nullptr && !(bcls->IsDynamicClass())) {
383 auto cls = static_cast<Class *>(bcls);
384 cls->GetName();
385 }
386 }
387 } else {
388 ASSERT(!reg_info.IsObject());
389 vreg.GetLong();
390 }
391 return true;
392 });
393
394 if (IsCFrame()) {
395 IterateObjects([](const auto &vreg) {
396 ASSERT(vreg.HasObject());
397 if (ObjectHeader *object = vreg.GetReference(); object != nullptr) {
398 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_NESTING_LEVEL)
399 if (auto *bcls = object->ClassAddr<BaseClass>(); bcls != nullptr && !(bcls->IsDynamicClass())) {
400 auto cls = static_cast<Class *>(bcls);
401 cls->GetName();
402 }
403 }
404 return true;
405 });
406 }
407 }
408 #endif // ifndef NDEBUG
409 }
410
411 // Dump function change StackWalker object-state, that's why it may be called only
412 // with rvalue reference.
413 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_COMMENT_ADDSPASE,C_RULE_ID_FUNCTION_SIZE)
Dump(std::ostream & os,bool print_vregs)414 void StackWalker::Dump(std::ostream &os, bool print_vregs /* = false */) &&
415 {
416 [[maybe_unused]] static constexpr size_t WIDTH_INDEX = 4;
417 [[maybe_unused]] static constexpr size_t WIDTH_REG = 4;
418 [[maybe_unused]] static constexpr size_t WIDTH_FRAME = 8;
419 [[maybe_unused]] static constexpr size_t WIDTH_LOCATION = 12;
420 [[maybe_unused]] static constexpr size_t WIDTH_TYPE = 20;
421
422 size_t frame_index = 0;
423 os << "Panda call stack:\n";
424 for (; HasFrame(); NextFrame()) {
425 os << std::setw(WIDTH_INDEX) << std::setfill(' ') << std::right << std::dec << frame_index << ": "
426 << std::setfill('0');
427 os << std::setw(WIDTH_FRAME) << std::hex;
428 os << (IsCFrame() ? reinterpret_cast<Frame *>(GetCFrame().GetFrameOrigin()) : GetIFrame()) << " in ";
429 DumpFrame(os);
430 os << std::endl;
431 if (print_vregs) {
432 IterateVRegsWithInfo([this, &os](auto reg_info, auto vreg) {
433 os << " " << std::setw(WIDTH_REG) << std::setfill(' ') << std::right
434 << (reg_info.IsAccumulator() ? "acc" : (std::string("v") + std::to_string(reg_info.GetIndex())));
435 os << " = " << std::left;
436 os << std::setw(WIDTH_TYPE) << std::setfill(' ');
437 switch (reg_info.GetType()) {
438 case VRegInfo::Type::INT64:
439 case VRegInfo::Type::INT32:
440 os << std::dec << vreg.GetLong();
441 break;
442 case VRegInfo::Type::FLOAT64:
443 case VRegInfo::Type::FLOAT32:
444 os << vreg.GetDouble();
445 break;
446 case VRegInfo::Type::BOOL:
447 os << (vreg.Get() ? "true" : "false");
448 break;
449 case VRegInfo::Type::OBJECT:
450 os << vreg.GetReference();
451 break;
452 case VRegInfo::Type::UNDEFINED:
453 os << "undefined";
454 break;
455 default:
456 os << "unknown";
457 break;
458 }
459 os << std::setw(WIDTH_LOCATION) << std::setfill(' ') << reg_info.GetTypeString(); // NOLINT
460 if (IsCFrame()) {
461 os << reg_info.GetLocationString() << ":" << std::dec << helpers::ToSigned(reg_info.GetValue());
462 } else {
463 os << '-';
464 }
465 os << std::endl;
466 return true;
467 });
468 }
469 frame_index++;
470 }
471 }
472
DumpFrame(std::ostream & os)473 void StackWalker::DumpFrame(std::ostream &os)
474 {
475 auto method = GetMethod();
476 os << method->GetFullName();
477 if (IsCFrame()) {
478 if (GetCFrame().IsJni()) {
479 os << " (native)";
480 } else {
481 os << " (compiled" << (GetCFrame().IsOsr() ? "/osr" : "") << ": npc=" << GetNativePc()
482 << (IsInlined() ? ", inlined) " : ") ");
483 }
484 } else {
485 os << " (managed)";
486 }
487 }
488
489 } // namespace panda
490