Exception的throw try catch的实现(待续

已下面java code为例:

public class ZygoteInit {
    ...
    public static void main(String argv[]) {}
       try {
            ...
            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }
            ...
    }

对应的汇编实现为:

0x74542405:    ldr.w    r0, [pc, #504]    ; 0x74542600        ; java.lang.RuntimeException
0x74542409:    ldr.w    lr, [r9, #300]    ; 0x12c            ; art::ArtThread.tlsPtr_.quick_entrypoints.pAllocObjectInitialized
0x7454240d:    mov    r1, r10
0x7454240f:    blx    lr
 
0x74542411:    mov    r11, r0
0x74542413:    ldr.w    r0, [r10, #20]
0x74542417:    movw    r2, #59620    ; 0xe8e4
0x7454241b:    ldr    r1, [r0, r2]
0x7454241d:    str    r1, [sp, #52]    ; 0x34
0x7454241f:    ldr.w    lr, [pc, #372]    ; 0x74542594
0x74542423:    ldr    r2, [sp, #52]    ; 0x34
0x74542425:    ldr.w    r0, [pc, #432]    ; 0x745425d8        ; java.lang.RuntimeException.<init> "(Ljava/lang/String;)V"
0x74542429:    mov    r1, r11
0x7454242b:    ldr.w    r12, [r1]
0x7454242f:    blx    lr
 
0x74542431:    ldr.w    lr, [r9, #564]    ; 0x234            ; art::ArtThread.tlsPtr_.quick_entrypoints.pDeliverException
0x74542435:    mov    r0, r11
0x74542437:    blx    lr

12、new的Native实现已经分析了new RuntimeException("No ABI list supplied.")的过程,这里再分析throw的实现。

明显,throw的操作跟这个pDeliverException相关,而这个pDeliverException指向的是art_quick_deliver_exception()。

(gdb) p *('art::Thread' *)0xb4f07800
$77 = {
  ...
  tlsPtr_ = {
    ...
    quick_entrypoints = {
      ...
      pTestSuspend = 0xb4cb1211 <art_quick_test_suspend+1>,
      pDeliverException = 0xb4cafcb1 <art_quick_deliver_exception+1>,
      pThrowArrayBounds = 0xb4cafd11 <art_quick_throw_array_bounds+1>,
      pThrowDivZero = 0xb4cafcf1 <art_quick_throw_div_zero+1>,
      pThrowNoSuchMethod = 0xb4cafd51 <art_quick_throw_no_such_method+1>,
      pThrowNullPointer = 0xb4cafcd1 <art_quick_throw_null_pointer_exception+1>,
      pThrowStackOverflow = 0xb4cafd31 <art_quick_throw_stack_overflow+1>,
      ...

这个值是线程初始化的时候赋上去的:

@arm/runtime/arch/arm/entrypoints_init_arm.cc
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
  // Interpreter
  ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
  ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
  ...
  // Thread
  qpoints->pTestSuspend = art_quick_test_suspend;
  // Throws
  qpoints->pDeliverException = art_quick_deliver_exception;
  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
  qpoints->pThrowDivZero = art_quick_throw_div_zero;
  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
};

看看这个art_quick_deliver_exception()的实现:

@arm/runtime/arch/arm/quick_entrypoints_arm.S
.macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name
    .extern \cxx_name
ENTRY \c_name
    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
    mov r1, r9                      @ pass Thread::Current
    mov r2, sp                      @ pass SP
    b   \cxx_name                   @ \cxx_name(Thread*, SP)
    bkpt
END \c_name
.endm
 
ONE_ARG_RUNTIME_EXCEPTION art_quick_deliver_exception, artDeliverExceptionFromCode

最终会调用artDeliverExceptionFromCode()

@arm/runtime/entrypoints/quick/quick_throw_entrypoints.cc
extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self, StackReference<mirror::ArtMethod>* sp)
    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
  if (exception == NULL) {
    self->ThrowNewException(throw_location, "Ljava/lang/NullPointerException;",  "throw with null exception");
  } else {
    self->SetException(throw_location, exception);
  }
  self->QuickDeliverException();
}

这个函数先调用Tread类的GetCurrentLocationForThrow()获取抛出异常的函数信息,

然后调用Tread类的SetException()将ThrowLocation和Exception写入Thread的tlsPtr_成员的exception和throw_location中。

再调用Tread类的QuickDeliverException(),找到Exception对应的catch代码块,并跳转到对应代码块中。

@art/runtime/thread.cc
ThrowLocation Thread::GetCurrentLocationForThrow() {
  Context* context = GetLongJumpContext();
  CurrentMethodVisitor visitor(this, context, true);
  visitor.WalkStack(false);
  ReleaseLongJumpContext(context);
  return ThrowLocation(visitor.this_object_, visitor.method_, visitor.dex_pc_);
}
@art/runtime/thread.h
void SetException(const ThrowLocation& throw_location, mirror::Throwable* new_exception)
    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  CHECK(new_exception != NULL);
  // TODO: DCHECK(!IsExceptionPending());
  tlsPtr_.exception = new_exception;
  tlsPtr_.throw_location = throw_location;
}

重点是QuickDeliverException():

@art/runtime/thread.cc
void Thread::QuickDeliverException() {
  // Get exception from thread.
  ThrowLocation throw_location;
  mirror::Throwable* exception = GetException(&throw_location);
 
  bool is_exception_reported = IsExceptionReportedToInstrumentation();
  ClearException();
  bool is_deoptimization = (exception == GetDeoptimizationException());
  QuickExceptionHandler exception_handler(this, is_deoptimization);
  if (is_deoptimization) {
    exception_handler.DeoptimizeStack();
  } else {
    exception_handler.FindCatch(throw_location, exception, is_exception_reported);
  }
  exception_handler.UpdateInstrumentationStack();
  exception_handler.DoLongJump();
  LOG(FATAL) << "UNREACHABLE";
}

先通过QuickExceptionHandler::FindCatch()在回溯调用栈,找到try catch块,

然后再调用QuickExceptionHandler::DoLongJump()跳转到catch块里。

@art/runtime/quick_exception_handler.cc
void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception,  bool is_exception_reported) {
 
  StackHandleScope<1> hs(self_);
  Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
  // Walk the stack to find catch handler or prepare for deoptimization.
  CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this);
  visitor.WalkStack(true);
  ...
}
 
void QuickExceptionHandler::DoLongJump() {
  // Place context back on thread so it will be available when we continue.
  self_->ReleaseLongJumpContext(context_);
  context_->SetSP(reinterpret_cast<uintptr_t>(handler_quick_frame_));
  CHECK_NE(handler_quick_frame_pc_, 0u);
  context_->SetPC(handler_quick_frame_pc_);
  context_->SmashCallerSaves();
  context_->DoLongJump();
}

找到try catch的方法是,用CatchBlockStackVisitor类回溯栈:

@art/runtime/quick_exception_handler.cc
class CatchBlockStackVisitor FINAL : public StackVisitor {
 public:
  CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,  QuickExceptionHandler* exception_handler)
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
      : StackVisitor(self, context), self_(self), exception_(exception),
        exception_handler_(exception_handler) {
  }
  bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
    mirror::ArtMethod* method = GetMethod();
    exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
    if (method == nullptr) {
      ...
      return false;  // End stack walk.
    }
    if (method->IsRuntimeMethod()) {
      ...
      return true;
    }
    StackHandleScope<1> hs(self_);
    return HandleTryItems(hs.NewHandle(method));
  }
 
 private:
  bool HandleTryItems(Handle<mirror::ArtMethod> method)
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
    uint32_t dex_pc = DexFile::kDexNoIndex;
    if (!method->IsNative()) {
      dex_pc = GetDexPc();
    }
    if (dex_pc != DexFile::kDexNoIndex) {
      bool clear_exception = false;
      StackHandleScope<1> hs(Thread::Current());
      Handle<mirror::Class> to_find(hs.NewHandle((*exception_)->GetClass()));
 
      uint32_t found_dex_pc = mirror::ArtMethod::FindCatchBlock(method, to_find, dex_pc, &clear_exception);
 
      exception_handler_->SetClearException(clear_exception);
 
      if (found_dex_pc != DexFile::kDexNoIndex) {
        exception_handler_->SetHandlerMethod(method.Get());
        exception_handler_->SetHandlerDexPc(found_dex_pc);
        exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc));
        exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
        return false;  // End stack walk.
      }
    }
    return true;  // Continue stack walk.
  }

这里调用mirror::ArtMethod::FindCatchBlock()方法,从dex文件里查找try catch块:

@art/mirror/art_method.cc
uint32_t ArtMethod::FindCatchBlock(Handle<ArtMethod> h_this, Handle<Class> exception_type, uint32_t dex_pc, bool* has_no_move_exception) {
  MethodHelper mh(h_this);
  const DexFile::CodeItem* code_item = h_this->GetCodeItem();
  // Set aside the exception while we resolve its type.
  Thread* self = Thread::Current();
  ThrowLocation throw_location;
  StackHandleScope<1> hs(self);
  Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException(&throw_location)));
  bool is_exception_reported = self->IsExceptionReportedToInstrumentation();
  self->ClearException();
  // Default to handler not found.
  uint32_t found_dex_pc = DexFile::kDexNoIndex;
 
  // Iterate over the catch handlers associated with dex_pc.
  for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
    uint16_t iter_type_idx = it.GetHandlerTypeIndex();
 
    // Catch all case
    if (iter_type_idx == DexFile::kDexNoIndex16) {  //这里是catch all
      found_dex_pc = it.GetHandlerAddress();
      break;
    }
    // Does this catch exception type apply?
    Class* iter_exception_type = mh.GetClassFromTypeIdx(iter_type_idx);
    if (UNLIKELY(iter_exception_type == nullptr)) {
      ...
    } else if (iter_exception_type->IsAssignableFrom(exception_type.Get())) {  //如果catch的exception是当前exception的父类,则found
      found_dex_pc = it.GetHandlerAddress();
      break;
    }
  }
  ...
  return found_dex_pc;
}

如果想了解DexFile中查找try catch语句的方法,可以研究CatchHandlerIterator()类,这里就不深究了。

posted on 2020-04-20 17:00  EMH1899  阅读(312)  评论(0)    收藏  举报

导航