当前位置:   article > 正文

Android开发中,我们java层崩溃时,虚拟机发生了什么?_got a deoptimization request on un-deoptimizable m

got a deoptimization request on un-deoptimizable method

1 背景

熟悉Android开发的同学都知道,如果我们应用程序中发生了java层的崩溃,我们可以通过下面方式捕获,

  1. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  2. @Override
  3. public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
  4. Log.i("jin", "uncaughtException " + e.getMessage());
  5. // do some something
  6. }
  7. });

如果崩溃在了主线程,这么操作就不好用了,因为在发生java崩溃的时候,主线程会退出Looper的loop循环,所以想要主线程异常也能捕获,并且正常运行的话,我们需要这么操作。

1234567
  1. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  2. @Override
  3. public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
  4. Log.i("jin", "uncaughtException " + e.getMessage());
  5. Looper.loop();
  6. }
  7. });

那么为什么我们可以通过这种方式拦截到java层崩溃呢?我们首先简单模拟一个崩溃,

  1. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  2. @Override
  3. public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
  4. Log.i("jin", "uncaughtException " + e.getMessage(), new Exception());
  5. Looper.loop();
  6. }
  7. });
  8. int i = 9 / 0;
  9. uncaughtException Unable to create application com.jin.DemoApplication: java.lang.ArithmeticException: divide by zero
  10. java.lang.Exception
  11. at com.jin$1.uncaughtException(DemoApplication.java:80)
  12. at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)
  13. at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)
  14. at java.lang.Thread.dispatchUncaughtException(Thread.java:2203)

通过堆栈,我们可以知道,出现的异常是ArithmeticException,原因是除数为0,并且调用的逻辑是从
Thread中的dispatchUncaughtException分发下来的,一直到我们自己复写的uncaughtException方法里面。

OK,我们再来模拟一下,

  1. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  2. @Override
  3. public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
  4. Log.i("jin", "uncaughtException " + e.getMessage(), new Exception());
  5. Looper.loop();
  6. }
  7. });
  8. try {
  9. int i = 9 / 0;
  10. } catch (Exception e) {
  11. Log.i("jin", "exception : " + e.getMessage(), new Exception());
  12. }
  13. exception : divide by zero
  14. java.lang.Exception
  15. at com.jin.DemoApplication.onCreate(DemoApplication.java:88)
  16. at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1193)
  17. at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6848)
  18. at android.app.ActivityThread.access$1400(ActivityThread.java:246)
  19. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1955)
  20. at android.os.Handler.dispatchMessage(Handler.java:106)
  21. at android.os.Looper.loop(Looper.java:236)
  22. at android.app.ActivityThread.main(ActivityThread.java:7876)
  23. at java.lang.reflect.Method.invoke(Native Method)
  24. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
  25. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

这段堆栈,大家就更熟悉了,无非是我们的Exception,被try-catch住了,那你知道虚拟机层trcy-catch是怎么处理的吗?需要补充的是,对于一些Exception来说,trcy-catch住不是一个好的手段,我们需要找到问题原因并且修复它,而不是针对可能出现的问题,catch住就完了。

2 虚拟机层探究一下

本文以Android9.0虚拟机为例,针对上面异常代码的处理流程,对虚拟机层探究一下。

2.1 解释器模式

2.1.1

对上面代码进行下面命令,得到上面代码的字节码

adb shell dexdump -d sdcard/app-debug.apk
  1. int i = 9 / 0;
  2. Log.i("jin", "exception : " ,new Exception())
  3. 0023: const/16 v0, #int 9 // #9
  4. 0025: div-int/lit8 v0, v0, #int 0 // #00
  5. 0027: new-instance v1, Ljava/lang/Exception; // type@0db2
  6. 0029: invoke-direct {v1}, Ljava/lang/Exception;.<init>:()V // method@fca9
  7. 002c: const-string v2, "jin" // string@6a64
  8. 002e: const-string v3, "exception" // string@5549
  9. 0030: invoke-static {v2, v3, v1}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I // method@0139
  10. 0033: return-void

我们可以看到int i = 9 / 0; 这段代码其实执行的就是div-int字节码。
在虚拟机中,针对div-int字节码的处理指令如下所示:
art/runtime/interpreter/interpreter_switch_impl.cc

  1. case Instruction::DIV_INT: {
  2. PREAMBLE();
  3. bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
  4. shadow_frame.GetVReg(inst->VRegB_23x()),
  5. shadow_frame.GetVReg(inst->VRegC_23x()));
  6. POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);

我们继续跟一下DoIntDivide方法,有4个参数,第一个参数ShadowFrame是栈帧,第二个参数是结果,第三个参数是被除数,第4个参数是除数,我们看逻辑中会对除数判断,如果divisor == 0,那么会执行ThrowArithmeticExceptionDivideByZero()方法。

art/runtime/interpreter/interpreter_common.h

  1. // Handles div-int, div-int/2addr, div-int/li16 and div-int/lit8 instructions.
  2. // Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
  3. static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg,
  4. int32_t dividend, int32_t divisor)
  5. REQUIRES_SHARED(Locks::mutator_lock_) {
  6. constexpr int32_t kMinInt = std::numeric_limits<int32_t>::min();
  7. if (UNLIKELY(divisor == 0)) {
  8. ThrowArithmeticExceptionDivideByZero();
  9. return false;
  10. }
  11. if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
  12. shadow_frame.SetVReg(result_reg, kMinInt);
  13. } else {
  14. shadow_frame.SetVReg(result_reg, dividend / divisor);
  15. }
  16. return true;
  17. }

我们接着分析下ThrowArithmeticExceptionDivideByZero()方法,看到代码以后,是不是有一种似曾相识的感觉?
哈哈,是的,我们在前面第一部分中java层打印出的堆栈异常,就是这里抛出的!!

art/runtime/common_throws.cc

  1. void ThrowArithmeticExceptionDivideByZero() {
  2. ThrowException("Ljava/lang/ArithmeticException;", nullptr, "divide by zero");
  3. }

我们看下ThrowException的实现,里面一顿处理,最后调用了下self->ThrowNewException(),通过虚拟机中线程Thread类,将异常抛出。
art/runtime/common_throws.cc

  1. static void ThrowException(const char* exception_descriptor,
  2. ObjPtr<mirror::Class> referrer,
  3. const char* fmt,
  4. va_list* args = nullptr)
  5. REQUIRES_SHARED(Locks::mutator_lock_) {
  6. std::ostringstream msg;
  7. if (args != nullptr) {
  8. std::string vmsg;
  9. StringAppendV(&vmsg, fmt, *args);
  10. msg << vmsg;
  11. } else {
  12. msg << fmt;
  13. }
  14. AddReferrerLocation(msg, referrer);
  15. Thread* self = Thread::Current();
  16. self->ThrowNewException(exception_descriptor, msg.str().c_str());
  17. }

2.1.2

我们继续看下Thread类,看它是怎么抛出异常的,重点做了两个事情,一个是构造异常对象,另一个是SetException,

art/runtime/thread.cc

  1. void Thread::ThrowNewException(const char* exception_class_descriptor,
  2. const char* msg) {
  3. // Callers should either clear or call ThrowNewWrappedException.
  4. AssertNoPendingExceptionForNewException(msg);
  5. ThrowNewWrappedException(exception_class_descriptor, msg);
  6. }
  7. void Thread::ThrowNewWrappedException(const char* exception_class_descriptor,
  8. const char* msg) {
  9. DCHECK_EQ(this, Thread::Current());
  10. ScopedObjectAccessUnchecked soa(this);
  11. StackHandleScope<3> hs(soa.Self());
  12. Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetCurrentClassLoader(soa.Self())));
  13. ScopedLocalRef<jobject> cause(GetJniEnv(), soa.AddLocalReference<jobject>(GetException()));
  14. ClearException();
  15. Runtime* runtime = Runtime::Current();
  16. auto* cl = runtime->GetClassLinker();
  17. Handle<mirror::Class> exception_class(
  18. hs.NewHandle(cl->FindClass(this, exception_class_descriptor, class_loader)));
  19. if (UNLIKELY(exception_class == nullptr)) {
  20. CHECK(IsExceptionPending());
  21. LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor);
  22. return;
  23. }
  24. if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(soa.Self(), exception_class, true,
  25. true))) {
  26. DCHECK(IsExceptionPending());
  27. return;
  28. }
  29. DCHECK(!runtime->IsStarted() || exception_class->IsThrowableClass());
  30. Handle<mirror::Throwable> exception(
  31. hs.NewHandle(ObjPtr<mirror::Throwable>::DownCast(exception_class->AllocObject(this))));
  32. // If we couldn't allocate the exception, throw the pre-allocated out of memory exception.
  33. if (exception == nullptr) {
  34. SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
  35. return;
  36. }
  37. // Choose an appropriate constructor and set up the arguments.
  38. const char* signature;
  39. ScopedLocalRef<jstring> msg_string(GetJniEnv(), nullptr);
  40. if (msg != nullptr) {
  41. // Ensure we remember this and the method over the String allocation.
  42. msg_string.reset(
  43. soa.AddLocalReference<jstring>(mirror::String::AllocFromModifiedUtf8(this, msg)));
  44. if (UNLIKELY(msg_string.get() == nullptr)) {
  45. CHECK(IsExceptionPending()); // OOME.
  46. return;
  47. }
  48. if (cause.get() == nullptr) {
  49. signature = "(Ljava/lang/String;)V";
  50. } else {
  51. signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V";
  52. }
  53. } else {
  54. if (cause.get() == nullptr) {
  55. signature = "()V";
  56. } else {
  57. signature = "(Ljava/lang/Throwable;)V";
  58. }
  59. }
  60. ArtMethod* exception_init_method =
  61. exception_class->FindConstructor(signature, cl->GetImagePointerSize());
  62. CHECK(exception_init_method != nullptr) << "No <init>" << signature << " in "
  63. << PrettyDescriptor(exception_class_descriptor);
  64. if (UNLIKELY(!runtime->IsStarted())) {
  65. // Something is trying to throw an exception without a started runtime, which is the common
  66. // case in the compiler. We won't be able to invoke the constructor of the exception, so set
  67. // the exception fields directly.
  68. if (msg != nullptr) {
  69. exception->SetDetailMessage(DecodeJObject(msg_string.get())->AsString());
  70. }
  71. if (cause.get() != nullptr) {
  72. exception->SetCause(DecodeJObject(cause.get())->AsThrowable());
  73. }
  74. .....省略部分代码
  75. if (LIKELY(!IsExceptionPending())) {
  76. SetException(exception.Get());
  77. }
  78. }
  79. }

其中构造异常对象的时候,我们会初始化Exception,然后调用父类的super方法,父类Throwable然后调用fillInStackTrace来填充堆栈,

  1. // Exception.java
  2. public class Exception extends Throwable {
  3. static final long serialVersionUID = -3387516993124229948L;
  4. /**
  5. * Constructs a new exception with {@code null} as its detail message.
  6. * The cause is not initialized, and may subsequently be initialized by a
  7. * call to {@link #initCause}.
  8. */
  9. public Exception() {
  10. super();
  11. }
  12. }
  13. // Throwable.java
  14. public Throwable() {
  15. fillInStackTrace();
  16. }
  17. public synchronized Throwable fillInStackTrace() {
  18. if (stackTrace != null ||
  19. backtrace != null /* Out of protocol state */ ) {
  20. // Android-changed: Use Android-specific nativeFillInStackTrace
  21. // fillInStackTrace(0);
  22. backtrace = nativeFillInStackTrace();
  23. // Android-changed: Use libcore.util.EmptyArray for the empty stack trace
  24. // stackTrace = UNASSIGNED_STACK;
  25. stackTrace = libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
  26. }
  27. return this;
  28. }
  29. // Android-changed: Use Android-specific nativeFillInStackTrace
  30. // private native Throwable fillInStackTrace(int dummy);
  31. @FastNative
  32. private static native Object nativeFillInStackTrace();

最终调用thread中的CreateInternalStackTrace去获取调用堆栈。
art/runtime/thread.cc

  1. template<bool kTransactionActive>
  2. jobject Thread::CreateInternalStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const {
  3. // Compute depth of stack, save frames if possible to avoid needing to recompute many.
  4. constexpr size_t kMaxSavedFrames = 256;
  5. std::unique_ptr<ArtMethodDexPcPair[]> saved_frames(new ArtMethodDexPcPair[kMaxSavedFrames]);
  6. FetchStackTraceVisitor count_visitor(const_cast<Thread*>(this),
  7. &saved_frames[0],
  8. kMaxSavedFrames);
  9. count_visitor.WalkStack();
  10. const uint32_t depth = count_visitor.GetDepth();
  11. const uint32_t skip_depth = count_visitor.GetSkipDepth();
  12. // Build internal stack trace.
  13. BuildInternalStackTraceVisitor<kTransactionActive> build_trace_visitor(soa.Self(),
  14. const_cast<Thread*>(this),
  15. skip_depth);
  16. if (!build_trace_visitor.Init(depth)) {
  17. return nullptr; // Allocation failed.
  18. }
  19. // If we saved all of the frames we don't even need to do the actual stack walk. This is faster
  20. // than doing the stack walk twice.
  21. if (depth < kMaxSavedFrames) {
  22. for (size_t i = 0; i < depth; ++i) {
  23. build_trace_visitor.AddFrame(saved_frames[i].first, saved_frames[i].second);
  24. }
  25. } else {
  26. build_trace_visitor.WalkStack();
  27. }
  28. mirror::ObjectArray<mirror::Object>* trace = build_trace_visitor.GetInternalStackTrace();
  29. if (kIsDebugBuild) {
  30. ObjPtr<mirror::PointerArray> trace_methods = build_trace_visitor.GetTraceMethodsAndPCs();
  31. // Second half of trace_methods is dex PCs.
  32. for (uint32_t i = 0; i < static_cast<uint32_t>(trace_methods->GetLength() / 2); ++i) {
  33. auto* method = trace_methods->GetElementPtrSize<ArtMethod*>(
  34. i, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
  35. CHECK(method != nullptr);
  36. }
  37. }
  38. return soa.AddLocalReference<jobject>(trace);
  39. }

2.1.3

我们回到上面Thread类中异常抛出的处理中,构造异常对象分析完了,我们再看下SetException到底在做什么,将一个tlsPtr_对象的exception变量进行赋值,这个tlsPtr_对象就是Thread中的一个结构体

  1. void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
  2. CHECK(new_exception != nullptr);
  3. // TODO: DCHECK(!IsExceptionPending());
  4. tlsPtr_.exception = new_exception.Ptr();
  5. }
  1. struct PACKED(sizeof(void*)) tls_ptr_sized_values {
  2. tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr),
  3. managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr),
  4. self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0),
  5. deps_or_stack_trace_sample(), wait_next(nullptr), monitor_enter_object(nullptr),
  6. top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
  7. instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
  8. stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
  9. frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0),
  10. last_no_thread_suspension_cause(nullptr), checkpoint_function(nullptr),
  11. thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr),
  12. thread_local_limit(nullptr),
  13. thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr),
  14. mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr),
  15. thread_local_alloc_stack_end(nullptr),
  16. flip_function(nullptr), method_verifier(nullptr), thread_local_mark_stack(nullptr),
  17. async_exception(nullptr) {
  18. std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
  19. }
  20. // The biased card table, see CardTable for details.
  21. uint8_t* card_table;
  22. // The pending exception or null.
  23. mirror::Throwable* exception;
  24. // The end of this thread's stack. This is the lowest safely-addressable address on the stack.
  25. // We leave extra space so there's room for the code that throws StackOverflowError.
  26. uint8_t* stack_end;
  27. // The top of the managed stack often manipulated directly by compiler generated code.
  28. ManagedStack managed_stack;
  29. // In certain modes, setting this to 0 will trigger a SEGV and thus a suspend check. It is
  30. // normally set to the address of itself.
  31. uintptr_t* suspend_trigger;
  32. // Every thread may have an associated JNI environment
  33. JNIEnvExt* jni_env;
  34. // Temporary storage to transfer a pre-allocated JNIEnvExt from the creating thread to the
  35. // created thread.
  36. JNIEnvExt* tmp_jni_env;
  37. // Initialized to "this". On certain architectures (such as x86) reading off of Thread::Current
  38. // is easy but getting the address of Thread::Current is hard. This field can be read off of
  39. // Thread::Current to give the address.
  40. Thread* self;
  41. // Our managed peer (an instance of java.lang.Thread). The jobject version is used during thread
  42. // start up, until the thread is registered and the local opeer_ is used.
  43. mirror::Object* opeer;
  44. jobject jpeer;
  45. // The "lowest addressable byte" of the stack.
  46. uint8_t* stack_begin;
  47. // Size of the stack.
  48. size_t stack_size;
  49. ....省略部分代码
  50. // Sampling profiler and AOT verification cannot happen on the same run, so we share
  51. // the same entry for the stack trace and the verifier deps.
  52. union DepsOrStackTraceSample {
  53. DepsOrStackTraceSample() {
  54. verifier_deps = nullptr;
  55. stack_trace_sample = nullptr;
  56. }
  57. // Pointer to previous stack trace captured by sampling profiler.
  58. std::vector<ArtMethod*>* stack_trace_sample;
  59. // When doing AOT verification, per-thread VerifierDeps.
  60. verifier::VerifierDeps* verifier_deps;
  61. } deps_or_stack_trace_sample;
  62. // The next thread in the wait set this thread is part of or null if not waiting.
  63. Thread* wait_next;
  64. StackedShadowFrameRecord* stacked_shadow_frame_record;
  65. // Deoptimization return value record stack.
  66. DeoptimizationContextRecord* deoptimization_context_stack;
  67. // For debugger, a linked list that keeps the mapping from frame_id to shadow frame.
  68. // Shadow frames may be created before deoptimization happens so that the debugger can
  69. // set local values there first.
  70. FrameIdToShadowFrame* frame_id_to_shadow_frame;
  71. // A cached copy of the java.lang.Thread's name.
  72. std::string* name;
  73. // A cached pthread_t for the pthread underlying this Thread*.
  74. pthread_t pthread_self;
  75. // If no_thread_suspension_ is > 0, what is causing that assertion.
  76. const char* last_no_thread_suspension_cause;
  77. // Pending checkpoint function or null if non-pending. If this checkpoint is set and someone\
  78. // requests another checkpoint, it goes to the checkpoint overflow list.
  79. Closure* checkpoint_function GUARDED_BY(Locks::thread_suspend_count_lock_);
  80. // Pending barriers that require passing or NULL if non-pending. Installation guarding by
  81. // Locks::thread_suspend_count_lock_.
  82. // They work effectively as art::Barrier, but implemented directly using AtomicInteger and futex
  83. // to avoid additional cost of a mutex and a condition variable, as used in art::Barrier.
  84. AtomicInteger* active_suspend_barriers[kMaxSuspendBarriers];
  85. // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM.
  86. uint8_t* thread_local_start;
  87. // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for
  88. // potentially better performance.
  89. uint8_t* thread_local_pos;
  90. uint8_t* thread_local_end;
  91. // Thread local limit is how much we can expand the thread local buffer to, it is greater or
  92. // equal to thread_local_end.
  93. uint8_t* thread_local_limit;
  94. size_t thread_local_objects;
  95. // Entrypoint function pointers.
  96. // TODO: move this to more of a global offset table model to avoid per-thread duplication.
  97. JniEntryPoints jni_entrypoints;
  98. QuickEntryPoints quick_entrypoints;
  99. ....省略部分代码
  100. } tlsPtr_;

2.1.4

这样子我们就分析完了第一部分,异常的抛出处理过程,抛出了异常,肯定需要处理异常啊?我们回到前面的代码逻辑,POSSIBLY_HANDLE_PENDING_EXCEPTION这个就是开始处理异常,
art/runtime/interpreter/interpreter_switch_impl.cc

  1. case Instruction::DIV_INT: {
  2. PREAMBLE();
  3. bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
  4. shadow_frame.GetVReg(inst->VRegB_23x()),
  5. shadow_frame.GetVReg(inst->VRegC_23x()));
  6. POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);

它的实现在这里
art/runtime/interpreter/interpreter_switch_impl.cc

  1. #define HANDLE_PENDING_EXCEPTION() HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instrumentation)
  2. #define HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instr) \
  3. do { \
  4. DCHECK(self->IsExceptionPending()); \
  5. self->AllowThreadSuspension(); \
  6. if (!MoveToExceptionHandler(self, shadow_frame, instr)) { \
  7. /* Structured locking is to be enforced for abnormal termination, too. */ \
  8. DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame); \
  9. if (interpret_one_instruction) { \
  10. /* Signal mterp to return to caller */ \
  11. shadow_frame.SetDexPC(dex::kDexNoIndex); \
  12. } \
  13. ctx->result = JValue(); /* Handled in caller. */ \
  14. return; \
  15. } else { \
  16. int32_t displacement = \
  17. static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc); \
  18. inst = inst->RelativeAt(displacement); \
  19. } \
  20. } while (false)

其中有个方法
MoveToExceptionHandler,这个返回boolean类型的值,我们看下这里是怎么处理的
art/runtime/interpreter/interpreter_common.cc

  1. bool MoveToExceptionHandler(Thread* self,
  2. ShadowFrame& shadow_frame,
  3. const instrumentation::Instrumentation* instrumentation) {
  4. self->VerifyStack();
  5. StackHandleScope<2> hs(self);
  6. Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException()));
  7. if (instrumentation != nullptr &&
  8. instrumentation->HasExceptionThrownListeners() &&
  9. self->IsExceptionThrownByCurrentMethod(exception.Get())) {
  10. // See b/65049545 for why we don't need to check to see if the exception has changed.
  11. instrumentation->ExceptionThrownEvent(self, exception.Get());
  12. }
  13. //FindCatchBlock去寻找异常处理块
  14. bool clear_exception = false;
  15. uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
  16. hs.NewHandle(exception->GetClass()), shadow_frame.GetDexPC(), &clear_exception);
  17. //注意这里,代表找不到异常处理,需要进行栈的回溯
  18. if (found_dex_pc == dex::kDexNoIndex) {
  19. if (instrumentation != nullptr) {
  20. if (shadow_frame.NeedsNotifyPop()) {
  21. instrumentation->WatchedFramePopped(self, shadow_frame);
  22. }
  23. // Exception is not caught by the current method. We will unwind to the
  24. // caller. Notify any instrumentation listener.
  25. instrumentation->MethodUnwindEvent(self,
  26. shadow_frame.GetThisObject(),
  27. shadow_frame.GetMethod(),
  28. shadow_frame.GetDexPC());
  29. }
  30. return false;
  31. } else {
  32. // 代表找到异常处理的地方,比如catch块,然后设置相关的found_dex_pc。
  33. shadow_frame.SetDexPC(found_dex_pc);
  34. if (instrumentation != nullptr && instrumentation->HasExceptionHandledListeners()) {
  35. self->ClearException();
  36. instrumentation->ExceptionHandledEvent(self, exception.Get());
  37. if (UNLIKELY(self->IsExceptionPending())) {
  38. // Exception handled event threw an exception. Try to find the handler for this one.
  39. return MoveToExceptionHandler(self, shadow_frame, instrumentation);
  40. } else if (!clear_exception) {
  41. self->SetException(exception.Get());
  42. }
  43. } else if (clear_exception) {
  44. self->ClearException();
  45. }
  46. return true;
  47. }
  48. }

代码中一些流程,我简单进行了备注,FindCatchBlock我们去看下实现

  1. uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type,
  2. uint32_t dex_pc, bool* has_no_move_exception) {
  3. // Set aside the exception while we resolve its type.
  4. Thread* self = Thread::Current();
  5. StackHandleScope<1> hs(self);
  6. Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException()));
  7. self->ClearException();
  8. // Default to handler not found.
  9. uint32_t found_dex_pc = dex::kDexNoIndex;
  10. // Iterate over the catch handlers associated with dex_pc.
  11. CodeItemDataAccessor accessor(DexInstructionData());
  12. for (CatchHandlerIterator it(accessor, dex_pc); it.HasNext(); it.Next()) {
  13. dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
  14. // Catch all case
  15. if (!iter_type_idx.IsValid()) {
  16. found_dex_pc = it.GetHandlerAddress();
  17. break;
  18. }
  19. // Does this catch exception type apply?
  20. ObjPtr<mirror::Class> iter_exception_type = ResolveClassFromTypeIndex(iter_type_idx);
  21. if (UNLIKELY(iter_exception_type == nullptr)) {
  22. // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
  23. // removed by a pro-guard like tool.
  24. // Note: this is not RI behavior. RI would have failed when loading the class.
  25. self->ClearException();
  26. // Delete any long jump context as this routine is called during a stack walk which will
  27. // release its in use context at the end.
  28. delete self->GetLongJumpContext();
  29. LOG(WARNING) << "Unresolved exception class when finding catch block: "
  30. << DescriptorToDot(GetTypeDescriptorFromTypeIdx(iter_type_idx));
  31. } else if (iter_exception_type->IsAssignableFrom(exception_type.Get())) {
  32. found_dex_pc = it.GetHandlerAddress();
  33. break;
  34. }
  35. }
  36. if (found_dex_pc != dex::kDexNoIndex) {
  37. const Instruction& first_catch_instr = accessor.InstructionAt(found_dex_pc);
  38. *has_no_move_exception = (first_catch_instr.Opcode() != Instruction::MOVE_EXCEPTION);
  39. }
  40. // Put the exception back.
  41. if (exception != nullptr) {
  42. self->SetException(exception.Get());
  43. }
  44. return found_dex_pc;
  45. }

里面的逻辑无非是寻找CatchHandler来处理,比如下面这段代码,0x0025 - 0x0027的代码逻辑,被0x0028处catch住,
所以虚拟机能找到对应的catch块来处理。

  1. try {
  2. int i = 9 / 0;
  3. } catch (Exception e) {
  4. Log.i("jin", "exception : " + e.getMessage(), new Exception());
  5. }
  6. 0023: const/16 v0, #int 9 // #9
  7. 0025: div-int/lit8 v0, v0, #int 0 // #00
  8. 0027: goto 0048 // +0021
  9. 0028: move-exception v0
  10. 0029: new-instance v1, Ljava/lang/StringBuilder; // type@0dcc
  11. 002b: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@fd11
  12. 002e: const-string v2, "exception : " // string@554a
  13. 0030: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@fd1a
  14. 0033: invoke-virtual {v0}, Ljava/lang/Exception;.getMessage:()Ljava/lang/String; // method@fcaa
  15. 0036: move-result-object v2
  16. 0037: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@fd1a
  17. 003a: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@fd21
  18. 003d: move-result-object v1
  19. 003e: new-instance v2, Ljava/lang/Exception; // type@0db2
  20. 0040: invoke-direct {v2}, Ljava/lang/Exception;.<init>:()V // method@fca9
  21. 0043: const-string v3, "jin" // string@6a65
  22. 0045: invoke-static {v3, v1, v2}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I // method@0139
  23. 0048: return-void
  24. catches : 1
  25. 0x0025 - 0x0027
  26. Ljava/lang/Exception; -> 0x0028

我们继续,如果有catch块能处理异常我们分析完了一部分,是怎么跳转到catch块执行的吗,如果没有catch块怎么办呢?
其实,我们在函数回溯后,会返回到art_quick_to_interpreter_bridge中执行,有一个RETURN_OR_DELIVER_PENDING_EXCEPTION的宏,

art/runtime/arch/arm64/quick_entrypoints_arm64.S

  1. ENTRY art_quick_to_interpreter_bridge
  2. SETUP_SAVE_REFS_AND_ARGS_FRAME // Set up frame and save arguments.
  3. // x0 will contain mirror::ArtMethod* method.
  4. mov x1, xSELF // How to get Thread::Current() ???
  5. mov x2, sp
  6. // uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Thread* self,
  7. // mirror::ArtMethod** sp)
  8. bl artQuickToInterpreterBridge
  9. RESTORE_SAVE_REFS_AND_ARGS_FRAME // TODO: no need to restore arguments in this case.
  10. REFRESH_MARKING_REGISTER
  11. fmov d0, x0
  12. RETURN_OR_DELIVER_PENDING_EXCEPTION
  13. END art_quick_to_interpreter_bridge
  14. //宏的实现
  15. .macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg
  16. ldr \reg, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
  17. cbnz \reg, 1f
  18. ret
  19. 1:
  20. DELIVER_PENDING_EXCEPTION
  21. .endm
  22. //最终实现
  23. .macro DELIVER_PENDING_EXCEPTION_FRAME_READY
  24. mov x0, xSELF
  25. // Point of no return.
  26. bl artDeliverPendingExceptionFromCode // artDeliverPendingExceptionFromCode(Thread*)
  27. brk 0 // Unreached
  28. .endm

宏的最终实现是
bl artDeliverPendingExceptionFromCode ,也就是需要跳转到这个artDeliverPendingExceptionFromCode去执行

art/runtime/entrypoints/quick/quick_throw_entrypoints.cc

  1. extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self)
  2. REQUIRES_SHARED(Locks::mutator_lock_) {
  3. ScopedQuickEntrypointChecks sqec(self);
  4. self->QuickDeliverException();
  5. }

最终是调用Thread的QuickDeliverException方法,

  1. void Thread::QuickDeliverException() {
  2. // Get exception from thread.
  3. ObjPtr<mirror::Throwable> exception = GetException();
  4. CHECK(exception != nullptr);
  5. if (exception == GetDeoptimizationException()) {
  6. artDeoptimize(this);
  7. UNREACHABLE();
  8. }
  9. ReadBarrier::MaybeAssertToSpaceInvariant(exception.Ptr());
  10. // This is a real exception: let the instrumentation know about it.
  11. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
  12. if (instrumentation->HasExceptionThrownListeners() &&
  13. IsExceptionThrownByCurrentMethod(exception)) {
  14. // Instrumentation may cause GC so keep the exception object safe.
  15. StackHandleScope<1> hs(this);
  16. HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
  17. instrumentation->ExceptionThrownEvent(this, exception.Ptr());
  18. }
  19. // Does instrumentation need to deoptimize the stack?
  20. // Note: we do this *after* reporting the exception to instrumentation in case it
  21. // now requires deoptimization. It may happen if a debugger is attached and requests
  22. // new events (single-step, breakpoint, ...) when the exception is reported.
  23. if (Dbg::IsForcedInterpreterNeededForException(this)) {
  24. NthCallerVisitor visitor(this, 0, false);
  25. visitor.WalkStack();
  26. if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
  27. // method_type shouldn't matter due to exception handling.
  28. const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
  29. // Save the exception into the deoptimization context so it can be restored
  30. // before entering the interpreter.
  31. PushDeoptimizationContext(
  32. JValue(),
  33. false /* is_reference */,
  34. exception,
  35. false /* from_code */,
  36. method_type);
  37. artDeoptimize(this);
  38. UNREACHABLE();
  39. } else {
  40. LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
  41. << visitor.caller->PrettyMethod();
  42. }
  43. }
  44. // Don't leave exception visible while we try to find the handler, which may cause class
  45. // resolution.
  46. ClearException();
  47. QuickExceptionHandler exception_handler(this, false);
  48. exception_handler.FindCatch(exception); //找到exception处理的地方
  49. exception_handler.UpdateInstrumentationStack();
  50. if (exception_handler.GetClearException()) {
  51. // Exception was cleared as part of delivery.
  52. DCHECK(!IsExceptionPending());
  53. } else {
  54. // Exception was put back with a throw location.
  55. DCHECK(IsExceptionPending());
  56. // Check the to-space invariant on the re-installed exception (if applicable).
  57. ReadBarrier::MaybeAssertToSpaceInvariant(GetException());
  58. }
  59. exception_handler.DoLongJump(); //跳转到异常处理的地址
  60. }

里面最重要的几个地方,FindCatch()和DoLongJump(),找到相应Exception处理的catch块,并且去完成真正的跳转。

  1. void QuickExceptionHandler::DoLongJump(bool smash_caller_saves) {
  2. // Place context back on thread so it will be available when we continue.
  3. self_->ReleaseLongJumpContext(context_);
  4. context_->SetSP(reinterpret_cast<uintptr_t>(handler_quick_frame_));
  5. CHECK_NE(handler_quick_frame_pc_, 0u);
  6. context_->SetPC(handler_quick_frame_pc_);
  7. context_->SetArg0(handler_quick_arg0_);
  8. if (smash_caller_saves) {
  9. context_->SmashCallerSaves();
  10. }
  11. context_->DoLongJump();
  12. UNREACHABLE();
  13. }

如果找不到catch块处理,就会去通过DetachCurrentThread()和Destroy()去完成

  1. void Thread::Destroy() {
  2. Thread* self = this;
  3. DCHECK_EQ(self, Thread::Current());
  4. if (tlsPtr_.jni_env != nullptr) {
  5. {
  6. ScopedObjectAccess soa(self);
  7. MonitorExitVisitor visitor(self);
  8. // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
  9. tlsPtr_.jni_env->monitors_.VisitRoots(&visitor, RootInfo(kRootVMInternal));
  10. }
  11. // Release locally held global references which releasing may require the mutator lock.
  12. if (tlsPtr_.jpeer != nullptr) {
  13. // If pthread_create fails we don't have a jni env here.
  14. tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
  15. tlsPtr_.jpeer = nullptr;
  16. }
  17. if (tlsPtr_.class_loader_override != nullptr) {
  18. tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.class_loader_override);
  19. tlsPtr_.class_loader_override = nullptr;
  20. }
  21. }
  22. if (tlsPtr_.opeer != nullptr) {
  23. ScopedObjectAccess soa(self);
  24. // We may need to call user-supplied managed code, do this before final clean-up.
  25. HandleUncaughtExceptions(soa);
  26. RemoveFromThreadGroup(soa);
  27. Runtime* runtime = Runtime::Current();
  28. if (runtime != nullptr) {
  29. runtime->GetRuntimeCallbacks()->ThreadDeath(self);
  30. }
  31. }
  32. ...省略部分代码
  33. }
  34. void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) {
  35. if (!IsExceptionPending()) {
  36. return;
  37. }
  38. ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer));
  39. ScopedThreadStateChange tsc(this, kNative);
  40. // Get and clear the exception.
  41. ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred());
  42. tlsPtr_.jni_env->ExceptionClear();
  43. // Call the Thread instance's dispatchUncaughtException(Throwable)
  44. tlsPtr_.jni_env->CallVoidMethod(peer.get(),
  45. WellKnownClasses::java_lang_Thread_dispatchUncaughtException,
  46. exception.get());
  47. // If the dispatchUncaughtException threw, clear that exception too.
  48. tlsPtr_.jni_env->ExceptionClear();
  49. }

最终通过jni调用Thread的dispatchUncaughtException方法执行,也就是回到了我们最初的java堆栈中。

2.2 机器码模式

在机器码中,原始的字节码从Throw指令会被翻译成HThrow IR,
art/compiler/optimizing/nodes.h

  1. class HThrow FINAL : public HTemplateInstruction<1> {
  2. public:
  3. HThrow(HInstruction* exception, uint32_t dex_pc)
  4. : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) {
  5. SetRawInputAt(0, exception);
  6. }
  7. bool IsControlFlow() const OVERRIDE { return true; }
  8. bool NeedsEnvironment() const OVERRIDE { return true; }
  9. bool CanThrow() const OVERRIDE { return true; }
  10. bool AlwaysThrows() const OVERRIDE { return true; }
  11. DECLARE_INSTRUCTION(Throw);
  12. protected:
  13. DEFAULT_COPY_CONSTRUCTOR(Throw);
  14. };

art/compiler/optimizing/code_generator_arm64.cc

  1. void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) {
  2. codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
  3. CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
  4. }
  5. /*
  6. * Called by managed code, saves callee saves and then calls artThrowException
  7. * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception.
  8. */

art/runtime/arch/arm64/quick_entrypoints_arm64.S

ONE_ARG_RUNTIME_EXCEPTION art_quick_deliver_exception, artDeliverExceptionFromCode

最终会调用artDeliverExceptionFromCode方法,里面最终调用的就是Thread的QuickDeliverException方法,和之前分析的流程重合了,就不重复描述了。

  1. extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
  2. REQUIRES_SHARED(Locks::mutator_lock_) {
  3. /*
  4. * exception may be null, in which case this routine should
  5. * throw NPE. NOTE: this is a convenience for generated code,
  6. * which previously did the null check inline and constructed
  7. * and threw a NPE if null. This routine responsible for setting
  8. * exception_ in thread and delivering the exception.
  9. */
  10. ScopedQuickEntrypointChecks sqec(self);
  11. if (exception == nullptr) {
  12. self->ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception");
  13. } else {
  14. self->SetException(exception);
  15. }
  16. self->QuickDeliverException();
  17. }

3 总结

其实异常的处理就两步,1 抛异常, 2 处理异常,要么catch块处理,要么uncaughtExceptionHandler处理。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/251170
推荐阅读
相关标签
  

闽ICP备14008679号