From 7ea6a170486d81b127e69673cd1020c4db628c93 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 19 May 2015 18:58:54 +0100 Subject: [PATCH] Don't hardcode the location of the caller. This is to avoid shooting ourselves in the foot when dealing with inlined frames. Instead, use common methods for fetching the caller and its dex pc. Change-Id: I3467a7b50cf163022d332e80356f0aab747de252 --- runtime/arch/arm/quick_entrypoints_arm.S | 10 +-- runtime/arch/arm64/quick_entrypoints_arm64.S | 10 ++- runtime/arch/mips/quick_entrypoints_mips.S | 15 ++--- runtime/arch/mips64/quick_entrypoints_mips64.S | 7 +- runtime/arch/x86/quick_entrypoints_x86.S | 11 +--- runtime/arch/x86_64/quick_entrypoints_x86_64.S | 10 ++- runtime/entrypoints/entrypoint_utils-inl.h | 23 +++++-- .../quick/quick_dexcache_entrypoints.cc | 12 ++-- .../quick/quick_trampoline_entrypoints.cc | 77 +++++++++++----------- 9 files changed, 80 insertions(+), 95 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 748857870..3c145d77a 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -313,8 +313,7 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain - * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the - * stack and call the appropriate C helper. + * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper. * NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1. * * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting @@ -330,13 +329,10 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr .extern \cxx_name ENTRY \c_name SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case allocation triggers GC - ldr r2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE] @ pass caller Method* - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP + mov r2, r9 @ pass Thread::Current + mov r3, sp .cfi_adjust_cfa_offset 16 bl \cxx_name @ (method_idx, this, caller, Thread*, SP) - add sp, #16 @ strip the extra frame .cfi_adjust_cfa_offset -16 mov r12, r1 @ save Method*->code_ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index f8b073442..6b16a2e55 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -459,8 +459,7 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain - * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the - * stack and call the appropriate C helper. + * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper. * NOTE: "this" is first visible argument of the target, and so can be found in arg1/x1. * * The helper will attempt to locate the target and return a 128-bit result in x0/x1 consisting @@ -483,10 +482,9 @@ ENTRY \c_name // Helper signature is always // (method_idx, *this_object, *caller_method, *self, sp) - ldr w2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE] // pass caller Method* - mov x3, xSELF // pass Thread::Current - mov x4, sp - bl \cxx_name // (method_idx, this, caller, Thread*, SP) + mov x2, xSELF // pass Thread::Current + mov x3, sp + bl \cxx_name // (method_idx, this, Thread*, SP) mov xIP0, x1 // save Method*->code_ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME cbz x0, 1f // did we find the target? if not go to exception delivery diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index ee5c59f96..92b180e60 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -439,8 +439,7 @@ END art_quick_throw_no_such_method /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/$a0 with the target Method*, arg0/$a0 will contain - * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the - * stack and call the appropriate C helper. + * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper. * NOTE: "this" is first visable argument of the target, and so can be found in arg1/$a1. * * The helper will attempt to locate the target and return a 64-bit result in $v0/$v1 consisting @@ -456,15 +455,13 @@ END art_quick_throw_no_such_method .extern \cxx_name ENTRY \c_name SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC - lw $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE+ARG_SLOT_SIZE($sp) # pass caller Method* - addiu $t0, $sp, ARG_SLOT_SIZE # save $sp (remove arg slots) - move $a3, rSELF # pass Thread::Current - jal \cxx_name # (method_idx, this, caller, Thread*, $sp) - sw $t0, 16($sp) # pass $sp - move $a0, $v0 # save target Method* + move $a2, rSELF # pass Thread::Current + jal \cxx_name # (method_idx, this, Thread*, $sp) + addiu $a3, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) + move $a0, $v0 # save target Method* RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME beqz $v0, 1f - move $t9, $v1 # save $v0->code_ + move $t9, $v1 # save $v0->code_ jalr $zero, $t9 nop 1: diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index ff79b5d77..b7320a61f 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -529,10 +529,9 @@ END art_quick_throw_no_such_method .extern \cxx_name ENTRY \c_name SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC - lwu $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE($sp) # pass caller Method* - move $a3, rSELF # pass Thread::Current - jal \cxx_name # (method_idx, this, caller, Thread*, $sp) - move $a4, $sp # pass $sp + move $a2, rSELF # pass Thread::Current + jal \cxx_name # (method_idx, this, Thread*, $sp) + move $a3, $sp # pass $sp move $a0, $v0 # save target Method* move $t9, $v1 # save $v0->code_ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 6ebeba3aa..d62c1bceb 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -278,8 +278,7 @@ TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromC /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain - * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the - * stack and call the appropriate C helper. + * the method_idx. This wrapper will save arg1-arg3 and call the appropriate C helper. * NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1. * * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting @@ -297,19 +296,15 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) movl %esp, %edx // remember SP // Outgoing argument set up - subl MACRO_LITERAL(12), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(12) PUSH edx // pass SP pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - pushl 32+32(%edx) // pass caller Method* - CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass arg2 PUSH eax // pass arg1 call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*, SP) movl %edx, %edi // save code pointer in EDI - addl MACRO_LITERAL(36), %esp // Pop arguments skip eax - CFI_ADJUST_CFA_OFFSET(-36) + addl MACRO_LITERAL(20), %esp // Pop arguments skip eax + CFI_ADJUST_CFA_OFFSET(-20) // Restore FPRs. movsd 0(%esp), %xmm0 diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index da4d92b88..ddeb5b8e5 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -341,8 +341,7 @@ TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromC /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain - * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the - * stack and call the appropriate C helper. + * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper. * NOTE: "this" is first visible argument of the target, and so can be found in arg1/rsi. * * The helper will attempt to locate the target and return a 128-bit result in rax/rdx consisting @@ -362,11 +361,10 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) // Helper signature is always // (method_idx, *this_object, *caller_method, *self, sp) - movl FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE(%rsp), %edx // pass caller Method* - movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread - movq %rsp, %r8 // pass SP + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread + movq %rsp, %rcx // pass SP - call VAR(cxx_name, 1) // cxx_name(arg1, arg2, caller method*, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*, SP) // save the code pointer movq %rax, %rdi movq %rdx, %rax diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 9292cff88..625e695ce 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -38,25 +38,34 @@ namespace art { -inline mirror::ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type) +inline mirror::ArtMethod* GetCalleeSaveMethodCaller(StackReference* sp, + Runtime::CalleeSaveType type, + bool do_caller_check = false) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - auto* refs_only_sp = self->GetManagedStack()->GetTopQuickFrame(); - DCHECK_EQ(refs_only_sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(type)); + DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(type)); const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); auto* caller_sp = reinterpret_cast*>( - reinterpret_cast(refs_only_sp) + callee_frame_size); + reinterpret_cast(sp) + callee_frame_size); auto* caller = caller_sp->AsMirrorPtr(); - if (kIsDebugBuild) { - NthCallerVisitor visitor(self, 1, true); + if (kIsDebugBuild && do_caller_check) { + // Note that do_caller_check is optional, as this method can be called by + // stubs, and tests without a proper call stack. + NthCallerVisitor visitor(Thread::Current(), 1, true); visitor.WalkStack(); - CHECK(caller == visitor.caller); + CHECK_EQ(caller, visitor.caller); } return caller; } +inline mirror::ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetCalleeSaveMethodCaller( + self->GetManagedStack()->GetTopQuickFrame(), type, true /* do_caller_check */); +} + template ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 46629f595..91488787d 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -25,8 +25,7 @@ namespace art { -extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, - Thread* self) +extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called to ensure static storage base is initialized for direct static field reads and writes. // A class may be accessing another class' fields when it doesn't have access, as access has been @@ -36,8 +35,7 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, return ResolveVerifyAndClinit(type_idx, caller, self, true, false); } -extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, - Thread* self) +extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called when method->dex_cache_resolved_types_[] misses. ScopedQuickEntrypointChecks sqec(self); @@ -45,8 +43,7 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, return ResolveVerifyAndClinit(type_idx, caller, self, false, false); } -extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, - Thread* self) +extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called when caller isn't guaranteed to have access to a type and the dex cache may be // unpopulated. @@ -55,8 +52,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type return ResolveVerifyAndClinit(type_idx, caller, self, false, true); } -extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, - Thread* self) +extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kRefsOnly); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2e7e2dfd7..345b0ade8 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -294,8 +294,13 @@ class QuickArgumentVisitor { static mirror::ArtMethod* GetCallingMethod(StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod()); - uint8_t* previous_sp = reinterpret_cast(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize; - return reinterpret_cast*>(previous_sp)->AsMirrorPtr(); + return GetCalleeSaveMethodCaller(sp, Runtime::kRefsAndArgs); + } + + static uint32_t GetCallingDexPc(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod()); + return GetCallingMethod(sp)->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp)); } // For the given quick ref and args quick frame, return the caller's PC. @@ -827,12 +832,13 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, // Compute details about the called method (avoid GCs) ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp); InvokeType invoke_type; MethodReference called_method(nullptr, 0); const bool called_method_known_on_entry = !called->IsRuntimeMethod(); + mirror::ArtMethod* caller = nullptr; if (!called_method_known_on_entry) { - uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp)); + caller = QuickArgumentVisitor::GetCallingMethod(sp); + uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); const DexFile::CodeItem* code; called_method.dex_file = caller->GetDexFile(); code = caller->GetCodeItem(); @@ -1946,16 +1952,13 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, jvalue result, // to hold the mutator lock (see SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) annotations). template -static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, - Thread* self, StackReference* sp); - -template -static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, - Thread* self, StackReference* sp) { +static TwoWordReturn artInvokeCommon(uint32_t method_idx, + mirror::Object* this_object, + Thread* self, + StackReference* sp) { ScopedQuickEntrypointChecks sqec(self); DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); + mirror::ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); if (UNLIKELY(method == nullptr)) { @@ -1994,7 +1997,6 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_o template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ TwoWordReturn artInvokeCommon(uint32_t method_idx, \ mirror::Object* this_object, \ - mirror::ArtMethod* caller_method, \ Thread* self, \ StackReference* sp) \ @@ -2012,58 +2014,58 @@ EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper, true); // See comments in runtime_support_asm.S extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck( - uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* self, + uint32_t method_idx, + mirror::Object* this_object, + Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, - caller_method, self, sp); + return artInvokeCommon(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck( - uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* self, + uint32_t method_idx, + mirror::Object* this_object, + Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, - self, sp); + return artInvokeCommon(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck( - uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* self, + uint32_t method_idx, + mirror::Object* this_object, + Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, - self, sp); + return artInvokeCommon(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck( - uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* self, + uint32_t method_idx, + mirror::Object* this_object, + Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, - self, sp); + return artInvokeCommon(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck( - uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* self, + uint32_t method_idx, + mirror::Object* this_object, + Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, - self, sp); + return artInvokeCommon(method_idx, this_object, self, sp); } // Determine target of interface dispatch. This object is known non-null. extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_method, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); + mirror::ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); mirror::ArtMethod* method; if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) { method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method); @@ -2075,12 +2077,7 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interfa } else { DCHECK(interface_method == Runtime::Current()->GetResolutionMethod()); - // Find the caller PC. - constexpr size_t pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs); - uintptr_t caller_pc = *reinterpret_cast(reinterpret_cast(sp) + pc_offset); - - // Map the caller PC to a dex PC. - uint32_t dex_pc = caller_method->ToDexPc(caller_pc); + uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); const DexFile::CodeItem* code = caller_method->GetCodeItem(); CHECK_LT(dex_pc, code->insns_size_in_code_units_); const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); -- 2.11.0