// The mid dex_pcs. Note that we skip the last one since we want to change that for // `handler_dex_pc`. for (size_t index = 0; index < infos.size() - 1; ++index) {
result.push_back(infos[index].GetDexPc());
}
}
// The innermost dex_pc has to be the handler dex_pc. In the case of no inline frames, it will be // just the one dex_pc. In the case of inlining we will be replacing the innermost InlineInfo's // dex_pc with this one.
result.push_back(handler_dex_pc); return result;
}
bool StackVisitor::GetVRegFromDebuggerShadowFrame(uint16_t vreg,
VRegKind kind,
uint32_t* val) const {
size_t frame_id = const_cast<StackVisitor*>(this)->GetFrameId();
ShadowFrame* shadow_frame = thread_->FindDebuggerShadowFrame(frame_id); if (shadow_frame != nullptr) { bool* updated_vreg_flags = thread_->GetUpdatedVRegFlags(frame_id);
DCHECK(updated_vreg_flags != nullptr); if (updated_vreg_flags[vreg]) { // Value is set by the debugger. if (kind == kReferenceVReg) {
*val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
shadow_frame->GetVRegReference(vreg)));
} else {
*val = shadow_frame->GetVReg(vreg);
} returntrue;
}
} // No value is set by the debugger. returnfalse;
}
bool StackVisitor::GetVReg(ArtMethod* m,
uint16_t vreg,
VRegKind kind,
uint32_t* val,
std::optional<DexRegisterLocation> location, bool need_full_register_list) const { if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod()); // Check if there is value set by the debugger. if (GetVRegFromDebuggerShadowFrame(vreg, kind, val)) { returntrue;
} bool result = false; if (cur_oat_quick_method_header_->IsNterpMethodHeader()) {
result = true;
*val = (kind == kReferenceVReg)
? NterpGetVRegReference(cur_quick_frame_, vreg)
: NterpGetVReg(cur_quick_frame_, vreg);
} else {
DCHECK(cur_oat_quick_method_header_->IsOptimized()); if (location.has_value() && kind != kReferenceVReg) {
uint32_t val2 = *val; // The caller already known the register location, so we can use the faster overload // which does not decode the stack maps.
result = GetVRegFromOptimizedCode(location.value(), val); // Compare to the slower overload.
DCHECK_EQ(result, GetVRegFromOptimizedCode(m, vreg, kind, &val2, need_full_register_list));
DCHECK_EQ(*val, val2);
} else {
result = GetVRegFromOptimizedCode(m, vreg, kind, val, need_full_register_list);
}
} if (kind == kReferenceVReg) { // Perform a read barrier in case we are in a different thread and GC is ongoing.
mirror::Object* out = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(*val));
uintptr_t ptr_out = reinterpret_cast<uintptr_t>(GcRoot<mirror::Object>(out).Read());
DCHECK_LT(ptr_out, std::numeric_limits<uint32_t>::max());
*val = static_cast<uint32_t>(ptr_out);
} return result;
} else {
DCHECK(cur_shadow_frame_ != nullptr); if (kind == kReferenceVReg) {
*val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
cur_shadow_frame_->GetVRegReference(vreg)));
} else {
*val = cur_shadow_frame_->GetVReg(vreg);
} returntrue;
}
}
if (kRuntimeQuickCodeISA == InstructionSet::kX86 && is_float) { // X86 float registers are 64-bit and each XMM register is provided as two separate // 32-bit registers by the context.
reg = (location_kind == DexRegisterLocation::Kind::kInFpuRegisterHigh)
? (2 * reg + 1)
: (2 * reg);
}
ShadowFrame* StackVisitor::PrepareSetVReg(ArtMethod* m, uint16_t vreg, bool wide) {
CodeItemDataAccessor accessor(m->DexInstructionData()); if (!accessor.HasCodeItem()) { return nullptr;
}
ShadowFrame* shadow_frame = GetCurrentShadowFrame(); if (shadow_frame == nullptr) { // This is a compiled frame: we must prepare and update a shadow frame that will // be executed by the interpreter after deoptimization of the stack. const size_t frame_id = GetFrameId(); const uint16_t num_regs = accessor.RegistersSize();
shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc());
CHECK(shadow_frame != nullptr); // Remember the vreg(s) has been set for debugging and must not be overwritten by the // original value during deoptimization of the stack.
thread_->GetUpdatedVRegFlags(frame_id)[vreg] = true; if (wide) {
thread_->GetUpdatedVRegFlags(frame_id)[vreg + 1] = true;
}
} return shadow_frame;
}
std::string StackVisitor::DescribeLocation() const {
std::string result("Visiting method '");
ArtMethod* m = GetMethod(); if (m == nullptr) { return"upcall";
}
result += m->PrettyMethod();
result += StringPrintf("' at dex PC 0x%04x", GetDexPc()); if (!IsShadowFrame()) {
result += StringPrintf(" (native PC %p)", reinterpret_cast<void*>(GetCurrentQuickFramePc()));
} return result;
}
void StackVisitor::SetMethod(ArtMethod* method) {
DCHECK(GetMethod() != nullptr); if (cur_shadow_frame_ != nullptr) {
cur_shadow_frame_->SetMethod(method);
} else {
DCHECK(cur_quick_frame_ != nullptr);
CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod: "
<< GetMethod()->PrettyMethod() << " is inlined into "
<< GetOuterMethod()->PrettyMethod();
*cur_quick_frame_ = method;
}
}
void StackVisitor::ValidateFrame() const { if (!kIsDebugBuild) { return;
}
ArtMethod* method = GetMethod();
ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass(); // Runtime methods have null declaring class. if (!method->IsRuntimeMethod()) {
CHECK(declaring_class != nullptr);
CHECK_EQ(declaring_class->GetClass(), declaring_class->GetClass()->GetClass())
<< declaring_class;
} else {
CHECK(declaring_class == nullptr);
}
Runtime* const runtime = Runtime::Current();
LinearAlloc* const linear_alloc = runtime->GetLinearAlloc(); if (!linear_alloc->Contains(method)) { // Check class linker linear allocs. // We get the canonical method as copied methods may have been allocated // by a different class loader. const PointerSize ptrSize = runtime->GetClassLinker()->GetImagePointerSize();
ArtMethod* canonical = method->GetCanonicalMethod(ptrSize);
ObjPtr<mirror::Class> klass = canonical->GetDeclaringClass();
LinearAlloc* const class_linear_alloc = (klass != nullptr)
? runtime->GetClassLinker()->GetAllocatorForClassLoader(klass->GetClassLoader())
: linear_alloc; if (!class_linear_alloc->Contains(canonical)) { // Check image space. bool in_image = false; for (auto& space : runtime->GetHeap()->GetContinuousSpaces()) { if (space->IsImageSpace()) { auto* image_space = space->AsImageSpace(); constauto& header = image_space->GetImageHeader(); const ImageSection& methods = header.GetMethodsSection(); const ImageSection& runtime_methods = header.GetRuntimeMethodsSection(); const size_t offset = reinterpret_cast<const uint8_t*>(canonical) - image_space->Begin(); if (methods.Contains(offset) || runtime_methods.Contains(offset)) {
in_image = true; break;
}
}
}
CHECK(in_image) << canonical->PrettyMethod() << " not in linear alloc or image";
}
} if (cur_quick_frame_ != nullptr) { // Frame consistency checks.
size_t frame_size = GetCurrentQuickFrameInfo().FrameSizeInBytes();
CHECK_NE(frame_size, 0u);
constexpr size_t kMaxExpectedFrameSize = GetStackOverflowReservedBytes(kRuntimeISA);
CHECK_LE(frame_size, kMaxExpectedFrameSize) << method->PrettyMethod();
size_t return_pc_offset = GetCurrentQuickFrameInfo().GetReturnPcOffset();
CHECK_LT(return_pc_offset, frame_size);
}
}
if (method->IsAbstract()) { return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
}
// This goes before IsProxyMethod since runtime methods have a null declaring class. if (method->IsRuntimeMethod()) { return runtime->GetRuntimeMethodFrameInfo(method);
}
if (method->IsProxyMethod()) { // There is only one direct method of a proxy class: the constructor. A direct method is // cloned from the original java.lang.reflect.Proxy and is executed as usual quick // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader.
DCHECK(!method->IsDirect() && !method->IsConstructor())
<< "Constructors of proxy classes must have a OatQuickMethodHeader"; return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
}
// The only remaining cases are for native methods that either // - use the Generic JNI stub, called either directly or through some // (resolution, instrumentation) trampoline; or // - fake a Generic JNI frame in art_jni_dlsym_lookup_critical_stub.
DCHECK(method->IsNative()); // Generic JNI frame is just like the SaveRefsAndArgs frame. // Note that HandleScope, if any, is below the frame. return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
}
if (cur_quick_frame_ != nullptr) { // Handle quick stack frames. // Can't be both a shadow and a quick fragment.
DCHECK(current_fragment->GetTopShadowFrame() == nullptr);
ArtMethod* method = *cur_quick_frame_; // TODO(lokeshgidra): revert the CL after the bug (b/440722522) is diagnosed or convert to // debug-only. if (kTolerateNullMethods && method == nullptr) {
LOG(FATAL_WITHOUT_ABORT) << "null art-method found at ManagedStack fragment: "
<< current_fragment << " cur_quick_frame_:" << cur_quick_frame_; goto visit_transitions;
} elseif (UNLIKELY(method == nullptr)) {
DescribeStack<kCount, /*kTolerateNullMethods=*/true>(thread_);
std::string walking_thread_name, cur_thread_name;
thread_->GetThreadName(walking_thread_name);
Thread::Current()->GetThreadName(cur_thread_name);
LOG(FATAL) << "StackWalk failed on Tid=" << thread_->GetThreadId()
<< " name:" << walking_thread_name
<< " by Tid:" << Thread::Current()->GetThreadId() << " name:" << cur_thread_name;
}
DCHECK(method != nullptr); bool header_retrieved = false; if (method->IsNative()) { // We do not have a PC for the first frame, so we cannot simply use // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have // changed since the frame was entered. The top quick frame tag indicates // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. if (UNLIKELY(current_fragment->GetTopQuickFrameGenericJniTag())) { // The generic JNI does not have any method header.
cur_oat_quick_method_header_ = nullptr;
} elseif (UNLIKELY(current_fragment->GetTopQuickFrameJitJniTag())) { // Should be JITed code.
Runtime* runtime = Runtime::Current(); constvoid* code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
CHECK(code != nullptr) << method->PrettyMethod();
cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code);
} else { // We are sure we are not running GenericJni here. Though the entry point could still be // GenericJnistub. The entry point is usually JITed or AOT code. It could be also a // resolution stub if the class isn't visibly initialized yet. constvoid* existing_entry_point = method->GetEntryPointFromQuickCompiledCode();
CHECK(existing_entry_point != nullptr);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker(); // Check whether we can quickly get the header from the current entrypoint. if (!class_linker->IsQuickGenericJniStub(existing_entry_point) &&
!class_linker->IsQuickResolutionStub(existing_entry_point)) {
cur_oat_quick_method_header_ =
OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
} else { constvoid* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); if (code != nullptr) {
cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code);
} else { // For non-debuggable runtimes, the JNI stub can be JIT-compiled or AOT-compiled, and // can also reuse the stub in boot images. Since we checked for AOT code earlier, we // must be running JITed code or boot JNI stub. // For debuggable runtimes, we won't be here as we never use AOT code in debuggable. // And the JIT situation is handled earlier as its SP will be tagged. But there is a // special case where we change runtime state from non-debuggable to debuggable in // the JNI implementation and do deopt inside, which could be treated as // a case of non-debuggable as well. if (runtime->GetJit() != nullptr) {
code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
} if (code == nullptr) { // Check if current method uses the boot JNI stub. constvoid* boot_jni_stub = class_linker->FindBootJniStub(method); if (boot_jni_stub != nullptr) {
code = EntryPointToCodePointer(boot_jni_stub);
}
}
CHECK(code != nullptr) << method->PrettyMethod();
cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code);
}
}
}
header_retrieved = true;
} while (method != nullptr) { if (!header_retrieved) {
cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_);
}
header_retrieved = false; // Force header retrieval in next iteration.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.