// 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.0.19Bemerkung:
(vorverarbeitet am 2026-09-28)
¤
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.