• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &reg_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