Files
ark_runtime_core/runtime/stack_walker.cpp
T
wangyantian 30ad7e3c60 Fix codex warnings
Signed-off-by: wangyantian <wangyantian@huawei.com>
2022-04-24 12:34:56 +08:00

491 lines
19 KiB
C++

/*
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "runtime/include/stack_walker-inl.h"
#include "runtime/entrypoints/entrypoints.h"
#include "runtime/include/runtime.h"
#include "runtime/include/thread.h"
#include "runtime/include/panda_vm.h"
#include <iomanip>
namespace panda {
StackWalker::StackWalker(ManagedThread *thread, UnwindPolicy policy)
: StackWalker(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc(), policy)
{
#ifndef NDEBUG
if (Runtime::GetOptions().IsVerifyCallStack()) {
StackWalker(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc(), policy)
.Verify();
}
#endif
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
StackWalker::StackWalker(void *fp, bool is_frame_compiled, uintptr_t npc, UnwindPolicy policy)
{
frame_ = GetTopFrameFromFp(fp, is_frame_compiled, npc);
if (policy == UnwindPolicy::SKIP_INLINED) {
inline_depth_ = -1;
}
}
void StackWalker::Reset(ManagedThread *thread)
{
frame_ = GetTopFrameFromFp(thread->GetCurrentFrame(), thread->IsCurrentFrameCompiled(), thread->GetNativePc());
}
/* static */
typename StackWalker::FrameVariant StackWalker::GetTopFrameFromFp(void *ptr, bool is_frame_compiled, uintptr_t npc)
{
if (!is_frame_compiled) {
return reinterpret_cast<Frame *>(ptr);
}
if (IsBoundaryFrame<FrameKind::INTERPRETER>(ptr)) {
auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(ptr);
if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
return CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp));
}
return CreateCFrame(
GetPrevFromBoundary<FrameKind::INTERPRETER>(ptr), GetReturnAddressFromBoundary<FrameKind::INTERPRETER>(ptr),
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
reinterpret_cast<SlotType *>(ptr) + BoundaryFrame<FrameKind::INTERPRETER>::CALLEES_OFFSET); // NOLINT
}
return CreateCFrame(reinterpret_cast<SlotType *>(ptr), npc, nullptr);
}
Method *StackWalker::GetMethod()
{
ASSERT(HasFrame());
if (!IsCFrame()) {
return GetIFrame()->GetMethod();
}
auto &cframe = GetCFrame();
ASSERT(cframe.IsJni());
return cframe.GetMethod();
}
template <bool create>
StackWalker::CFrameType StackWalker::CreateCFrameForC2IBridge(Frame *frame)
{
auto prev = GetPrevFromBoundary<FrameKind::INTERPRETER>(frame);
ASSERT(GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) != FrameBridgeKind::BYPASS);
// NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
if constexpr (create) {
return CreateCFrame(reinterpret_cast<SlotType *>(prev),
GetReturnAddressFromBoundary<FrameKind::INTERPRETER>(frame),
GetCalleeStackFromBoundary<FrameKind::INTERPRETER>(frame));
}
return CFrameType(prev);
}
StackWalker::CFrameType StackWalker::CreateCFrame(void *ptr, [[maybe_unused]] uintptr_t npc,
[[maybe_unused]] SlotType *callee_stack,
[[maybe_unused]] CalleeStorage *prev_callees)
{
CFrameType cframe(ptr);
ASSERT(cframe.IsNativeMethod());
return cframe;
}
void StackWalker::InitCalleeBuffer(SlotType *callee_stack, CalleeStorage *prev_callees)
{
if (callee_stack == nullptr && prev_callees == nullptr) {
return;
}
bool prev_is_jni = IsCFrame() ? GetCFrame().IsJni() : false;
size_t callee_regs_count = GetCalleeRegsCount(ARCH, false);
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
auto start_slot = callee_stack - callee_regs_count;
for (size_t reg = GetFirstCalleeReg(ARCH, false); reg <= GetLastCalleeReg(ARCH, false); reg++) {
size_t offset = reg - GetFirstCalleeReg(ARCH, false);
// if it's a top cframe or previous frame has saved the register, then copy it from previous frame's stack
if (prev_callees == nullptr || prev_is_jni || (prev_callees->callee_regs_mask & (1U << reg)) != 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
callee_stack_.stack[offset] =
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
start_slot + (callee_regs_count - static_cast<size_t>(Popcount(callee_stack_.callee_regs_mask >> reg)));
} else {
callee_stack_.stack[offset] = prev_callees->stack[offset];
}
}
size_t callee_vregs_count = GetCalleeRegsCount(ARCH, true);
start_slot -= callee_vregs_count; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for (size_t reg = GetFirstCalleeReg(ARCH, true); reg <= GetLastCalleeReg(ARCH, true); reg++) {
size_t offset = callee_regs_count + reg - GetFirstCalleeReg(ARCH, true);
// if it's a top cframe or previous frame has saved the register, then copy it from previous frame's stack
if (prev_callees == nullptr || prev_is_jni || (prev_callees->callee_fp_regs_mask & (1U << reg)) != 0) {
callee_stack_.stack[offset] =
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
start_slot +
(callee_vregs_count - static_cast<size_t>(Popcount(callee_stack_.callee_fp_regs_mask >> reg)));
} else {
callee_stack_.stack[offset] = prev_callees->stack[offset];
}
}
}
Frame::VRegister StackWalker::GetVRegValue(size_t vreg_num)
{
ASSERT(!IsCFrame());
ASSERT(vreg_num < GetIFrame()->GetSize());
return GetIFrame()->GetVReg(vreg_num);
}
template <typename T>
void StackWalker::SetVRegValue(VRegInfo reg_info, T value)
{
if (IsCFrame()) {
auto &cframe = GetCFrame();
if constexpr (sizeof(T) == sizeof(uint64_t)) { // NOLINT
cframe.SetVRegValue(reg_info, bit_cast<uint64_t>(value), callee_stack_.stack.data());
} else { // NOLINT
static_assert(sizeof(T) == sizeof(uint32_t));
cframe.SetVRegValue(reg_info, static_cast<uint64_t>(bit_cast<uint32_t>(value)), callee_stack_.stack.data());
}
} else {
auto &vreg = GetIFrame()->GetVReg(reg_info.GetIndex());
if constexpr (std::is_same_v<T, ObjectHeader *>) { // NOLINT
ASSERT(vreg.HasObject() && "Trying to change object variable by scalar value");
vreg.SetReference(value);
} else { // NOLINT
ASSERT(!vreg.HasObject() && "Trying to change object variable by scalar value");
vreg.Set(value);
}
}
}
template void StackWalker::SetVRegValue(VRegInfo reg_info, uint32_t value);
template void StackWalker::SetVRegValue(VRegInfo reg_info, int32_t value);
template void StackWalker::SetVRegValue(VRegInfo reg_info, uint64_t value);
template void StackWalker::SetVRegValue(VRegInfo reg_info, int64_t value);
template void StackWalker::SetVRegValue(VRegInfo reg_info, float value);
template void StackWalker::SetVRegValue(VRegInfo reg_info, double value);
template void StackWalker::SetVRegValue(VRegInfo reg_info, ObjectHeader *value);
void StackWalker::NextFrame()
{
if (IsCFrame()) {
NextFromCFrame();
} else {
NextFromIFrame();
}
}
void StackWalker::NextFromCFrame()
{
if (IsInlined()) {
if (policy_ != UnwindPolicy::SKIP_INLINED) {
inline_depth_--;
return;
}
inline_depth_ = -1;
}
if (policy_ == UnwindPolicy::ONLY_INLINED) {
frame_ = nullptr;
return;
}
auto prev = GetCFrame().GetPrevFrame();
if (prev == nullptr) {
frame_ = nullptr;
return;
}
auto frame_method = GetBoundaryFrameMethod<FrameKind::COMPILER>(prev);
switch (frame_method) {
case FrameBridgeKind::INTERPRETER_TO_COMPILED_CODE: {
auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
frame_ = CreateCFrameForC2IBridge<true>(prev_frame);
break;
}
frame_ = reinterpret_cast<Frame *>(prev_frame);
break;
}
case FrameBridgeKind::BYPASS: {
auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
frame_ = CreateCFrameForC2IBridge<true>(prev_frame);
break;
}
frame_ = CreateCFrame(reinterpret_cast<SlotType *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev)),
GetReturnAddressFromBoundary<FrameKind::COMPILER>(prev),
GetCalleeStackFromBoundary<FrameKind::COMPILER>(prev));
break;
}
default:
prev_callee_stack_ = callee_stack_;
frame_ = CreateCFrame(reinterpret_cast<SlotType *>(prev), GetCFrame().GetLr(),
GetCFrame().GetCalleeSaveStack(), &prev_callee_stack_);
break;
}
}
void StackWalker::NextFromIFrame()
{
if (policy_ == UnwindPolicy::ONLY_INLINED) {
frame_ = nullptr;
return;
}
auto prev = GetIFrame()->GetPrevFrame();
if (prev == nullptr) {
frame_ = nullptr;
return;
}
if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(prev);
if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
frame_ = CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp));
} else {
frame_ = CreateCFrameForC2IBridge<true>(prev);
}
} else {
frame_ = reinterpret_cast<Frame *>(prev);
}
}
FrameAccessor StackWalker::GetNextFrame()
{
if (IsCFrame()) {
if (IsInlined()) {
return FrameAccessor(frame_);
}
auto prev = GetCFrame().GetPrevFrame();
if (prev == nullptr) {
return FrameAccessor(nullptr);
}
auto frame_method = GetBoundaryFrameMethod<FrameKind::COMPILER>(prev);
switch (frame_method) {
case FrameBridgeKind::INTERPRETER_TO_COMPILED_CODE: {
auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
return FrameAccessor(CreateCFrameForC2IBridge<false>(prev_frame));
}
return FrameAccessor(prev_frame);
}
case FrameBridgeKind::BYPASS: {
auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
return FrameAccessor(CreateCFrameForC2IBridge<false>(prev_frame));
}
return FrameAccessor(
CFrameType(reinterpret_cast<SlotType *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev))));
}
default:
return FrameAccessor(CFrameType(reinterpret_cast<SlotType *>(prev)));
}
} else {
auto prev = GetIFrame()->GetPrevFrame();
if (prev == nullptr) {
return FrameAccessor(nullptr);
}
if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
auto bp = GetPrevFromBoundary<FrameKind::INTERPRETER>(prev);
if (GetBoundaryFrameMethod<FrameKind::COMPILER>(bp) == BYPASS) {
return FrameAccessor(CreateCFrame(GetPrevFromBoundary<FrameKind::COMPILER>(bp),
GetReturnAddressFromBoundary<FrameKind::COMPILER>(bp),
GetCalleeStackFromBoundary<FrameKind::COMPILER>(bp)));
}
return FrameAccessor(CreateCFrameForC2IBridge<false>(prev));
}
return FrameAccessor(reinterpret_cast<Frame *>(prev));
}
}
FrameKind StackWalker::GetPreviousFrameKind() const
{
if (IsCFrame()) {
auto prev = GetCFrame().GetPrevFrame();
if (prev == nullptr) {
return FrameKind::NONE;
}
if (IsBoundaryFrame<FrameKind::COMPILER>(prev)) {
return FrameKind::INTERPRETER;
}
return FrameKind::COMPILER;
}
auto prev = GetIFrame()->GetPrevFrame();
if (prev == nullptr) {
return FrameKind::NONE;
}
if (IsBoundaryFrame<FrameKind::INTERPRETER>(prev)) {
return FrameKind::COMPILER;
}
return FrameKind::INTERPRETER;
}
bool StackWalker::IsCompilerBoundFrame(SlotType *prev)
{
if (IsBoundaryFrame<FrameKind::COMPILER>(prev)) {
return true;
}
if (GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) == FrameBridgeKind::BYPASS) {
auto prev_frame = reinterpret_cast<Frame *>(GetPrevFromBoundary<FrameKind::COMPILER>(prev));
// Case for clinit:
// Compiled code -> C2I -> InitializeClass -> call clinit -> I2C -> compiled code for clinit
if (prev_frame != nullptr && IsBoundaryFrame<FrameKind::INTERPRETER>(prev_frame)) {
return true;
}
}
return false;
}
Frame *StackWalker::ConvertToIFrame([[maybe_unused]] FrameKind *prev_frame_kind,
[[maybe_unused]] uint32_t *num_inlined_methods)
{
if (!IsCFrame()) {
return GetIFrame();
}
UNREACHABLE();
}
bool StackWalker::IsDynamicMethod() const
{
// Dynamic method may have no class
return GetMethod()->GetClass() == nullptr ||
Runtime::GetCurrent()->GetLanguageContext(*GetMethod()).IsDynamicLanguage();
}
void StackWalker::Verify()
{
#ifndef NDEBUG
for (; HasFrame(); NextFrame()) {
ASSERT(GetMethod() != nullptr);
[[maybe_unused]] bool is_dynamic = IsDynamicMethod();
IterateVRegsWithInfo([this, is_dynamic]([[maybe_unused]] const auto &reg_info, const auto &vreg) {
if (vreg.HasObject()) {
// In dynamic methods all reg_infos are generic values.
// Use Frame::VRegister::HasObject() to detect objects
ASSERT(is_dynamic || reg_info.IsObject());
if (ObjectHeader *object = vreg.GetReference(); object != nullptr && IsCFrame()) {
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_NESTING_LEVEL)
if (auto *bcls = object->ClassAddr<BaseClass>(); bcls != nullptr && !(bcls->IsDynamicClass())) {
auto cls = static_cast<Class *>(bcls);
cls->GetName();
}
}
} else {
ASSERT(!reg_info.IsObject());
vreg.GetLong();
}
return true;
});
if (IsCFrame()) {
IterateObjects([](const auto &vreg) {
ASSERT(vreg.HasObject());
if (ObjectHeader *object = vreg.GetReference(); object != nullptr) {
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_NESTING_LEVEL)
if (auto *bcls = object->ClassAddr<BaseClass>(); bcls != nullptr && !(bcls->IsDynamicClass())) {
auto cls = static_cast<Class *>(bcls);
cls->GetName();
}
}
return true;
});
}
}
#endif // ifndef NDEBUG
}
// Dump function change StackWalker object-state, that's why it may be called only
// with rvalue reference.
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_COMMENT_ADDSPASE,C_RULE_ID_FUNCTION_SIZE)
void StackWalker::Dump(std::ostream &os, bool print_vregs /* = false */) &&
{
[[maybe_unused]] static constexpr size_t WIDTH_INDEX = 4;
[[maybe_unused]] static constexpr size_t WIDTH_REG = 4;
[[maybe_unused]] static constexpr size_t WIDTH_FRAME = 8;
[[maybe_unused]] static constexpr size_t WIDTH_LOCATION = 12;
[[maybe_unused]] static constexpr size_t WIDTH_TYPE = 20;
size_t frame_index = 0;
os << "Panda call stack:\n";
for (; HasFrame(); NextFrame()) {
os << std::setw(WIDTH_INDEX) << std::setfill(' ') << std::right << std::dec << frame_index << ": "
<< std::setfill('0');
os << std::setw(WIDTH_FRAME) << std::hex;
os << (IsCFrame() ? reinterpret_cast<Frame *>(GetCFrame().GetFrameOrigin()) : GetIFrame()) << " in ";
DumpFrame(os);
os << std::endl;
if (print_vregs) {
IterateVRegsWithInfo([this, &os](auto reg_info, auto vreg) {
os << " " << std::setw(WIDTH_REG) << std::setfill(' ') << std::right
<< (reg_info.IsAccumulator() ? "acc" : (std::string("v") + std::to_string(reg_info.GetIndex())));
os << " = " << std::left;
os << std::setw(WIDTH_TYPE) << std::setfill(' ');
switch (reg_info.GetType()) {
case VRegInfo::Type::INT64:
case VRegInfo::Type::INT32:
os << std::dec << vreg.GetLong();
break;
case VRegInfo::Type::FLOAT64:
case VRegInfo::Type::FLOAT32:
os << vreg.GetDouble();
break;
case VRegInfo::Type::BOOL:
os << (vreg.Get() ? "true" : "false");
break;
case VRegInfo::Type::OBJECT:
os << vreg.GetReference();
break;
case VRegInfo::Type::UNDEFINED:
os << "undefined";
break;
default:
os << "unknown";
break;
}
os << std::setw(WIDTH_LOCATION) << std::setfill(' ') << reg_info.GetTypeString(); // NOLINT
if (IsCFrame()) {
os << reg_info.GetLocationString() << ":" << std::dec << helpers::ToSigned(reg_info.GetValue());
} else {
os << '-';
}
os << std::endl;
return true;
});
}
frame_index++;
}
}
void StackWalker::DumpFrame(std::ostream &os)
{
auto method = GetMethod();
os << method->GetFullName();
if (IsCFrame()) {
if (GetCFrame().IsJni()) {
os << " (native)";
} else {
os << " (compiled" << (GetCFrame().IsOsr() ? "/osr" : "") << ": npc=" << GetNativePc()
<< (IsInlined() ? ", inlined) " : ") ");
}
} else {
os << " (managed)";
}
}
} // namespace panda