diff options
94 files changed, 3210 insertions, 1888 deletions
@@ -305,7 +305,7 @@ oat-target-sync: oat-target # oatdump targets .PHONY: dump-oat -dump-oat: dump-oat-core dump-oat-boot dump-oat-Calculator +dump-oat: dump-oat-core dump-oat-boot .PHONY: dump-oat-core dump-oat-core: dump-oat-core-host dump-oat-core-target diff --git a/build/Android.common.mk b/build/Android.common.mk index 2472158..7faf452 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -246,6 +246,7 @@ LIBART_COMMON_SRC_FILES := \ src/thread.cc \ src/thread_list.cc \ src/thread_pool.cc \ + src/throw_location.cc \ src/trace.cc \ src/utf.cc \ src/utils.cc \ @@ -263,7 +264,6 @@ LIBART_COMMON_SRC_FILES += \ src/oat/runtime/context.cc \ src/oat/runtime/support_alloc.cc \ src/oat/runtime/support_cast.cc \ - src/oat/runtime/support_debug.cc \ src/oat/runtime/support_deoptimize.cc \ src/oat/runtime/support_dexcache.cc \ src/oat/runtime/support_field.cc \ diff --git a/src/base/mutex-inl.h b/src/base/mutex-inl.h index 3cb43a8..f911054 100644 --- a/src/base/mutex-inl.h +++ b/src/base/mutex-inl.h @@ -97,8 +97,9 @@ inline void BaseMutex::RegisterAsLocked(Thread* self) { BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i)); if (UNLIKELY(held_mutex != NULL)) { LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" " - << "(level " << LockLevel(i) << ") while locking \"" << name_ << "\" " - << "(level " << level_ << ")"; + << "(level " << LockLevel(i) << " - " << i + << ") while locking \"" << name_ << "\" " + << "(level " << level_ << " - " << static_cast<int>(level_) << ")"; if (i > kAbortLock) { // Only abort in the check below if this is more than abort level lock. bad_mutexes_held = true; diff --git a/src/check_jni.cc b/src/check_jni.cc index 57ce432..30d5099 100644 --- a/src/check_jni.cc +++ b/src/check_jni.cc @@ -44,7 +44,7 @@ namespace art { static void JniAbort(const char* jni_function_name, const char* msg) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - mirror::AbstractMethod* current_method = self->GetCurrentMethod(); + mirror::AbstractMethod* current_method = self->GetCurrentMethod(NULL); std::ostringstream os; os << "JNI DETECTED ERROR IN APPLICATION: " << msg; @@ -401,8 +401,7 @@ class ScopedCheck { * * Use the kFlag_NullableUtf flag where 'u' field(s) are nullable. */ - void Check(bool entry, const char* fmt0, ...) - SHARED_LOCKS_REQUIRED (Locks::mutator_lock_) { + void Check(bool entry, const char* fmt0, ...) SHARED_LOCKS_REQUIRED (Locks::mutator_lock_) { va_list ap; const mirror::AbstractMethod* traceMethod = NULL; @@ -411,7 +410,7 @@ class ScopedCheck { // use DetachCurrentThread or GetEnv on a thread that's not yet attached. Thread* self = Thread::Current(); if ((flags_ & kFlag_Invocation) == 0 || self != NULL) { - traceMethod = self->GetCurrentMethod(); + traceMethod = self->GetCurrentMethod(NULL); } } @@ -812,14 +811,19 @@ class ScopedCheck { // Verify that, if an exception has been raised, the native code doesn't // make any JNI calls other than the Exception* methods. if ((flags & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) { - std::string type(PrettyTypeOf(self->GetException())); + ThrowLocation throw_location; + mirror::Throwable* exception = self->GetException(&throw_location); + std::string type(PrettyTypeOf(exception)); // TODO: write native code that doesn't require allocation for dumping an exception. // TODO: do we care any more? art always dumps pending exceptions on aborting threads. - if (type != "java.lang.OutOfMemoryError") { - JniAbortF(function_name_, "JNI %s called with pending exception: %s", - function_name_, type.c_str(), jniGetStackTrace(soa_.Env()).c_str()); + bool with_stack_trace = (type != "java.lang.OutOfMemoryError"); + if (with_stack_trace) { + JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s\n%s", + function_name_, type.c_str(), throw_location.Dump().c_str(), + jniGetStackTrace(soa_.Env()).c_str()); } else { - JniAbortF(function_name_, "JNI %s called with %s pending", function_name_, type.c_str()); + JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s", + function_name_, type.c_str(), throw_location.Dump().c_str()); } return; } @@ -1772,7 +1776,7 @@ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D'); JniAbortF(__FUNCTION__, "non-nullable address is NULL"); } if (capacity <= 0) { - JniAbortF(__FUNCTION__, "capacity must be greater than 0: %d", capacity); + JniAbortF(__FUNCTION__, "capacity must be greater than 0: %lld", capacity); } return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity)); } diff --git a/src/class_linker.cc b/src/class_linker.cc index a9e17b2..4774c63 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -80,51 +80,9 @@ static void ThrowNoClassDefFoundError(const char* fmt, ...) static void ThrowNoClassDefFoundError(const char* fmt, ...) { va_list args; va_start(args, fmt); - Thread::Current()->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args); - va_end(args); -} - -static void ThrowClassFormatError(const char* fmt, ...) - __attribute__((__format__(__printf__, 1, 2))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -static void ThrowClassFormatError(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - Thread::Current()->ThrowNewExceptionV("Ljava/lang/ClassFormatError;", fmt, args); - va_end(args); -} - -static void ThrowLinkageError(const char* fmt, ...) - __attribute__((__format__(__printf__, 1, 2))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -static void ThrowLinkageError(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - Thread::Current()->ThrowNewExceptionV("Ljava/lang/LinkageError;", fmt, args); - va_end(args); -} - -static void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, const StringPiece& type, - const StringPiece& name) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ClassHelper kh(c); - std::ostringstream msg; - msg << "No " << scope << "field " << name << " of type " << type - << " in class " << kh.GetDescriptor() << " or its superclasses"; - std::string location(kh.GetLocation()); - if (!location.empty()) { - msg << " (defined in " << location << ")"; - } - Thread::Current()->ThrowNewException("Ljava/lang/NoSuchFieldError;", msg.str().c_str()); -} - -static void ThrowNullPointerException(const char* fmt, ...) - __attribute__((__format__(__printf__, 1, 2))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -static void ThrowNullPointerException(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - Thread::Current()->ThrowNewExceptionV("Ljava/lang/NullPointerException;", fmt, args); + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args); va_end(args); } @@ -139,18 +97,19 @@ static void ThrowEarlierClassFailure(mirror::Class* c) } CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus(); + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); if (c->GetVerifyErrorClass() != NULL) { // TODO: change the verifier to store an _instance_, with a useful detail message? ClassHelper ve_ch(c->GetVerifyErrorClass()); - std::string error_descriptor(ve_ch.GetDescriptor()); - Thread::Current()->ThrowNewException(error_descriptor.c_str(), PrettyDescriptor(c).c_str()); + self->ThrowNewException(throw_location, ve_ch.GetDescriptor(), PrettyDescriptor(c).c_str()); } else { - ThrowNoClassDefFoundError("%s", PrettyDescriptor(c).c_str()); + self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;", + PrettyDescriptor(c).c_str()); } } -static void WrapExceptionInInitializer() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Thread* self = Thread::Current(); JNIEnv* env = self->GetJniEnv(); @@ -163,7 +122,8 @@ static void WrapExceptionInInitializer() // We only wrap non-Error exceptions; an Error can just be used as-is. if (!is_error) { - self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", NULL); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;", NULL); } } @@ -1244,8 +1204,7 @@ static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) ObjectLock lock(self, klass); // Check for circular dependencies between classes. if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) { - self->ThrowNewException("Ljava/lang/ClassCircularityError;", - PrettyDescriptor(klass).c_str()); + ThrowClassCircularityError(klass); klass->SetStatus(mirror::Class::kStatusError); return NULL; } @@ -1260,9 +1219,7 @@ static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) } // Return the loaded class. No exceptions should be pending. CHECK(klass->IsResolved()) << PrettyClass(klass); - CHECK(!self->IsExceptionPending()) - << PrettyClass(klass) << " " << PrettyTypeOf(self->GetException()) << "\n" - << self->GetException()->Dump(); + self->AssertNoPendingException(); return klass; } @@ -1335,13 +1292,13 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade WellKnownClasses::java_lang_ClassLoader_loadClass, class_name_object.get())); } - if (soa.Env()->ExceptionCheck()) { + if (soa.Self()->IsExceptionPending()) { // If the ClassLoader threw, pass that exception up. return NULL; } else if (result.get() == NULL) { // broken loader - throw NPE to be compatible with Dalvik - ThrowNullPointerException("ClassLoader.loadClass returned null for %s", - class_name_string.c_str()); + ThrowNullPointerException(NULL, StringPrintf("ClassLoader.loadClass returned null for %s", + class_name_string.c_str()).c_str()); return NULL; } else { // success, return mirror::Class* @@ -1560,7 +1517,7 @@ const OatFile::OatMethod ClassLinker::GetOatMethodFor(const mirror::AbstractMeth // Special case to get oat code without overwriting a trampoline. const void* ClassLinker::GetOatCodeFor(const mirror::AbstractMethod* method) { - CHECK(Runtime::Current()->IsCompiler() || method->GetDeclaringClass()->IsInitializing()); + CHECK(!method->IsAbstract()) << PrettyMethod(method); const void* result = GetOatMethodFor(method).GetCode(); if (result == NULL) { // No code? You must mean to go into the interpreter. @@ -1587,7 +1544,8 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { if (class_data == NULL) { return; // no fields or methods - for example a marker interface } - if (!Runtime::Current()->IsStarted() || Runtime::Current()->UseCompileTimeClassPath()) { + Runtime* runtime = Runtime::Current(); + if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) { // OAT file unavailable return; } @@ -1603,32 +1561,26 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { } size_t method_index = 0; // Link the code of methods skipped by LinkCode - const void* trampoline = Runtime::Current()->GetResolutionStubArray(Runtime::kStaticMethod)->GetData(); for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { mirror::AbstractMethod* method = klass->GetDirectMethod(i); - if (Runtime::Current()->IsMethodTracingActive()) { - Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - if (instrumentation->GetSavedCodeFromMap(method) == trampoline) { - const void* code = oat_class->GetOatMethod(method_index).GetCode(); - instrumentation->ResetSavedCode(method); - method->SetCode(code); - instrumentation->SaveAndUpdateCode(method); - } - } else if (method->GetCode() == trampoline) { + if (method->IsStatic()) { const void* code = oat_class->GetOatMethod(method_index).GetCode(); if (code == NULL) { // No code? You must mean to go into the interpreter. code = GetInterpreterEntryPoint(); } - method->SetCode(code); + runtime->GetInstrumentation()->UpdateMethodsCode(method, code); } method_index++; } + // Ignore virtual methods on the iterator. } static void LinkCode(SirtRef<mirror::AbstractMethod>& method, const OatFile::OatClass* oat_class, uint32_t method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Method shouldn't have already been linked. + DCHECK(method->GetCode() == NULL); // Every kind of method should at least get an invoke stub from the oat_method. // non-abstract methods also get their code pointers. const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index); @@ -1641,23 +1593,22 @@ static void LinkCode(SirtRef<mirror::AbstractMethod>& method, const OatFile::Oat } if (method->IsStatic() && !method->IsConstructor()) { - // For static methods excluding the class initializer, install the trampoline + // For static methods excluding the class initializer, install the trampoline. method->SetCode(runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData()); } + if (method->IsNative()) { - // unregistering restores the dlsym lookup stub + // Unregistering restores the dlsym lookup stub. method->UnregisterNative(Thread::Current()); } - if (Runtime::Current()->IsMethodTracingActive()) { - Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - instrumentation->SaveAndUpdateCode(method.get()); - } - if (method->GetCode() == NULL) { // No code? You must mean to go into the interpreter. method->SetCode(GetInterpreterEntryPoint()); } + + // Allow instrumentation its chance to hijack code. + runtime->GetInstrumentation()->UpdateMethodsCode(method.get(), method->GetCode()); } void ClassLinker::LoadClass(const DexFile& dex_file, @@ -2229,7 +2180,6 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { // Verify super class. mirror::Class* super = klass->GetSuperClass(); - std::string error_msg; if (super != NULL) { // Acquire lock to prevent races on verifying the super class. ObjectLock lock(self, super); @@ -2238,18 +2188,17 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { Runtime::Current()->GetClassLinker()->VerifyClass(super); } if (!super->IsCompileTimeVerified()) { - error_msg = "Rejecting class "; - error_msg += PrettyDescriptor(klass); - error_msg += " that attempts to sub-class erroneous class "; - error_msg += PrettyDescriptor(super); + std::string error_msg(StringPrintf("Rejecting class %s that attempts to sub-class erroneous class %s", + PrettyDescriptor(klass).c_str(), + PrettyDescriptor(super).c_str())); LOG(ERROR) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); - SirtRef<mirror::Throwable> cause(self, self->GetException()); + SirtRef<mirror::Throwable> cause(self, self->GetException(NULL)); if (cause.get() != NULL) { self->ClearException(); } - self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str()); + ThrowVerifyError(klass, "%s", error_msg.c_str()); if (cause.get() != NULL) { - self->GetException()->SetCause(cause.get()); + self->GetException(NULL)->SetCause(cause.get()); } klass->SetStatus(mirror::Class::kStatusError); return; @@ -2264,13 +2213,12 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { if (oat_file_class_status == mirror::Class::kStatusError) { LOG(WARNING) << "Skipping runtime verification of erroneous class " << PrettyDescriptor(klass) << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); - error_msg = "Rejecting class "; - error_msg += PrettyDescriptor(klass); - error_msg += " because it failed compile-time verification"; - Thread::Current()->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str()); + ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification", + PrettyDescriptor(klass).c_str()); klass->SetStatus(mirror::Class::kStatusError); return; } + std::string error_msg; if (!preverified) { verifier_failure = verifier::MethodVerifier::VerifyClass(klass, error_msg); } @@ -2301,7 +2249,7 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() << " because: " << error_msg; self->AssertNoPendingException(); - self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str()); + ThrowVerifyError(klass, "%s", error_msg.c_str()); klass->SetStatus(mirror::Class::kStatusError); } } @@ -2463,7 +2411,7 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, klass->SetSuperClass(proxy_class); // The super class is java.lang.reflect.Proxy klass->SetStatus(mirror::Class::kStatusLoaded); // Class is now effectively in the loaded state - DCHECK(!Thread::Current()->IsExceptionPending()); + self->AssertNoPendingException(); // Link the fields and virtual methods, creating vtable and iftables if (!LinkClass(klass, interfaces)) { @@ -2798,7 +2746,7 @@ bool ClassLinker::ValidateSuperClassDescriptors(const mirror::Class* klass) { const mirror::AbstractMethod* method = klass->GetVTable()->Get(i); if (method != super->GetVTable()->Get(i) && !IsSameMethodSignatureInDifferentClassContexts(method, super, klass)) { - ThrowLinkageError("Class %s method %s resolves differently in superclass %s", + ThrowLinkageError(klass, "Class %s method %s resolves differently in superclass %s", PrettyDescriptor(klass).c_str(), PrettyMethod(method).c_str(), PrettyDescriptor(super).c_str()); return false; @@ -2813,7 +2761,7 @@ bool ClassLinker::ValidateSuperClassDescriptors(const mirror::Class* klass) { const mirror::AbstractMethod* method = iftable->GetMethodArray(i)->Get(j); if (!IsSameMethodSignatureInDifferentClassContexts(method, interface, method->GetDeclaringClass())) { - ThrowLinkageError("Class %s method %s resolves differently in interface %s", + ThrowLinkageError(klass, "Class %s method %s resolves differently in interface %s", PrettyDescriptor(method->GetDeclaringClass()).c_str(), PrettyMethod(method).c_str(), PrettyDescriptor(interface).c_str()); @@ -2996,10 +2944,9 @@ bool ClassLinker::LoadSuperAndInterfaces(SirtRef<mirror::Class>& klass, const De } // Verify if (!klass->CanAccess(super_class)) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", - "Class %s extended by class %s is inaccessible", - PrettyDescriptor(super_class).c_str(), - PrettyDescriptor(klass.get()).c_str()); + ThrowIllegalAccessError(klass.get(), "Class %s extended by class %s is inaccessible", + PrettyDescriptor(super_class).c_str(), + PrettyDescriptor(klass.get()).c_str()); return false; } klass->SetSuperClass(super_class); @@ -3016,10 +2963,9 @@ bool ClassLinker::LoadSuperAndInterfaces(SirtRef<mirror::Class>& klass, const De // Verify if (!klass->CanAccess(interface)) { // TODO: the RI seemed to ignore this in my testing. - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", - "Interface %s implemented by class %s is inaccessible", - PrettyDescriptor(interface).c_str(), - PrettyDescriptor(klass.get()).c_str()); + ThrowIllegalAccessError(klass.get(), "Interface %s implemented by class %s is inaccessible", + PrettyDescriptor(interface).c_str(), + PrettyDescriptor(klass.get()).c_str()); return false; } } @@ -3034,31 +2980,28 @@ bool ClassLinker::LinkSuperClass(SirtRef<mirror::Class>& klass) { mirror::Class* super = klass->GetSuperClass(); if (klass.get() == GetClassRoot(kJavaLangObject)) { if (super != NULL) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassFormatError;", - "java.lang.Object must not have a superclass"); + ThrowClassFormatError(klass.get(), "java.lang.Object must not have a superclass"); return false; } return true; } if (super == NULL) { - ThrowLinkageError("No superclass defined for class %s", PrettyDescriptor(klass.get()).c_str()); + ThrowLinkageError(klass.get(), "No superclass defined for class %s", + PrettyDescriptor(klass.get()).c_str()); return false; } // Verify if (super->IsFinal() || super->IsInterface()) { - Thread* self = Thread::Current(); - self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;", - "Superclass %s of %s is %s", - PrettyDescriptor(super).c_str(), - PrettyDescriptor(klass.get()).c_str(), - super->IsFinal() ? "declared final" : "an interface"); + ThrowIncompatibleClassChangeError(klass.get(), "Superclass %s of %s is %s", + PrettyDescriptor(super).c_str(), + PrettyDescriptor(klass.get()).c_str(), + super->IsFinal() ? "declared final" : "an interface"); return false; } if (!klass->CanAccess(super)) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", - "Superclass %s is inaccessible by %s", - PrettyDescriptor(super).c_str(), - PrettyDescriptor(klass.get()).c_str()); + ThrowIllegalAccessError(klass.get(), "Superclass %s is inaccessible to class %s", + PrettyDescriptor(super).c_str(), + PrettyDescriptor(klass.get()).c_str()); return false; } @@ -3074,8 +3017,9 @@ bool ClassLinker::LinkSuperClass(SirtRef<mirror::Class>& klass) { } // Disallow custom direct subclasses of java.lang.ref.Reference. if (init_done_ && super == GetClassRoot(kJavaLangRefReference)) { - ThrowLinkageError("Class %s attempts to subclass java.lang.ref.Reference, which is not allowed", - PrettyDescriptor(klass.get()).c_str()); + ThrowLinkageError(klass.get(), + "Class %s attempts to subclass java.lang.ref.Reference, which is not allowed", + PrettyDescriptor(klass.get()).c_str()); return false; } @@ -3096,7 +3040,7 @@ bool ClassLinker::LinkMethods(SirtRef<mirror::Class>& klass, // No vtable. size_t count = klass->NumVirtualMethods(); if (!IsUint(16, count)) { - ThrowClassFormatError("Too many methods on interface: %zd", count); + ThrowClassFormatError(klass.get(), "Too many methods on interface: %zd", count); return false; } for (size_t i = 0; i < count; ++i) { @@ -3133,7 +3077,7 @@ bool ClassLinker::LinkVirtualMethods(SirtRef<mirror::Class>& klass) { if (local_mh.HasSameNameAndSignature(&super_mh)) { if (klass->CanAccessMember(super_method->GetDeclaringClass(), super_method->GetAccessFlags())) { if (super_method->IsFinal()) { - ThrowLinkageError("Method %s overrides final method in class %s", + ThrowLinkageError(klass.get(), "Method %s overrides final method in class %s", PrettyMethod(local_method).c_str(), super_mh.GetDeclaringClassDescriptor()); return false; @@ -3156,7 +3100,7 @@ bool ClassLinker::LinkVirtualMethods(SirtRef<mirror::Class>& klass) { } } if (!IsUint(16, actual_count)) { - ThrowClassFormatError("Too many methods defined on class: %zd", actual_count); + ThrowClassFormatError(klass.get(), "Too many methods defined on class: %zd", actual_count); return false; } // Shrink vtable if possible @@ -3169,7 +3113,7 @@ bool ClassLinker::LinkVirtualMethods(SirtRef<mirror::Class>& klass) { CHECK(klass.get() == GetClassRoot(kJavaLangObject)); uint32_t num_virtual_methods = klass->NumVirtualMethods(); if (!IsUint(16, num_virtual_methods)) { - ThrowClassFormatError("Too many methods: %d", num_virtual_methods); + ThrowClassFormatError(klass.get(), "Too many methods: %d", num_virtual_methods); return false; } SirtRef<mirror::ObjectArray<mirror::AbstractMethod> > @@ -3238,10 +3182,9 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, DCHECK(interface != NULL); if (!interface->IsInterface()) { ClassHelper ih(interface); - self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;", - "Class %s implements non-interface class %s", - PrettyDescriptor(klass.get()).c_str(), - PrettyDescriptor(ih.GetDescriptor()).c_str()); + ThrowIncompatibleClassChangeError(klass.get(), "Class %s implements non-interface class %s", + PrettyDescriptor(klass.get()).c_str(), + PrettyDescriptor(ih.GetDescriptor()).c_str()); return false; } // Check if interface is already in iftable @@ -3297,7 +3240,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, AllocMethodArray(self, num_methods); iftable->SetMethodArray(i, method_array); mirror::ObjectArray<mirror::AbstractMethod>* vtable = klass->GetVTableDuringLinking(); - for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { + for (size_t j = 0; j < num_methods; ++j) { mirror::AbstractMethod* interface_method = interface->GetVirtualMethod(j); interface_mh.ChangeMethod(interface_method); int32_t k; @@ -3314,9 +3257,10 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, vtable_mh.ChangeMethod(vtable_method); if (interface_mh.HasSameNameAndSignature(&vtable_mh)) { if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) { - self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", - "Implementation not public: %s", - PrettyMethod(vtable_method).c_str()); + ThrowIllegalAccessError(klass.get(), + "Method '%s' implementing interface method '%s' is not public", + PrettyMethod(vtable_method).c_str(), + PrettyMethod(interface_method).c_str()); return false; } method_array->Set(j, vtable_method); @@ -3657,12 +3601,15 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, // same name to be loaded simultaneously by different loaders dex_cache->SetResolvedType(type_idx, resolved); } else { - CHECK(Thread::Current()->IsExceptionPending()) + Thread* self = Thread::Current(); + CHECK(self->IsExceptionPending()) << "Expected pending exception for failed resolution of: " << descriptor; - // Convert a ClassNotFoundException to a NoClassDefFoundError - if (Thread::Current()->GetException()->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + // Convert a ClassNotFoundException to a NoClassDefFoundError. + SirtRef<mirror::Throwable> cause(self, self->GetException(NULL)); + if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { Thread::Current()->ClearException(); ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); + self->GetException(NULL)->SetCause(cause.get()); } } } @@ -3779,7 +3726,7 @@ mirror::AbstractMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, if (resolved != NULL) { ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); } else { - ThrowNoSuchMethodError(type, klass, name, signature, referrer); + ThrowNoSuchMethodError(type, klass, name, signature); } } break; @@ -3791,12 +3738,12 @@ mirror::AbstractMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, if (resolved != NULL) { ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); } else { - ThrowNoSuchMethodError(type, klass, name, signature, referrer); + ThrowNoSuchMethodError(type, klass, name, signature); } } break; case kSuper: - ThrowNoSuchMethodError(type, klass, name, signature, referrer); + ThrowNoSuchMethodError(type, klass, name, signature); break; case kVirtual: if (resolved != NULL) { @@ -3806,7 +3753,7 @@ mirror::AbstractMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, if (resolved != NULL) { ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); } else { - ThrowNoSuchMethodError(type, klass, name, signature, referrer); + ThrowNoSuchMethodError(type, klass, name, signature); } } break; diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc index 6b8083f..c47ce4a 100644 --- a/src/class_linker_test.cc +++ b/src/class_linker_test.cc @@ -46,7 +46,7 @@ class ClassLinkerTest : public CommonTest { EXPECT_TRUE(class_linker_->FindSystemClass(descriptor.c_str()) == NULL); Thread* self = Thread::Current(); EXPECT_TRUE(self->IsExceptionPending()); - Object* exception = self->GetException(); + Object* exception = self->GetException(NULL); self->ClearException(); Class* exception_class = class_linker_->FindSystemClass("Ljava/lang/NoClassDefFoundError;"); EXPECT_TRUE(exception->InstanceOf(exception_class)); diff --git a/src/common_throws.cc b/src/common_throws.cc index 8673d11..0bb9da2 100644 --- a/src/common_throws.cc +++ b/src/common_throws.cc @@ -32,135 +32,98 @@ namespace art { -static void AddReferrerLocation(std::ostream& os, const mirror::AbstractMethod* referrer) +static void AddReferrerLocation(std::ostream& os, const mirror::Class* referrer) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (referrer != NULL) { - ClassHelper kh(referrer->GetDeclaringClass()); + ClassHelper kh(referrer); std::string location(kh.GetLocation()); if (!location.empty()) { - os << " (accessed from " << location << ")"; + os << " (declaration of '" << PrettyDescriptor(referrer) + << "' appears in " << location << ")"; } } } -static void AddReferrerLocationFromClass(std::ostream& os, mirror::Class* referrer) +static void ThrowException(const ThrowLocation* throw_location, const char* exception_descriptor, + const mirror::Class* referrer, const char* fmt, va_list* args = NULL) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (referrer != NULL) { - ClassHelper kh(referrer); - std::string location(kh.GetLocation()); - if (!location.empty()) { - os << " (declaration of '" << PrettyDescriptor(referrer) - << "' appears in " << location << ")"; - } + std::ostringstream msg; + if (args != NULL) { + std::string vmsg; + StringAppendV(&vmsg, fmt, *args); + msg << vmsg; + } else { + msg << fmt; + } + AddReferrerLocation(msg, referrer); + Thread* self = Thread::Current(); + if (throw_location == NULL) { + ThrowLocation computed_throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewException(computed_throw_location, exception_descriptor, msg.str().c_str()); + } else { + self->ThrowNewException(*throw_location, exception_descriptor, msg.str().c_str()); } } -// NullPointerException +// ArithmeticException -void ThrowNullPointerExceptionForFieldAccess(mirror::Field* field, bool is_read) { - std::ostringstream msg; - msg << "Attempt to " << (is_read ? "read from" : "write to") - << " field '" << PrettyField(field, true) << "' on a null object reference"; - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str()); +void ThrowArithmeticExceptionDivideByZero(Thread* self) { + ThrowException(NULL, "Ljava/lang/ArithmeticException;", NULL, "divide by zero"); } -void ThrowNullPointerExceptionForMethodAccess(mirror::AbstractMethod* caller, uint32_t method_idx, - InvokeType type) { - mirror::DexCache* dex_cache = caller->GetDeclaringClass()->GetDexCache(); - const DexFile& dex_file = *dex_cache->GetDexFile(); - std::ostringstream msg; - msg << "Attempt to invoke " << type << " method '" - << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference"; - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str()); +// ArrayIndexOutOfBoundsException + +void ThrowArrayIndexOutOfBoundsException(int index, int length) { + ThrowException(NULL, "Ljava/lang/ArrayIndexOutOfBoundsException;", NULL, + StringPrintf("length=%d; index=%d", length, index).c_str()); } -void ThrowNullPointerExceptionFromDexPC(mirror::AbstractMethod* throw_method, uint32_t dex_pc) { - const DexFile::CodeItem* code = MethodHelper(throw_method).GetCodeItem(); - CHECK_LT(dex_pc, code->insns_size_in_code_units_); - const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); - DecodedInstruction dec_insn(instr); - switch (instr->Opcode()) { - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: - ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kDirect); - break; - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: - ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kVirtual); - break; - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: - ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kInterface); - break; - case Instruction::IGET: - case Instruction::IGET_WIDE: - case Instruction::IGET_OBJECT: - case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: { - mirror::Field* field = - Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false); - ThrowNullPointerExceptionForFieldAccess(field, true /* read */); - break; - } - case Instruction::IPUT: - case Instruction::IPUT_WIDE: - case Instruction::IPUT_OBJECT: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: { - mirror::Field* field = - Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false); - ThrowNullPointerExceptionForFieldAccess(field, false /* write */); - break; - } - case Instruction::AGET: - case Instruction::AGET_WIDE: - case Instruction::AGET_OBJECT: - case Instruction::AGET_BOOLEAN: - case Instruction::AGET_BYTE: - case Instruction::AGET_CHAR: - case Instruction::AGET_SHORT: - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", - "Attempt to read from null array"); - break; - case Instruction::APUT: - case Instruction::APUT_WIDE: - case Instruction::APUT_OBJECT: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_BYTE: - case Instruction::APUT_CHAR: - case Instruction::APUT_SHORT: - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", - "Attempt to write to null array"); - break; - case Instruction::ARRAY_LENGTH: - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", - "Attempt to get length of null array"); - break; - default: { - // TODO: We should have covered all the cases where we expect a NPE above, this - // message/logging is so we can improve any cases we've missed in the future. - const DexFile& dex_file = *throw_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); - std::string message("Null pointer exception during instruction '"); - message += instr->DumpString(&dex_file); - message += "'"; - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str()); - break; - } - } +// ArrayStoreException + +void ThrowArrayStoreException(const mirror::Class* element_class, + const mirror::Class* array_class) { + ThrowException(NULL, "Ljava/lang/ArrayStoreException;", NULL, + StringPrintf("%s cannot be stored in an array of type %s", + PrettyDescriptor(element_class).c_str(), + PrettyDescriptor(array_class).c_str()).c_str()); +} + +// ClassCastException + +void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type) { + ThrowException(NULL, "Ljava/lang/ClassCastException;", NULL, + StringPrintf("%s cannot be cast to %s", + PrettyDescriptor(src_type).c_str(), + PrettyDescriptor(dest_type).c_str()).c_str()); +} + +void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg) { + ThrowException(throw_location, "Ljava/lang/ClassCastException;", NULL, msg); +} + +// ClassCircularityError + +void ThrowClassCircularityError(mirror::Class* c) { + std::ostringstream msg; + msg << PrettyDescriptor(c); + ThrowException(NULL, "Ljava/lang/ClassCircularityError;", c, msg.str().c_str()); } +// ClassFormatError + +void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/lang/ClassFormatError;", referrer, fmt, &args); + va_end(args);} + // IllegalAccessError void ThrowIllegalAccessErrorClass(mirror::Class* referrer, mirror::Class* accessed) { std::ostringstream msg; msg << "Illegal class access: '" << PrettyDescriptor(referrer) << "' attempting to access '" << PrettyDescriptor(accessed) << "'"; - AddReferrerLocationFromClass(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str()); } void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed, @@ -171,24 +134,21 @@ void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirr msg << "Illegal class access ('" << PrettyDescriptor(referrer) << "' attempting to access '" << PrettyDescriptor(accessed) << "') in attempt to invoke " << type << " method " << PrettyMethod(called).c_str(); - AddReferrerLocation(msg, caller); - Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str()); } void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::AbstractMethod* accessed) { std::ostringstream msg; msg << "Method '" << PrettyMethod(accessed) << "' is inaccessible to class '" << PrettyDescriptor(referrer) << "'"; - AddReferrerLocationFromClass(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str()); } void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::Field* accessed) { std::ostringstream msg; msg << "Field '" << PrettyField(accessed, false) << "' is inaccessible to class '" << PrettyDescriptor(referrer) << "'"; - AddReferrerLocationFromClass(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str()); } void ThrowIllegalAccessErrorFinalField(const mirror::AbstractMethod* referrer, @@ -196,10 +156,24 @@ void ThrowIllegalAccessErrorFinalField(const mirror::AbstractMethod* referrer, std::ostringstream msg; msg << "Final field '" << PrettyField(accessed, false) << "' cannot be written to by method '" << PrettyMethod(referrer) << "'"; - AddReferrerLocation(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer != NULL ? referrer->GetClass() : NULL, + msg.str().c_str()); +} + +void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...){ + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, fmt, &args); + va_end(args); } +// IllegalArgumentException + +void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg) { + ThrowException(throw_location, "Ljava/lang/IllegalArgumentException;", NULL, msg); +} + + // IncompatibleClassChangeError void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type, @@ -208,9 +182,9 @@ void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType foun std::ostringstream msg; msg << "The method '" << PrettyMethod(method) << "' was expected to be of type " << expected_type << " but instead was found to be of type " << found_type; - AddReferrerLocation(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;", - msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", + referrer != NULL ? referrer->GetClass() : NULL, + msg.str().c_str()); } void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::AbstractMethod* interface_method, @@ -224,9 +198,9 @@ void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::Ab << "' does not implement interface '" << PrettyDescriptor(interface_method->GetDeclaringClass()) << "' in call to '" << PrettyMethod(interface_method) << "'"; - AddReferrerLocation(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;", - msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", + referrer != NULL ? referrer->GetClass() : NULL, + msg.str().c_str()); } void ThrowIncompatibleClassChangeErrorField(const mirror::Field* resolved_field, bool is_static, @@ -235,30 +209,192 @@ void ThrowIncompatibleClassChangeErrorField(const mirror::Field* resolved_field, msg << "Expected '" << PrettyField(resolved_field) << "' to be a " << (is_static ? "static" : "instance") << " field" << " rather than a " << (is_static ? "instance" : "static") << " field"; - AddReferrerLocation(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;", - msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", referrer->GetClass(), + msg.str().c_str()); +} + +void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...){ + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", referrer, fmt, &args); + va_end(args); +} + +// LinkageError + +void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/lang/LinkageError;", referrer, fmt, &args); + va_end(args); +} + +// NegativeArraySizeException + +void ThrowNegativeArraySizeException(int size) { + ThrowException(NULL, "Ljava/lang/NegativeArraySizeException;", NULL, StringPrintf("%d", size).c_str()); +} + +void ThrowNegativeArraySizeException(const char* msg) { + ThrowException(NULL, "Ljava/lang/NegativeArraySizeException;", NULL, msg); +} + +// NoSuchFieldError + +void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, + const StringPiece& type, const StringPiece& name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ClassHelper kh(c); + std::ostringstream msg; + msg << "No " << scope << "field " << name << " of type " << type + << " in class " << kh.GetDescriptor() << " or its superclasses"; + ThrowException(NULL, "Ljava/lang/NoSuchFieldError;", c, msg.str().c_str()); } // NoSuchMethodError void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, - const StringPiece& signature, const mirror::AbstractMethod* referrer) { + const StringPiece& signature) { std::ostringstream msg; ClassHelper kh(c); msg << "No " << type << " method " << name << signature << " in class " << kh.GetDescriptor() << " or its super classes"; - AddReferrerLocation(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str()); + ThrowException(NULL, "Ljava/lang/NoSuchMethodError;", c, msg.str().c_str()); } -void ThrowNoSuchMethodError(uint32_t method_idx, const mirror::AbstractMethod* referrer) { - mirror::DexCache* dex_cache = referrer->GetDeclaringClass()->GetDexCache(); +void ThrowNoSuchMethodError(uint32_t method_idx) { + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); const DexFile& dex_file = *dex_cache->GetDexFile(); std::ostringstream msg; msg << "No method '" << PrettyMethod(method_idx, dex_file, true) << "'"; - AddReferrerLocation(msg, referrer); - Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str()); + ThrowException(&throw_location, "Ljava/lang/NoSuchMethodError;", + throw_location.GetMethod()->GetDeclaringClass(), msg.str().c_str()); +} + +// NullPointerException + +void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location, + mirror::Field* field, bool is_read) { + std::ostringstream msg; + msg << "Attempt to " << (is_read ? "read from" : "write to") + << " field '" << PrettyField(field, true) << "' on a null object reference"; + ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); +} + +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, + InvokeType type) { + mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + std::ostringstream msg; + msg << "Attempt to invoke " << type << " method '" + << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference"; + ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); +} + +void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { + const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem(); + uint32_t throw_dex_pc = throw_location.GetDexPc(); + CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_); + const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]); + DecodedInstruction dec_insn(instr); + switch (instr->Opcode()) { + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: + ThrowNullPointerExceptionForMethodAccess(throw_location, dec_insn.vB, kDirect); + break; + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: + ThrowNullPointerExceptionForMethodAccess(throw_location, dec_insn.vB, kVirtual); + break; + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + ThrowNullPointerExceptionForMethodAccess(throw_location, dec_insn.vB, kInterface); + break; + case Instruction::IGET: + case Instruction::IGET_WIDE: + case Instruction::IGET_OBJECT: + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: + case Instruction::IGET_CHAR: + case Instruction::IGET_SHORT: { + mirror::Field* field = + Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, + throw_location.GetMethod(), false); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); + break; + } + case Instruction::IPUT: + case Instruction::IPUT_WIDE: + case Instruction::IPUT_OBJECT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: { + mirror::Field* field = + Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, + throw_location.GetMethod(), false); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); + break; + } + case Instruction::AGET: + case Instruction::AGET_WIDE: + case Instruction::AGET_OBJECT: + case Instruction::AGET_BOOLEAN: + case Instruction::AGET_BYTE: + case Instruction::AGET_CHAR: + case Instruction::AGET_SHORT: + ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, + "Attempt to read from null array"); + break; + case Instruction::APUT: + case Instruction::APUT_WIDE: + case Instruction::APUT_OBJECT: + case Instruction::APUT_BOOLEAN: + case Instruction::APUT_BYTE: + case Instruction::APUT_CHAR: + case Instruction::APUT_SHORT: + ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, + "Attempt to write to null array"); + break; + case Instruction::ARRAY_LENGTH: + ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, + "Attempt to get length of null array"); + break; + default: { + // TODO: We should have covered all the cases where we expect a NPE above, this + // message/logging is so we can improve any cases we've missed in the future. + const DexFile& dex_file = + *throw_location.GetMethod()->GetDeclaringClass()->GetDexCache()->GetDexFile(); + ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, + StringPrintf("Null pointer exception during instruction '%s'", + instr->DumpString(&dex_file).c_str()).c_str()); + break; + } + } +} + +void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg) { + ThrowException(throw_location, "Ljava/lang/NullPointerException;", NULL, msg); +} + +// RuntimeException + +void ThrowRuntimeException(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/lang/RuntimeException;", NULL, fmt, &args); + va_end(args); +} + +// VerifyError + +void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/lang/VerifyError;", referrer, fmt, &args); + va_end(args); } } // namespace art diff --git a/src/common_throws.h b/src/common_throws.h index 9e28bd7..5555435 100644 --- a/src/common_throws.h +++ b/src/common_throws.h @@ -28,17 +28,39 @@ class Field; class Object; } // namespace mirror class StringPiece; +class ThrowLocation; -// NullPointerException +// ArithmeticException + +void ThrowArithmeticExceptionDivideByZero(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// ArrayIndexOutOfBoundsException -void ThrowNullPointerExceptionForFieldAccess(mirror::Field* field, bool is_read) +void ThrowArrayIndexOutOfBoundsException(int index, int length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -void ThrowNullPointerExceptionForMethodAccess(mirror::AbstractMethod* caller, uint32_t method_idx, - InvokeType type) +// ArrayStoreException + +void ThrowArrayStoreException(const mirror::Class* element_class, + const mirror::Class* array_class) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// ClassCircularityError + +void ThrowClassCircularityError(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// ClassCastException + +void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -void ThrowNullPointerExceptionFromDexPC(mirror::AbstractMethod* throw_method, uint32_t dex_pc) +// ClassFormatError + +void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // IllegalAccessError @@ -62,6 +84,15 @@ void ThrowIllegalAccessErrorFinalField(const mirror::AbstractMethod* referrer, mirror::Field* accessed) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// IllegalArgumentException + +void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // IncompatibleClassChangeError void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type, @@ -78,13 +109,66 @@ void ThrowIncompatibleClassChangeErrorField(const mirror::Field* resolved_field, const mirror::AbstractMethod* referrer) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// LinkageError + +void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// NegativeArraySizeException + +void ThrowNegativeArraySizeException(int size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +void ThrowNegativeArraySizeException(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + +// NoSuchFieldError + +void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, + const StringPiece& type, const StringPiece& name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // NoSuchMethodError void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, - const StringPiece& signature, const mirror::AbstractMethod* referrer) + const StringPiece& signature) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +void ThrowNoSuchMethodError(uint32_t method_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// NullPointerException + +void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location, + mirror::Field* field, + bool is_read) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -void ThrowNoSuchMethodError(uint32_t method_idx, const mirror::AbstractMethod* referrer) +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + uint32_t method_idx, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// RuntimeException + +void ThrowRuntimeException(const char* fmt, ...) + __attribute__((__format__(__printf__, 1, 2))) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// VerifyError + +void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); } // namespace art diff --git a/src/compiler/dex/frontend.h b/src/compiler/dex/frontend.h index 2e62dc8..49e0852 100644 --- a/src/compiler/dex/frontend.h +++ b/src/compiler/dex/frontend.h @@ -55,7 +55,6 @@ enum opt_control_vector { // Force code generation paths for testing. enum debugControlVector { - kDebugDisplayMissingTargets, kDebugVerbose, kDebugDumpCFG, kDebugSlowFieldPath, diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc index c13e797..4fadc9d 100644 --- a/src/compiler/dex/quick/gen_common.cc +++ b/src/compiler/dex/quick/gen_common.cc @@ -507,17 +507,6 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, } } - -// Debugging routine - if null target, branch to DebugMe -void Mir2Lir::GenShowTarget() -{ - DCHECK_NE(cu_->instruction_set, kX86) << "unimplemented GenShowTarget"; - LIR* branch_over = OpCmpImmBranch(kCondNe, TargetReg(kInvokeTgt), 0, NULL); - LoadWordDisp(TargetReg(kSelf), ENTRYPOINT_OFFSET(pDebugMe), TargetReg(kInvokeTgt)); - LIR* target = NewLIR0(kPseudoTargetLabel); - branch_over->target = target; -} - void Mir2Lir::HandleSuspendLaunchPads() { LIR** suspend_label = reinterpret_cast<LIR**>(suspend_launchpads_.elem_list); diff --git a/src/compiler/dex/quick/gen_invoke.cc b/src/compiler/dex/quick/gen_invoke.cc index 3e946f8..c0bae29 100644 --- a/src/compiler/dex/quick/gen_invoke.cc +++ b/src/compiler/dex/quick/gen_invoke.cc @@ -1375,9 +1375,6 @@ void Mir2Lir::GenInvoke(CallInfo* info) vtable_idx, direct_code, direct_method, original_type); } - if (cu_->enable_debug & (1 << kDebugDisplayMissingTargets)) { - GenShowTarget(); - } LIR* call_inst; if (cu_->instruction_set != kX86) { call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); diff --git a/src/compiler/dex/quick/mir_to_lir.h b/src/compiler/dex/quick/mir_to_lir.h index 69ebc7e..aec0cc1 100644 --- a/src/compiler/dex/quick/mir_to_lir.h +++ b/src/compiler/dex/quick/mir_to_lir.h @@ -396,7 +396,6 @@ class Mir2Lir : public Backend { bool is_long_or_double, bool is_object); void GenSget(uint32_t field_idx, RegLocation rl_dest, bool is_long_or_double, bool is_object); - void GenShowTarget(); void GenIGet(uint32_t field_idx, int opt_flags, OpSize size, RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object); void GenIPut(uint32_t field_idx, int opt_flags, OpSize size, diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 700936c..4e8ebbd 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -1202,9 +1202,8 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ manager->GetClassLinker()->FindClass(descriptor, soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader())); if (klass == NULL) { - Thread* self = Thread::Current(); - CHECK(self->IsExceptionPending()); - self->ClearException(); + CHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); /* * At compile time, we can still structurally verify the class even if FindClass fails. @@ -1230,13 +1229,13 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ if (klass->IsErroneous()) { // ClassLinker::VerifyClass throws, which isn't useful in the compiler. - CHECK(Thread::Current()->IsExceptionPending()); - Thread::Current()->ClearException(); + CHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); } CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous()) << PrettyDescriptor(klass) << ": state=" << klass->GetStatus(); - CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException()); + soa.Self()->AssertNoPendingException(); } void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file, @@ -1435,7 +1434,6 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()); const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def); mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); - Thread* self = Thread::Current(); bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1; bool can_init_static_fields = compiling_boot && manager->GetCompiler()->IsImageClass(descriptor); @@ -1447,9 +1445,9 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent // lock ordering and consequent potential deadlock. static Mutex lock1("Initializer lock", kMonitorLock); - MutexLock mu(self, lock1); + MutexLock mu(soa.Self(), lock1); // The lock required to initialize the class. - ObjectLock lock2(self, klass); + ObjectLock lock2(soa.Self(), klass); // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { manager->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields); @@ -1473,7 +1471,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } else { manager->GetClassLinker()->EnsureInitialized(klass, true, can_init_static_fields); } - CHECK(!self->IsExceptionPending()) << self->GetException()->Dump(); + soa.Self()->AssertNoPendingException(); } } } @@ -1494,7 +1492,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } } // Clear any class not found or verification exceptions. - self->ClearException(); + soa.Self()->ClearException(); } void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, @@ -1651,7 +1649,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t if (self->IsExceptionPending()) { ScopedObjectAccess soa(self); LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n" - << self->GetException()->Dump(); + << self->GetException(NULL)->Dump(); } } diff --git a/src/compiler/llvm/runtime_support_llvm.cc b/src/compiler/llvm/runtime_support_llvm.cc index d9b879a..d6c8181 100644 --- a/src/compiler/llvm/runtime_support_llvm.cc +++ b/src/compiler/llvm/runtime_support_llvm.cc @@ -135,15 +135,16 @@ void art_portable_unlock_object_from_code(mirror::Object* obj, Thread* thread) obj->MonitorExit(thread); } -void art_portable_test_suspend_from_code(Thread* thread) +void art_portable_test_suspend_from_code(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CheckSuspend(thread); - if (thread->ReadFlag(kEnterInterpreter)) { + CheckSuspend(self); + if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) { // Save out the shadow frame to the heap - ShadowFrameCopyVisitor visitor(thread); + ShadowFrameCopyVisitor visitor(self); visitor.WalkStack(true); - thread->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy(), JValue()); - thread->SetException(reinterpret_cast<mirror::Throwable*>(-1)); + self->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy()); + self->SetDeoptimizationReturnValue(JValue()); + self->SetException(ThrowLocation(), reinterpret_cast<mirror::Throwable*>(-1)); } } @@ -175,51 +176,59 @@ bool art_portable_is_exception_pending_from_code() { } void art_portable_throw_div_zero_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Thread::Current()->ThrowNewException("Ljava/lang/ArithmeticException;", - "divide by zero"); + ThrowArithmeticExceptionDivideByZero(Thread::Current()); } void art_portable_throw_array_bounds_from_code(int32_t index, int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "length=%d; index=%d", length, index); + ThrowArrayIndexOutOfBoundsException(index, length); } void art_portable_throw_no_such_method_from_code(int32_t method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // We need the calling method as context for the method_idx. - mirror::AbstractMethod* method = Thread::Current()->GetCurrentMethod(); - ThrowNoSuchMethodError(method_idx, method); + ThrowNoSuchMethodError(method_idx); } void art_portable_throw_null_pointer_exception_from_code(uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::AbstractMethod* throw_method = - Thread::Current()->GetManagedStack()->GetTopShadowFrame()->GetMethod(); - ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc); + // TODO: remove dex_pc argument from caller. + UNUSED(dex_pc); + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionFromDexPC(throw_location); } void art_portable_throw_stack_overflow_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ThrowStackOverflowError(Thread::Current()); } -void art_portable_throw_exception_from_code(mirror::Object* exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Thread::Current()->DeliverException(static_cast<mirror::Throwable*>(exception)); +void art_portable_throw_exception_from_code(mirror::Throwable* exception) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + if (exception == NULL) { + ThrowNullPointerException(NULL, "throw with null exception"); + } else { + self->SetException(throw_location, exception); + } } void* art_portable_get_and_clear_exception(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(self->IsExceptionPending()); - mirror::Throwable* exception = self->GetException(); + // TODO: make this inline. + mirror::Throwable* exception = self->GetException(NULL); self->ClearException(); return exception; } int32_t art_portable_find_catch_block_from_code(mirror::AbstractMethod* current_method, uint32_t ti_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Throwable* exception = Thread::Current()->GetException(); - // Check for magic deoptimization exception. - if (reinterpret_cast<int32_t>(exception) == -1) { + Thread* self = Thread::Current(); // TODO: make an argument. + ThrowLocation throw_location; + mirror::Throwable* exception = self->GetException(&throw_location); + // Check for special deoptimization exception. + if (UNLIKELY(reinterpret_cast<int32_t>(exception) == -1)) { return -1; } mirror::Class* exception_type = exception->GetClass(); @@ -229,26 +238,40 @@ int32_t art_portable_find_catch_block_from_code(mirror::AbstractMethod* current_ const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset); int iter_index = 0; + int result = -1; + uint32_t catch_dex_pc = -1; // Iterate over the catch handlers associated with dex_pc for (CatchHandlerIterator it(*code_item, *try_item); it.HasNext(); it.Next()) { uint16_t iter_type_idx = it.GetHandlerTypeIndex(); // Catch all case if (iter_type_idx == DexFile::kDexNoIndex16) { - return iter_index; + catch_dex_pc = it.GetHandlerAddress(); + result = iter_index; + break; } // Does this catch exception type apply? mirror::Class* iter_exception_type = mh.GetDexCacheResolvedType(iter_type_idx); - if (iter_exception_type == NULL) { - // The verifier should take care of resolving all exception classes early + if (UNLIKELY(iter_exception_type == NULL)) { + // TODO: check, the verifier (class linker?) should take care of resolving all exception + // classes early. LOG(WARNING) << "Unresolved exception class when finding catch block: " << mh.GetTypeDescriptorFromTypeIdx(iter_type_idx); } else if (iter_exception_type->IsAssignableFrom(exception_type)) { - return iter_index; + catch_dex_pc = it.GetHandlerAddress(); + result = iter_index; + break; } ++iter_index; } - // Handler not found - return -1; + if (result != -1) { + // Handler found. + Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(self, + throw_location, + current_method, + catch_dex_pc, + exception); + } + return result; } @@ -640,14 +663,12 @@ void art_portable_check_cast_from_code(const mirror::Class* dest_type, const mir DCHECK(dest_type->IsClass()) << PrettyClass(dest_type); DCHECK(src_type->IsClass()) << PrettyClass(src_type); if (UNLIKELY(!dest_type->IsAssignableFrom(src_type))) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassCastException;", - "%s cannot be cast to %s", - PrettyDescriptor(src_type).c_str(), - PrettyDescriptor(dest_type).c_str()); + ThrowClassCastException(dest_type, src_type); } } -void art_portable_check_put_array_element_from_code(const mirror::Object* element, const mirror::Object* array) +void art_portable_check_put_array_element_from_code(const mirror::Object* element, + const mirror::Object* array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (element == NULL) { return; @@ -658,10 +679,7 @@ void art_portable_check_put_array_element_from_code(const mirror::Object* elemen mirror::Class* component_type = array_class->GetComponentType(); mirror::Class* element_class = element->GetClass(); if (UNLIKELY(!component_type->IsAssignableFrom(element_class))) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "%s cannot be stored in an array of type %s", - PrettyDescriptor(element_class).c_str(), - PrettyDescriptor(array_class).c_str()); + ThrowArrayStoreException(element_class, array_class); } return; } diff --git a/src/debugger.cc b/src/debugger.cc index 080288f..9bd1eb5 100644 --- a/src/debugger.cc +++ b/src/debugger.cc @@ -46,6 +46,7 @@ #include "sirt_ref.h" #include "stack_indirect_reference_table.h" #include "thread_list.h" +#include "throw_location.h" #include "utf.h" #include "well_known_classes.h" @@ -104,6 +105,55 @@ struct SingleStepControl { int stack_depth; }; +class DebugInstrumentationListener : public instrumentation::InstrumentationListener { + public: + DebugInstrumentationListener() {} + virtual ~DebugInstrumentationListener() {} + + virtual void MethodEntered(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (method->IsNative()) { + // TODO: post location events is a suspension point and native method entry stubs aren't. + return; + } + Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry); + } + + virtual void MethodExited(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc, const JValue& return_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + UNUSED(return_value); + if (method->IsNative()) { + // TODO: post location events is a suspension point and native method entry stubs aren't. + return; + } + Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit); + } + + virtual void MethodUnwind(Thread* thread, const mirror::AbstractMethod* method, + uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // We're not recorded to listen to this kind of event, so complain. + LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method) + << " " << dex_pc; + } + + virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t new_dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc); + } + + virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostException(thread, throw_location, catch_method, catch_dex_pc, exception_object); + } + +} gDebugInstrumentationListener; + // JDWP is allowed unless the Zygote forbids it. static bool gJdwpAllowed = true; @@ -140,7 +190,7 @@ static size_t gAllocRecordCount GUARDED_BY(gAllocTrackerLock) = 0; static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); static SingleStepControl gSingleStepControl GUARDED_BY(Locks::breakpoint_lock_); -static bool IsBreakpoint(mirror::AbstractMethod* m, uint32_t dex_pc) +static bool IsBreakpoint(const mirror::AbstractMethod* m, uint32_t dex_pc) LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); @@ -466,15 +516,6 @@ bool Dbg::IsDisposed() { return gDisposed; } -static void SetDebuggerUpdatesEnabledCallback(Thread* t, void* user_data) { - t->SetDebuggerUpdatesEnabled(*reinterpret_cast<bool*>(user_data)); -} - -static void SetDebuggerUpdatesEnabled(bool enabled) { - MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); - Runtime::Current()->GetThreadList()->ForEach(SetDebuggerUpdatesEnabledCallback, &enabled); -} - void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -483,16 +524,26 @@ void Dbg::GoActive() { return; } - LOG(INFO) << "Debugger is active"; - { // TODO: dalvik only warned if there were breakpoints left over. clear in Dbg::Disconnected? MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); CHECK_EQ(gBreakpoints.size(), 0U); } + Runtime* runtime = Runtime::Current(); + runtime->GetThreadList()->SuspendAll(); + Thread* self = Thread::Current(); + ThreadState old_state = self->SetStateUnsafe(kRunnable); + CHECK_NE(old_state, kRunnable); + runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, + instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kDexPcMoved); gDebuggerActive = true; - SetDebuggerUpdatesEnabled(true); + CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); + runtime->GetThreadList()->ResumeAll(); + + LOG(INFO) << "Debugger is active"; } void Dbg::Disconnected() { @@ -500,11 +551,22 @@ void Dbg::Disconnected() { LOG(INFO) << "Debugger is no longer active"; + // Suspend all threads and exclusively acquire the mutator lock. Set the state of the thread + // to kRunnable to avoid scoped object access transitions. Remove the debugger as a listener + // and clear the object registry. + Runtime* runtime = Runtime::Current(); + runtime->GetThreadList()->SuspendAll(); + Thread* self = Thread::Current(); + ThreadState old_state = self->SetStateUnsafe(kRunnable); + runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, + instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kDexPcMoved); gDebuggerActive = false; - SetDebuggerUpdatesEnabled(false); - gRegistry->Clear(); gDebuggerConnected = false; + CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); + runtime->GetThreadList()->ResumeAll(); } bool Dbg::IsDebuggerActive() { @@ -1902,34 +1964,16 @@ struct GetThisVisitor : public StackVisitor { virtual bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS { if (frame_id != GetFrameId()) { return true; // continue - } - mirror::AbstractMethod* m = GetMethod(); - if (m->IsNative() || m->IsStatic()) { - this_object = NULL; } else { - uint16_t reg = DemangleSlot(0, m); - this_object = reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kReferenceVReg)); + this_object = GetThisObject(); + return false; } - return false; } mirror::Object* this_object; JDWP::FrameId frame_id; }; -static mirror::Object* GetThis(Thread* self, mirror::AbstractMethod* m, size_t frame_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO: should we return the 'this' we passed through to non-static native methods? - if (m->IsNative() || m->IsStatic()) { - return NULL; - } - - UniquePtr<Context> context(Context::Create()); - GetThisVisitor visitor(self, context.get(), frame_id); - visitor.WalkStack(); - return visitor.this_object; -} - JDWP::JdwpError Dbg::GetThisObject(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, JDWP::ObjectId* result) { ScopedObjectAccessUnchecked soa(Thread::Current()); @@ -2176,7 +2220,8 @@ void Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int sl visitor.WalkStack(); } -void Dbg::PostLocationEvent(const mirror::AbstractMethod* m, int dex_pc, mirror::Object* this_object, int event_flags) { +void Dbg::PostLocationEvent(const mirror::AbstractMethod* m, int dex_pc, + mirror::Object* this_object, int event_flags) { mirror::Class* c = m->GetDeclaringClass(); JDWP::JdwpLocation location; @@ -2194,29 +2239,25 @@ void Dbg::PostLocationEvent(const mirror::AbstractMethod* m, int dex_pc, mirror: gJdwpState->PostLocationEvent(&location, this_id, event_flags); } -void Dbg::PostException(Thread* thread, - JDWP::FrameId throw_frame_id, mirror::AbstractMethod* throw_method, - uint32_t throw_dex_pc, mirror::AbstractMethod* catch_method, +void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, mirror::Throwable* exception_object) { if (!IsDebuggerActive()) { return; } - JDWP::JdwpLocation throw_location; - SetLocation(throw_location, throw_method, throw_dex_pc); + JDWP::JdwpLocation jdwp_throw_location; + SetLocation(jdwp_throw_location, throw_location.GetMethod(), throw_location.GetDexPc()); JDWP::JdwpLocation catch_location; SetLocation(catch_location, catch_method, catch_dex_pc); // We need 'this' for InstanceOnly filters. - UniquePtr<Context> context(Context::Create()); - GetThisVisitor visitor(thread, context.get(), throw_frame_id); - visitor.WalkStack(); - JDWP::ObjectId this_id = gRegistry->Add(visitor.this_object); - + JDWP::ObjectId this_id = gRegistry->Add(throw_location.GetThis()); JDWP::ObjectId exception_id = gRegistry->Add(exception_object); JDWP::RefTypeId exception_class_id = gRegistry->AddRefType(exception_object->GetClass()); - gJdwpState->PostException(&throw_location, exception_id, exception_class_id, &catch_location, this_id); + gJdwpState->PostException(&jdwp_throw_location, exception_id, exception_class_id, &catch_location, + this_id); } void Dbg::PostClassPrepare(mirror::Class* c) { @@ -2232,20 +2273,9 @@ void Dbg::PostClassPrepare(mirror::Class* c) { gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state); } -void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self) { - if (!IsDebuggerActive() || dex_pc == -2 /* fake method exit */) { - return; - } - - size_t frame_id; - mirror::AbstractMethod* m = self->GetCurrentMethod(NULL, &frame_id); - //LOG(INFO) << "UpdateDebugger " << PrettyMethod(m) << "@" << dex_pc << " frame " << frame_id; - - if (dex_pc == -1) { - // We use a pc of -1 to represent method entry, since we might branch back to pc 0 later. - // This means that for this special notification, there can't be anything else interesting - // going on, so we're done already. - Dbg::PostLocationEvent(m, 0, GetThis(self, m, frame_id), kMethodEntry); +void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* m, uint32_t dex_pc) { + if (!IsDebuggerActive() || dex_pc == static_cast<uint32_t>(-2) /* fake method exit */) { return; } @@ -2259,7 +2289,7 @@ void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self) { // If the debugger is single-stepping one of our threads, check to // see if we're that thread and we've reached a step point. MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); - if (gSingleStepControl.is_active && gSingleStepControl.thread == self) { + if (gSingleStepControl.is_active && gSingleStepControl.thread == thread) { CHECK(!m->IsNative()); if (gSingleStepControl.step_depth == JDWP::SD_INTO) { // Step into method calls. We break when the line number @@ -2282,7 +2312,7 @@ void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self) { // might get unrolled past it by an exception, and it's tricky // to identify recursion.) - int stack_depth = GetStackDepth(self); + int stack_depth = GetStackDepth(thread); if (stack_depth < gSingleStepControl.stack_depth) { // popped up one or more frames, always trigger @@ -2307,7 +2337,7 @@ void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self) { // with the PC at the next instruction in the returned-to // function, rather than the end of the returning function. - int stack_depth = GetStackDepth(self); + int stack_depth = GetStackDepth(thread); if (stack_depth < gSingleStepControl.stack_depth) { event_flags |= kSingleStep; VLOG(jdwp) << "SS method pop"; @@ -2316,27 +2346,10 @@ void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self) { } } - // Check to see if this is a "return" instruction. JDWP says we should - // send the event *after* the code has been executed, but it also says - // the location we provide is the last instruction. Since the "return" - // instruction has no interesting side effects, we should be safe. - // (We can't just move this down to the returnFromMethod label because - // we potentially need to combine it with other events.) - // We're also not supposed to generate a method exit event if the method - // terminates "with a thrown exception". - if (dex_pc >= 0) { - const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); - CHECK(code_item != NULL) << PrettyMethod(m) << " @" << dex_pc; - CHECK_LT(dex_pc, static_cast<int32_t>(code_item->insns_size_in_code_units_)); - if (Instruction::At(&code_item->insns_[dex_pc])->IsReturn()) { - event_flags |= kMethodExit; - } - } - // If there's something interesting going on, see if it matches one // of the debugger filters. if (event_flags != 0) { - Dbg::PostLocationEvent(m, dex_pc, GetThis(self, m, frame_id), event_flags); + Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags); } } @@ -2706,8 +2719,19 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { // We can be called while an exception is pending. We need // to preserve that across the method invocation. - SirtRef<mirror::Throwable> old_exception(soa.Self(), soa.Self()->GetException()); - soa.Self()->ClearException(); + SirtRef<mirror::Object> old_throw_this_object(soa.Self(), NULL); + SirtRef<mirror::AbstractMethod> old_throw_method(soa.Self(), NULL); + SirtRef<mirror::Throwable> old_exception(soa.Self(), NULL); + uint32_t old_throw_dex_pc; + { + ThrowLocation old_throw_location; + mirror::Throwable* old_exception_obj = soa.Self()->GetException(&old_throw_location); + old_throw_this_object.reset(old_throw_location.GetThis()); + old_throw_method.reset(old_throw_location.GetMethod()); + old_exception.reset(old_exception_obj); + old_throw_dex_pc = old_throw_location.GetDexPc(); + soa.Self()->ClearException(); + } // Translate the method through the vtable, unless the debugger wants to suppress it. mirror::AbstractMethod* m = pReq->method_; @@ -2731,12 +2755,13 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { arg_array.BuildArgArray(soa, pReq->receiver_, reinterpret_cast<jvalue*>(pReq->arg_values_)); InvokeWithArgArray(soa, m, &arg_array, &pReq->result_value, mh.GetShorty()[0]); - pReq->exception = gRegistry->Add(soa.Self()->GetException()); + mirror::Throwable* exception = soa.Self()->GetException(NULL); + soa.Self()->ClearException(); + pReq->exception = gRegistry->Add(exception); pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty()); if (pReq->exception != 0) { - mirror::Object* exc = soa.Self()->GetException(); - VLOG(jdwp) << " JDWP invocation returning with exception=" << exc << " " << PrettyTypeOf(exc); - soa.Self()->ClearException(); + VLOG(jdwp) << " JDWP invocation returning with exception=" << exception + << " " << exception->Dump(); pReq->result_value.SetJ(0); } else if (pReq->result_tag == JDWP::JT_OBJECT) { /* if no exception thrown, examine object result more closely */ @@ -2759,7 +2784,9 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { } if (old_exception.get() != NULL) { - soa.Self()->SetException(old_exception.get()); + ThrowLocation gc_safe_throw_location(old_throw_this_object.get(), old_throw_method.get(), + old_throw_dex_pc); + soa.Self()->SetException(gc_safe_throw_location, old_exception.get()); } } @@ -2943,9 +2970,6 @@ void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) { ScopedObjectAccessUnchecked soa(Thread::Current()); JDWP::ObjectId id = gRegistry->Add(t->GetPeer()); gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR")); - // If this thread's just joined the party while we're already debugging, make sure it knows - // to give us updates when it's running. - t->SetDebuggerUpdatesEnabled(true); } Dbg::DdmSendThreadNotification(t, type); } @@ -3326,9 +3350,9 @@ void Dbg::DdmSendHeapSegments(bool native) { Heap* heap = Runtime::Current()->GetHeap(); const Spaces& spaces = heap->GetSpaces(); Thread* self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) { if ((*cur)->IsAllocSpace()) { - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); (*cur)->AsAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); } } diff --git a/src/debugger.h b/src/debugger.h index ad01011..eb17695 100644 --- a/src/debugger.h +++ b/src/debugger.h @@ -39,6 +39,7 @@ class Throwable; } // namespace mirror struct AllocRecord; class Thread; +class ThrowLocation; /* * Invoke-during-breakpoint support. @@ -101,8 +102,8 @@ class Dbg { * when the debugger attaches. */ static void Connected(); - static void GoActive() LOCKS_EXCLUDED(Locks::breakpoint_lock_); - static void Disconnected() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void GoActive() LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::mutator_lock_); + static void Disconnected() LOCKS_EXCLUDED(Locks::mutator_lock_); static void Disposed(); // Returns true if we're actually debugging with a real debugger, false if it's @@ -326,9 +327,8 @@ class Dbg { static void PostLocationEvent(const mirror::AbstractMethod* method, int pcOffset, mirror::Object* thisPtr, int eventFlags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void PostException(Thread* thread, JDWP::FrameId throw_frame_id, - mirror::AbstractMethod* throw_method, - uint32_t throw_dex_pc, mirror::AbstractMethod* catch_method, + static void PostException(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, mirror::Throwable* exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void PostThreadStart(Thread* t) @@ -338,7 +338,8 @@ class Dbg { static void PostClassPrepare(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void UpdateDebugger(int32_t dex_pc, Thread* self) + static void UpdateDebugger(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t new_dex_pc) LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/dex_file.h b/src/dex_file.h index 2da3e32..002d79c 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -866,10 +866,10 @@ class DexFile { Index index_; // The base address of the memory mapping. - const byte* begin_; + const byte* const begin_; // The size of the underlying memory allocation in bytes. - size_t size_; + const size_t size_; // Typically the dex file name when available, alternatively some identifying string. // @@ -883,6 +883,7 @@ class DexFile { UniquePtr<MemMap> mem_map_; // A cached com.android.dex.Dex instance, possibly NULL. Use GetDexObject. + // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar. mutable jobject dex_object_; // Points to the header section. diff --git a/src/gc/large_object_space.cc b/src/gc/large_object_space.cc index df7e68d..c3bf382 100644 --- a/src/gc/large_object_space.cc +++ b/src/gc/large_object_space.cc @@ -50,7 +50,7 @@ void LargeObjectSpace::CopyLiveToMarked() { LargeObjectMapSpace::LargeObjectMapSpace(const std::string& name) : LargeObjectSpace(name), - lock_("large object space lock", kAllocSpaceLock) + lock_("large object map space lock", kAllocSpaceLock) { } diff --git a/src/gc/mark_sweep.cc b/src/gc/mark_sweep.cc index 055a7e7..35e75cb 100644 --- a/src/gc/mark_sweep.cc +++ b/src/gc/mark_sweep.cc @@ -135,8 +135,8 @@ void MarkSweep::BindBitmaps() { MarkSweep::MarkSweep(Heap* heap, bool is_concurrent) : GarbageCollector(heap), gc_barrier_(new Barrier(0)), - large_object_lock_("large object lock"), - mark_stack_expand_lock_("mark stack expand lock"), + large_object_lock_("mark sweep large object lock", kMarkSweepLargeObjectLock), + mark_stack_expand_lock_("mark sweep mark stack expand lock"), timings_(GetName(), true), cumulative_timings_(GetName()), is_concurrent_(is_concurrent) { diff --git a/src/gc_map.h b/src/gc_map.h index 8e4dbdb..473b39a 100644 --- a/src/gc_map.h +++ b/src/gc_map.h @@ -66,8 +66,11 @@ class NativePcOffsetToReferenceMap { const uint8_t* FindBitMap(uintptr_t native_pc_offset) { size_t num_entries = NumEntries(); size_t index = Hash(native_pc_offset) % num_entries; + size_t misses = 0; while (GetNativePcOffset(index) != native_pc_offset) { index = (index + 1) % num_entries; + misses++; + DCHECK_LT(misses, num_entries) << "Failed to find offset: " << native_pc_offset; } return GetBitMap(index); } diff --git a/src/heap.cc b/src/heap.cc index 2f7cb24..468e800 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -611,44 +611,46 @@ void Heap::DumpSpaces() { } void Heap::VerifyObjectBody(const mirror::Object* obj) { - if (!IsAligned<kObjectAlignment>(obj)) { + if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { LOG(FATAL) << "Object isn't aligned: " << obj; } - - // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the - // heap_bitmap_lock_. - if (!GetLiveBitmap()->Test(obj)) { - // Check the allocation stack / live stack. - if (!std::binary_search(live_stack_->Begin(), live_stack_->End(), obj) && - std::find(allocation_stack_->Begin(), allocation_stack_->End(), obj) == - allocation_stack_->End()) { - if (large_object_space_->GetLiveObjects()->Test(obj)) { - DumpSpaces(); - LOG(FATAL) << "Object is dead: " << obj; + if (UNLIKELY(GetObjectsAllocated() <= 10)) { // Ignore early dawn of the universe verifications. + return; + } + const byte* raw_addr = reinterpret_cast<const byte*>(obj) + + mirror::Object::ClassOffset().Int32Value(); + const mirror::Class* c = *reinterpret_cast<mirror::Class* const *>(raw_addr); + if (UNLIKELY(c == NULL)) { + LOG(FATAL) << "Null class in object: " << obj; + } else if (UNLIKELY(!IsAligned<kObjectAlignment>(c))) { + LOG(FATAL) << "Class isn't aligned: " << c << " in object: " << obj; + } + // Check obj.getClass().getClass() == obj.getClass().getClass().getClass() + // Note: we don't use the accessors here as they have internal sanity checks + // that we don't want to run + raw_addr = reinterpret_cast<const byte*>(c) + mirror::Object::ClassOffset().Int32Value(); + const mirror::Class* c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr); + raw_addr = reinterpret_cast<const byte*>(c_c) + mirror::Object::ClassOffset().Int32Value(); + const mirror::Class* c_c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr); + CHECK_EQ(c_c, c_c_c); + + if (verify_object_mode_ != kVerifyAllFast) { + // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the + // heap_bitmap_lock_. + if (!GetLiveBitmap()->Test(obj)) { + // Check the allocation stack / live stack. + if (!std::binary_search(live_stack_->Begin(), live_stack_->End(), obj) && + std::find(allocation_stack_->Begin(), allocation_stack_->End(), obj) == + allocation_stack_->End()) { + if (large_object_space_->GetLiveObjects()->Test(obj)) { + DumpSpaces(); + LOG(FATAL) << "Object is dead: " << obj; + } } } - } - - // Ignore early dawn of the universe verifications - if (verify_object_mode_ != kVerifyAllFast && GetObjectsAllocated() > 10) { - const byte* raw_addr = reinterpret_cast<const byte*>(obj) + - mirror::Object::ClassOffset().Int32Value(); - const mirror::Class* c = *reinterpret_cast<mirror::Class* const *>(raw_addr); - if (c == NULL) { - LOG(FATAL) << "Null class in object: " << obj; - } else if (!IsAligned<kObjectAlignment>(c)) { - LOG(FATAL) << "Class isn't aligned: " << c << " in object: " << obj; - } else if (!GetLiveBitmap()->Test(c)) { + if (!GetLiveBitmap()->Test(c)) { LOG(FATAL) << "Class of object is dead: " << c << " in object: " << obj; } - // Check obj.getClass().getClass() == obj.getClass().getClass().getClass() - // Note: we don't use the accessors here as they have internal sanity checks - // that we don't want to run - raw_addr = reinterpret_cast<const byte*>(c) + mirror::Object::ClassOffset().Int32Value(); - const mirror::Class* c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr); - raw_addr = reinterpret_cast<const byte*>(c_c) + mirror::Object::ClassOffset().Int32Value(); - const mirror::Class* c_c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr); - CHECK_EQ(c_c, c_c_c); } } diff --git a/src/hprof/hprof.cc b/src/hprof/hprof.cc index 472cc67..7539066 100644 --- a/src/hprof/hprof.cc +++ b/src/hprof/hprof.cc @@ -41,6 +41,7 @@ #include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" #include "globals.h" @@ -450,16 +451,14 @@ class Hprof { if (fd_ >= 0) { out_fd = dup(fd_); if (out_fd < 0) { - self->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno)); + ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno)); return; } } else { out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644); if (out_fd < 0) { - self->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(), - strerror(errno)); + ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(), + strerror(errno)); return; } } @@ -470,7 +469,7 @@ class Hprof { if (!okay) { std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", filename_.c_str(), strerror(errno))); - self->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str()); + ThrowRuntimeException("%s", msg.c_str()); LOG(ERROR) << msg; } } diff --git a/src/instrumentation.cc b/src/instrumentation.cc index 81fe637..55e93cb 100644 --- a/src/instrumentation.cc +++ b/src/instrumentation.cc @@ -18,14 +18,17 @@ #include <sys/uio.h> +#include "atomic_integer.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "debugger.h" +#include "dex_file-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" #include "mirror/abstract_method-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "nth_caller_visitor.h" #if !defined(ART_USE_PORTABLE_COMPILER) #include "oat/runtime/oat_support_entrypoints.h" #endif @@ -34,205 +37,521 @@ #include "scoped_thread_state_change.h" #include "thread.h" #include "thread_list.h" -#include "trace.h" namespace art { +namespace instrumentation { -static bool InstallStubsClassVisitor(mirror::Class* klass, void*) +static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - for (size_t i = 0; i < klass->NumDirectMethods(); i++) { - mirror::AbstractMethod* method = klass->GetDirectMethod(i); - if (instrumentation->GetSavedCodeFromMap(method) == NULL) { - instrumentation->SaveAndUpdateCode(method); - } - } - - for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { - mirror::AbstractMethod* method = klass->GetVirtualMethod(i); - if (instrumentation->GetSavedCodeFromMap(method) == NULL) { - instrumentation->SaveAndUpdateCode(method); - } - } - return true; + Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); + return instrumentation->InstallStubsForClass(klass); } -static bool UninstallStubsClassVisitor(mirror::Class* klass, void*) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); +bool Instrumentation::InstallStubsForClass(mirror::Class* klass) { + bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_; + ClassLinker* class_linker = NULL; + if (uninstall) { + class_linker = Runtime::Current()->GetClassLinker(); + } + bool is_initialized = klass->IsInitialized(); for (size_t i = 0; i < klass->NumDirectMethods(); i++) { mirror::AbstractMethod* method = klass->GetDirectMethod(i); - if (instrumentation->GetSavedCodeFromMap(method) != NULL) { - instrumentation->ResetSavedCode(method); + if (!method->IsAbstract()) { + const void* new_code; + if (uninstall) { + if (is_initialized || !method->IsStatic() || method->IsConstructor()) { + new_code = class_linker->GetOatCodeFor(method); + } else { + new_code = Runtime::Current()->GetResolutionStubArray(Runtime::kStaticMethod)->GetData(); + } + } else { // !uninstall + if (!interpreter_stubs_installed_ || method->IsNative()) { + new_code = GetInstrumentationEntryPoint(); + } else { + new_code = GetInterpreterEntryPoint(); + } + } + method->SetCode(new_code); } } - for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { mirror::AbstractMethod* method = klass->GetVirtualMethod(i); - if (instrumentation->GetSavedCodeFromMap(method) != NULL) { - instrumentation->ResetSavedCode(method); + if (!method->IsAbstract()) { + const void* new_code; + if (uninstall) { + new_code = class_linker->GetOatCodeFor(method); + } else { // !uninstall + if (!interpreter_stubs_installed_ || method->IsNative()) { + new_code = GetInstrumentationEntryPoint(); + } else { + new_code = GetInterpreterEntryPoint(); + } + } + method->SetCode(new_code); } } return true; } -void InstrumentationInstallStack(Thread* self, void* arg) +// Places the instrumentation exit pc as the return PC for every quick frame. This also allows +// deoptimization of quick frames to interpreter frames. +static void InstrumentationInstallStack(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { struct InstallStackVisitor : public StackVisitor { - InstallStackVisitor(Thread* self, uintptr_t instrumentation_exit_pc) - : StackVisitor(self, NULL), self_(self), - instrumentation_exit_pc_(instrumentation_exit_pc) {} + InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc) + : StackVisitor(thread, context), instrumentation_stack_(thread->GetInstrumentationStack()), + instrumentation_exit_pc_(instrumentation_exit_pc), last_return_pc_(0) {} virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::AbstractMethod* m = GetMethod(); if (GetCurrentQuickFrame() == NULL) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Ignoring a shadow frame. Frame " << GetFrameId() + << " Method=" << PrettyMethod(m); + } return true; // Ignore shadow frames. } - mirror::AbstractMethod* m = GetMethod(); if (m == NULL) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Skipping upcall. Frame " << GetFrameId(); + } + last_return_pc_ = 0; return true; // Ignore upcalls. } - if (m->GetDexMethodIndex() == DexFile::kDexNoIndex16) { + if (m->IsRuntimeMethod()) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId(); + } + last_return_pc_ = GetReturnPc(); return true; // Ignore unresolved methods since they will be instrumented after resolution. } - uintptr_t pc = GetReturnPc(); - InstrumentationStackFrame instrumentation_frame(m, pc, GetFrameId()); - self_->PushBackInstrumentationStackFrame(instrumentation_frame); + if (kVerboseInstrumentation) { + LOG(INFO) << " Installing exit stub in " << DescribeLocation(); + } + uintptr_t return_pc = GetReturnPc(); + CHECK_NE(return_pc, instrumentation_exit_pc_); + CHECK_NE(return_pc, 0U); + InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId()); + if (kVerboseInstrumentation) { + LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump(); + } + instrumentation_stack_->push_back(instrumentation_frame); + dex_pcs_.push_back(m->ToDexPc(last_return_pc_)); SetReturnPc(instrumentation_exit_pc_); + last_return_pc_ = return_pc; return true; // Continue. } - Thread* const self_; + std::deque<InstrumentationStackFrame>* const instrumentation_stack_; + std::vector<uint32_t> dex_pcs_; const uintptr_t instrumentation_exit_pc_; + uintptr_t last_return_pc_; }; + if (kVerboseInstrumentation) { + std::string thread_name; + thread->GetThreadName(thread_name); + LOG(INFO) << "Installing exit stubs in " << thread_name; + } + UniquePtr<Context> context(Context::Create()); uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc(); - InstallStackVisitor visitor(self, instrumentation_exit_pc); + InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc); visitor.WalkStack(true); - Trace* trace = reinterpret_cast<Trace*>(arg); - if (trace != NULL) { - std::deque<InstrumentationStackFrame>::const_reverse_iterator it = - self->GetInstrumentationStack()->rbegin(); - std::deque<InstrumentationStackFrame>::const_reverse_iterator end = - self->GetInstrumentationStack()->rend(); - for (; it != end; ++it) { - trace->LogMethodTraceEvent(self, (*it).method_, Trace::kMethodTraceEnter); - } + + // Create method enter events for all methods current on the thread's stack. + Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); + typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It; + for (It it = thread->GetInstrumentationStack()->rbegin(), + end = thread->GetInstrumentationStack()->rend(); it != end; ++it) { + mirror::Object* this_object = (*it).this_object_; + mirror::AbstractMethod* method = (*it).method_; + uint32_t dex_pc = visitor.dex_pcs_.back(); + visitor.dex_pcs_.pop_back(); + instrumentation->MethodEnterEvent(thread, this_object, method, dex_pc); } + thread->VerifyStack(); } -static void InstrumentationRestoreStack(Thread* self, void*) +// Removes the instrumentation exit pc as the return PC for every quick frame. +static void InstrumentationRestoreStack(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { struct RestoreStackVisitor : public StackVisitor { - RestoreStackVisitor(Thread* self, uintptr_t instrumentation_exit_pc) - : StackVisitor(self, NULL), self_(self), - instrumentation_exit_pc_(instrumentation_exit_pc) {} + RestoreStackVisitor(Thread* thread, uintptr_t instrumentation_exit_pc, + Instrumentation* instrumentation) + : StackVisitor(thread, NULL), thread_(thread), + instrumentation_exit_pc_(instrumentation_exit_pc), + instrumentation_(instrumentation), + instrumentation_stack_(thread->GetInstrumentationStack()), + frames_removed_(0) {} virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (self_->IsInstrumentationStackEmpty()) { + if (instrumentation_stack_->size() == 0) { return false; // Stop. } mirror::AbstractMethod* m = GetMethod(); + if (GetCurrentQuickFrame() == NULL) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Ignoring a shadow frame. Frame " << GetFrameId() << " Method=" << PrettyMethod(m); + } + return true; // Ignore shadow frames. + } if (m == NULL) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Skipping upcall. Frame " << GetFrameId(); + } return true; // Ignore upcalls. } - uintptr_t pc = GetReturnPc(); - if (pc == instrumentation_exit_pc_) { - InstrumentationStackFrame instrumentation_frame = self_->PopInstrumentationStackFrame(); - SetReturnPc(instrumentation_frame.return_pc_); - CHECK(m == instrumentation_frame.method_); - CHECK_EQ(GetFrameId(), instrumentation_frame.frame_id_); - Runtime* runtime = Runtime::Current(); - if (runtime->IsMethodTracingActive()) { - Trace* trace = runtime->GetInstrumentation()->GetTrace(); - trace->LogMethodTraceEvent(self_, m, Trace::kMethodTraceExit); + typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It; // TODO: C++0x auto + bool removed_stub = false; + // TODO: make this search more efficient? + for (It it = instrumentation_stack_->begin(), end = instrumentation_stack_->end(); it != end; + ++it) { + InstrumentationStackFrame instrumentation_frame = *it; + if (instrumentation_frame.frame_id_ == GetFrameId()) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Removing exit stub in " << DescribeLocation(); + } + CHECK(m == instrumentation_frame.method_) << PrettyMethod(m); + SetReturnPc(instrumentation_frame.return_pc_); + // Create the method exit events. As the methods didn't really exit the result is 0. + instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m, + GetDexPc(), JValue()); + frames_removed_++; + removed_stub = true; + break; + } + } + if (!removed_stub) { + if (kVerboseInstrumentation) { + LOG(INFO) << " No exit stub in " << DescribeLocation(); + DescribeStack(thread_); } } return true; // Continue. } - Thread* const self_; + Thread* const thread_; const uintptr_t instrumentation_exit_pc_; + Instrumentation* const instrumentation_; + std::deque<instrumentation::InstrumentationStackFrame>* const instrumentation_stack_; + size_t frames_removed_; }; - uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc(); - RestoreStackVisitor visitor(self, instrumentation_exit_pc); - visitor.WalkStack(true); + if (kVerboseInstrumentation) { + std::string thread_name; + thread->GetThreadName(thread_name); + LOG(INFO) << "Removing exit stubs in " << thread_name; + } + std::deque<instrumentation::InstrumentationStackFrame>* stack = thread->GetInstrumentationStack(); + if (stack->size() > 0) { + Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); + uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc(); + RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation); + visitor.WalkStack(true); + CHECK_EQ(visitor.frames_removed_, stack->size()); + while (stack->size() > 0) { + stack->pop_front(); + } + } } -Instrumentation::~Instrumentation() { - delete trace_; +void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) { + Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); + bool require_entry_exit_stubs = false; + bool require_interpreter = false; + if ((events & kMethodEntered) != 0) { + method_entry_listeners_.push_back(listener); + require_entry_exit_stubs = true; + have_method_entry_listeners_ = true; + } + if ((events & kMethodExited) != 0) { + method_exit_listeners_.push_back(listener); + require_entry_exit_stubs = true; + have_method_exit_listeners_ = true; + } + if ((events & kMethodUnwind) != 0) { + method_unwind_listeners_.push_back(listener); + have_method_unwind_listeners_ = true; + } + if ((events & kDexPcMoved) != 0) { + dex_pc_listeners_.push_back(listener); + require_interpreter = true; + have_dex_pc_listeners_ = true; + } + ConfigureStubs(require_entry_exit_stubs, require_interpreter); } -void Instrumentation::InstallStubs() { - Thread* self = Thread::Current(); - Locks::thread_list_lock_->AssertNotHeld(self); - Runtime::Current()->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, NULL); - MutexLock mu(self, *Locks::thread_list_lock_); - Runtime::Current()->GetThreadList()->ForEach(InstrumentationInstallStack, GetTrace()); +void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) { + Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); + bool require_entry_exit_stubs = false; + bool require_interpreter = false; + + if ((events & kMethodEntered) != 0) { + bool contains = std::find(method_entry_listeners_.begin(), method_entry_listeners_.end(), + listener) != method_entry_listeners_.end(); + if (contains) { + method_entry_listeners_.remove(listener); + } + have_method_entry_listeners_ = method_entry_listeners_.size() > 0; + require_entry_exit_stubs |= have_method_entry_listeners_; + } + if ((events & kMethodExited) != 0) { + bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(), + listener) != method_exit_listeners_.end(); + if (contains) { + method_exit_listeners_.remove(listener); + } + have_method_exit_listeners_ = method_exit_listeners_.size() > 0; + require_entry_exit_stubs |= have_method_exit_listeners_; + } + if ((events & kMethodUnwind) != 0) { + method_unwind_listeners_.remove(listener); + } + if ((events & kDexPcMoved) != 0) { + bool contains = std::find(dex_pc_listeners_.begin(), dex_pc_listeners_.end(), + listener) != dex_pc_listeners_.end(); + if (contains) { + dex_pc_listeners_.remove(listener); + } + have_dex_pc_listeners_ = dex_pc_listeners_.size() > 0; + require_interpreter |= have_dex_pc_listeners_; + } + ConfigureStubs(require_entry_exit_stubs, require_interpreter); } -void Instrumentation::UninstallStubs() { +void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) { + interpret_only_ = require_interpreter || forced_interpret_only_; + // Compute what level of instrumentation is required and compare to current. + int desired_level, current_level; + if (require_interpreter) { + desired_level = 2; + } else if (require_entry_exit_stubs) { + desired_level = 1; + } else { + desired_level = 0; + } + if (interpreter_stubs_installed_) { + current_level = 2; + } else if (entry_exit_stubs_installed_) { + current_level = 1; + } else { + current_level = 0; + } + if (desired_level == current_level) { + // We're already set. + return; + } Thread* self = Thread::Current(); + Runtime* runtime = Runtime::Current(); Locks::thread_list_lock_->AssertNotHeld(self); - Runtime::Current()->GetClassLinker()->VisitClasses(UninstallStubsClassVisitor, NULL); - MutexLock mu(self, *Locks::thread_list_lock_); - Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, NULL); + if (desired_level > 0) { + if (require_interpreter) { + interpreter_stubs_installed_ = true; + } else { + CHECK(require_entry_exit_stubs); + entry_exit_stubs_installed_ = true; + } + runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this); + instrumentation_stubs_installed_ = true; + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + runtime->GetThreadList()->ForEach(InstrumentationInstallStack, this); + } else { + interpreter_stubs_installed_ = false; + entry_exit_stubs_installed_ = false; + runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this); + instrumentation_stubs_installed_ = false; + MutexLock mu(self, *Locks::thread_list_lock_); + Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this); + } } -void Instrumentation::AddSavedCodeToMap(const mirror::AbstractMethod* method, const void* code) { - saved_code_map_.Put(method, code); +void Instrumentation::UpdateMethodsCode(mirror::AbstractMethod* method, const void* code) const { + if (LIKELY(!instrumentation_stubs_installed_)) { + method->SetCode(code); + } } -void Instrumentation::RemoveSavedCodeFromMap(const mirror::AbstractMethod* method) { - saved_code_map_.erase(method); +const void* Instrumentation::GetQuickCodeFor(const mirror::AbstractMethod* method) const { + Runtime* runtime = Runtime::Current(); + if (LIKELY(!instrumentation_stubs_installed_)) { + const void* code = method->GetCode(); + DCHECK(code != NULL); + if (LIKELY(code != runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData())) { + return code; + } + } + return runtime->GetClassLinker()->GetOatCodeFor(method); } -const void* Instrumentation::GetSavedCodeFromMap(const mirror::AbstractMethod* method) { - typedef SafeMap<const mirror::AbstractMethod*, const void*>::const_iterator It; // TODO: C++0x auto - It it = saved_code_map_.find(method); - if (it == saved_code_map_.end()) { - return NULL; - } else { - return it->second; +void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc) const { + typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto + for (It it = method_entry_listeners_.begin(), end = method_entry_listeners_.end(); it != end; + ++it) { + (*it)->MethodEntered(thread, this_object, method, dex_pc); } } -void Instrumentation::SaveAndUpdateCode(mirror::AbstractMethod* method) { -#if defined(ART_USE_PORTABLE_COMPILER) - UNUSED(method); - UNIMPLEMENTED(FATAL); -#else - void* instrumentation_stub = GetInstrumentationEntryPoint(); - CHECK(GetSavedCodeFromMap(method) == NULL); - AddSavedCodeToMap(method, method->GetCode()); - method->SetCode(instrumentation_stub); -#endif +void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc, const JValue& return_value) const { + typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto + for (It it = method_exit_listeners_.begin(), end = method_exit_listeners_.end(); it != end; + ++it) { + (*it)->MethodExited(thread, this_object, method, dex_pc, return_value); + } } -void Instrumentation::ResetSavedCode(mirror::AbstractMethod* method) { - CHECK(GetSavedCodeFromMap(method) != NULL); - method->SetCode(GetSavedCodeFromMap(method)); - RemoveSavedCodeFromMap(method); +void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc) const { + if (have_method_unwind_listeners_) { + typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto + for (It it = method_unwind_listeners_.begin(), end = method_unwind_listeners_.end(); it != end; + ++it) { + (*it)->MethodUnwind(thread, method, dex_pc); + } + } } -Trace* Instrumentation::GetTrace() const { - return trace_; +void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc) const { + // TODO: STL copy-on-write collection? The copy below is due to the debug listener having an + // action where it can remove itself as a listener and break the iterator. The copy only works + // around the problem and in general we may have to move to something like reference counting to + // ensure listeners are deleted correctly. + std::list<InstrumentationListener*> copy(dex_pc_listeners_); + typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto + for (It it = copy.begin(), end = copy.end(); it != end; ++it) { + (*it)->DexPcMoved(thread, this_object, method, dex_pc); + } } -void Instrumentation::SetTrace(Trace* trace) { - trace_ = trace; +void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, + uint32_t catch_dex_pc, + mirror::Throwable* exception_object) { + if (have_exception_caught_listeners_) { + typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto + for (It it = exception_caught_listeners_.begin(), end = exception_caught_listeners_.end(); + it != end; ++it) { + (*it)->ExceptionCaught(thread, throw_location, catch_method, catch_dex_pc, exception_object); + } + } } -void Instrumentation::RemoveTrace() { - delete trace_; - trace_ = NULL; +static void CheckStackDepth(Thread* self, const InstrumentationStackFrame& instrumentation_frame, + int delta) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + size_t frame_id = StackVisitor::ComputeNumFrames(self) + delta; + if (frame_id != instrumentation_frame.frame_id_) { + LOG(ERROR) << "Expected frame_id=" << frame_id << " but found " + << instrumentation_frame.frame_id_; + StackVisitor::DescribeStack(self); + CHECK_EQ(frame_id, instrumentation_frame.frame_id_); + } } -uint32_t InstrumentationMethodUnwindFromCode(Thread* self) { - Trace* trace = Runtime::Current()->GetInstrumentation()->GetTrace(); - InstrumentationStackFrame instrumentation_frame = self->PopInstrumentationStackFrame(); +void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object, + mirror::AbstractMethod* method, + uintptr_t lr) { + // We have a callee-save frame meaning this value is guaranteed to never be 0. + size_t frame_id = StackVisitor::ComputeNumFrames(self); + std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack(); + if (kVerboseInstrumentation) { + LOG(INFO) << "Entering " << PrettyMethod(method) << " from PC " << (void*)lr; + } + instrumentation::InstrumentationStackFrame instrumentation_frame(this_object, method, lr, + frame_id); + stack->push_front(instrumentation_frame); + + MethodEnterEvent(self, this_object, method, 0); +} + +uint64_t Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc, + uint64_t gpr_result, uint64_t fpr_result) { + // Do the pop. + std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack(); + CHECK_GT(stack->size(), 0U); + InstrumentationStackFrame instrumentation_frame = stack->front(); + stack->pop_front(); + + // Set return PC and check the sanity of the stack. + *return_pc = instrumentation_frame.return_pc_; + CheckStackDepth(self, instrumentation_frame, 0); + + mirror::AbstractMethod* method = instrumentation_frame.method_; + char return_shorty = MethodHelper(method).GetShorty()[0]; + JValue return_value; + if (return_shorty == 'V') { + return_value.SetJ(0); + } else if (return_shorty == 'F' || return_shorty == 'D') { + return_value.SetJ(fpr_result); + } else { + return_value.SetJ(gpr_result); + } + // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to + // return_pc. + uint32_t dex_pc = DexFile::kDexNoIndex; + mirror::Object* this_object = instrumentation_frame.this_object_; + MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value); + + bool deoptimize = false; + if (interpreter_stubs_installed_) { + // Deoptimize unless we're returning to an upcall. + NthCallerVisitor visitor(self, 1, true); + visitor.WalkStack(true); + deoptimize = visitor.caller != NULL; + if (deoptimize && kVerboseInstrumentation) { + LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller); + } + } + if (deoptimize) { + if (kVerboseInstrumentation) { + LOG(INFO) << "Deoptimizing from " << PrettyMethod(method) + << " result is " << std::hex << return_value.GetJ(); + } + self->SetDeoptimizationReturnValue(return_value); + return static_cast<uint64_t>(GetDeoptimizationEntryPoint()) | + (static_cast<uint64_t>(*return_pc) << 32); + } else { + if (kVerboseInstrumentation) { + LOG(INFO) << "Returning from " << PrettyMethod(method) << " to PC " << (void*)(*return_pc); + } + return *return_pc; + } +} + +void Instrumentation::PopMethodForUnwind(Thread* self, bool is_deoptimization) const { + // Do the pop. + std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack(); + CHECK_GT(stack->size(), 0U); + InstrumentationStackFrame instrumentation_frame = stack->front(); + // TODO: bring back CheckStackDepth(self, instrumentation_frame, 2); + stack->pop_front(); + mirror::AbstractMethod* method = instrumentation_frame.method_; - uint32_t lr = instrumentation_frame.return_pc_; + if (is_deoptimization) { + if (kVerboseInstrumentation) { + LOG(INFO) << "Popping for deoptimization " << PrettyMethod(method); + } + } else { + if (kVerboseInstrumentation) { + LOG(INFO) << "Popping for unwind " << PrettyMethod(method); + } - trace->LogMethodTraceEvent(self, method, Trace::kMethodTraceUnwind); + // Notify listeners of method unwind. + // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to + // return_pc. + uint32_t dex_pc = DexFile::kDexNoIndex; + MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc); + } +} - return lr; +std::string InstrumentationStackFrame::Dump() const { + std::ostringstream os; + os << "Frame " << frame_id_ << " " << PrettyMethod(method_) << ":" + << reinterpret_cast<void*>(return_pc_) << " this=" << reinterpret_cast<void*>(this_object_); + return os.str(); } +} // namespace instrumentation } // namespace art diff --git a/src/instrumentation.h b/src/instrumentation.h index fb49bf8..6a4a142 100644 --- a/src/instrumentation.h +++ b/src/instrumentation.h @@ -18,61 +18,261 @@ #define ART_SRC_INSTRUMENTATION_H_ #include "base/macros.h" -#include "safe_map.h" +#include "locks.h" #include <stdint.h> +#include <list> namespace art { - namespace mirror { class AbstractMethod; -} +class Class; +class Object; +class Throwable; +} // namespace mirror +union JValue; class Thread; -class Trace; +class ThrowLocation; -uint32_t InstrumentationMethodUnwindFromCode(Thread* self); +namespace instrumentation { -struct InstrumentationStackFrame { - InstrumentationStackFrame() : method_(NULL), return_pc_(0), frame_id_(0) {} - InstrumentationStackFrame(mirror::AbstractMethod* method, uintptr_t return_pc, size_t frame_id) - : method_(method), return_pc_(return_pc), frame_id_(frame_id) { - } - mirror::AbstractMethod* method_; - uintptr_t return_pc_; - size_t frame_id_; +const bool kVerboseInstrumentation = false; + +// Instrumentation event listener API. Registered listeners will get the appropriate call back for +// the events they are listening for. The call backs supply the thread, method and dex_pc the event +// occurred upon. The thread may or may not be Thread::Current(). +struct InstrumentationListener { + InstrumentationListener() {} + virtual ~InstrumentationListener() {} + + // Call-back for when a method is entered. + virtual void MethodEntered(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + + // Call-back for when a method is exited. + // TODO: its likely passing the return value would be useful, however, we may need to get and + // parse the shorty to determine what kind of register holds the result. + virtual void MethodExited(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc, + const JValue& return_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + + // Call-back for when a method is popped due to an exception throw. A method will either cause a + // MethodExited call-back or a MethodUnwind call-back when its activation is removed. + virtual void MethodUnwind(Thread* thread, const mirror::AbstractMethod* method, + uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + + // Call-back for when the dex pc moves in a method. + virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t new_dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + + // Call-back when an exception is caught. + virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; }; +// Instrumentation is a catch-all for when extra information is required from the runtime. The +// typical use for instrumentation is for profiling and debugging. Instrumentation may add stubs +// to method entry and exit, it may also force execution to be switched to the interpreter and +// trigger deoptimization. class Instrumentation { public: - Instrumentation() {} - ~Instrumentation(); + enum InstrumentationEvent { + kMethodEntered = 1, + kMethodExited = 2, + kMethodUnwind = 4, + kDexPcMoved = 8, + kExceptionCaught = 16 + }; + + Instrumentation() : + instrumentation_stubs_installed_(false), entry_exit_stubs_installed_(false), + interpreter_stubs_installed_(false), + interpret_only_(false), forced_interpret_only_(false), + have_method_entry_listeners_(false), have_method_exit_listeners_(false), + have_method_unwind_listeners_(false), have_dex_pc_listeners_(false), + have_exception_caught_listeners_(false) {} + + // Add a listener to be notified of the masked together sent of instrumentation events. This + // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy + // for saying you should have suspended all threads (installing stubs while threads are running + // will break). + void AddListener(InstrumentationListener* listener, uint32_t events) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + + // Removes a listener possibly removing instrumentation stubs. + void RemoveListener(InstrumentationListener* listener, uint32_t events) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + + // Update the code of a method respecting any installed stubs. + void UpdateMethodsCode(mirror::AbstractMethod* method, const void* code) const; + + // Get the quick code for the given method. More efficient than asking the class linker as it + // will short-cut to GetCode if instrumentation and static method resolution stubs aren't + // installed. + const void* GetQuickCodeFor(const mirror::AbstractMethod* method) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void ForceInterpretOnly() { + interpret_only_ = true; + forced_interpret_only_ = true; + } + + // Called by AbstractMethod::Invoke to determine dispatch mechanism. + bool InterpretOnly() const { + return interpret_only_; + } + + bool ShouldPortableCodeDeoptimize() const { + return instrumentation_stubs_installed_; + } + + bool AreExitStubsInstalled() const { + return instrumentation_stubs_installed_; + } + + // Inform listeners that a method has been entered. A dex PC is provided as we may install + // listeners into executing code and get method enter events for methods already on the stack. + void MethodEnterEvent(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (have_method_entry_listeners_) { + MethodEnterEventImpl(thread, this_object, method, dex_pc); + } + } + + // Inform listeners that a method has been exited. + void MethodExitEvent(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc, + const JValue& return_value) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (have_method_exit_listeners_) { + MethodExitEventImpl(thread, this_object, method, dex_pc, return_value); + } + } + + // Inform listeners that a method has been exited due to an exception. + void MethodUnwindEvent(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Inform listeners that the dex pc has moved (only supported by the interpreter). + void DexPcMovedEvent(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (have_dex_pc_listeners_) { + DexPcMovedEventImpl(thread, this_object, method, dex_pc); + } + } - // Replaces code of each method with a pointer to a stub for method tracing. - void InstallStubs() LOCKS_EXCLUDED(Locks::thread_list_lock_); + // Inform listeners that an exception was caught. + void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Restores original code for each method and fixes the return values of each thread's stack. - void UninstallStubs() LOCKS_EXCLUDED(Locks::thread_list_lock_); + // Called when an instrumented method is entered. The intended link register (lr) is saved so + // that returning causes a branch to the method exit stub. Generates method enter events. + void PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object, + mirror::AbstractMethod* method, uintptr_t lr) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const void* GetSavedCodeFromMap(const mirror::AbstractMethod* method); - void SaveAndUpdateCode(mirror::AbstractMethod* method); - void ResetSavedCode(mirror::AbstractMethod* method); + // Called when an instrumented method is exited. Removes the pushed instrumentation frame + // returning the intended link register. Generates method exit events. + uint64_t PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc, uint64_t gpr_result, + uint64_t fpr_result) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Trace* GetTrace() const; - void SetTrace(Trace* trace); - void RemoveTrace(); + // Pops an instrumentation frame from the current thread and generate an unwind event. + void PopMethodForUnwind(Thread* self, bool is_deoptimization) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Call back for configure stubs. + bool InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: - void AddSavedCodeToMap(const mirror::AbstractMethod* method, const void* code); - void RemoveSavedCodeFromMap(const mirror::AbstractMethod* method); + // Does the job of installing or removing instrumentation code within methods. + void ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + + void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void MethodExitEventImpl(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, + uint32_t dex_pc, const JValue& return_value) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Have we hijacked AbstractMethod::code_ so that it calls instrumentation/interpreter code? + bool instrumentation_stubs_installed_; + + // Have we hijacked AbstractMethod::code_ to reference the enter/exit stubs? + bool entry_exit_stubs_installed_; + + // Have we hijacked AbstractMethod::code_ to reference the enter interpreter stub? + bool interpreter_stubs_installed_; + + // Do we need the fidelity of events that we only get from running within the interpreter? + bool interpret_only_; - // Maps a method to its original code pointer. - SafeMap<const mirror::AbstractMethod*, const void*> saved_code_map_; + // Did the runtime request we only run in the interpreter? ie -Xint mode. + bool forced_interpret_only_; - Trace* trace_; + // Do we have any listeners for method entry events? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_method_entry_listeners_; + + // Do we have any listeners for method exit events? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_method_exit_listeners_; + + // Do we have any listeners for method unwind events? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_method_unwind_listeners_; + + // Do we have any listeners for dex move events? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_dex_pc_listeners_; + + // Do we have any exception caught listeners? Short-cut to avoid taking the instrumentation_lock_. + bool have_exception_caught_listeners_; + + // The event listeners, written to with the mutator_lock_ exclusively held. + std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); DISALLOW_COPY_AND_ASSIGN(Instrumentation); }; +// An element in the instrumentation side stack maintained in art::Thread. +struct InstrumentationStackFrame { + InstrumentationStackFrame(mirror::Object* this_object, mirror::AbstractMethod* method, + uintptr_t return_pc, size_t frame_id) + : this_object_(this_object), method_(method), return_pc_(return_pc), frame_id_(frame_id) { + } + + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::Object* this_object_; + mirror::AbstractMethod* method_; + const uintptr_t return_pc_; + const size_t frame_id_; +}; + +} // namespace instrumentation } // namespace art #endif // ART_SRC_INSTRUMENTATION_H_ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 91b381c..b82c632 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -21,7 +21,6 @@ #include "base/logging.h" #include "class_linker-inl.h" #include "common_throws.h" -#include "debugger.h" #include "dex_file-inl.h" #include "dex_instruction.h" #include "gc/card_table-inl.h" @@ -50,10 +49,6 @@ static const int32_t kMinInt = std::numeric_limits<int32_t>::min(); static const int64_t kMaxLong = std::numeric_limits<int64_t>::max(); static const int64_t kMinLong = std::numeric_limits<int64_t>::min(); -static JDWP::FrameId throw_frame_id_ = 0; -static AbstractMethod* throw_method_ = NULL; -static uint32_t throw_dex_pc_ = 0; - static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method, Object* receiver, uint32_t* args, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -441,7 +436,7 @@ static void DoFieldGet(Thread* self, ShadowFrame& shadow_frame, } else { obj = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(f, true); + ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true); return; } } @@ -488,7 +483,8 @@ static void DoFieldPut(Thread* self, ShadowFrame& shadow_frame, } else { obj = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(f, false); + ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), + f, false); return; } } @@ -523,7 +519,7 @@ static void DoFieldPut(Thread* self, ShadowFrame& shadow_frame, static void DoIntDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero"); + ThrowArithmeticExceptionDivideByZero(self); } else if (UNLIKELY(dividend == kMinInt && divisor == -1)) { shadow_frame.SetVReg(result_reg, kMinInt); } else { @@ -534,7 +530,7 @@ static void DoIntDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_r static void DoIntRemainder(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero"); + ThrowArithmeticExceptionDivideByZero(self); } else if (UNLIKELY(dividend == kMinInt && divisor == -1)) { shadow_frame.SetVReg(result_reg, 0); } else { @@ -545,7 +541,7 @@ static void DoIntRemainder(Thread* self, ShadowFrame& shadow_frame, size_t resul static void DoLongDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero"); + ThrowArithmeticExceptionDivideByZero(self); } else if (UNLIKELY(dividend == kMinLong && divisor == -1)) { shadow_frame.SetVRegLong(result_reg, kMinLong); } else { @@ -556,7 +552,7 @@ static void DoLongDivide(Thread* self, ShadowFrame& shadow_frame, size_t result_ static void DoLongRemainder(Thread* self, ShadowFrame& shadow_frame, size_t result_reg, int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(divisor == 0)) { - self->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero"); + ThrowArithmeticExceptionDivideByZero(self); } else if (UNLIKELY(dividend == kMinLong && divisor == -1)) { shadow_frame.SetVRegLong(result_reg, 0); } else { @@ -567,41 +563,46 @@ static void DoLongRemainder(Thread* self, ShadowFrame& shadow_frame, size_t resu static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(!shadow_frame.HasReferenceArray())) { + LOG(FATAL) << "Invalid shadow frame for interpreter use"; + return JValue(); + } + self->VerifyStack(); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); const uint16_t* insns = code_item->insns_; const Instruction* inst = Instruction::At(insns + shadow_frame.GetDexPC()); - bool entry = (inst->GetDexPc(insns) == 0); + if (inst->GetDexPc(insns) == 0) { // We are entering the method as opposed to deoptimizing.. + instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(), shadow_frame.GetMethod(), + 0); + } while (true) { CheckSuspend(self); uint32_t dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); - if (entry) { - Dbg::UpdateDebugger(-1, self); - } - entry = false; - Dbg::UpdateDebugger(dex_pc, self); + instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(), shadow_frame.GetMethod(), + dex_pc); DecodedInstruction dec_insn(inst); const bool kTracing = false; if (kTracing) { - LOG(INFO) << PrettyMethod(shadow_frame.GetMethod()) - << StringPrintf("\n0x%x: %s\nReferences:", - inst->GetDexPc(insns), inst->DumpString(&mh.GetDexFile()).c_str()); +#define TRACE_LOG std::cerr + TRACE_LOG << PrettyMethod(shadow_frame.GetMethod()) + << StringPrintf("\n0x%x: ", inst->GetDexPc(insns)) + << inst->DumpString(&mh.GetDexFile()) << "\n"; for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { - Object* o = shadow_frame.GetVRegReference(i); - if (o != NULL) { - if (o->GetClass()->IsStringClass() && o->AsString()->GetCharArray() != NULL) { - LOG(INFO) << i << ": java.lang.String " << static_cast<void*>(o) - << " \"" << o->AsString()->ToModifiedUtf8() << "\""; + uint32_t raw_value = shadow_frame.GetVReg(i); + Object* ref_value = shadow_frame.GetVRegReference(i); + TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value); + if (ref_value != NULL) { + if (ref_value->GetClass()->IsStringClass() && + ref_value->AsString()->GetCharArray() != NULL) { + TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\""; } else { - LOG(INFO) << i << ": " << PrettyTypeOf(o) << " " << static_cast<void*>(o); + TRACE_LOG << "/" << PrettyTypeOf(ref_value); } - } else { - LOG(INFO) << i << ": null"; } } - LOG(INFO) << "vregs:"; - for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { - LOG(INFO) << StringPrintf("%d: %08x", i, shadow_frame.GetVReg(i)); - } + TRACE_LOG << "\n"; +#undef TRACE_LOG } const Instruction* next_inst = inst->Next(); switch (dec_insn.opcode) { @@ -632,31 +633,42 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c shadow_frame.SetVRegReference(dec_insn.vA, result_register.GetL()); break; case Instruction::MOVE_EXCEPTION: { - Throwable* exception = self->GetException(); + Throwable* exception = self->GetException(NULL); self->ClearException(); shadow_frame.SetVRegReference(dec_insn.vA, exception); break; } case Instruction::RETURN_VOID: { JValue result; - result.SetJ(0); + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(), + shadow_frame.GetMethod(), shadow_frame.GetDexPC(), + result); return result; } case Instruction::RETURN: { JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(dec_insn.vA)); + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(), + shadow_frame.GetMethod(), shadow_frame.GetDexPC(), + result); return result; } case Instruction::RETURN_WIDE: { JValue result; result.SetJ(shadow_frame.GetVRegLong(dec_insn.vA)); + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(), + shadow_frame.GetMethod(), shadow_frame.GetDexPC(), + result); return result; } case Instruction::RETURN_OBJECT: { JValue result; result.SetJ(0); result.SetL(shadow_frame.GetVRegReference(dec_insn.vA)); + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(), + shadow_frame.GetMethod(), shadow_frame.GetDexPC(), + result); return result; } case Instruction::CONST_4: { @@ -721,7 +733,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::MONITOR_ENTER: { Object* obj = shadow_frame.GetVRegReference(dec_insn.vA); if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); } else { DoMonitorEnter(self, obj); } @@ -730,7 +742,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::MONITOR_EXIT: { Object* obj = shadow_frame.GetVRegReference(dec_insn.vA); if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); } else { DoMonitorExit(self, obj); } @@ -743,10 +755,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c } else { Object* obj = shadow_frame.GetVRegReference(dec_insn.vA); if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) { - self->ThrowNewExceptionF("Ljava/lang/ClassCastException;", - "%s cannot be cast to %s", - PrettyDescriptor(obj->GetClass()).c_str(), - PrettyDescriptor(c).c_str()); + ThrowClassCastException(c, obj->GetClass()); } } break; @@ -764,7 +773,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::ARRAY_LENGTH: { Object* array = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(array == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } shadow_frame.SetVReg(dec_insn.vA, array->AsArray()->GetLength()); @@ -787,7 +796,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c int32_t length = dec_insn.vA; CHECK(is_range || length <= 5); if (UNLIKELY(length < 0)) { - self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length); + ThrowNegativeArraySizeException(length); break; } Class* arrayClass = ResolveVerifyAndClinit(dec_insn.vB, shadow_frame.GetMethod(), self, false, true); @@ -799,11 +808,11 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c Class* componentClass = arrayClass->GetComponentType(); if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { - self->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "Bad filled array request for type %s", - PrettyDescriptor(componentClass).c_str()); + ThrowRuntimeException("Bad filled array request for type %s", + PrettyDescriptor(componentClass).c_str()); } else { - self->ThrowNewExceptionF("Ljava/lang/InternalError;", + self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), + "Ljava/lang/InternalError;", "Found type %s; filled-new-array not implemented for anything but \'int\'", PrettyDescriptor(componentClass).c_str()); } @@ -902,9 +911,12 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; } case Instruction::THROW: { - Object* o = shadow_frame.GetVRegReference(dec_insn.vA); - Throwable* t = (o == NULL) ? NULL : o->AsThrowable(); - self->DeliverException(t); + Object* exception = shadow_frame.GetVRegReference(dec_insn.vA); + if (exception == NULL) { + ThrowNullPointerException(NULL, "throw with null exception"); + } else { + self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable()); + } break; } case Instruction::GOTO: @@ -962,8 +974,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::FILL_ARRAY_DATA: { Object* obj = shadow_frame.GetVRegReference(dec_insn.vA); if (UNLIKELY(obj == NULL)) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;", - "null array in FILL_ARRAY_DATA"); + ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); break; } Array* array = obj->AsArray(); @@ -972,9 +983,10 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c const Instruction::ArrayDataPayload* payload = reinterpret_cast<const Instruction::ArrayDataPayload*>(insns + dex_pc + dec_insn.vB); if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "failed FILL_ARRAY_DATA; length=%d, index=%d", - array->GetLength(), payload->element_count); + self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), + "Ljava/lang/ArrayIndexOutOfBoundsException;", + "failed FILL_ARRAY_DATA; length=%d, index=%d", + array->GetLength(), payload->element_count); break; } uint32_t size_in_bytes = payload->element_count * payload->element_width; @@ -1068,7 +1080,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET_BOOLEAN: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1078,7 +1090,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET_BYTE: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1088,7 +1100,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET_CHAR: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1098,7 +1110,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET_SHORT: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1108,7 +1120,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1118,7 +1130,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET_WIDE: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1128,7 +1140,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c case Instruction::AGET_OBJECT: { Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1139,7 +1151,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c uint8_t val = shadow_frame.GetVReg(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1150,7 +1162,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c int8_t val = shadow_frame.GetVReg(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1161,7 +1173,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c uint16_t val = shadow_frame.GetVReg(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1172,7 +1184,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c int16_t val = shadow_frame.GetVReg(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1183,7 +1195,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c int32_t val = shadow_frame.GetVReg(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1194,7 +1206,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c int64_t val = shadow_frame.GetVRegLong(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1205,7 +1217,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c Object* val = shadow_frame.GetVRegReference(dec_insn.vA); Object* a = shadow_frame.GetVRegReference(dec_insn.vB); if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns)); + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); break; } int32_t index = shadow_frame.GetVReg(dec_insn.vC); @@ -1777,22 +1789,21 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c break; } if (UNLIKELY(self->IsExceptionPending())) { - if (throw_frame_id_ == 0) { - throw_method_ = shadow_frame.GetMethod(); - throw_dex_pc_ = dex_pc; - } - throw_frame_id_++; + self->VerifyStack(); + ThrowLocation throw_location; + mirror::Throwable* exception = self->GetException(&throw_location); uint32_t found_dex_pc = - shadow_frame.GetMethod()->FindCatchBlock(self->GetException()->GetClass(), - inst->GetDexPc(insns)); + shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), inst->GetDexPc(insns)); if (found_dex_pc == DexFile::kDexNoIndex) { JValue result; result.SetJ(0); + instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(), + shadow_frame.GetMethod(), shadow_frame.GetDexPC()); return result; // Handler in caller. } else { - Dbg::PostException(self, throw_frame_id_, throw_method_, throw_dex_pc_, - shadow_frame.GetMethod(), found_dex_pc, self->GetException()); - throw_frame_id_ = 0; + Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(self, throw_location, + shadow_frame.GetMethod(), + found_dex_pc, exception); next_inst = Instruction::At(insns + found_dex_pc); } } @@ -1816,8 +1827,9 @@ void EnterInterpreterFromInvoke(Thread* self, AbstractMethod* method, Object* re num_regs = code_item->registers_size_; num_ins = code_item->ins_size_; } else if (method->IsAbstract()) { - self->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;", "abstract method \"%s\"", - PrettyMethod(method).c_str()); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;", + "abstract method \"%s\"", PrettyMethod(method).c_str()); return; } else { DCHECK(method->IsNative()); @@ -1884,23 +1896,18 @@ void EnterInterpreterFromInvoke(Thread* self, AbstractMethod* method, Object* re self->PopShadowFrame(); } -JValue EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame& shadow_frame, JValue ret_val) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - MethodHelper mh(shadow_frame.GetMethod()); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - return Execute(self, mh, code_item, shadow_frame, ret_val); -} - -void EnterInterpreterFromLLVM(Thread* self, ShadowFrame* shadow_frame, JValue* ret_val) +void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JValue* ret_val) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { JValue value; - MethodHelper mh(shadow_frame->GetMethod()); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); + value.SetJ(ret_val->GetJ()); // Set value to last known result in case the shadow frame chain is empty. + MethodHelper mh; while (shadow_frame != NULL) { + self->SetTopOfShadowStack(shadow_frame); + mh.ChangeMethod(shadow_frame->GetMethod()); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); value = Execute(self, mh, code_item, *shadow_frame, value); ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); - mh.ChangeMethod(shadow_frame->GetMethod()); delete old_frame; } ret_val->SetJ(value.GetJ()); diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index 556b044..cf47b68 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -33,15 +33,13 @@ class Thread; namespace interpreter { +// Called by AbstractMethod::Invoke, shadow frames arguments are taken from the args array. extern void EnterInterpreterFromInvoke(Thread* self, mirror::AbstractMethod* method, mirror::Object* receiver, uint32_t* args, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -extern JValue EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame& shadow_frame, - JValue ret_val) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - -void EnterInterpreterFromLLVM(Thread* self, ShadowFrame* shadow_frame, JValue* result) +extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, + JValue* ret_val) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, diff --git a/src/invoke_arg_array_builder.h b/src/invoke_arg_array_builder.h index aca1091..a6e99a5 100644 --- a/src/invoke_arg_array_builder.h +++ b/src/invoke_arg_array_builder.h @@ -43,13 +43,24 @@ class ArgArray { public: explicit ArgArray(const char* shorty, uint32_t shorty_len) : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) { - // TODO: This code is conservative. The multiply by 2 is to handle the case where all args are - // doubles or longs. We could scan the shorty to use the arg array more often. - if (shorty_len * 2 <= kSmallArgArraySize) { + size_t num_slots = shorty_len + 1; // +1 in case of receiver. + if (LIKELY((num_slots * 2) < kSmallArgArraySize)) { + // We can trivially use the small arg array. arg_array_ = small_arg_array_; } else { - large_arg_array_.reset(new uint32_t[shorty_len_ * 2]); - arg_array_ = large_arg_array_.get(); + // Analyze shorty to see if we need the large arg array. + for (size_t i = 1; i < shorty_len; ++i) { + char c = shorty[i]; + if (c == 'J' || c == 'D') { + num_slots++; + } + } + if (num_slots <= kSmallArgArraySize) { + arg_array_ = small_arg_array_; + } else { + large_arg_array_.reset(new uint32_t[num_slots]); + arg_array_ = large_arg_array_.get(); + } } } diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc index eb385fc..5b65aa4 100644 --- a/src/jdwp/jdwp_event.cc +++ b/src/jdwp/jdwp_event.cc @@ -1069,21 +1069,31 @@ void JdwpState::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) { // Try to avoid blocking GC during a send, but only safe when not using mutexes at a lower-level // than mutator for lock ordering reasons. Thread* self = Thread::Current(); - bool safe_to_release_mutator_lock_over_send; - for (size_t i=0; i < kMutatorLock; ++i) { - if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) { - safe_to_release_mutator_lock_over_send = false; - break; + bool safe_to_release_mutator_lock_over_send = !Locks::mutator_lock_->IsExclusiveHeld(self); + if (safe_to_release_mutator_lock_over_send) { + for (size_t i=0; i < kMutatorLock; ++i) { + if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) { + safe_to_release_mutator_lock_over_send = false; + break; + } } } + bool success; if (safe_to_release_mutator_lock_over_send) { // Change state to waiting to allow GC, ... while we're sending. self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend); - (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1); + success = (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1); self->TransitionFromSuspendedToRunnable(); } else { // Send and possibly block GC... - (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1); + success = (*transport_->sendBufferedRequest)(this, wrapiov, iov_count + 1); + } + if (!success) { + LOG(INFO) << StringPrintf("JDWP send of type %c%c%c%c failed.", + static_cast<uint8_t>(type >> 24), + static_cast<uint8_t>(type >> 16), + static_cast<uint8_t>(type >> 8), + static_cast<uint8_t>(type)); } } diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc index 4e738ff..1f121f8 100644 --- a/src/jdwp/jdwp_main.cc +++ b/src/jdwp/jdwp_main.cc @@ -334,6 +334,7 @@ void JdwpState::Run() { /* set the thread state to kWaitingInMainDebuggerLoop so GCs don't wait for us */ CHECK_EQ(thread_->GetState(), kNative); + Locks::mutator_lock_->AssertNotHeld(thread_); thread_->SetState(kWaitingInMainDebuggerLoop); /* @@ -421,10 +422,9 @@ void JdwpState::Run() { // Release session state, e.g. remove breakpoint instructions. ResetState(); - - // Tell the rest of the runtime that the debugger is no longer around. - Dbg::Disconnected(); } + // Tell the rest of the runtime that the debugger is no longer around. + Dbg::Disconnected(); /* if we had threads suspended, resume them now */ Dbg::UndoDebuggerSuspensions(); diff --git a/src/jni_internal.cc b/src/jni_internal.cc index 6df03e9..a6c9fa1 100644 --- a/src/jni_internal.cc +++ b/src/jni_internal.cc @@ -112,7 +112,7 @@ static void CheckMethodArguments(AbstractMethod* m, uint32_t* args) CHECK(self->IsExceptionPending()); LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: " << mh.GetTypeDescriptorFromTypeIdx(type_idx) << "\n" - << self->GetException()->Dump(); + << self->GetException(NULL)->Dump(); self->ClearException(); ++error_count; } else if (!param_type->IsPrimitive()) { @@ -214,8 +214,10 @@ static std::string NormalizeJniClassDescriptor(const char* name) { static void ThrowNoSuchMethodError(ScopedObjectAccess& soa, Class* c, const char* name, const char* sig, const char* kind) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;", - "no %s method \"%s.%s%s\"", kind, ClassHelper(c).GetDescriptor(), name, sig); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchMethodError;", + "no %s method \"%s.%s%s\"", + kind, ClassHelper(c).GetDescriptor(), name, sig); } static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, @@ -248,7 +250,7 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, static ClassLoader* GetClassLoader(const ScopedObjectAccess& soa) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - AbstractMethod* method = soa.Self()->GetCurrentMethod(); + AbstractMethod* method = soa.Self()->GetCurrentMethod(NULL); if (method == NULL || method == soa.DecodeMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) { return soa.Self()->GetClassLoaderOverride(); @@ -276,10 +278,14 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con if (field_type == NULL) { // Failed to find type from the signature of the field. DCHECK(soa.Self()->IsExceptionPending()); + ThrowLocation throw_location; + SirtRef<mirror::Throwable> cause(soa.Self(), soa.Self()->GetException(&throw_location)); soa.Self()->ClearException(); - soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;", - "no type \"%s\" found and so no field \"%s\" could be found in class " - "\"%s\" or its superclasses", sig, name, ClassHelper(c).GetDescriptor()); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", + "no type \"%s\" found and so no field \"%s\" could be found in class " + "\"%s\" or its superclasses", sig, name, + ClassHelper(c).GetDescriptor()); + soa.Self()->GetException(NULL)->SetCause(cause.get()); return NULL; } if (is_static) { @@ -288,9 +294,10 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con field = c->FindInstanceField(name, ClassHelper(field_type).GetDescriptor()); } if (field == NULL) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;", - "no \"%s\" field \"%s\" in class \"%s\" or its superclasses", sig, - name, ClassHelper(c).GetDescriptor()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", + "no \"%s\" field \"%s\" in class \"%s\" or its superclasses", + sig, name, ClassHelper(c).GetDescriptor()); return NULL; } return soa.EncodeField(field); @@ -314,16 +321,19 @@ static void ThrowAIOOBE(ScopedObjectAccess& soa, Array* array, jsize start, jsize length, const char* identifier) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string type(PrettyTypeOf(array)); - soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "%s offset=%d length=%d %s.length=%d", - type.c_str(), start, length, identifier, array->GetLength()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;", + "%s offset=%d length=%d %s.length=%d", + type.c_str(), start, length, identifier, array->GetLength()); } static void ThrowSIOOBE(ScopedObjectAccess& soa, jsize start, jsize length, jsize array_length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", - "offset=%d length=%d string.length()=%d", start, length, array_length); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/StringIndexOutOfBoundsException;", + "offset=%d length=%d string.length()=%d", start, length, + array_length); } int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause) @@ -362,10 +372,9 @@ int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobj if (exception.get() == NULL) { return JNI_ERR; } - ScopedObjectAccess soa(env); - soa.Self()->SetException(soa.Decode<Throwable*>(exception.get())); - + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->SetException(throw_location, soa.Decode<Throwable*>(exception.get())); return JNI_OK; } @@ -678,7 +687,8 @@ class JNI { if (exception == NULL) { return JNI_ERR; } - soa.Self()->SetException(exception); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->SetException(throw_location, exception); return JNI_OK; } @@ -697,31 +707,42 @@ class JNI { static void ExceptionDescribe(JNIEnv* env) { ScopedObjectAccess soa(env); - Thread* self = soa.Self(); - Throwable* original_exception = self->GetException(); - self->ClearException(); - - ScopedLocalRef<jthrowable> exception(env, soa.AddLocalReference<jthrowable>(original_exception)); + SirtRef<mirror::Object> old_throw_this_object(soa.Self(), NULL); + SirtRef<mirror::AbstractMethod> old_throw_method(soa.Self(), NULL); + SirtRef<mirror::Throwable> old_exception(soa.Self(), NULL); + uint32_t old_throw_dex_pc; + { + ThrowLocation old_throw_location; + mirror::Throwable* old_exception_obj = soa.Self()->GetException(&old_throw_location); + old_throw_this_object.reset(old_throw_location.GetThis()); + old_throw_method.reset(old_throw_location.GetMethod()); + old_exception.reset(old_exception_obj); + old_throw_dex_pc = old_throw_location.GetDexPc(); + soa.Self()->ClearException(); + } + ScopedLocalRef<jthrowable> exception(env, soa.AddLocalReference<jthrowable>(old_exception.get())); ScopedLocalRef<jclass> exception_class(env, env->GetObjectClass(exception.get())); jmethodID mid = env->GetMethodID(exception_class.get(), "printStackTrace", "()V"); if (mid == NULL) { LOG(WARNING) << "JNI WARNING: no printStackTrace()V in " - << PrettyTypeOf(original_exception); + << PrettyTypeOf(old_exception.get()); } else { env->CallVoidMethod(exception.get(), mid); - if (self->IsExceptionPending()) { - LOG(WARNING) << "JNI WARNING: " << PrettyTypeOf(self->GetException()) + if (soa.Self()->IsExceptionPending()) { + LOG(WARNING) << "JNI WARNING: " << PrettyTypeOf(soa.Self()->GetException(NULL)) << " thrown while calling printStackTrace"; - self->ClearException(); + soa.Self()->ClearException(); } } + ThrowLocation gc_safe_throw_location(old_throw_this_object.get(), old_throw_method.get(), + old_throw_dex_pc); - self->SetException(original_exception); + soa.Self()->SetException(gc_safe_throw_location, old_exception.get()); } static jthrowable ExceptionOccurred(JNIEnv* env) { ScopedObjectAccess soa(env); - Object* exception = soa.Self()->GetException(); + Object* exception = soa.Self()->GetException(NULL); return soa.AddLocalReference<jthrowable>(exception); } @@ -2134,10 +2155,10 @@ class JNI { static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) { if (capacity < 0) { - JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %d", capacity); + JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %lld", capacity); } if (address == NULL && capacity != 0) { - JniAbortF("NewDirectByteBuffer", "non-zero capacity for NULL pointer: %d", capacity); + JniAbortF("NewDirectByteBuffer", "non-zero capacity for NULL pointer: %lld", capacity); } // At the moment, the Java side is limited to 32 bits. @@ -2676,7 +2697,7 @@ JavaVMExt::JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options) force_copy(false), // TODO: add a way to enable this trace(options->jni_trace_), work_around_app_jni_bugs(false), - pins_lock("JNI pin table lock"), + pins_lock("JNI pin table lock", kPinTableLock), pin_table("pin table", kPinTableInitial, kPinTableMax), globals_lock("JNI global reference table lock"), globals(gGlobalsInitial, gGlobalsMax, kGlobal), @@ -2889,9 +2910,10 @@ void* JavaVMExt::FindCodeForNativeMethod(AbstractMethod* m) { MutexLock mu(self, libraries_lock); native_method = libraries->FindNativeMethod(m, detail); } - // throwing can cause libraries_lock to be reacquired + // Throwing can cause libraries_lock to be reacquired. if (native_method == NULL) { - self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str()); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str()); } return native_method; } diff --git a/src/jni_internal.h b/src/jni_internal.h index 9c067de..131032a 100644 --- a/src/jni_internal.h +++ b/src/jni_internal.h @@ -49,7 +49,8 @@ class ScopedObjectAccess; class Thread; void SetJniGlobalsMax(size_t max); -void JniAbortF(const char* jni_function_name, const char* fmt, ...); +void JniAbortF(const char* jni_function_name, const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))); void* FindNativeMethod(Thread* thread); void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods, size_t method_count); diff --git a/src/jvalue.h b/src/jvalue.h index fa85937..66cd93e 100644 --- a/src/jvalue.h +++ b/src/jvalue.h @@ -19,6 +19,8 @@ #include "base/macros.h" +#include <stdint.h> + namespace art { namespace mirror { class Object; diff --git a/src/locks.cc b/src/locks.cc index 27b9d4b..eb0620c 100644 --- a/src/locks.cc +++ b/src/locks.cc @@ -29,6 +29,7 @@ ReaderWriterMutex* Locks::mutator_lock_ = NULL; Mutex* Locks::runtime_shutdown_lock_ = NULL; Mutex* Locks::thread_list_lock_ = NULL; Mutex* Locks::thread_suspend_count_lock_ = NULL; +Mutex* Locks::trace_lock_ = NULL; Mutex* Locks::unexpected_signal_lock_ = NULL; void Locks::Init() { @@ -42,6 +43,7 @@ void Locks::Init() { DCHECK(mutator_lock_ != NULL); DCHECK(thread_list_lock_ != NULL); DCHECK(thread_suspend_count_lock_ != NULL); + DCHECK(trace_lock_ != NULL); DCHECK(unexpected_signal_lock_ != NULL); } else { logging_lock_ = new Mutex("logging lock", kLoggingLock, true); @@ -61,6 +63,8 @@ void Locks::Init() { thread_list_lock_ = new Mutex("thread list lock", kThreadListLock); DCHECK(thread_suspend_count_lock_ == NULL); thread_suspend_count_lock_ = new Mutex("thread suspend count lock", kThreadSuspendCountLock); + DCHECK(trace_lock_ == NULL); + trace_lock_ = new Mutex("trace lock", kTraceLock); DCHECK(unexpected_signal_lock_ == NULL); unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true); } diff --git a/src/locks.h b/src/locks.h index c0f6ae5..568f950 100644 --- a/src/locks.h +++ b/src/locks.h @@ -37,17 +37,20 @@ enum LockLevel { kThreadSuspendCountLock, kAbortLock, kDefaultMutexLevel, + kJdwpSerialLock, kAllocSpaceLock, + kMarkSweepLargeObjectLock, + kPinTableLock, kLoadLibraryLock, kClassLinkerClassesLock, kBreakpointLock, + kJdwpObjectRegistryLock, kThreadListLock, kBreakpointInvokeLock, - kJdwpObjectRegistryLock, + kTraceLock, kJdwpEventListLock, kJdwpAttachLock, kJdwpStartLock, - kJdwpSerialLock, kRuntimeShutdownLock, kHeapBitmapLock, kMonitorLock, @@ -136,8 +139,11 @@ class Locks { // Guards breakpoints and single-stepping. static Mutex* breakpoint_lock_ ACQUIRED_AFTER(thread_list_lock_); + // Guards trace requests. + static Mutex* trace_lock_ ACQUIRED_AFTER(breakpoint_lock_); + // Guards lists of classes within the class linker. - static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_); + static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_); // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code // doesn't try to hold a higher level Mutex. diff --git a/src/mirror/abstract_method-inl.h b/src/mirror/abstract_method-inl.h index 2049748..fd02474 100644 --- a/src/mirror/abstract_method-inl.h +++ b/src/mirror/abstract_method-inl.h @@ -20,6 +20,7 @@ #include "abstract_method.h" #include "dex_file.h" +#include "oat/runtime/oat_support_entrypoints.h" #include "object_array.h" #include "runtime.h" @@ -113,6 +114,9 @@ inline void AbstractMethod::AssertPcIsWithinCode(uintptr_t pc) const { if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) { return; } + if (GetCode() == GetInterpreterEntryPoint()) { + return; + } Runtime* runtime = Runtime::Current(); if (GetCode() == runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData()) { return; diff --git a/src/mirror/abstract_method.cc b/src/mirror/abstract_method.cc index f74814c..3ab3a93 100644 --- a/src/mirror/abstract_method.cc +++ b/src/mirror/abstract_method.cc @@ -151,23 +151,9 @@ AbstractMethod* AbstractMethod::FindOverriddenMethod() const { return result; } -static const void* GetOatCode(const AbstractMethod* m) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Runtime* runtime = Runtime::Current(); - const void* code = m->GetCode(); - // Peel off any method tracing trampoline. - if (runtime->IsMethodTracingActive() && runtime->GetInstrumentation()->GetSavedCodeFromMap(m) != NULL) { - code = runtime->GetInstrumentation()->GetSavedCodeFromMap(m); - } - // Peel off any resolution stub. - if (code == runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData()) { - code = runtime->GetClassLinker()->GetOatCodeFor(m); - } - return code; -} - uintptr_t AbstractMethod::NativePcOffset(const uintptr_t pc) const { - return pc - reinterpret_cast<uintptr_t>(GetOatCode(this)); + const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this); + return pc - reinterpret_cast<uintptr_t>(code); } // Find the lowest-address native safepoint pc for a given dex pc @@ -181,7 +167,8 @@ uintptr_t AbstractMethod::ToFirstNativeSafepointPc(const uint32_t dex_pc) const size_t mapping_table_length = GetPcToDexMappingTableLength(); for (size_t i = 0; i < mapping_table_length; i += 2) { if (mapping_table[i + 1] == dex_pc) { - return mapping_table[i] + reinterpret_cast<uintptr_t>(GetOatCode(this)); + const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this); + return mapping_table[i] + reinterpret_cast<uintptr_t>(code); } } LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc @@ -201,14 +188,16 @@ uint32_t AbstractMethod::ToDexPc(const uintptr_t pc) const { return DexFile::kDexNoIndex; // Special no mapping case } size_t mapping_table_length = GetPcToDexMappingTableLength(); - uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetOatCode(this)); + const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this); + uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(code); for (size_t i = 0; i < mapping_table_length; i += 2) { if (mapping_table[i] == sought_offset) { return mapping_table[i + 1]; } } - LOG(ERROR) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset) - << "(PC " << reinterpret_cast<void*>(pc) << ") in " << PrettyMethod(this); + LOG(FATAL) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset) + << "(PC " << reinterpret_cast<void*>(pc) << ", code=" << code + << ") in " << PrettyMethod(this); return DexFile::kDexNoIndex; #else // Compiler LLVM doesn't use the machine pc, we just use dex pc instead. @@ -227,7 +216,8 @@ uintptr_t AbstractMethod::ToNativePc(const uint32_t dex_pc) const { uint32_t map_offset = mapping_table[i]; uint32_t map_dex_offset = mapping_table[i + 1]; if (map_dex_offset == dex_pc) { - return reinterpret_cast<uintptr_t>(GetOatCode(this)) + map_offset; + const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this); + return reinterpret_cast<uintptr_t>(code) + map_offset; } } LOG(FATAL) << "Looking up Dex PC not contained in method, 0x" << std::hex << dex_pc @@ -270,14 +260,16 @@ void AbstractMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JV ManagedStack fragment; self->PushManagedStackFragment(&fragment); + Runtime* runtime = Runtime::Current(); // Call the invoke stub, passing everything as arguments. - if (UNLIKELY(!Runtime::Current()->IsStarted())){ + if (UNLIKELY(!runtime->IsStarted())){ LOG(INFO) << "Not invoking " << PrettyMethod(this) << " for a runtime that isn't started"; if (result != NULL) { result->SetJ(0); } } else { - bool interpret = self->ReadFlag(kEnterInterpreter) && !IsNative() && !IsProxyMethod(); + bool interpret = runtime->GetInstrumentation()->InterpretOnly() && !IsNative() && + !IsProxyMethod(); const bool kLogInvocationStartAndReturn = false; if (GetCode() != NULL) { if (!interpret) { @@ -289,15 +281,15 @@ void AbstractMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JV #else (*art_quick_invoke_stub)(this, args, args_size, self, result, result_type); #endif - if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException()) == -1)) { + if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException(NULL)) == -1)) { // Unusual case where we were running LLVM generated code and an // exception was thrown to force the activations to be removed from the // stack. Continue execution in the interpreter. - JValue value; self->ClearException(); - ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(&value); + ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result); + self->SetTopOfStack(NULL, 0); self->SetTopOfShadowStack(shadow_frame); - interpreter::EnterInterpreterFromLLVM(self, shadow_frame, result); + interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result); } if (kLogInvocationStartAndReturn) { LOG(INFO) << StringPrintf("Returned '%s' code=%p", PrettyMethod(this).c_str(), GetCode()); diff --git a/src/mirror/abstract_method.h b/src/mirror/abstract_method.h index d10031a..9440915 100644 --- a/src/mirror/abstract_method.h +++ b/src/mirror/abstract_method.h @@ -321,6 +321,11 @@ class MANAGED AbstractMethod : public Object { return GetFrameSizeInBytes() - kPointerSize; } + size_t GetSirtOffsetInBytes() const { + CHECK(IsNative()); + return kPointerSize; + } + bool IsRegistered() const; void RegisterNative(Thread* self, const void* native_method) diff --git a/src/mirror/array.cc b/src/mirror/array.cc index d0b3838..84c2dc6 100644 --- a/src/mirror/array.cc +++ b/src/mirror/array.cc @@ -18,6 +18,7 @@ #include "class.h" #include "class-inl.h" +#include "common_throws.h" #include "dex_file-inl.h" #include "gc/card_table-inl.h" #include "object-inl.h" @@ -43,10 +44,10 @@ Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, // Check for overflow and throw OutOfMemoryError if this was an unreasonable request. size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size); - if (data_size >> component_shift != size_t(component_count) || size < data_size) { - self->ThrowNewExceptionF("Ljava/lang/OutOfMemoryError;", - "%s of length %d would overflow", - PrettyDescriptor(array_class).c_str(), component_count); + if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) { + self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow", + PrettyDescriptor(array_class).c_str(), + component_count).c_str()); return NULL; } @@ -108,8 +109,7 @@ Array* Array::CreateMultiArray(Thread* self, Class* element_class, IntArray* dim for (int i = 0; i < num_dimensions; i++) { int dimension = dimensions->Get(i); if (UNLIKELY(dimension < 0)) { - self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", - "Dimension %d: %d", i, dimension); + ThrowNegativeArraySizeException(StringPrintf("Dimension %d: %d", i, dimension).c_str()); return NULL; } } @@ -135,15 +135,12 @@ Array* Array::CreateMultiArray(Thread* self, Class* element_class, IntArray* dim } bool Array::ThrowArrayIndexOutOfBoundsException(int32_t index) const { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "length=%i; index=%i", length_, index); + art::ThrowArrayIndexOutOfBoundsException(index, GetLength()); return false; } bool Array::ThrowArrayStoreException(Object* object) const { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "%s cannot be stored in an array of type %s", - PrettyTypeOf(object).c_str(), PrettyTypeOf(this).c_str()); + art::ThrowArrayStoreException(object->GetClass(), this->GetClass()); return false; } diff --git a/src/mirror/class-inl.h b/src/mirror/class-inl.h index ec92c19..d7afed6 100644 --- a/src/mirror/class-inl.h +++ b/src/mirror/class-inl.h @@ -24,7 +24,7 @@ #include "dex_cache.h" #include "field.h" #include "iftable.h" -#include "object_array.h" +#include "object_array-inl.h" #include "runtime.h" #include "string.h" diff --git a/src/mirror/class.cc b/src/mirror/class.cc index ba3556e..15129ab 100644 --- a/src/mirror/class.cc +++ b/src/mirror/class.cc @@ -60,10 +60,22 @@ void Class::SetStatus(Status new_status) { if (new_status == kStatusError) { CHECK_NE(GetStatus(), kStatusError) << PrettyClass(this); - // stash current exception + // Stash current exception. Thread* self = Thread::Current(); - SirtRef<Throwable> exception(self, self->GetException()); - CHECK(exception.get() != NULL); + SirtRef<mirror::Object> old_throw_this_object(self, NULL); + SirtRef<mirror::AbstractMethod> old_throw_method(self, NULL); + SirtRef<mirror::Throwable> old_exception(self, NULL); + uint32_t old_throw_dex_pc; + { + ThrowLocation old_throw_location; + mirror::Throwable* old_exception_obj = self->GetException(&old_throw_location); + old_throw_this_object.reset(old_throw_location.GetThis()); + old_throw_method.reset(old_throw_location.GetMethod()); + old_exception.reset(old_exception_obj); + old_throw_dex_pc = old_throw_location.GetDexPc(); + self->ClearException(); + } + CHECK(old_exception.get() != NULL); // clear exception to call FindSystemClass self->ClearException(); @@ -71,15 +83,18 @@ void Class::SetStatus(Status new_status) { Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;"); CHECK(!self->IsExceptionPending()); - // only verification errors, not initialization problems, should set a verify error. - // this is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case. - Class* exception_class = exception->GetClass(); + // Only verification errors, not initialization problems, should set a verify error. + // This is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case. + Class* exception_class = old_exception->GetClass(); if (!eiie_class->IsAssignableFrom(exception_class)) { SetVerifyErrorClass(exception_class); } - // restore exception - self->SetException(exception.get()); + // Restore exception. + ThrowLocation gc_safe_throw_location(old_throw_this_object.get(), old_throw_method.get(), + old_throw_dex_pc); + + self->SetException(gc_safe_throw_location, old_exception.get()); } return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false); } diff --git a/src/mirror/object_test.cc b/src/mirror/object_test.cc index eed96bd..5c7ec11 100644 --- a/src/mirror/object_test.cc +++ b/src/mirror/object_test.cc @@ -116,12 +116,12 @@ TEST_F(ObjectTest, AllocObjectArray) { EXPECT_TRUE(oa->Get(-1) == NULL); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass()); soa.Self()->ClearException(); EXPECT_TRUE(oa->Get(2) == NULL); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass()); soa.Self()->ClearException(); ASSERT_TRUE(oa->GetClass() != NULL); @@ -166,12 +166,12 @@ void TestPrimitiveArray(ClassLinker* cl) { EXPECT_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass()); soa.Self()->ClearException(); EXPECT_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_EQ(aioobe, soa.Self()->GetException(NULL)->GetClass()); soa.Self()->ClearException(); } @@ -231,7 +231,7 @@ TEST_F(ObjectTest, CreateMultiArray) { dims->Set(0, -1); multi = Array::CreateMultiArray(soa.Self(), c.get(), dims.get()); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException()->GetClass()), + EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException(NULL)->GetClass()), "java.lang.NegativeArraySizeException"); soa.Self()->ClearException(); diff --git a/src/mirror/string.cc b/src/mirror/string.cc index f571fb8..45a6779 100644 --- a/src/mirror/string.cc +++ b/src/mirror/string.cc @@ -103,8 +103,9 @@ uint16_t String::CharAt(int32_t index) const { // bounds check itself. if (index < 0 || index >= count_) { Thread* self = Thread::Current(); - self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", - "length=%i; index=%i", count_, index); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/StringIndexOutOfBoundsException;", + "length=%i; index=%i", count_, index); return 0; } return GetCharArray()->Get(index + GetOffset()); diff --git a/src/mirror/throwable.cc b/src/mirror/throwable.cc index d1192b0..bbff9c2 100644 --- a/src/mirror/throwable.cc +++ b/src/mirror/throwable.cc @@ -35,7 +35,9 @@ Class* Throwable::java_lang_Throwable_ = NULL; void Throwable::SetCause(Throwable* cause) { CHECK(cause != NULL); CHECK(cause != this); - CHECK(GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false) == NULL); + Throwable* current_cause = GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), + false); + CHECK(current_cause == NULL || current_cause == this); SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false); } diff --git a/src/monitor.cc b/src/monitor.cc index 2377734..11790e5 100644 --- a/src/monitor.cc +++ b/src/monitor.cc @@ -254,10 +254,12 @@ static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { va_list args; va_start(args, fmt); - Thread::Current()->ThrowNewExceptionV("Ljava/lang/IllegalMonitorStateException;", fmt, args); + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionV(throw_location, "Ljava/lang/IllegalMonitorStateException;", fmt, args); if (!Runtime::Current()->IsStarted()) { std::ostringstream ss; - Thread::Current()->Dump(ss); + self->Dump(ss); std::string str(ss.str()); LOG(ERROR) << "IllegalMonitorStateException: " << str; } @@ -411,8 +413,9 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why) { // Enforce the timeout range. if (ms < 0 || ns < 0 || ns > 999999) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "timeout arguments out of range: ms=%lld ns=%d", ms, ns); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;", + "timeout arguments out of range: ms=%lld ns=%d", ms, ns); return; } @@ -517,7 +520,8 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, self->interrupted_ = false; } if (interruptShouldThrow) { - Thread::Current()->ThrowNewException("Ljava/lang/InterruptedException;", NULL); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL); } } } diff --git a/src/monitor_android.cc b/src/monitor_android.cc index d3ac143..9265cd6 100644 --- a/src/monitor_android.cc +++ b/src/monitor_android.cc @@ -77,7 +77,7 @@ void Monitor::LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample cp = EventLogWriteInt(cp, wait_ms); // Emit the source code file name, <= 37 bytes. - uintptr_t pc; + uint32_t pc; mirror::AbstractMethod* m = self->GetCurrentMethod(&pc); const char* filename; uint32_t line_number; diff --git a/src/native/dalvik_system_DexFile.cc b/src/native/dalvik_system_DexFile.cc index 7c6fbd9..d703f83 100644 --- a/src/native/dalvik_system_DexFile.cc +++ b/src/native/dalvik_system_DexFile.cc @@ -18,6 +18,7 @@ #include "base/logging.h" #include "class_linker.h" +#include "common_throws.h" #include "dex_file-inl.h" #include "gc/space.h" #include "image.h" @@ -103,18 +104,18 @@ static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jst } if (dex_file == NULL) { LOG(WARNING) << "Failed to open dex file: " << source; - Thread::Current()->ThrowNewExceptionF("Ljava/io/IOException;", "Unable to open dex file: %s", - source.c_str()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;", + "Unable to open dex file: %s", source.c_str()); return 0; } return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file)); } -static const DexFile* toDexFile(int dex_file_address) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address)); if (dex_file == NULL) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;", "dex_file == null"); + ThrowNullPointerException(NULL, "dex_file == null"); } return dex_file; } @@ -188,7 +189,9 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename if (!OS::FileExists(filename.c_str())) { LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist"; ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewExceptionF("Ljava/io/FileNotFoundException;", "%s", filename.c_str()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;", + "%s", filename.c_str()); return JNI_TRUE; } diff --git a/src/native/dalvik_system_VMDebug.cc b/src/native/dalvik_system_VMDebug.cc index dc07a31..992998e 100644 --- a/src/native/dalvik_system_VMDebug.cc +++ b/src/native/dalvik_system_VMDebug.cc @@ -18,6 +18,7 @@ #include <unistd.h> #include "class_linker.h" +#include "common_throws.h" #include "debugger.h" #include "hprof/hprof.h" #include "jni_internal.h" @@ -68,8 +69,9 @@ static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceF int fd = dup(originalFd); if (fd < 0) { ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "dup(%d) failed: %s", originalFd, strerror(errno)); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/RuntimeException;", + "dup(%d) failed: %s", originalFd, strerror(errno)); return; } @@ -90,7 +92,7 @@ static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring java } static jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) { - return Runtime::Current()->IsMethodTracingActive(); + return Trace::IsMethodTracingActive(); } static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { @@ -119,24 +121,26 @@ static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { return Dbg::LastDebuggerActivity(); } -static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { +static void ThrowUnsupportedOperationException(JNIEnv* env) { ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", ""); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewException(throw_location, "Ljava/lang/UnsupportedOperationException;", NULL); +} + +static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { + ThrowUnsupportedOperationException(env); } static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) { - ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", ""); + ThrowUnsupportedOperationException(env); } static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) { - ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", ""); + ThrowUnsupportedOperationException(env); } static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) { - ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", ""); + ThrowUnsupportedOperationException(env); } static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { @@ -166,8 +170,7 @@ static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, job // Only one of these may be NULL. if (javaFilename == NULL && javaFd == NULL) { ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", - "fileName == null && fd == null"); + ThrowNullPointerException(NULL, "fileName == null && fd == null"); return; } @@ -187,8 +190,7 @@ static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, job fd = jniGetFDFromFileDescriptor(env, javaFd); if (fd < 0) { ScopedObjectAccess soa(env); - Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", - "Invalid file descriptor"); + ThrowRuntimeException("Invalid file descriptor"); return; } } diff --git a/src/native/dalvik_system_VMRuntime.cc b/src/native/dalvik_system_VMRuntime.cc index a13d07a..d2ef43c 100644 --- a/src/native/dalvik_system_VMRuntime.cc +++ b/src/native/dalvik_system_VMRuntime.cc @@ -17,6 +17,7 @@ #include <limits.h> #include "class_linker.h" +#include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" #include "jni_internal.h" @@ -57,11 +58,11 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaEle mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); if (element_class == NULL) { - soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "element class == null"); + ThrowNullPointerException(NULL, "element class == null"); return NULL; } if (length < 0) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length); + ThrowNegativeArraySizeException(length); return NULL; } @@ -84,7 +85,7 @@ static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { ScopedObjectAccess soa(env); mirror::Array* array = soa.Decode<mirror::Array*>(javaArray); if (!array->IsArrayInstance()) { - soa.Self()->ThrowNewException("Ljava/lang/IllegalArgumentException;", "not an array"); + ThrowIllegalArgumentException(NULL, "not an array"); return 0; } // TODO: we should also check that this is a non-movable array. diff --git a/src/native/java_lang_Class.cc b/src/native/java_lang_Class.cc index 72f4c18..a729699 100644 --- a/src/native/java_lang_Class.cc +++ b/src/native/java_lang_Class.cc @@ -53,8 +53,9 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean // is especially handy for array types, since we want to avoid // auto-generating bogus array classes. if (!IsValidBinaryClassName(name.c_str())) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;", - "Invalid name: %s", name.c_str()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ClassNotFoundException;", + "Invalid name: %s", name.c_str()); return NULL; } diff --git a/src/native/java_lang_String.cc b/src/native/java_lang_String.cc index 44ab1ca..3e9c3f3 100644 --- a/src/native/java_lang_String.cc +++ b/src/native/java_lang_String.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "common_throws.h" #include "jni_internal.h" #include "mirror/string.h" #include "scoped_thread_state_change.h" @@ -22,12 +23,11 @@ namespace art { static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) { + ScopedObjectAccess soa(env); if (UNLIKELY(javaRhs == NULL)) { - ScopedLocalRef<jclass> npe(env, env->FindClass("java/lang/NullPointerException")); - env->ThrowNew(npe.get(), "rhs == null"); + ThrowNullPointerException(NULL, "rhs == null"); return -1; } else { - ScopedObjectAccess soa(env); return soa.Decode<mirror::String*>(javaThis)->CompareTo(soa.Decode<mirror::String*>(javaRhs)); } } diff --git a/src/native/java_lang_System.cc b/src/native/java_lang_System.cc index 5572623..d8df9d9 100644 --- a/src/native/java_lang_System.cc +++ b/src/native/java_lang_System.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "common_throws.h" #include "gc/card_table-inl.h" #include "jni_internal.h" #include "mirror/array.h" @@ -171,31 +172,33 @@ namespace art { static void ThrowArrayStoreException_NotAnArray(const char* identifier, mirror::Object* array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string actualType(PrettyTypeOf(array)); - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "%s of type %s is not an array", identifier, actualType.c_str()); + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;", + "%s of type %s is not an array", identifier, actualType.c_str()); } static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) { ScopedObjectAccess soa(env); // Null pointer checks. - if (javaSrc == NULL) { - soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "src == null"); + if (UNLIKELY(javaSrc == NULL)) { + ThrowNullPointerException(NULL, "src == null"); return; } - if (javaDst == NULL) { - soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null"); + if (UNLIKELY(javaDst == NULL)) { + ThrowNullPointerException(NULL, "dst == null"); return; } // Make sure source and destination are both arrays. mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc); mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst); - if (!srcObject->IsArrayInstance()) { + if (UNLIKELY(!srcObject->IsArrayInstance())) { ThrowArrayStoreException_NotAnArray("source", srcObject); return; } - if (!dstObject->IsArrayInstance()) { + if (UNLIKELY(!dstObject->IsArrayInstance())) { ThrowArrayStoreException_NotAnArray("destination", dstObject); return; } @@ -205,21 +208,24 @@ static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, mirror::Class* dstComponentType = dstArray->GetClass()->GetComponentType(); // Bounds checking. - if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d", - srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length); + if (UNLIKELY(srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length)) { + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;", + "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d", + srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length); return; } // Handle primitive arrays. if (srcComponentType->IsPrimitive() || dstComponentType->IsPrimitive()) { // If one of the arrays holds a primitive type the other array must hold the exact same type. - if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) { + if (UNLIKELY(srcComponentType != dstComponentType)) { std::string srcType(PrettyTypeOf(srcArray)); std::string dstType(PrettyTypeOf(dstArray)); - soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;", + "Incompatible types: src=%s, dst=%s", + srcType.c_str(), dstType.c_str()); return; } @@ -299,12 +305,13 @@ static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, } Runtime::Current()->GetHeap()->WriteBarrierArray(dstArray, dstPos, length); - if (i != length) { + if (UNLIKELY(i != length)) { std::string actualSrcType(PrettyTypeOf(o)); std::string dstType(PrettyTypeOf(dstArray)); - soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "source[%d] of type %s cannot be stored in destination array of type %s", - srcPos + i, actualSrcType.c_str(), dstType.c_str()); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;", + "source[%d] of type %s cannot be stored in destination array of type %s", + srcPos + i, actualSrcType.c_str(), dstType.c_str()); return; } } diff --git a/src/native/java_lang_Thread.cc b/src/native/java_lang_Thread.cc index ca4be9d..7ccfaaa 100644 --- a/src/native/java_lang_Thread.cc +++ b/src/native/java_lang_Thread.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "common_throws.h" #include "debugger.h" #include "jni_internal.h" #include "monitor.h" @@ -90,7 +91,7 @@ static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject ScopedObjectAccess soa(env); mirror::Object* object = soa.Decode<mirror::Object*>(java_object); if (object == NULL) { - Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "object == null"); + ThrowNullPointerException(NULL, "object == null"); return JNI_FALSE; } MutexLock mu(soa.Self(), *Locks::thread_list_lock_); diff --git a/src/native/java_lang_reflect_Array.cc b/src/native/java_lang_reflect_Array.cc index af7a77a..45ec0ad 100644 --- a/src/native/java_lang_reflect_Array.cc +++ b/src/native/java_lang_reflect_Array.cc @@ -15,6 +15,7 @@ */ #include "class_linker.h" +#include "common_throws.h" #include "dex_file-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" @@ -44,7 +45,7 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl DCHECK(javaElementClass != NULL); mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); if (UNLIKELY(length < 0)) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length); + ThrowNegativeArraySizeException(length); return NULL; } std::string descriptor("["); diff --git a/src/native/java_lang_reflect_Constructor.cc b/src/native/java_lang_reflect_Constructor.cc index fb84dfd..9180217 100644 --- a/src/native/java_lang_reflect_Constructor.cc +++ b/src/native/java_lang_reflect_Constructor.cc @@ -37,9 +37,12 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA ScopedObjectAccess soa(env); mirror::AbstractMethod* m = soa.Decode<mirror::Object*>(javaMethod)->AsMethod(); mirror::Class* c = m->GetDeclaringClass(); - if (c->IsAbstract()) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", - "Can't instantiate abstract class %s", PrettyDescriptor(c).c_str()); + if (UNLIKELY(c->IsAbstract())) { + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/InstantiationException;", + "Can't instantiate %s %s", + c->IsInterface() ? "interface" : "abstract class", + PrettyDescriptor(c).c_str()); return NULL; } diff --git a/src/native/java_lang_reflect_Field.cc b/src/native/java_lang_reflect_Field.cc index 922fe00..b0daa91 100644 --- a/src/native/java_lang_reflect_Field.cc +++ b/src/native/java_lang_reflect_Field.cc @@ -16,6 +16,7 @@ #include "class_linker.h" #include "class_linker-inl.h" +#include "common_throws.h" #include "dex_file-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" @@ -71,22 +72,23 @@ static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirr // Never okay. break; } - soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "Not a primitive field: %s", PrettyField(f).c_str()); + ThrowIllegalArgumentException(NULL, + StringPrintf("Not a primitive field: %s", + PrettyField(f).c_str()).c_str()); return false; } -static bool CheckReceiver(const ScopedObjectAccess& soa, jobject javaObj, mirror::Field* f, - mirror::Object*& o) +static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::Field* f, + mirror::Object*& class_or_rcvr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (f->IsStatic()) { - o = f->GetDeclaringClass(); + class_or_rcvr = f->GetDeclaringClass(); return true; } - o = soa.Decode<mirror::Object*>(javaObj); + class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr); mirror::Class* declaringClass = f->GetDeclaringClass(); - if (!VerifyObjectInClass(o, declaringClass)) { + if (!VerifyObjectInClass(class_or_rcvr, declaringClass)) { return false; } return true; @@ -126,8 +128,8 @@ static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, // Widen it if necessary (and possible). JValue wide_value; mirror::Class* dst_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(dst_descriptor); - if (!ConvertPrimitiveValue(FieldHelper(f).GetTypeAsPrimitiveType(), dst_type->GetPrimitiveType(), - field_value, wide_value)) { + if (!ConvertPrimitiveValue(NULL, false, FieldHelper(f).GetTypeAsPrimitiveType(), + dst_type->GetPrimitiveType(), field_value, wide_value)) { return JValue(); } return wide_value; @@ -205,8 +207,8 @@ static void SetFieldValue(mirror::Object* o, mirror::Field* f, const JValue& new // Else fall through to report an error. case Primitive::kPrimVoid: // Never okay. - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "Not a primitive field: %s", PrettyField(f).c_str()); + ThrowIllegalArgumentException(NULL, StringPrintf("Not a primitive field: %s", + PrettyField(f).c_str()).c_str()); return; } @@ -247,15 +249,15 @@ static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, c } FieldHelper fh(f); if (!fh.IsPrimitiveType()) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "Not a primitive field: %s", PrettyField(f).c_str()); + ThrowIllegalArgumentException(NULL, StringPrintf("Not a primitive field: %s", + PrettyField(f).c_str()).c_str()); return; } // Widen the value if necessary (and possible). JValue wide_value; mirror::Class* src_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(src_descriptor); - if (!ConvertPrimitiveValue(src_type->GetPrimitiveType(), fh.GetTypeAsPrimitiveType(), + if (!ConvertPrimitiveValue(NULL, false, src_type->GetPrimitiveType(), fh.GetTypeAsPrimitiveType(), new_value, wide_value)) { return; } diff --git a/src/nth_caller_visitor.h b/src/nth_caller_visitor.h index 7d9feb6..c32a46a 100644 --- a/src/nth_caller_visitor.h +++ b/src/nth_caller_visitor.h @@ -18,6 +18,7 @@ #define ART_SRC_NTH_CALLER_VISITOR_H_ #include "mirror/abstract_method.h" +#include "locks.h" #include "stack.h" namespace art { @@ -25,19 +26,32 @@ class Thread; // Walks up the stack 'n' callers, when used with Thread::WalkStack. struct NthCallerVisitor : public StackVisitor { - NthCallerVisitor(Thread* thread, size_t n) - : StackVisitor(thread, NULL), n(n), count(0), caller(NULL) {} - - bool VisitFrame() { - DCHECK(caller == NULL); - if (count++ == n) { - caller = GetMethod(); - return false; + NthCallerVisitor(Thread* thread, size_t n, bool include_runtime_and_upcalls = false) + : StackVisitor(thread, NULL), n(n), include_runtime_and_upcalls_(include_runtime_and_upcalls), + count(0), caller(NULL) {} + + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::AbstractMethod* m = GetMethod(); + bool do_count = false; + if (m == NULL || m->IsRuntimeMethod()) { + // Upcall. + do_count = include_runtime_and_upcalls_; + } else { + do_count = true; + } + if (do_count) { + DCHECK(caller == NULL); + if (count == n) { + caller = m; + return false; + } + count++; } return true; } - size_t n; + const size_t n; + const bool include_runtime_and_upcalls_; size_t count; mirror::AbstractMethod* caller; }; diff --git a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc index 58341bf..1276f78 100644 --- a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc +++ b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc @@ -33,10 +33,6 @@ extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, extern "C" void art_quick_can_put_array_element_from_code(void*, void*); extern "C" void art_quick_check_cast_from_code(void*, void*); -// Debug entrypoints. -extern void DebugMe(mirror::AbstractMethod* method, uint32_t info); -extern "C" void art_quick_update_debugger(void*, void*, int32_t, void*); - // DexCache entrypoints. extern "C" void* art_quick_initialize_static_storage_from_code(uint32_t, void*); extern "C" void* art_quick_initialize_type_from_code(uint32_t, void*); @@ -145,10 +141,6 @@ void InitEntryPoints(EntryPoints* points) { points->pCanPutArrayElementFromCode = art_quick_can_put_array_element_from_code; points->pCheckCastFromCode = art_quick_check_cast_from_code; - // Debug - points->pDebugMe = DebugMe; - points->pUpdateDebuggerFromCode = NULL; // Controlled by SetDebuggerUpdatesEnabled. - // DexCache points->pInitializeStaticStorage = art_quick_initialize_static_storage_from_code; points->pInitializeTypeAndVerifyAccessFromCode = art_quick_initialize_type_and_verify_access_from_code; @@ -236,10 +228,6 @@ void InitEntryPoints(EntryPoints* points) { points->pThrowStackOverflowFromCode = art_quick_throw_stack_overflow_from_code; }; -void ChangeDebuggerEntryPoint(EntryPoints* points, bool enabled) { - points->pUpdateDebuggerFromCode = (enabled ? art_quick_update_debugger : NULL); -} - uintptr_t GetInstrumentationExitPc() { return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code); } diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S index fe7d69f..96b3980 100644 --- a/src/oat/runtime/arm/runtime_support_arm.S +++ b/src/oat/runtime/arm/runtime_support_arm.S @@ -340,22 +340,6 @@ ENTRY art_quick_invoke_stub END art_quick_invoke_stub /* - * On entry, r0 and r1 must be preserved, r2 is dex PC - */ - .extern artUpdateDebuggerFromCode -ENTRY art_quick_update_debugger - mov r3, r0 @ stash away r0 so that it's saved as if it were an argument - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - mov r0, r2 @ arg0 is dex PC - mov r1, rSELF @ arg1 is Thread* - mov r2, sp @ arg2 is sp - bl artUpdateDebuggerFromCode @ artUpdateDebuggerFromCode(int32_t, Thread*, Method**) - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME - mov r0, r3 @ restore original r0 - bx lr -END art_quick_update_debugger - - /* * On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_ */ ENTRY art_quick_do_long_jump @@ -979,21 +963,21 @@ END art_quick_interpreter_entry .extern artInstrumentationMethodEntryFromCode .extern artInstrumentationMethodExitFromCode ENTRY art_quick_instrumentation_entry_from_code - mov r12, sp @ remember bottom of caller's frame - push {r0-r3} @ save arguments (4 words) - .save {r0-r3} + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + str r0, [sp, #4] @ preserve r0 + mov r12, sp @ remember sp + str lr, [sp, #-16]! @ expand the frame and pass LR + .pad #16 .cfi_adjust_cfa_offset 16 - .cfi_rel_offset r0, 0 - .cfi_rel_offset r1, 4 - .cfi_rel_offset r2, 8 - .cfi_rel_offset r3, 12 - mov r1, r9 @ pass Thread::Current - mov r2, r12 @ pass SP - mov r3, lr @ pass LR - blx artInstrumentationMethodEntryFromCode @ (Method*, Thread*, SP, LR) - mov r12, r0 @ r12 holds reference to code - pop {r0-r3} @ restore arguments + .cfi_rel_offset lr, 0 + mov r2, r9 @ pass Thread::Current + mov r3, r12 @ pass SP + blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, SP, LR) + add sp, #16 @ remove out argument and padding from stack .cfi_adjust_cfa_offset -16 + mov r12, r0 @ r12 holds reference to code + ldr r0, [sp, #4] @ restore r0 + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME blx r12 @ call method with lr set to art_quick_instrumentation_exit_from_code END art_quick_instrumentation_entry_from_code .type art_quick_instrumentation_exit_from_code, #function @@ -1001,51 +985,44 @@ END art_quick_instrumentation_entry_from_code art_quick_instrumentation_exit_from_code: .cfi_startproc .fnstart + mov lr, #0 @ link register is to here, so clobber with 0 for later checks + SETUP_REF_ONLY_CALLEE_SAVE_FRAME mov r12, sp @ remember bottom of caller's frame push {r0-r1} @ save return value .save {r0-r1} .cfi_adjust_cfa_offset 8 .cfi_rel_offset r0, 0 .cfi_rel_offset r1, 4 - sub sp, #8 @ align stack + sub sp, #8 @ space for return value argument .pad #8 .cfi_adjust_cfa_offset 8 + strd r0, [sp] @ r0/r1 -> [sp] for fpr_res + mov r2, r0 @ pass return value as gpr_res + mov r3, r1 mov r0, r9 @ pass Thread::Current mov r1, r12 @ pass SP - blx artInstrumentationMethodExitFromCode @ (Thread*, SP) + blx artInstrumentationMethodExitFromCode @ (Thread*, SP, gpr_res, fpr_res) add sp, #8 .cfi_adjust_cfa_offset -8 + mov r2, r0 @ link register saved by instrumentation mov lr, r1 @ r1 is holding link register if we're to bounce to deoptimize pop {r0, r1} @ restore return value - .cfi_adjust_cfa_offset -8 + add sp, #32 @ remove callee save frame + .cfi_adjust_cfa_offset -32 bx r2 @ return END art_quick_instrumentation_exit_from_code /* - * The thread's enter interpreter flag is set and so we should transition to the interpreter - * rather than allow execution to continue in the frame below. There may be live results in - * registers depending on how complete the operation is when we safepoint - for example, a - * set operation may have completed while a get operation needs writing back into the vregs. + * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization + * will long jump to the upcall with a special exception of -1. */ .extern artDeoptimize - .extern artEnterInterpreterFromDeoptimize ENTRY art_quick_deoptimize SETUP_REF_ONLY_CALLEE_SAVE_FRAME - mov r2, r9 @ Set up args. - mov r3, sp - blx artDeoptimize @ artDeoptimize(return value, Thread*, SP) - @ Returns caller method's frame size. - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - cmp r0, #0 @ Was the caller an upcall? - bxeq lr @ Return if caller was upcall. - add r12, sp, r0 @ r12 == bottom of caller's frame. - ldr lr, [r12, #-4] @ Restore lr. - mov sp, r12 @ Remove frame. - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - blx artEnterInterpreterFromDeoptimize @ Enter interpreter, callee-save ends stack fragment. - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - bx lr @ Return to caller. + mov r0, r9 @ Set up args. + mov r1, sp + blx artDeoptimize @ artDeoptimize(Thread*, SP) END art_quick_deoptimize /* diff --git a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc index ea861e8..599b14c 100644 --- a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc +++ b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc @@ -33,10 +33,6 @@ extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, extern "C" void art_quick_can_put_array_element_from_code(void*, void*); extern "C" void art_quick_check_cast_from_code(void*, void*); -// Debug entrypoints. -extern void DebugMe(mirror::AbstractMethod* method, uint32_t info); -extern "C" void art_quick_update_debugger(void*, void*, int32_t, void*); - // DexCache entrypoints. extern "C" void* art_quick_initialize_static_storage_from_code(uint32_t, void*); extern "C" void* art_quick_initialize_type_from_code(uint32_t, void*); @@ -147,10 +143,6 @@ void InitEntryPoints(EntryPoints* points) { points->pCanPutArrayElementFromCode = art_quick_can_put_array_element_from_code; points->pCheckCastFromCode = art_quick_check_cast_from_code; - // Debug - points->pDebugMe = DebugMe; - points->pUpdateDebuggerFromCode = NULL; // Controlled by SetDebuggerUpdatesEnabled. - // DexCache points->pInitializeStaticStorage = art_quick_initialize_static_storage_from_code; points->pInitializeTypeAndVerifyAccessFromCode = art_quick_initialize_type_and_verify_access_from_code; @@ -237,10 +229,6 @@ void InitEntryPoints(EntryPoints* points) { points->pThrowStackOverflowFromCode = art_quick_throw_stack_overflow_from_code; }; -void ChangeDebuggerEntryPoint(EntryPoints* points, bool enabled) { - points->pUpdateDebuggerFromCode = (enabled ? art_quick_update_debugger : NULL); -} - uintptr_t GetInstrumentationExitPc() { return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code); } diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S index 0fc2437..529fd0d 100644 --- a/src/oat/runtime/mips/runtime_support_mips.S +++ b/src/oat/runtime/mips/runtime_support_mips.S @@ -204,24 +204,6 @@ .endm /* - * On entry, $a0 and $a1 must be preserved, $a2 is dex PC - */ - .extern artUpdateDebuggerFromCode -ENTRY art_quick_update_debugger - GENERATE_GLOBAL_POINTER - move $a3, $a0 # stash away $a0 so that it's saved as if it were an argument - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - move $a0, $a2 # arg0 is dex PC - move $a1, rSELF # arg1 is Thread* - move $a2, $sp # arg2 is $sp - jal artUpdateDebuggerFromCode # artUpdateDebuggerFromCode(int32_t, Thread*, Method**) - nop - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME - jr $ra - move $a0, $a3 # restore original $a0 -END art_quick_update_debugger - - /* * On entry $a0 is uint32_t* gprs_ and $a1 is uint32_t* fprs_ * FIXME: just guessing about the shape of the jmpbuf. Where will pc be? */ @@ -989,29 +971,22 @@ END art_quick_interpreter_entry .extern artInstrumentationMethodExitFromCode ENTRY art_quick_instrumentation_entry_from_code GENERATE_GLOBAL_POINTER + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME move $t0, $sp # remember bottom of caller's frame - addiu $sp, $sp, -16 # save arguments (4 words) - .cfi_adjust_cfa_offset 16 - sw $a0, 0($sp) - .cfi_rel_offset 4, 0 - sw $a1, 4($sp) - .cfi_rel_offset 5, 4 - sw $a2, 8($sp) - .cfi_rel_offset 6, 8 - sw $a3, 12($sp) - .cfi_rel_offset 7, 12 - move $a3, $ra # pass $ra - move $a2, $t0 # pass $sp - jal artInstrumentationMethodEntryFromCode # (Method*, Thread*, SP, LR) - move $a1, rSELF # pass Thread::Current + addiu $sp, $sp, -32 # space for args, pad (3 words), arguments (5 words) + .cfi_adjust_cfa_offset 32 + sw $a0, 28($sp) # save arg0 + sw $ra, 16($sp) # pass $ra + move $a3, $t0 # pass $sp + jal artInstrumentationMethodEntryFromCode # (Method*, Object*, Thread*, SP, LR) + move $a2, rSELF # pass Thread::Current move $t9, $v0 # $t9 holds reference to code - lw $a0, 0($sp) - lw $a1, 4($sp) - lw $a2, 8($sp) - lw $a3, 12($sp) + lw $a0, 28($sp) # restore arg0 + addiu $sp, $sp, 32 # remove args + .cfi_adjust_cfa_offset -32 + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME jalr $t9 # call method - addiu $sp, $sp, 16 - .cfi_adjust_cfa_offset -16 + nop END art_quick_instrumentation_entry_from_code /* intentional fallthrough */ .global art_quick_instrumentation_exit_from_code @@ -1020,53 +995,46 @@ art_quick_instrumentation_exit_from_code: addiu $t9, $ra, 4 # put current address into $t9 to rebuild $gp GENERATE_GLOBAL_POINTER move $t0, $sp # remember bottom of caller's frame - addiu $sp, $sp, -16 # save return values - .cfi_adjust_cfa_offset 16 - sw $v0, 0($sp) + SETUP_REF_ONLY_CALLEE_SAVE_FRAME + addiu $sp, $sp, -48 # save return values and set up args + .cfi_adjust_cfa_offset 48 + sw $v0, 32($sp) .cfi_rel_offset 2, 0 - sw $v1, 4($sp) + sw $v1, 36($sp) .cfi_rel_offset 3, 4 + s.s $f0, 40($sp) + s.s $f1, 44($sp) + s.s $f0, 16($sp) # pass fpr result + s.s $f1, 20($sp) + move $a2, $v0 # pass gpr result + move $a3, $v1 move $a1, $t0 # pass $sp - jal artInstrumentationMethodExitFromCode # (Thread*, SP) + jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res) move $a0, rSELF # pass Thread::Current move $t0, $v0 # set aside returned link register move $ra, $v1 # set link register for deoptimization - lw $v0, 0($sp) - lw $v1, 4($sp) + lw $v0, 32($sp) # restore return values + lw $v1, 36($sp) + l.s $f0, 40($sp) + l.s $f1, 44($sp) jr $t0 # return - addiu $sp, $sp, 16 - .cfi_adjust_cfa_offset -16 + addiu $sp, $sp, 112 # 48 bytes of args + 64 bytes of callee save frame + .cfi_adjust_cfa_offset -112 END art_quick_instrumentation_exit_from_code /* - * The thread's enter interpreter flag is set and so we should transition to the interpreter - * rather than allow execution to continue in the frame below. There may be live results in - * registers depending on how complete the operation is when we safepoint - for example, a - * set operation may have completed while a get operation needs writing back into the vregs. + * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization + * will long jump to the upcall with a special exception of -1. */ .extern artDeoptimize .extern artEnterInterpreterFromDeoptimize ENTRY art_quick_deoptimize GENERATE_GLOBAL_POINTER SETUP_REF_ONLY_CALLEE_SAVE_FRAME - move $a0, $v0 # pass first half of return value - move $a1, $v1 # pass second half of return value - move $a2, rSELF # pass Thread::current - jal artDeoptimize # artDeoptimize(return value, Thread*, SP) + move $a0, rSELF # pass Thread::current + jal artDeoptimize # artDeoptimize(Thread*, SP) # Returns caller method's frame size. - move $a3, $sp # pass $sp - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - beqz $v0, 1f # Return if caller was upcall. - add $t9, $sp, $v0 # $t9 == bottom of caller's frame. - lw $ra, -4($t9) # Restore $ra. - move $sp, $t9 # Remove frame. - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - jal artEnterInterpreterFromDeoptimize # Enter interpreter, callee-save ends stack fragment. - nop - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME -1: - jr $ra # Return to caller. - nop + move $a1, $sp # pass $sp END art_quick_deoptimize /* diff --git a/src/oat/runtime/oat_support_entrypoints.h b/src/oat/runtime/oat_support_entrypoints.h index ee59df4..9cd9cc7 100644 --- a/src/oat/runtime/oat_support_entrypoints.h +++ b/src/oat/runtime/oat_support_entrypoints.h @@ -46,10 +46,6 @@ struct PACKED(4) EntryPoints { void (*pCanPutArrayElementFromCode)(void*, void*); void (*pCheckCastFromCode)(void*, void*); - // Debug - void (*pDebugMe)(mirror::AbstractMethod*, uint32_t); - void (*pUpdateDebuggerFromCode)(void*, void*, int32_t, void*); - // DexCache void* (*pInitializeStaticStorage)(uint32_t, void*); void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*); diff --git a/src/oat/runtime/support_cast.cc b/src/oat/runtime/support_cast.cc index 0b1fb74..fe91e61 100644 --- a/src/oat/runtime/support_cast.cc +++ b/src/oat/runtime/support_cast.cc @@ -32,19 +32,16 @@ extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, } // Check whether it is safe to cast one class to the other, throw exception and return -1 on failure -extern "C" int artCheckCastFromCode(const mirror::Class* a, const mirror::Class* b, Thread* self, - mirror::AbstractMethod** sp) +extern "C" int artCheckCastFromCode(mirror::Class* src_type, mirror::Class* dest_type, + Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(a->IsClass()) << PrettyClass(a); - DCHECK(b->IsClass()) << PrettyClass(b); - if (LIKELY(b->IsAssignableFrom(a))) { + DCHECK(src_type->IsClass()) << PrettyClass(src_type); + DCHECK(dest_type->IsClass()) << PrettyClass(dest_type); + if (LIKELY(dest_type->IsAssignableFrom(src_type))) { return 0; // Success } else { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - self->ThrowNewExceptionF("Ljava/lang/ClassCastException;", - "%s cannot be cast to %s", - PrettyDescriptor(a).c_str(), - PrettyDescriptor(b).c_str()); + ThrowClassCastException(dest_type, src_type); return -1; // Failure } } @@ -63,10 +60,7 @@ extern "C" int artCanPutArrayElementFromCode(const mirror::Object* element, return 0; // Success } else { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "%s cannot be stored in an array of type %s", - PrettyDescriptor(element_class).c_str(), - PrettyDescriptor(array_class).c_str()); + ThrowArrayStoreException(element_class, array_class); return -1; // Failure } } diff --git a/src/oat/runtime/support_debug.cc b/src/oat/runtime/support_debug.cc deleted file mode 100644 index 0d67dd9..0000000 --- a/src/oat/runtime/support_debug.cc +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "callee_save_frame.h" -#include "debugger.h" - -namespace art { - -/* - * Report location to debugger. Note: dex_pc is the current offset within - * the method. However, because the offset alone cannot distinguish between - * method entry and offset 0 within the method, we'll use an offset of -1 - * to denote method entry. - */ -extern "C" void artUpdateDebuggerFromCode(int32_t dex_pc, Thread* self, mirror::AbstractMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); - Dbg::UpdateDebugger(dex_pc, self); -} - -// Temporary debugging hook for compiler. -extern void DebugMe(mirror::AbstractMethod* method, uint32_t info) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - LOG(INFO) << "DebugMe"; - if (method != NULL) { - LOG(INFO) << PrettyMethod(method); - } - LOG(INFO) << "Info: " << info; -} - -} // namespace art diff --git a/src/oat/runtime/support_deoptimize.cc b/src/oat/runtime/support_deoptimize.cc index 2cc5dd3..0b0a7c3 100644 --- a/src/oat/runtime/support_deoptimize.cc +++ b/src/oat/runtime/support_deoptimize.cc @@ -28,88 +28,11 @@ namespace art { -extern "C" uint64_t artDeoptimize(JValue ret_val, Thread* self, mirror::AbstractMethod** sp) +extern "C" void artDeoptimize(Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - // Return value may hold Object* so avoid suspension. - const char* old_cause = self->StartAssertNoThreadSuspension("Deoptimizing stack frame"); - CHECK(old_cause == NULL); - class DeoptimizationVisitor : public StackVisitor { - public: - DeoptimizationVisitor(Thread* thread, Context* context) - : StackVisitor(thread, context), shadow_frame_(NULL), runtime_frames_(0) { } - - virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::AbstractMethod* m = GetMethod(); - if (m->IsRuntimeMethod()) { - if (runtime_frames_ == 0) { - runtime_frames_++; - return true; // Skip the callee save frame. - } else { - return false; // Caller was an upcall. - } - } - MethodHelper mh(m); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - CHECK(code_item != NULL); - uint16_t num_regs = code_item->registers_size_; - shadow_frame_ = ShadowFrame::Create(num_regs, NULL, m, GetDexPc()); - std::vector<int32_t> kinds = DescribeVRegs(m->GetDexMethodIndex(), &mh.GetDexFile(), - mh.GetDexCache(), mh.GetClassLoader(), - mh.GetClassDefIndex(), code_item, m, - m->GetAccessFlags(), GetDexPc()); - for(uint16_t reg = 0; reg < num_regs; reg++) { - VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2)); - switch (kind) { - case kUndefined: - shadow_frame_->SetVReg(reg, 0xEBADDE09); - break; - case kConstant: - shadow_frame_->SetVReg(reg, kinds.at((reg * 2) + 1)); - break; - default: - shadow_frame_->SetVReg(reg, GetVReg(m, reg, kind)); - break; - } - } - return false; // Stop now we have built the shadow frame. - } - - std::vector<int32_t> DescribeVRegs(uint32_t dex_method_idx, - const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, - uint32_t class_def_idx, - const DexFile::CodeItem* code_item, - mirror::AbstractMethod* method, - uint32_t method_access_flags, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - verifier::MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item, - dex_method_idx, method, method_access_flags, true); - verifier.Verify(); - return verifier.DescribeVRegs(dex_pc); - } - - ShadowFrame* shadow_frame_; - uint32_t runtime_frames_; - } visitor(self, self->GetLongJumpContext()); - visitor.WalkStack(false); - if (visitor.shadow_frame_ != NULL) { - self->SetDeoptimizationShadowFrame(visitor.shadow_frame_, ret_val); - return (*sp)->GetFrameSizeInBytes(); - } else { - return 0; // Caller was an upcall. - } -} - - -extern "C" JValue artEnterInterpreterFromDeoptimize(Thread* self, mirror::AbstractMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - JValue return_value; - UniquePtr<ShadowFrame> shadow_frame(self->GetAndClearDeoptimizationShadowFrame(&return_value)); - self->EndAssertNoThreadSuspension(NULL); - return interpreter::EnterInterpreterFromDeoptimize(self, *shadow_frame.get(), return_value); + self->SetException(ThrowLocation(), reinterpret_cast<mirror::Throwable*>(-1)); + self->QuickDeliverException(); } } // namespace art diff --git a/src/oat/runtime/support_field.cc b/src/oat/runtime/support_field.cc index 43d5c9b..5821063 100644 --- a/src/oat/runtime/support_field.cc +++ b/src/oat/runtime/support_field.cc @@ -86,7 +86,8 @@ extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(field, true); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->Get32(obj); } @@ -106,7 +107,8 @@ extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object* field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(field, true); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->Get64(obj); } @@ -127,7 +129,8 @@ extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror: field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(field, true); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->GetObj(obj); } @@ -204,7 +207,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(field, false); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { field->Set32(obj, new_value); return 0; // success @@ -230,7 +234,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(field, false); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { field->Set64(obj, new_value); return 0; // success @@ -255,7 +260,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(field, false); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { field->SetObj(obj, new_value); return 0; // success diff --git a/src/oat/runtime/support_fillarray.cc b/src/oat/runtime/support_fillarray.cc index 73f832a..a0b06fb 100644 --- a/src/oat/runtime/support_fillarray.cc +++ b/src/oat/runtime/support_fillarray.cc @@ -15,6 +15,7 @@ */ #include "callee_save_frame.h" +#include "common_throws.h" #include "dex_instruction.h" #include "mirror/array.h" #include "mirror/object-inl.h" @@ -43,15 +44,15 @@ extern "C" int artHandleFillArrayDataFromCode(mirror::Array* array, FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature)); if (UNLIKELY(array == NULL)) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;", - "null array in FILL_ARRAY_DATA"); + ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); return -1; // Error } DCHECK(array->IsArrayInstance() && !array->IsObjectArray()); if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "failed FILL_ARRAY_DATA; length=%d, index=%d", - array->GetLength(), payload->element_count); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;", + "failed FILL_ARRAY_DATA; length=%d, index=%d", + array->GetLength(), payload->element_count); return -1; // Error } uint32_t size_in_bytes = payload->element_count * payload->element_width; diff --git a/src/oat/runtime/support_instrumentation.cc b/src/oat/runtime/support_instrumentation.cc index 6598f19..8f56ce3 100644 --- a/src/oat/runtime/support_instrumentation.cc +++ b/src/oat/runtime/support_instrumentation.cc @@ -14,58 +14,51 @@ * limitations under the License. */ -#include "base/logging.h" +#include "callee_save_frame.h" #include "instrumentation.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" #include "runtime.h" #include "thread-inl.h" -#include "trace.h" namespace art { extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::AbstractMethod* method, + mirror::Object* this_object, Thread* self, mirror::AbstractMethod** sp, uintptr_t lr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - self->SetTopOfStack(sp, lr); - self->VerifyStack(); - Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - // +1 as frame id's start at 1, +1 as we haven't yet built this method's frame. - size_t frame_id = StackVisitor::ComputeNumFrames(self) + 2; - InstrumentationStackFrame instrumentation_frame(method, lr, frame_id); - self->PushInstrumentationStackFrame(instrumentation_frame); - - Trace* trace = instrumentation->GetTrace(); - if (trace != NULL) { - trace->LogMethodTraceEvent(self, method, Trace::kMethodTraceEnter); - } - - return instrumentation->GetSavedCodeFromMap(method); + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? NULL : this_object, + method, lr); + const void* result = instrumentation->GetQuickCodeFor(method); + CHECK(result != NULL) << PrettyMethod(method); + return result; } -extern "C" uint64_t artInstrumentationMethodExitFromCode(Thread* self, mirror::AbstractMethod** sp) +extern "C" uint64_t artInstrumentationMethodExitFromCode(Thread* self, mirror::AbstractMethod** sp, + uint64_t gpr_result, uint64_t fpr_result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // TODO: use FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly) not the hand inlined below. + // We use the hand inline version to ensure the return_pc is assigned before verifying the + // stack. + // Be aware the store below may well stomp on an incoming argument. + Locks::mutator_lock_->AssertSharedHeld(self); + mirror::AbstractMethod* callee_save = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsOnly); + *sp = callee_save; + uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) + + callee_save->GetReturnPcOffsetInBytes()); + CHECK(*return_pc == 0); self->SetTopOfStack(sp, 0); self->VerifyStack(); - // +1 as frame id's start at 1, +1 as we want the called frame not the frame being returned into. - size_t frame_id = StackVisitor::ComputeNumFrames(self) + 2; - InstrumentationStackFrame instrumentation_frame; - instrumentation_frame = self->PopInstrumentationStackFrame(); - if (frame_id != instrumentation_frame.frame_id_) { - LOG(ERROR) << "Expected frame_id=" << frame_id << " but found " << instrumentation_frame.frame_id_; - StackVisitor::DescribeStack(self); - } - Runtime* runtime = Runtime::Current(); - if (runtime->IsMethodTracingActive()) { - Trace* trace = runtime->GetInstrumentation()->GetTrace(); - trace->LogMethodTraceEvent(self, instrumentation_frame.method_, Trace::kMethodTraceExit); - } - if (self->ReadFlag(kEnterInterpreter)) { - return static_cast<uint64_t>(GetDeoptimizationEntryPoint()) | - (static_cast<uint64_t>(instrumentation_frame.return_pc_) << 32); - } else { - return instrumentation_frame.return_pc_; - } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + uint64_t return_or_deoptimize_pc = instrumentation->PopInstrumentationStackFrame(self, return_pc, + gpr_result, + fpr_result); + self->VerifyStack(); + return return_or_deoptimize_pc; } } // namespace art diff --git a/src/oat/runtime/support_jni.cc b/src/oat/runtime/support_jni.cc index ee19d4e..bc9cc45 100644 --- a/src/oat/runtime/support_jni.cc +++ b/src/oat/runtime/support_jni.cc @@ -33,7 +33,7 @@ extern void* FindNativeMethod(Thread* self) { DCHECK(Thread::Current() == self); ScopedObjectAccess soa(self); - mirror::AbstractMethod* method = self->GetCurrentMethod(); + mirror::AbstractMethod* method = self->GetCurrentMethod(NULL); DCHECK(method != NULL); // Lookup symbol address for method, on failure we'll return NULL with an @@ -139,7 +139,7 @@ extern "C" const void* artWorkAroundAppJniBugs(Thread* self, intptr_t* sp) // | unused | // | unused | // | unused | <- sp - mirror::AbstractMethod* jni_method = self->GetCurrentMethod(); + mirror::AbstractMethod* jni_method = self->GetCurrentMethod(NULL); DCHECK(jni_method->IsNative()) << PrettyMethod(jni_method); intptr_t* arg_ptr = sp + 4; // pointer to r1 on stack // Fix up this/jclass argument diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc index 0cb3fe4..0e00dfd 100644 --- a/src/oat/runtime/support_stubs.cc +++ b/src/oat/runtime/support_stubs.cc @@ -242,7 +242,7 @@ const void* UnresolvedDirectMethodTrampolineFromCode(mirror::AbstractMethod* cal // go into deliver exception with the pending exception in r0 CHECK(thread->IsExceptionPending()); code = reinterpret_cast<void*>(art_quick_deliver_exception_from_code); - regs[0] = reinterpret_cast<uintptr_t>(thread->GetException()); + regs[0] = reinterpret_cast<uintptr_t>(thread->GetException(NULL)); thread->ClearException(); } else { // Expect class to at least be initializing. @@ -350,13 +350,14 @@ const void* UnresolvedDirectMethodTrampolineFromCode(mirror::AbstractMethod* cal #if !defined(ART_USE_PORTABLE_COMPILER) // Called by the AbstractMethodError. Called by stub code. -extern void ThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* thread, +extern void ThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); - thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;", - "abstract method \"%s\"", PrettyMethod(method).c_str()); - thread->QuickDeliverException(); + FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;", + "abstract method \"%s\"", PrettyMethod(method).c_str()); + self->QuickDeliverException(); } #else // ART_USE_PORTABLE_COMPILER extern void ThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* thread, diff --git a/src/oat/runtime/support_throw.cc b/src/oat/runtime/support_throw.cc index 80ba118..b8c68a5 100644 --- a/src/oat/runtime/support_throw.cc +++ b/src/oat/runtime/support_throw.cc @@ -23,14 +23,6 @@ namespace art { -// Used to implement MOVE_EXCEPTION. -extern "C" void* GetAndClearException(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(self->IsExceptionPending()); - mirror::Throwable* exception = self->GetException(); - self->ClearException(); - return exception; -} - // Deliver an exception that's pending on thread helping set up a callee save frame on the way. extern "C" void artDeliverPendingExceptionFromCode(Thread* thread, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -39,7 +31,7 @@ extern "C" void artDeliverPendingExceptionFromCode(Thread* thread, mirror::Abstr } // Called by generated call to throw an exception. -extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* thread, +extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { /* @@ -49,9 +41,15 @@ extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread * and threw a NPE if NULL. This routine responsible for setting * exception_ in thread and delivering the exception. */ - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); - thread->DeliverException(exception); - thread->QuickDeliverException(); + 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(); } // Called by generated call to throw a NPE exception. @@ -59,29 +57,27 @@ extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - uint32_t dex_pc; - mirror::AbstractMethod* throw_method = self->GetCurrentMethod(&dex_pc); - ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + ThrowNullPointerExceptionFromDexPC(throw_location); self->QuickDeliverException(); } // Called by generated call to throw an arithmetic divide by zero exception. -extern "C" void artThrowDivZeroFromCode(Thread* thread, +extern "C" void artThrowDivZeroFromCode(Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); - thread->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero"); - thread->QuickDeliverException(); + FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ThrowArithmeticExceptionDivideByZero(self); + self->QuickDeliverException(); } // Called by generated call to throw an array index out of bounds exception. -extern "C" void artThrowArrayBoundsFromCode(int index, int limit, Thread* thread, +extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); - thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", - "length=%d; index=%d", limit, index); - thread->QuickDeliverException(); + FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ThrowArrayIndexOutOfBoundsException(index, length); + self->QuickDeliverException(); } extern "C" void artThrowStackOverflowFromCode(Thread* self, mirror::AbstractMethod** sp) @@ -95,8 +91,7 @@ extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self, mirror::AbstractMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - mirror::AbstractMethod* method = self->GetCurrentMethod(); - ThrowNoSuchMethodError(method_idx, method); + ThrowNoSuchMethodError(method_idx); self->QuickDeliverException(); } diff --git a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc index a7c518a..708e04e 100644 --- a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc +++ b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc @@ -33,10 +33,6 @@ extern "C" uint32_t art_quick_is_assignable_from_code(const mirror::Class* klass extern "C" void art_quick_can_put_array_element_from_code(void*, void*); extern "C" void art_quick_check_cast_from_code(void*, void*); -// Debug entrypoints. -extern void DebugMe(mirror::AbstractMethod* method, uint32_t info); -extern "C" void art_quick_update_debugger(void*, void*, int32_t, void*); - // DexCache entrypoints. extern "C" void* art_quick_initialize_static_storage_from_code(uint32_t, void*); extern "C" void* art_quick_initialize_type_from_code(uint32_t, void*); @@ -130,10 +126,6 @@ void InitEntryPoints(EntryPoints* points) { points->pCanPutArrayElementFromCode = art_quick_can_put_array_element_from_code; points->pCheckCastFromCode = art_quick_check_cast_from_code; - // Debug - points->pDebugMe = DebugMe; - points->pUpdateDebuggerFromCode = NULL; // Controlled by SetDebuggerUpdatesEnabled. - // DexCache points->pInitializeStaticStorage = art_quick_initialize_static_storage_from_code; points->pInitializeTypeAndVerifyAccessFromCode = art_quick_initialize_type_and_verify_access_from_code; @@ -220,10 +212,6 @@ void InitEntryPoints(EntryPoints* points) { points->pThrowStackOverflowFromCode = art_quick_throw_stack_overflow_from_code; }; -void ChangeDebuggerEntryPoint(EntryPoints* points, bool enabled) { - points->pUpdateDebuggerFromCode = (enabled ? art_quick_update_debugger : NULL); -} - uintptr_t GetInstrumentationExitPc() { return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code); } diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S index 4b4689f..4900b84 100644 --- a/src/oat/runtime/x86/runtime_support_x86.S +++ b/src/oat/runtime/x86/runtime_support_x86.S @@ -512,26 +512,6 @@ TWO_ARG_DOWNCALL art_quick_initialize_static_storage_from_code, artInitializeSta TWO_ARG_DOWNCALL art_quick_initialize_type_from_code, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access_from_code, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO - /* - * On entry, eax and ecx must be preserved, edx is dex PC - */ -DEFINE_FUNCTION art_quick_update_debugger - mov %eax, %ebx // stash away eax so that it's saved as if it were an argument - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - subl LITERAL(4), %esp // alignment padding - .cfi_adjust_cfa_offset 4 - PUSH esp // pass arg2 (sp) - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - .cfi_adjust_cfa_offset 4 - PUSH edx // pass arg0 (dex pc) - call SYMBOL(artUpdateDebuggerFromCode) // artUpdateDebuggerFromCode(int32_t, Thread*, Method**) - addl LITERAL(16), %esp // pop arguments - .cfi_adjust_cfa_offset -16 - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME - mov %ebx, %eax // restore original eax - ret -END_FUNCTION art_quick_update_debugger - ONE_ARG_DOWNCALL art_quick_lock_object_from_code, artLockObjectFromCode, ret ONE_ARG_DOWNCALL art_quick_unlock_object_from_code, artUnlockObjectFromCode, RETURN_IF_EAX_ZERO @@ -879,6 +859,7 @@ DEFINE_FUNCTION art_quick_set_obj_static_from_code mov %esp, %ebx // remember SP mov 32(%esp), %edx // get referrer subl LITERAL(12), %esp // alignment padding + .cfi_adjust_cfa_offset 12 PUSH ebx // pass SP pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() .cfi_adjust_cfa_offset 4 @@ -972,7 +953,7 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler END_FUNCTION art_quick_proxy_invoke_handler DEFINE_FUNCTION art_quick_interpreter_entry - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save frame and Method* + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save frame mov %esp, %edx // remember SP PUSH eax // alignment padding PUSH edx // pass SP @@ -992,71 +973,84 @@ END_FUNCTION art_quick_interpreter_entry * Routine that intercepts method calls and returns. */ DEFINE_FUNCTION art_quick_instrumentation_entry_from_code - xchgl %eax, (%esp) // place LR in eax, save eax - PUSH ecx // save ecx - PUSH edx // save edx - PUSH ebx // save ebx - lea 16(%esp), %edx // remember bottom of caller's frame - PUSH eax // pass LR - PUSH edx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + movl %esp, %edx // Save SP. + PUSH eax // Save eax which will be clobbered by the callee-save method. + subl LITERAL(8), %esp // Align stack. + .cfi_adjust_cfa_offset 8 + pushl 40(%esp) // Pass LR. .cfi_adjust_cfa_offset 4 - pushl 24(%esp) // pass Method* + PUSH edx // Pass SP. + pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current(). .cfi_adjust_cfa_offset 4 - call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Thread*, SP, LR) - addl LITERAL(16), %esp // pop arguments - POP ebx // restore ebx - POP edx // restore edx - movl (%esp), %ecx // restore ecx (without popping) - movl %eax, (%esp) // place method's code pointer on stack - movl 4(%esp), %eax // restore eax (without popping) - movl LITERAL(SYMBOL(art_quick_instrumentation_exit_from_code)), 4(%esp) - // place instrumentation exit as return pc - ret // call method (and pop) + PUSH ecx // Pass receiver. + PUSH eax // Pass Method*. + call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR) + addl LITERAL(28), %esp // Pop arguments upto saved Method*. + movl 28(%esp), %edi // Restore edi. + movl %eax, 28(%esp) // Place code* over edi, just under return pc. + movl LITERAL(SYMBOL(art_quick_instrumentation_exit_from_code)), 32(%esp) + // Place instrumentation exit as return pc. + movl (%esp), %eax // Restore eax. + movl 8(%esp), %ecx // Restore ecx. + movl 12(%esp), %edx // Restore edx. + movl 16(%esp), %ebx // Restore ebx. + movl 20(%esp), %ebp // Restore ebp. + movl 24(%esp), %esi // Restore esi. + addl LITERAL(28), %esp // Wind stack back upto code*. + ret // Call method (and pop). END_FUNCTION art_quick_instrumentation_entry_from_code + DEFINE_FUNCTION art_quick_instrumentation_exit_from_code - mov %esp, %ecx // remember bottom of caller's frame - PUSH edx // save return value - PUSH eax // save other half of return value - PUSH ecx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current + pushl LITERAL(0) // Push a fake return PC as there will be none on the stack. + SETUP_REF_ONLY_CALLEE_SAVE_FRAME + mov %esp, %ecx // Remember SP + subl LITERAL(8), %esp // Save float return value. + .cfi_adjust_cfa_offset 8 + movd %xmm0, (%esp) + PUSH edx // Save gpr return value. + PUSH eax + subl LITERAL(8), %esp // Align stack + movd %xmm0, (%esp) + subl LITERAL(8), %esp // Pass float return value. + .cfi_adjust_cfa_offset 8 + movd %xmm0, (%esp) + PUSH edx // Pass gpr return value. + PUSH eax + PUSH ecx // Pass SP. + pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current. .cfi_adjust_cfa_offset 4 - call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP) - mov %eax, %ecx // move returned link register - // TODO: Set link register for deopt - addl LITERAL(8), %esp // pop arguments + call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP, gpr_result, fpr_result) + mov %eax, %ecx // Move returned link register. + addl LITERAL(32), %esp // Pop arguments. + .cfi_adjust_cfa_offset -32 + movl %edx, %ebx // Move returned link register for deopt + // (ebx is pretending to be our LR). + POP eax // Restore gpr return value. + POP edx + movd (%esp), %xmm0 // Restore fpr return value. + addl LITERAL(8), %esp .cfi_adjust_cfa_offset -8 - POP eax // restore return value - POP edx // restore other half of return value - jmp *%ecx // return + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + addl LITERAL(4), %esp // Remove fake return pc. + jmp *%ecx // Return. END_FUNCTION art_quick_instrumentation_exit_from_code /* - * The thread's enter interpreter flag is set and so we should transition to the interpreter - * rather than allow execution to continue in the frame below. There may be live results in - * registers depending on how complete the operation is when we safepoint - for example, a - * set operation may have completed while a get operation needs writing back into the vregs. + * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization + * will long jump to the upcall with a special exception of -1. */ DEFINE_FUNCTION art_quick_deoptimize + pushl %ebx // Fake that we were called. SETUP_REF_ONLY_CALLEE_SAVE_FRAME - PUSH esp // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + mov %esp, %ecx // Remember SP. + subl LITERAL(8), %esp // Align stack. + .cfi_adjust_cfa_offset 8 + PUSH ecx // Pass SP. + pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current(). .cfi_adjust_cfa_offset 4 - PUSH edx // push half of return value - PUSH eax // push other half of return value - call SYMBOL(artDeoptimize) // artDeoptimize(return value, Thread*, SP) - // Returns caller method's frame size. - addl LITERAL(16), %esp // pop arguments - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - testl %eax, %eax // Was the caller an upcall? - jz 1f // Return if caller was upcall. - lea (%esp, %eax), %edx // edx == bottom of caller's frame. - mov %edx, %esp // Remove frame. - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - call SYMBOL(artEnterInterpreterFromDeoptimize) // Enter interpreter, callee-save ends stack fragment. - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME -1: - ret // Return to caller. + call SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP) + int3 // Unreachable. END_FUNCTION art_quick_deoptimize /* diff --git a/src/reflection.cc b/src/reflection.cc index 73a8a53..467575c 100644 --- a/src/reflection.cc +++ b/src/reflection.cc @@ -17,6 +17,7 @@ #include "reflection.h" #include "class_linker.h" +#include "common_throws.h" #include "dex_file-inl.h" #include "invoke_arg_array_builder.h" #include "jni_internal.h" @@ -64,9 +65,9 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject uint32_t classes_size = classes == NULL ? 0 : classes->Size(); uint32_t arg_count = (objects != NULL) ? objects->GetLength() : 0; if (arg_count != classes_size) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "wrong number of arguments; expected %d, got %d", - classes_size, arg_count); + ThrowIllegalArgumentException(NULL, + StringPrintf("Wrong number of arguments; expected %d, got %d", + classes_size, arg_count).c_str()); return NULL; } @@ -103,23 +104,23 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject } bool VerifyObjectInClass(mirror::Object* o, mirror::Class* c) { - const char* exception = NULL; if (o == NULL) { - exception = "Ljava/lang/NullPointerException;"; + ThrowNullPointerException(NULL, "null receiver"); + return false; } else if (!o->InstanceOf(c)) { - exception = "Ljava/lang/IllegalArgumentException;"; - } - if (exception != NULL) { std::string expected_class_name(PrettyDescriptor(c)); std::string actual_class_name(PrettyTypeOf(o)); - Thread::Current()->ThrowNewExceptionF(exception, "expected receiver of type %s, but got %s", - expected_class_name.c_str(), actual_class_name.c_str()); + ThrowIllegalArgumentException(NULL, + StringPrintf("Expected receiver of type %s, but got %s", + expected_class_name.c_str(), + actual_class_name.c_str()).c_str()); return false; } return true; } -bool ConvertPrimitiveValue(Primitive::Type srcType, Primitive::Type dstType, +bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result, + Primitive::Type srcType, Primitive::Type dstType, const JValue& src, JValue& dst) { CHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot); switch (dstType) { @@ -196,10 +197,18 @@ bool ConvertPrimitiveValue(Primitive::Type srcType, Primitive::Type dstType, default: break; } - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "invalid primitive conversion from %s to %s", - PrettyDescriptor(srcType).c_str(), - PrettyDescriptor(dstType).c_str()); + if (!unbox_for_result) { + ThrowIllegalArgumentException(throw_location, + StringPrintf("Invalid primitive conversion from %s to %s", + PrettyDescriptor(srcType).c_str(), + PrettyDescriptor(dstType).c_str()).c_str()); + } else { + ThrowClassCastException(throw_location, + StringPrintf("Couldn't convert result of type %s to %s", + PrettyDescriptor(srcType).c_str(), + PrettyDescriptor(dstType).c_str() + ).c_str()); + } return false; } @@ -271,32 +280,48 @@ static std::string UnboxingFailureKind(mirror::AbstractMethod* m, int index, mir return "result"; } -static bool UnboxPrimitive(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value, +static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object* o, + mirror::Class* dst_class, JValue& unboxed_value, mirror::AbstractMethod* m, int index, mirror::Field* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool unbox_for_result = (f == NULL) && (index == -1); if (!dst_class->IsPrimitive()) { - if (o != NULL && !o->InstanceOf(dst_class)) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "%s has type %s, got %s", - UnboxingFailureKind(m, index, f).c_str(), - PrettyDescriptor(dst_class).c_str(), - PrettyTypeOf(o).c_str()); + if (UNLIKELY(o != NULL && !o->InstanceOf(dst_class))) { + if (!unbox_for_result) { + ThrowIllegalArgumentException(throw_location, + StringPrintf("%s has type %s, got %s", + UnboxingFailureKind(m, index, f).c_str(), + PrettyDescriptor(dst_class).c_str(), + PrettyTypeOf(o).c_str()).c_str()); + } else { + ThrowClassCastException(throw_location, + StringPrintf("Couldn't convert result of type %s to %s", + PrettyTypeOf(o).c_str(), + PrettyDescriptor(dst_class).c_str() + ).c_str()); + } return false; } unboxed_value.SetL(o); return true; - } else if (dst_class->GetPrimitiveType() == Primitive::kPrimVoid) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "can't unbox %s to void", - UnboxingFailureKind(m, index, f).c_str()); + } + if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) { + ThrowIllegalArgumentException(throw_location, + StringPrintf("Can't unbox %s to void", + UnboxingFailureKind(m, index, f).c_str()).c_str()); return false; } - - if (o == NULL) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "%s has type %s, got null", - UnboxingFailureKind(m, index, f).c_str(), - PrettyDescriptor(dst_class).c_str()); + if (UNLIKELY(o == NULL)) { + if (!unbox_for_result) { + ThrowIllegalArgumentException(throw_location, + StringPrintf("%s has type %s, got null", + UnboxingFailureKind(m, index, f).c_str(), + PrettyDescriptor(dst_class).c_str()).c_str()); + } else { + ThrowNullPointerException(throw_location, + StringPrintf("Expected to unbox a '%s' primitive type but was returned null", + PrettyDescriptor(dst_class).c_str()).c_str()); + } return false; } @@ -330,32 +355,35 @@ static bool UnboxPrimitive(mirror::Object* o, mirror::Class* dst_class, JValue& src_class = class_linker->FindPrimitiveClass('S'); boxed_value.SetS(primitive_field->GetShort(o)); } else { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", - "%s has type %s, got %s", - UnboxingFailureKind(m, index, f).c_str(), - PrettyDescriptor(dst_class).c_str(), - PrettyDescriptor(src_descriptor.c_str()).c_str()); + ThrowIllegalArgumentException(throw_location, + StringPrintf("%s has type %s, got %s", + UnboxingFailureKind(m, index, f).c_str(), + PrettyDescriptor(dst_class).c_str(), + PrettyDescriptor(src_descriptor.c_str()).c_str() + ).c_str()); return false; } - return ConvertPrimitiveValue(src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(), + return ConvertPrimitiveValue(throw_location, unbox_for_result, + src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(), boxed_value, unboxed_value); } bool UnboxPrimitiveForArgument(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value, mirror::AbstractMethod* m, size_t index) { CHECK(m != NULL); - return UnboxPrimitive(o, dst_class, unboxed_value, m, index, NULL); + return UnboxPrimitive(NULL, o, dst_class, unboxed_value, m, index, NULL); } bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value, mirror::Field* f) { CHECK(f != NULL); - return UnboxPrimitive(o, dst_class, unboxed_value, NULL, -1, f); + return UnboxPrimitive(NULL, o, dst_class, unboxed_value, NULL, -1, f); } -bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value) { - return UnboxPrimitive(o, dst_class, unboxed_value, NULL, -1, NULL); +bool UnboxPrimitiveForResult(const ThrowLocation& throw_location, mirror::Object* o, + mirror::Class* dst_class, JValue& unboxed_value) { + return UnboxPrimitive(&throw_location, o, dst_class, unboxed_value, NULL, -1, NULL); } } // namespace art diff --git a/src/reflection.h b/src/reflection.h index 8f32243..e9f4e08 100644 --- a/src/reflection.h +++ b/src/reflection.h @@ -29,6 +29,7 @@ class Object; } // namespace mirror union JValue; class ScopedObjectAccess; +class ThrowLocation; mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -38,11 +39,13 @@ bool UnboxPrimitiveForArgument(mirror::Object* o, mirror::Class* dst_class, JVal bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value, mirror::Field* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue& unboxed_value) +bool UnboxPrimitiveForResult(const ThrowLocation& throw_location, mirror::Object* o, + mirror::Class* dst_class, JValue& unboxed_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -bool ConvertPrimitiveValue(Primitive::Type src_class, Primitive::Type dst_class, const JValue& src, - JValue& dst) +bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result, + Primitive::Type src_class, Primitive::Type dst_class, + const JValue& src, JValue& dst) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); jobject InvokeMethod(const ScopedObjectAccess& soa, jobject method, jobject receiver, jobject args) diff --git a/src/runtime.cc b/src/runtime.cc index 3e9cd8e..23a7309 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -43,6 +43,7 @@ #include "jni_internal.h" #include "mirror/abstract_method-inl.h" #include "mirror/array.h" +#include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field.h" #include "mirror/field-inl.h" @@ -97,7 +98,7 @@ Runtime::Runtime() stats_enabled_(false), method_trace_(0), method_trace_file_size_(0), - instrumentation_(NULL), + instrumentation_(), use_compile_time_class_path_(false), main_thread_group_(NULL), system_thread_group_(NULL) { @@ -119,11 +120,7 @@ Runtime::~Runtime() { } shutting_down_ = true; } - - if (IsMethodTracingActive()) { - Trace::Shutdown(); - } - delete instrumentation_; + Trace::Shutdown(); // Make sure to let the GC complete if it is running. heap_->WaitForConcurrentGcToComplete(self); @@ -171,8 +168,11 @@ struct AbortState { os << "Aborting thread:\n"; self->Dump(os); if (self->IsExceptionPending()) { - os << "Pending " << PrettyTypeOf(self->GetException()) << " on thread:\n" - << self->GetException()->Dump(); + ThrowLocation throw_location; + mirror::Throwable* exception = self->GetException(&throw_location); + os << "Pending exception " << PrettyTypeOf(exception) + << " thrown by '" << throw_location.Dump() << "\n" + << exception->Dump(); } } DumpAllThreads(os, self); @@ -652,9 +652,9 @@ bool Runtime::Start() { // Pre-allocate an OutOfMemoryError for the double-OOME case. Thread* self = Thread::Current(); - self->ThrowNewException("Ljava/lang/OutOfMemoryError;", + self->ThrowNewException(ThrowLocation(), "Ljava/lang/OutOfMemoryError;", "OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack available"); - pre_allocated_OutOfMemoryError_ = self->GetException(); + pre_allocated_OutOfMemoryError_ = self->GetException(NULL); self->ClearException(); // Restore main thread state to kNative as expected by native code. @@ -794,7 +794,6 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { is_compiler_ = options->is_compiler_; is_zygote_ = options->is_zygote_; - interpreter_only_ = options->interpreter_only_; is_concurrent_gc_enabled_ = options->is_concurrent_gc_enabled_; vfprintf_ = options->hook_vfprintf_; @@ -809,6 +808,10 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { intern_table_ = new InternTable; + if (options->interpreter_only_) { + GetInstrumentation()->ForceInterpretOnly(); + } + heap_ = new Heap(options->heap_initial_size_, options->heap_growth_limit_, options->heap_min_free_, @@ -1196,28 +1199,6 @@ void Runtime::SetCalleeSaveMethod(mirror::AbstractMethod* method, CalleeSaveType callee_save_methods_[type] = method; } -void Runtime::EnableMethodTracing(Trace* trace) { - CHECK(!IsMethodTracingActive()); - if (instrumentation_ == NULL) { - instrumentation_ = new Instrumentation(); - } - instrumentation_->SetTrace(trace); -} - -void Runtime::DisableMethodTracing() { - CHECK(IsMethodTracingActive()); - instrumentation_->RemoveTrace(); -} - -bool Runtime::IsMethodTracingActive() const { - return instrumentation_ != NULL && instrumentation_->GetTrace() != NULL; -} - -Instrumentation* Runtime::GetInstrumentation() const { - CHECK(IsMethodTracingActive()); - return instrumentation_; -} - const std::vector<const DexFile*>& Runtime::GetCompileTimeClassPath(jobject class_loader) { if (class_loader == NULL) { return GetClassLinker()->GetBootClassPath(); diff --git a/src/runtime.h b/src/runtime.h index f8788ad..67c8e36 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -30,6 +30,7 @@ #include "globals.h" #include "heap.h" #include "instruction_set.h" +#include "instrumentation.h" #include "jobject_comparator.h" #include "locks.h" #include "root_visitor.h" @@ -49,7 +50,6 @@ class Throwable; class ClassLinker; class DexFile; class Heap; -class Instrumentation; class InternTable; struct JavaVMExt; class MonitorList; @@ -112,10 +112,6 @@ class Runtime { return is_zygote_; } - bool InterpreterOnly() const { - return interpreter_only_; - } - bool IsConcurrentGcEnabled() const { return is_concurrent_gc_enabled_; } @@ -234,8 +230,7 @@ class Runtime { void DirtyRoots(); // Visit all the roots. - void VisitRoots(RootVisitor* visitor, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Visit all of the roots we can do safely do concurrently. void VisitConcurrentRoots(RootVisitor* visitor, void* arg); @@ -350,10 +345,9 @@ class Runtime { bool InitZygote(); void DidForkFromZygote(); - void EnableMethodTracing(Trace* trace); - void DisableMethodTracing(); - bool IsMethodTracingActive() const; - Instrumentation* GetInstrumentation() const; + instrumentation::Instrumentation* GetInstrumentation() { + return &instrumentation_; + } bool UseCompileTimeClassPath() const { return use_compile_time_class_path_; @@ -383,7 +377,6 @@ class Runtime { bool is_compiler_; bool is_zygote_; - bool interpreter_only_; bool is_concurrent_gc_enabled_; // The host prefix is used during cross compilation. It is removed @@ -466,7 +459,7 @@ class Runtime { bool method_trace_; std::string method_trace_file_; size_t method_trace_file_size_; - Instrumentation* instrumentation_; + instrumentation::Instrumentation instrumentation_; typedef SafeMap<jobject, std::vector<const DexFile*>, JobjectComparator> CompileTimeClassPaths; CompileTimeClassPaths compile_time_class_paths_; diff --git a/src/runtime_support.cc b/src/runtime_support.cc index 5b2c58c..b601f8c 100644 --- a/src/runtime_support.cc +++ b/src/runtime_support.cc @@ -101,16 +101,16 @@ int32_t art_f2i(float f) { namespace art { // Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::AbstractMethod* method, +mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::AbstractMethod* referrer, int32_t component_count, Thread* self, bool access_check) { if (UNLIKELY(component_count < 0)) { - self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count); + ThrowNegativeArraySizeException(component_count); return NULL; // Failure } - mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx); + mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->Get(type_idx); if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve - klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); + klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer); if (klass == NULL) { // Error DCHECK(self->IsExceptionPending()); return NULL; // Failure @@ -118,20 +118,21 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::AbstractMet } if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) { if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) { - self->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "Bad filled array request for type %s", - PrettyDescriptor(klass).c_str()); + ThrowRuntimeException("Bad filled array request for type %s", + PrettyDescriptor(klass).c_str()); } else { - self->ThrowNewExceptionF("Ljava/lang/InternalError;", + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK(throw_location.GetMethod() == referrer); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/InternalError;", "Found type %s; filled-new-array not implemented for anything but \'int\'", PrettyDescriptor(klass).c_str()); } return NULL; // Failure } else { if (access_check) { - mirror::Class* referrer = method->GetDeclaringClass(); - if (UNLIKELY(!referrer->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer, klass); + mirror::Class* referrer_klass = referrer->GetDeclaringClass(); + if (UNLIKELY(!referrer_klass->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referrer_klass, klass); return NULL; // Failure } } @@ -194,7 +195,9 @@ mirror::Field* FindFieldFromCode(uint32_t field_idx, const mirror::AbstractMetho FieldHelper fh(resolved_field); if (UNLIKELY(fh.IsPrimitiveType() != is_primitive || fh.FieldSize() != expected_size)) { - self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;", + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK(throw_location.GetMethod() == referrer); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", "Attempted read of %zd-bit %s on field '%s'", expected_size * (32 / sizeof(int32_t)), is_primitive ? "primitive" : "non-primitive", @@ -232,7 +235,9 @@ mirror::AbstractMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* } else if (UNLIKELY(this_object == NULL && type != kStatic)) { // Maintain interpreter-like semantics where NullPointerException is thrown // after potential NoSuchMethodError from class linker. - ThrowNullPointerExceptionForMethodAccess(referrer, method_idx, type); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK(referrer == throw_location.GetMethod()); + ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type); return NULL; // Failure. } else { if (!access_check) { @@ -320,7 +325,7 @@ mirror::AbstractMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* // Behavior to agree with that of the verifier. MethodHelper mh(resolved_method); ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(), - mh.GetSignature(), referrer); + mh.GetSignature()); return NULL; // Failure. } } @@ -363,10 +368,12 @@ mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, const mirror::AbstractM void ThrowStackOverflowError(Thread* self) { CHECK(!self->IsHandlingStackOverflow()) << "Recursive stack overflow."; - // Remove extra entry pushed onto second stack during method tracing. - if (Runtime::Current()->IsMethodTracingActive()) { - InstrumentationMethodUnwindFromCode(self); + + if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + // Remove extra entry pushed onto second stack during method tracing. + Runtime::Current()->GetInstrumentation()->PopMethodForUnwind(self, false); } + self->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute. JNIEnvExt* env = self->GetJniEnv(); std::string msg("stack size "); @@ -430,33 +437,36 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char invocation_args); // Unbox result and handle error conditions. - if (!soa.Self()->IsExceptionPending()) { - if (shorty[0] == 'V' || result == NULL) { + if (LIKELY(!soa.Self()->IsExceptionPending())) { + if (shorty[0] == 'V' || (shorty[0] == 'L' && result == NULL)) { // Do nothing. return zero; } else { - JValue result_unboxed; - MethodHelper mh(soa.Decode<mirror::AbstractMethod*>(interface_method_jobj)); - mirror::Class* result_type = mh.GetReturnType(); mirror::Object* result_ref = soa.Decode<mirror::Object*>(result); - bool unboxed_okay = UnboxPrimitiveForResult(result_ref, result_type, result_unboxed); - if (!unboxed_okay) { - // UnboxPrimitiveForResult creates an IllegalArgumentException. Discard and create a - // meaningful ClassCastException. + mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj); + mirror::AbstractMethod* interface_method = + soa.Decode<mirror::AbstractMethod*>(interface_method_jobj); + mirror::Class* result_type = MethodHelper(interface_method).GetReturnType(); + mirror::AbstractMethod* proxy_method; + if (interface_method->GetDeclaringClass()->IsInterface()) { + proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface(interface_method); + } else { + // Proxy dispatch to a method defined in Object. + DCHECK(interface_method->GetDeclaringClass()->IsObjectClass()); + proxy_method = interface_method; + } + ThrowLocation throw_location(rcvr, proxy_method, -1); + JValue result_unboxed; + if (!UnboxPrimitiveForResult(throw_location, result_ref, result_type, result_unboxed)) { DCHECK(soa.Self()->IsExceptionPending()); - soa.Self()->ClearException(); - soa.Self()->ThrowNewException("Ljava/lang/ClassCastException;", - StringPrintf("Couldn't convert result of type %s to %s", - PrettyTypeOf(result_ref).c_str(), - PrettyDescriptor(result_type).c_str() - ).c_str()); + return zero; } return result_unboxed; } } else { // In the case of checked exceptions that aren't declared, the exception must be wrapped by // a UndeclaredThrowableException. - mirror::Throwable* exception = soa.Self()->GetException(); + mirror::Throwable* exception = soa.Self()->GetException(NULL); if (exception->IsCheckedException()) { mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj); mirror::SynthesizedProxyClass* proxy_class = @@ -482,7 +492,9 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char declares_exception = declared_exception->IsAssignableFrom(exception_class); } if (!declares_exception) { - soa.Self()->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", + ThrowLocation throw_location(rcvr, proxy_method, -1); + soa.Self()->ThrowNewWrappedException(throw_location, + "Ljava/lang/reflect/UndeclaredThrowableException;", NULL); } } diff --git a/src/runtime_support.h b/src/runtime_support.h index 89026c1..c7eb957 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -68,7 +68,8 @@ static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::Abs } if (access_check) { if (UNLIKELY(!klass->IsInstantiable())) { - self->ThrowNewException("Ljava/lang/InstantiationError;", + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;", PrettyDescriptor(klass).c_str()); return NULL; // Failure } @@ -95,7 +96,7 @@ static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::Abstr Thread* self, bool access_check) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(component_count < 0)) { - self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count); + ThrowNegativeArraySizeException(component_count); return NULL; // Failure } mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx); @@ -257,8 +258,9 @@ static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) UNLOCK_FUNCTION(monitor_lock_) { // Save any pending exception over monitor exit call. mirror::Throwable* saved_exception = NULL; + ThrowLocation saved_throw_location; if (UNLIKELY(self->IsExceptionPending())) { - saved_exception = self->GetException(); + saved_exception = self->GetException(&saved_throw_location); self->ClearException(); } // Decode locked object and unlock, before popping local references. @@ -267,11 +269,11 @@ static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) LOG(FATAL) << "Synchronized JNI code returning with an exception:\n" << saved_exception->Dump() << "\nEncountered second exception during implicit MonitorExit:\n" - << self->GetException()->Dump(); + << self->GetException(NULL)->Dump(); } // Restore pending exception. if (saved_exception != NULL) { - self->SetException(saved_exception); + self->SetException(saved_throw_location, saved_exception); } } @@ -280,14 +282,12 @@ static inline void CheckReferenceResult(mirror::Object* o, Thread* self) if (o == NULL) { return; } + mirror::AbstractMethod* m = self->GetCurrentMethod(NULL); if (o == kInvalidIndirectRefObject) { - JniAbortF(NULL, "invalid reference returned from %s", - PrettyMethod(self->GetCurrentMethod()).c_str()); + JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str()); } // Make sure that the result is an instance of the type this method was expected to return. - mirror::AbstractMethod* m = self->GetCurrentMethod(); - MethodHelper mh(m); - mirror::Class* return_type = mh.GetReturnType(); + mirror::Class* return_type = MethodHelper(m).GetReturnType(); if (!o->InstanceOf(return_type)) { JniAbortF(NULL, "attempt to return an instance of %s from %s", diff --git a/src/stack.cc b/src/stack.cc index 66051f2..8690a36 100644 --- a/src/stack.cc +++ b/src/stack.cc @@ -24,9 +24,28 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "thread_list.h" +#include "throw_location.h" namespace art { +mirror::Object* ShadowFrame::GetThisObject() const { + mirror::AbstractMethod* m = GetMethod(); + if (m->IsStatic()) { + return NULL; + } else if (m->IsNative()) { + return GetVRegReference(0); + } else { + const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); + CHECK(code_item != NULL) << PrettyMethod(m); + uint16_t reg = code_item->registers_size_ - code_item->ins_size_; + return GetVRegReference(reg); + } +} + +ThrowLocation ShadowFrame::GetCurrentLocationForThrow() const { + return ThrowLocation(GetThisObject(), GetMethod(), GetDexPC()); +} + size_t ManagedStack::NumJniShadowFrameReferences() const { size_t count = 0; for (const ManagedStack* current_fragment = this; current_fragment != NULL; @@ -59,7 +78,7 @@ StackVisitor::StackVisitor(Thread* thread, Context* context) : thread_(thread), cur_shadow_frame_(NULL), cur_quick_frame_(NULL), cur_quick_frame_pc_(0), num_frames_(0), cur_depth_(0), context_(context) { - DCHECK(thread == Thread::Current() || thread->IsSuspended()); + DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread; } uint32_t StackVisitor::GetDexPc() const { @@ -72,6 +91,33 @@ uint32_t StackVisitor::GetDexPc() const { } } +mirror::Object* StackVisitor::GetThisObject() const { + mirror::AbstractMethod* m = GetMethod(); + if (m->IsStatic()) { + return NULL; + } else if (m->IsNative()) { + if (cur_quick_frame_ != NULL) { + StackIndirectReferenceTable* sirt = + reinterpret_cast<StackIndirectReferenceTable*>( + reinterpret_cast<char*>(cur_quick_frame_) + + m->GetSirtOffsetInBytes()); + return sirt->GetReference(0); + } else { + return cur_shadow_frame_->GetVRegReference(0); + } + } else { + const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); + if (code_item == NULL) { + UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method" + << PrettyMethod(m); + return NULL; + } else { + uint16_t reg = code_item->registers_size_ - code_item->ins_size_; + return reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kReferenceVReg)); + } + } +} + size_t StackVisitor::GetNativePcOffset() const { DCHECK(!IsShadowFrame()); return GetMethod()->NativePcOffset(cur_quick_frame_pc_); @@ -198,7 +244,7 @@ std::string StackVisitor::DescribeLocation() const { return result; } -InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const { +instrumentation::InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const { return thread_->GetInstrumentationStack()->at(depth); } @@ -221,9 +267,8 @@ void StackVisitor::SanityCheckFrame() const { void StackVisitor::WalkStack(bool include_transitions) { DCHECK(thread_ == Thread::Current() || thread_->IsSuspended()); - const std::deque<InstrumentationStackFrame>* instrumentation_stack = - thread_->GetInstrumentationStack(); - bool method_tracing_active = instrumentation_stack != NULL; + CHECK_EQ(cur_depth_, 0U); + bool exit_stubs_installed = Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled(); uint32_t instrumentation_stack_depth = 0; for (const ManagedStack* current_fragment = thread_->GetManagedStack(); current_fragment != NULL; current_fragment = current_fragment->GetLink()) { @@ -235,6 +280,7 @@ void StackVisitor::WalkStack(bool include_transitions) { DCHECK(current_fragment->GetTopShadowFrame() == NULL); mirror::AbstractMethod* method = *cur_quick_frame_; while (method != NULL) { + DCHECK(cur_quick_frame_pc_ != GetInstrumentationExitPc()); SanityCheckFrame(); bool should_continue = VisitFrame(); if (UNLIKELY(!should_continue)) { @@ -248,16 +294,24 @@ void StackVisitor::WalkStack(bool include_transitions) { size_t return_pc_offset = method->GetReturnPcOffsetInBytes(); byte* return_pc_addr = reinterpret_cast<byte*>(cur_quick_frame_) + return_pc_offset; uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr); - if (UNLIKELY(method_tracing_active)) { + if (UNLIKELY(exit_stubs_installed)) { // While profiling, the return pc is restored from the side stack, except when walking // the stack for an exception where the side stack will be unwound in VisitFrame. - // TODO: stop using include_transitions as a proxy for is this the catch block visitor. - if (GetInstrumentationExitPc() == return_pc && !include_transitions) { - // TODO: unify trace and managed stack. - InstrumentationStackFrame instrumentation_frame = GetInstrumentationStackFrame(instrumentation_stack_depth); + if (GetInstrumentationExitPc() == return_pc) { + instrumentation::InstrumentationStackFrame instrumentation_frame = + GetInstrumentationStackFrame(instrumentation_stack_depth); instrumentation_stack_depth++; - CHECK(instrumentation_frame.method_ == GetMethod()) << "Excepted: " << PrettyMethod(method) + if (instrumentation_frame.method_ != GetMethod()) { + LOG(FATAL) << "Expected: " << PrettyMethod(instrumentation_frame.method_) << " Found: " << PrettyMethod(GetMethod()); + } + if (num_frames_ != 0) { + // Check agreement of frame Ids only if num_frames_ is computed to avoid infinite + // recursion. + CHECK(instrumentation_frame.frame_id_ == GetFrameId()) + << "Expected: " << instrumentation_frame.frame_id_ + << " Found: " << GetFrameId(); + } return_pc = instrumentation_frame.return_pc_; } } @@ -278,13 +332,16 @@ void StackVisitor::WalkStack(bool include_transitions) { cur_shadow_frame_ = cur_shadow_frame_->GetLink(); } while(cur_shadow_frame_ != NULL); } - cur_depth_++; if (include_transitions) { bool should_continue = VisitFrame(); if (!should_continue) { return; } } + cur_depth_++; + } + if (num_frames_ != 0) { + CHECK_EQ(cur_depth_, num_frames_); } } diff --git a/src/stack.h b/src/stack.h index e0cb28e..eb187b2 100644 --- a/src/stack.h +++ b/src/stack.h @@ -168,6 +168,10 @@ class ShadowFrame { return method_; } + mirror::Object* GetThisObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ThrowLocation GetCurrentLocationForThrow() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetMethod(mirror::AbstractMethod* method) { DCHECK_NE(method, static_cast<void*>(NULL)); method_ = method; @@ -368,6 +372,8 @@ class StackVisitor { uint32_t GetDexPc() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* GetThisObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t GetNativePcOffset() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uintptr_t* CalleeSaveAddress(int num, size_t frame_size) const { @@ -383,7 +389,7 @@ class StackVisitor { // Returns the height of the stack in the managed stack frames, including transitions. size_t GetFrameHeight() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetNumFrames() - cur_depth_; + return GetNumFrames() - cur_depth_ - 1; } // Returns a frame ID for JDWP use, starting from 1. @@ -503,7 +509,7 @@ class StackVisitor { private: - InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const; + instrumentation::InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const; void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/thread.cc b/src/thread.cc index 9c58b6d..2c955b1 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -61,6 +61,7 @@ #include "thread_list.h" #include "utils.h" #include "verifier/dex_gc_map.h" +#include "verifier/method_verifier.h" #include "well_known_classes.h" namespace art { @@ -92,24 +93,16 @@ void Thread::InitFunctionPointers() { InitEntryPoints(&entrypoints_); } -void Thread::SetDebuggerUpdatesEnabled(bool enabled) { -#if !defined(ART_USE_PORTABLE_COMPILER) - ChangeDebuggerEntryPoint(&entrypoints_, enabled); -#else - UNIMPLEMENTED(FATAL); -#endif +void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf) { + deoptimization_shadow_frame_ = sf; } - -void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf, const JValue& ret_val) { - CHECK(sf != NULL); - deoptimization_shadow_frame_ = sf; +void Thread::SetDeoptimizationReturnValue(const JValue& ret_val) { deoptimization_return_value_.SetJ(ret_val.GetJ()); } ShadowFrame* Thread::GetAndClearDeoptimizationShadowFrame(JValue* ret_val) { ShadowFrame* sf = deoptimization_shadow_frame_; - DCHECK(sf != NULL); deoptimization_shadow_frame_ = NULL; ret_val->SetJ(deoptimization_return_value_.GetJ()); return sf; @@ -327,9 +320,6 @@ void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) { InitFunctionPointers(); InitCardTable(); InitTid(); - if (Runtime::Current()->InterpreterOnly()) { - AtomicSetFlag(kEnterInterpreter); - } // Set pthread_self_ ahead of pthread_setspecific, that makes Thread::Current function, this // avoids pthread_self_ ever being invalid when discovered from Thread::Current(). pthread_self_ = pthread_self(); @@ -864,7 +854,7 @@ static bool ShouldShowNativeStack(const Thread* thread) { // We don't just check kNative because native methods will be in state kSuspended if they're // calling back into the VM, or kBlocked if they're blocked on a monitor, or one of the // thread-startup states if it's early enough in their life cycle (http://b/7432159). - mirror::AbstractMethod* current_method = thread->GetCurrentMethod(); + mirror::AbstractMethod* current_method = thread->GetCurrentMethod(NULL); return current_method != NULL && current_method->IsNative(); } @@ -948,7 +938,8 @@ Thread::Thread(bool daemon) throwing_OutOfMemoryError_(false), debug_suspend_count_(0), debug_invoke_req_(new DebugInvokeReq), - instrumentation_stack_(new std::deque<InstrumentationStackFrame>), + deoptimization_shadow_frame_(NULL), + instrumentation_stack_(new std::deque<instrumentation::InstrumentationStackFrame>), name_(new std::string(kThreadNameDuringStartup)), daemon_(daemon), pthread_self_(0), @@ -975,7 +966,7 @@ bool Thread::IsStillStarting() const { void Thread::AssertNoPendingException() const { if (UNLIKELY(IsExceptionPending())) { ScopedObjectAccess soa(Thread::Current()); - mirror::Throwable* exception = GetException(); + mirror::Throwable* exception = GetException(NULL); LOG(FATAL) << "No pending exception expected: " << exception->Dump(); } } @@ -1427,82 +1418,131 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job return result; } -void Thread::ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...) { +void Thread::ThrowNewExceptionF(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* fmt, ...) { va_list args; va_start(args, fmt); - ThrowNewExceptionV(exception_class_descriptor, fmt, args); + ThrowNewExceptionV(throw_location, exception_class_descriptor, + fmt, args); va_end(args); } -void Thread::ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap) { +void Thread::ThrowNewExceptionV(const ThrowLocation& throw_location, + const char* exception_class_descriptor, + const char* fmt, va_list ap) { std::string msg; StringAppendV(&msg, fmt, ap); - ThrowNewException(exception_class_descriptor, msg.c_str()); + ThrowNewException(throw_location, exception_class_descriptor, msg.c_str()); } -void Thread::ThrowNewException(const char* exception_class_descriptor, const char* msg) { +void Thread::ThrowNewException(const ThrowLocation& throw_location, const char* exception_class_descriptor, + const char* msg) { AssertNoPendingException(); // Callers should either clear or call ThrowNewWrappedException. - ThrowNewWrappedException(exception_class_descriptor, msg); -} - -void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg) { - // Convert "Ljava/lang/Exception;" into JNI-style "java/lang/Exception". - CHECK_EQ('L', exception_class_descriptor[0]); - std::string descriptor(exception_class_descriptor + 1); - CHECK_EQ(';', descriptor[descriptor.length() - 1]); - descriptor.erase(descriptor.length() - 1); - - JNIEnv* env = GetJniEnv(); - jobject cause = env->ExceptionOccurred(); - env->ExceptionClear(); + ThrowNewWrappedException(throw_location, exception_class_descriptor, msg); +} + +void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, + const char* exception_class_descriptor, + const char* msg) { + DCHECK_EQ(this, Thread::Current()); + // Ensure we don't forget arguments over object allocation. + SirtRef<mirror::Object> saved_throw_this(this, throw_location.GetThis()); + SirtRef<mirror::AbstractMethod> saved_throw_method(this, throw_location.GetMethod()); + // Ignore the cause throw location. TODO: should we report this as a re-throw? + SirtRef<mirror::Throwable> cause(this, GetException(NULL)); + ClearException(); + Runtime* runtime = Runtime::Current(); - ScopedLocalRef<jclass> exception_class(env, env->FindClass(descriptor.c_str())); - if (exception_class.get() == NULL) { - LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI FindClass failed: " - << PrettyTypeOf(GetException()); + mirror::ClassLoader* cl = NULL; + if (throw_location.GetMethod() != NULL) { + cl = throw_location.GetMethod()->GetDeclaringClass()->GetClassLoader(); + } + SirtRef<mirror::Class> + exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, cl)); + if (UNLIKELY(exception_class.get() == NULL)) { CHECK(IsExceptionPending()); + LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor); return; } - if (!Runtime::Current()->IsStarted()) { - // Something is trying to throw an exception without a started - // runtime, which is the common case in the compiler. We won't be - // able to invoke the constructor of the exception, so use - // AllocObject which will not invoke a constructor. - ScopedLocalRef<jthrowable> exception( - env, reinterpret_cast<jthrowable>(env->AllocObject(exception_class.get()))); - if (exception.get() != NULL) { - ScopedObjectAccessUnchecked soa(env); - mirror::Throwable* t = - reinterpret_cast<mirror::Throwable*>(soa.Self()->DecodeJObject(exception.get())); - t->SetDetailMessage(mirror::String::AllocFromModifiedUtf8(soa.Self(), msg)); - if (cause != NULL) { - t->SetCause(soa.Decode<mirror::Throwable*>(cause)); - } - soa.Self()->SetException(t); + + if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(exception_class.get(), true, true))) { + DCHECK(IsExceptionPending()); + return; + } + DCHECK(!runtime->IsStarted() || exception_class->IsThrowableClass()); + SirtRef<mirror::Throwable> exception(this, + down_cast<mirror::Throwable*>(exception_class->AllocObject(this))); + + // Choose an appropriate constructor and set up the arguments. + const char* signature; + SirtRef<mirror::String> msg_string(this, NULL); + if (msg != NULL) { + // Ensure we remember this and the method over the String allocation. + msg_string.reset(mirror::String::AllocFromModifiedUtf8(this, msg)); + if (UNLIKELY(msg_string.get() == NULL)) { + CHECK(IsExceptionPending()); // OOME. + return; + } + if (cause.get() == NULL) { + signature = "(Ljava/lang/String;)V"; } else { - LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI AllocObject failed: " - << PrettyTypeOf(GetException()); - CHECK(IsExceptionPending()); + signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V"; + } + } else { + if (cause.get() == NULL) { + signature = "()V"; + } else { + signature = "(Ljava/lang/Throwable;)V"; } - return; } - int rc = ::art::ThrowNewException(env, exception_class.get(), msg, cause); - if (rc != JNI_OK) { - LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI ThrowNew failed: " - << PrettyTypeOf(GetException()); - CHECK(IsExceptionPending()); + mirror::AbstractMethod* exception_init_method = + exception_class->FindDeclaredDirectMethod("<init>", signature); + + CHECK(exception_init_method != NULL) << "No <init>" << signature << " in " + << PrettyDescriptor(exception_class_descriptor); + + if (UNLIKELY(!runtime->IsStarted())) { + // Something is trying to throw an exception without a started runtime, which is the common + // case in the compiler. We won't be able to invoke the constructor of the exception, so set + // the exception fields directly. + if (msg != NULL) { + exception->SetDetailMessage(msg_string.get()); + } + if (cause.get() != NULL) { + exception->SetCause(cause.get()); + } + ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(), + throw_location.GetDexPc()); + SetException(gc_safe_throw_location, exception.get()); + } else { + ArgArray args("VLL", 3); + args.Append(reinterpret_cast<uint32_t>(exception.get())); + if (msg != NULL) { + args.Append(reinterpret_cast<uint32_t>(msg_string.get())); + } + if (cause.get() != NULL) { + args.Append(reinterpret_cast<uint32_t>(cause.get())); + } + JValue result; + exception_init_method->Invoke(this, args.GetArray(), args.GetNumBytes(), &result, 'V'); + if (LIKELY(!IsExceptionPending())) { + ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(), + throw_location.GetDexPc()); + SetException(gc_safe_throw_location, exception.get()); + } } } void Thread::ThrowOutOfMemoryError(const char* msg) { LOG(ERROR) << StringPrintf("Throwing OutOfMemoryError \"%s\"%s", msg, (throwing_OutOfMemoryError_ ? " (recursive case)" : "")); + ThrowLocation throw_location = GetCurrentLocationForThrow(); if (!throwing_OutOfMemoryError_) { throwing_OutOfMemoryError_ = true; - ThrowNewException("Ljava/lang/OutOfMemoryError;", msg); + ThrowNewException(throw_location, "Ljava/lang/OutOfMemoryError;", msg); } else { Dump(LOG(ERROR)); // The pre-allocated OOME has no stack, so help out and log one. - SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + SetException(throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError()); } throwing_OutOfMemoryError_ = false; } @@ -1538,8 +1578,6 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { ENTRY_POINT_INFO(pInstanceofNonTrivialFromCode), ENTRY_POINT_INFO(pCanPutArrayElementFromCode), ENTRY_POINT_INFO(pCheckCastFromCode), - ENTRY_POINT_INFO(pDebugMe), - ENTRY_POINT_INFO(pUpdateDebuggerFromCode), ENTRY_POINT_INFO(pInitializeStaticStorage), ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccessFromCode), ENTRY_POINT_INFO(pInitializeTypeFromCode), @@ -1644,13 +1682,17 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset, size_t size_of_ static const bool kDebugExceptionDelivery = false; class CatchBlockStackVisitor : public StackVisitor { public: - CatchBlockStackVisitor(Thread* self, mirror::Throwable* exception) + CatchBlockStackVisitor(Thread* self, const ThrowLocation& throw_location, + mirror::Throwable* exception, bool is_deoptimization) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : StackVisitor(self, self->GetLongJumpContext()), - self_(self), exception_(exception), to_find_(exception->GetClass()), throw_method_(NULL), - throw_frame_id_(0), throw_dex_pc_(0), handler_quick_frame_(NULL), - handler_quick_frame_pc_(0), handler_dex_pc_(0), native_method_count_(0), - method_tracing_active_(Runtime::Current()->IsMethodTracingActive()) { + self_(self), exception_(exception), is_deoptimization_(is_deoptimization), + to_find_(is_deoptimization ? NULL : exception->GetClass()), throw_location_(throw_location), + handler_quick_frame_(NULL), handler_quick_frame_pc_(0), handler_dex_pc_(0), + native_method_count_(0), + method_tracing_active_(is_deoptimization || + Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()), + instrumentation_frames_to_pop_(0), top_shadow_frame_(NULL), prev_shadow_frame_(NULL) { // Exception not in root sets, can't allow GC. last_no_assert_suspension_cause_ = self->StartAssertNoThreadSuspension("Finding catch block"); } @@ -1659,38 +1701,38 @@ class CatchBlockStackVisitor : public StackVisitor { LOG(FATAL) << "UNREACHABLE"; // Expected to take long jump. } - bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::AbstractMethod* method = GetMethod(); if (method == NULL) { // This is the upcall, we remember the frame and last pc so that we may long jump to them. handler_quick_frame_pc_ = GetCurrentQuickFramePc(); handler_quick_frame_ = GetCurrentQuickFrame(); return false; // End stack walk. - } - uint32_t dex_pc = DexFile::kDexNoIndex; - if (method->IsRuntimeMethod()) { - // ignore callee save method - DCHECK(method->IsCalleeSaveMethod()); } else { - if (throw_method_ == NULL) { - throw_method_ = method; - throw_frame_id_ = GetFrameId(); - throw_dex_pc_ = GetDexPc(); + if (UNLIKELY(method_tracing_active_ && + GetInstrumentationExitPc() == GetReturnPc())) { + // Keep count of the number of unwinds during instrumentation. + instrumentation_frames_to_pop_++; } - if (method->IsNative()) { - native_method_count_++; + if (method->IsRuntimeMethod()) { + // Ignore callee save method. + DCHECK(method->IsCalleeSaveMethod()); + return true; + } else if (is_deoptimization_) { + return HandleDeoptimization(method); } else { - // Unwind stack when an exception occurs during instrumentation - if (UNLIKELY(method_tracing_active_ && - GetInstrumentationExitPc() == GetCurrentQuickFramePc())) { - uintptr_t pc = InstrumentationMethodUnwindFromCode(Thread::Current()); - dex_pc = method->ToDexPc(pc); - } else { - dex_pc = GetDexPc(); - } + return HandleTryItems(method); } } + } + + bool HandleTryItems(mirror::AbstractMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint32_t dex_pc = DexFile::kDexNoIndex; + if (method->IsNative()) { + native_method_count_++; + } else { + dex_pc = GetDexPc(); + } if (dex_pc != DexFile::kDexNoIndex) { uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc); if (found_dex_pc != DexFile::kDexNoIndex) { @@ -1703,22 +1745,81 @@ class CatchBlockStackVisitor : public StackVisitor { return true; // Continue stack walk. } + bool HandleDeoptimization(mirror::AbstractMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + MethodHelper mh(m); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + CHECK(code_item != NULL); + uint16_t num_regs = code_item->registers_size_; + uint32_t dex_pc = GetDexPc(); + const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); + uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits(); + ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, m, new_dex_pc); + verifier::MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), code_item, + m->GetDexMethodIndex(), m, m->GetAccessFlags(), false); + verifier.Verify(); + std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc); + for(uint16_t reg = 0; reg < num_regs; reg++) { + VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2)); + switch (kind) { + case kUndefined: + new_frame->SetVReg(reg, 0xEBADDE09); + break; + case kConstant: + new_frame->SetVReg(reg, kinds.at((reg * 2) + 1)); + break; + case kReferenceVReg: + new_frame->SetVRegReference(reg, + reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind))); + break; + default: + new_frame->SetVReg(reg, GetVReg(m, reg, kind)); + break; + } + } + if (prev_shadow_frame_ != NULL) { + prev_shadow_frame_->SetLink(new_frame); + } else { + top_shadow_frame_ = new_frame; + } + prev_shadow_frame_ = new_frame; + return true; + } + void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::AbstractMethod* catch_method = *handler_quick_frame_; - if (kDebugExceptionDelivery) { - if (catch_method == NULL) { + if (catch_method == NULL) { + if (kDebugExceptionDelivery) { LOG(INFO) << "Handler is upcall"; - } else { + } + } else { + CHECK(!is_deoptimization_); + if (instrumentation_frames_to_pop_ > 0) { + // Don't pop the instrumentation frame of the catch handler. + instrumentation_frames_to_pop_--; + } + if (kDebugExceptionDelivery) { const DexFile& dex_file = *catch_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); int line_number = dex_file.GetLineNumFromPC(catch_method, handler_dex_pc_); LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")"; } } - self_->SetException(exception_); // Exception back in root set. + // Put exception back in root set and clear throw location. + self_->SetException(ThrowLocation(), exception_); self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_); - // Do debugger PostException after allowing thread suspension again. - Dbg::PostException(self_, throw_frame_id_, throw_method_, throw_dex_pc_, - catch_method, handler_dex_pc_, exception_); + // Do instrumentation events after allowing thread suspension again. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (size_t i = 0; i < instrumentation_frames_to_pop_; ++i) { + // We pop the instrumentation stack here so as not to corrupt it during the stack walk. + instrumentation->PopMethodForUnwind(self_, is_deoptimization_); + } + if (!is_deoptimization_) { + instrumentation->ExceptionCaughtEvent(self_, throw_location_, catch_method, handler_dex_pc_, + exception_); + } else { + // TODO: proper return value. + self_->SetDeoptimizationShadowFrame(top_shadow_frame_); + } // 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_)); @@ -1729,13 +1830,13 @@ class CatchBlockStackVisitor : public StackVisitor { } private: - Thread* self_; - mirror::Throwable* exception_; + Thread* const self_; + mirror::Throwable* const exception_; + const bool is_deoptimization_; // The type of the exception catch block to find. - mirror::Class* to_find_; - mirror::AbstractMethod* throw_method_; - JDWP::FrameId throw_frame_id_; - uint32_t throw_dex_pc_; + mirror::Class* const to_find_; + // Location of the throw. + const ThrowLocation& throw_location_; // Quick frame with found handler or last frame if no handler found. mirror::AbstractMethod** handler_quick_frame_; // PC to branch to for the handler. @@ -1748,21 +1849,32 @@ class CatchBlockStackVisitor : public StackVisitor { const bool method_tracing_active_; // Support for nesting no thread suspension checks. const char* last_no_assert_suspension_cause_; + // Number of frames to pop in long jump. + size_t instrumentation_frames_to_pop_; + ShadowFrame* top_shadow_frame_; + ShadowFrame* prev_shadow_frame_; }; void Thread::QuickDeliverException() { - mirror::Throwable* exception = GetException(); // Get exception from thread + // Get exception from thread. + ThrowLocation throw_location; + mirror::Throwable* exception = GetException(&throw_location); CHECK(exception != NULL); // Don't leave exception visible while we try to find the handler, which may cause class // resolution. ClearException(); + bool is_deoptimization = (exception == reinterpret_cast<mirror::Throwable*>(-1)); if (kDebugExceptionDelivery) { - mirror::String* msg = exception->GetDetailMessage(); - std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : ""); - DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) - << ": " << str_msg << "\n"); + if (!is_deoptimization) { + mirror::String* msg = exception->GetDetailMessage(); + std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : ""); + DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) + << ": " << str_msg << "\n"); + } else { + DumpStack(LOG(INFO) << "Deoptimizing: "); + } } - CatchBlockStackVisitor catch_finder(this, exception); + CatchBlockStackVisitor catch_finder(this, throw_location, exception, is_deoptimization); catch_finder.WalkStack(true); catch_finder.DoLongJump(); LOG(FATAL) << "UNREACHABLE"; @@ -1779,39 +1891,45 @@ Context* Thread::GetLongJumpContext() { return result; } -mirror::AbstractMethod* Thread::GetCurrentMethod(uint32_t* dex_pc, size_t* frame_id) const { - struct CurrentMethodVisitor : public StackVisitor { - CurrentMethodVisitor(Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, NULL), method_(NULL), dex_pc_(0), frame_id_(0) {} - - virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::AbstractMethod* m = GetMethod(); - if (m->IsRuntimeMethod()) { - // Continue if this is a runtime method. - return true; - } - method_ = m; - dex_pc_ = GetDexPc(); - frame_id_ = GetFrameId(); - return false; +struct CurrentMethodVisitor : public StackVisitor { + CurrentMethodVisitor(Thread* thread, Context* context) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(thread, context), this_object_(NULL), method_(NULL), dex_pc_(0) {} + virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::AbstractMethod* m = GetMethod(); + if (m->IsRuntimeMethod()) { + // Continue if this is a runtime method. + return true; + } + if (context_ != NULL) { + this_object_ = GetThisObject(); } - mirror::AbstractMethod* method_; - uint32_t dex_pc_; - size_t frame_id_; - }; + method_ = m; + dex_pc_ = GetDexPc(); + return false; + } + mirror::Object* this_object_; + mirror::AbstractMethod* method_; + uint32_t dex_pc_; +}; - CurrentMethodVisitor visitor(const_cast<Thread*>(this)); +mirror::AbstractMethod* Thread::GetCurrentMethod(uint32_t* dex_pc) const { + CurrentMethodVisitor visitor(const_cast<Thread*>(this), NULL); visitor.WalkStack(false); if (dex_pc != NULL) { *dex_pc = visitor.dex_pc_; } - if (frame_id != NULL) { - *frame_id = visitor.frame_id_; - } return visitor.method_; } +ThrowLocation Thread::GetCurrentLocationForThrow() { + Context* context = GetLongJumpContext(); + CurrentMethodVisitor visitor(this, context); + visitor.WalkStack(false); + ReleaseLongJumpContext(context); + return ThrowLocation(visitor.this_object_, visitor.method_, visitor.dex_pc_); +} + bool Thread::HoldsLock(mirror::Object* object) { if (object == NULL) { return false; @@ -1981,6 +2099,7 @@ void Thread::VerifyRoots(VerifyRootVisitor* visitor, void* arg) { if (exception_ != NULL) { VerifyRootWrapperCallback(exception_, &wrapperArg); } + throw_location_.VisitRoots(VerifyRootWrapperCallback, &wrapperArg); if (class_loader_override_ != NULL) { VerifyRootWrapperCallback(class_loader_override_, &wrapperArg); } @@ -1995,6 +2114,17 @@ void Thread::VerifyRoots(VerifyRootVisitor* visitor, void* arg) { ReferenceMapVisitor<VerifyCallbackVisitor> mapper(this, context, visitorToCallback); mapper.WalkStack(); ReleaseLongJumpContext(context); + + std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack = GetInstrumentationStack(); + typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It; + for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) { + mirror::Object* this_object = (*it).this_object_; + if (this_object != NULL) { + VerifyRootWrapperCallback(this_object, &wrapperArg); + } + mirror::AbstractMethod* method = (*it).method_; + VerifyRootWrapperCallback(method, &wrapperArg); + } } void Thread::VisitRoots(RootVisitor* visitor, void* arg) { @@ -2004,6 +2134,7 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { if (exception_ != NULL) { visitor(exception_, arg); } + throw_location_.VisitRoots(visitor, arg); if (class_loader_override_ != NULL) { visitor(class_loader_override_, arg); } @@ -2018,6 +2149,17 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitorToCallback); mapper.WalkStack(); ReleaseLongJumpContext(context); + + std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack = GetInstrumentationStack(); + typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It; + for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) { + mirror::Object* this_object = (*it).this_object_; + if (this_object != NULL) { + visitor(this_object, arg); + } + mirror::AbstractMethod* method = (*it).method_; + visitor(method, arg); + } } static void VerifyObject(const mirror::Object* root, void* arg) { diff --git a/src/thread.h b/src/thread.h index dd67a21..37f2721 100644 --- a/src/thread.h +++ b/src/thread.h @@ -36,6 +36,7 @@ #include "stack.h" #include "stack_indirect_reference_table.h" #include "thread_state.h" +#include "throw_location.h" #include "UniquePtr.h" namespace art { @@ -80,14 +81,13 @@ enum ThreadPriority { enum ThreadFlag { kSuspendRequest = 1, // If set implies that suspend_count_ > 0 and the Thread should enter the // safepoint handler. - kCheckpointRequest = 2, // Request that the thread do some checkpoint work and then continue. - kEnterInterpreter = 4, // Instruct managed code it should enter the interpreter. + kCheckpointRequest = 2 // Request that the thread do some checkpoint work and then continue. }; class PACKED(4) Thread { public: // Space to throw a StackOverflowError in. - static const size_t kStackOverflowReservedBytes = 10 * KB; + static const size_t kStackOverflowReservedBytes = 16 * KB; // Creates a new native thread corresponding to the given managed peer. // Used to implement Thread.start. @@ -279,28 +279,27 @@ class PACKED(4) Thread { return exception_ != NULL; } - mirror::Throwable* GetException() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Throwable* GetException(ThrowLocation* throw_location) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (throw_location != NULL) { + *throw_location = throw_location_; + } return exception_; } void AssertNoPendingException() const; - void SetException(mirror::Throwable* new_exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void SetException(const ThrowLocation& throw_location, mirror::Throwable* new_exception) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(new_exception != NULL); // TODO: DCHECK(!IsExceptionPending()); exception_ = new_exception; + throw_location_ = throw_location; } void ClearException() { exception_ = NULL; - } - - void DeliverException(mirror::Throwable* exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (exception == NULL) { - ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception"); - } else { - SetException(exception); - } + throw_location_.Clear(); } // Find catch block and perform long jump to appropriate exception handle @@ -312,9 +311,11 @@ class PACKED(4) Thread { long_jump_context_ = context; } - mirror::AbstractMethod* GetCurrentMethod(uint32_t* dex_pc = NULL, size_t* frame_id = NULL) const + mirror::AbstractMethod* GetCurrentMethod(uint32_t* dex_pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ThrowLocation GetCurrentLocationForThrow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetTopOfStack(void* stack, uintptr_t pc) { mirror::AbstractMethod** top_method = reinterpret_cast<mirror::AbstractMethod**>(stack); managed_stack_.SetTopQuickFrame(top_method); @@ -330,32 +331,30 @@ class PACKED(4) Thread { } // If 'msg' is NULL, no detail message is set. - void ThrowNewException(const char* exception_class_descriptor, const char* msg) + void ThrowNewException(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // If 'msg' is NULL, no detail message is set. An exception must be pending, and will be // used as the new exception's cause. - void ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg) + void ThrowNewWrappedException(const ThrowLocation& throw_location, + const char* exception_class_descriptor, + const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...) - __attribute__((format(printf, 3, 4))) + void ThrowNewExceptionF(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* fmt, ...) + __attribute__((format(printf, 4, 5))) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap) + void ThrowNewExceptionV(const ThrowLocation& throw_location, + const char* exception_class_descriptor, const char* fmt, va_list ap) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // OutOfMemoryError is special, because we need to pre-allocate an instance. // Only the GC should call this. void ThrowOutOfMemoryError(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - //QuickFrameIterator FindExceptionHandler(void* throw_pc, void** handler_pc); - - void* FindExceptionHandlerInMethod(const mirror::AbstractMethod* method, - void* throw_pc, - const DexFile& dex_file, - ClassLinker* class_linker); - static void Startup(); static void FinishStartup(); static void Shutdown(); @@ -395,8 +394,7 @@ class PACKED(4) Thread { static jobjectArray InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal, jobjectArray output_array = NULL, int* stack_depth = NULL); - void VisitRoots(RootVisitor* visitor, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void VerifyRoots(VerifyRootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -535,34 +533,15 @@ class PACKED(4) Thread { return debug_invoke_req_; } - void SetDebuggerUpdatesEnabled(bool enabled); - - void SetDeoptimizationShadowFrame(ShadowFrame* sf, const JValue& ret_val); + void SetDeoptimizationShadowFrame(ShadowFrame* sf); + void SetDeoptimizationReturnValue(const JValue& ret_val); ShadowFrame* GetAndClearDeoptimizationShadowFrame(JValue* ret_val); - const std::deque<InstrumentationStackFrame>* GetInstrumentationStack() const { + std::deque<instrumentation::InstrumentationStackFrame>* GetInstrumentationStack() { return instrumentation_stack_; } - bool IsInstrumentationStackEmpty() const { - return instrumentation_stack_->empty(); - } - - void PushInstrumentationStackFrame(const InstrumentationStackFrame& frame) { - instrumentation_stack_->push_front(frame); - } - - void PushBackInstrumentationStackFrame(const InstrumentationStackFrame& frame) { - instrumentation_stack_->push_back(frame); - } - - InstrumentationStackFrame PopInstrumentationStackFrame() { - InstrumentationStackFrame frame = instrumentation_stack_->front(); - instrumentation_stack_->pop_front(); - return frame; - } - BaseMutex* GetHeldMutex(LockLevel level) const { return held_mutexes_[level]; } @@ -598,13 +577,15 @@ class PACKED(4) Thread { void CreatePeer(const char* name, bool as_daemon, jobject thread_group); friend class Runtime; // For CreatePeer. - // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit and ~Thread. + // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and + // Dbg::Disconnected. ThreadState SetStateUnsafe(ThreadState new_state) { ThreadState old_state = GetState(); state_and_flags_.as_struct.state = new_state; return old_state; } friend class SignalCatcher; // For SetStateUnsafe. + friend class Dbg; // For SetStateUnsafe. void VerifyStackImpl() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -710,6 +691,8 @@ class PACKED(4) Thread { // System thread id. pid_t tid_; + ThrowLocation throw_location_; + // Guards the 'interrupted_' and 'wait_monitor_' members. mutable Mutex* wait_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable* wait_cond_ GUARDED_BY(wait_mutex_); @@ -755,7 +738,7 @@ class PACKED(4) Thread { // Additional stack used by method instrumentation to store method and return pc values. // Stored as a pointer since std::deque is not PACKED. - std::deque<InstrumentationStackFrame>* instrumentation_stack_; + std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack_; // A cached copy of the java.lang.Thread's name. std::string* name_; diff --git a/src/throw_location.cc b/src/throw_location.cc new file mode 100644 index 0000000..84d2c9b --- /dev/null +++ b/src/throw_location.cc @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "throw_location.h" + +#include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "utils.h" + +namespace art { + +std::string ThrowLocation::Dump() const { + return StringPrintf("%s:%d", PrettyMethod(method_).c_str(), + MethodHelper(method_).GetLineNumFromDexPC(dex_pc_)); +} + +void ThrowLocation::VisitRoots(RootVisitor* visitor, void* arg) { + if (this_object_ != NULL) { + visitor(this_object_, arg); + } + if (method_ != NULL) { + visitor(method_, arg); + } +} + +} // namespace art diff --git a/src/throw_location.h b/src/throw_location.h new file mode 100644 index 0000000..8c1b941 --- /dev/null +++ b/src/throw_location.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_THROW_LOCATION_H_ +#define ART_SRC_THROW_LOCATION_H_ + +#include "base/macros.h" +#include "root_visitor.h" + +#include <stdint.h> +#include <string> + +namespace art { + +namespace mirror { +class AbstractMethod; +class Object; +} // mirror + +class PACKED(4) ThrowLocation { + public: + ThrowLocation() { + Clear(); + } + + ThrowLocation(mirror::Object* throw_this_object, mirror::AbstractMethod* throw_method, + uint32_t throw_dex_pc) : + this_object_(throw_this_object), + method_(throw_method), + dex_pc_(throw_dex_pc) {} + + mirror::Object* GetThis() const { + return this_object_; + } + + mirror::AbstractMethod* GetMethod() const { + return method_; + } + + uint32_t GetDexPc() const { + return dex_pc_; + } + + void Clear() { + this_object_ = NULL; + method_ = NULL; + dex_pc_ = -1; + } + + std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void VisitRoots(RootVisitor* visitor, void* arg); + + private: + // The 'this' reference of the throwing method. + mirror::Object* this_object_; + // The throwing method. + mirror::AbstractMethod* method_; + // The instruction within the throwing method. + uint32_t dex_pc_; +}; + +} // namespace art + +#endif // ART_SRC_THROW_LOCATION_H_ diff --git a/src/trace.cc b/src/trace.cc index 859f523..3293290 100644 --- a/src/trace.cc +++ b/src/trace.cc @@ -20,6 +20,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" #include "instrumentation.h" @@ -73,7 +74,14 @@ namespace art { // // All values are stored in little-endian order. -static const uint32_t kTraceMethodActionMask = 0x03; // two bits +enum TraceAction { + kTraceMethodEnter = 0x00, // method entry + kTraceMethodExit = 0x01, // method exit + kTraceUnroll = 0x02, // method exited by exception unrolling + // 0x03 currently unused + kTraceMethodActionMask = 0x03, // two bits +}; + static const char kTraceTokenChar = '*'; static const uint16_t kTraceHeaderLength = 32; static const uint32_t kTraceMagicValue = 0x574f4c53; @@ -82,34 +90,57 @@ static const uint16_t kTraceVersionDualClock = 3; static const uint16_t kTraceRecordSizeSingleClock = 10; // using v2 static const uint16_t kTraceRecordSizeDualClock = 14; // using v3 with two timestamps -static ProfilerClockSource gDefaultTraceClockSource = kProfilerClockSourceDual; +#if defined(HAVE_POSIX_CLOCKS) +ProfilerClockSource Trace::default_clock_source_ = kProfilerClockSourceDual; +#else +ProfilerClockSource Trace::default_clock_source_ = kProfilerClockSourceWall; +#endif + +Trace* Trace::the_trace_ = NULL; -static inline uint32_t TraceMethodId(uint32_t methodValue) { - return (methodValue & ~kTraceMethodActionMask); +static mirror::AbstractMethod* DecodeTraceMethodId(uint32_t tmid) { + return reinterpret_cast<mirror::AbstractMethod*>(tmid & ~kTraceMethodActionMask); } -static inline uint32_t TraceMethodCombine(uint32_t method, uint8_t traceEvent) { - return (method | traceEvent); +static TraceAction DecodeTraceAction(uint32_t tmid) { + return static_cast<TraceAction>(tmid & kTraceMethodActionMask); } -void Trace::SetDefaultClockSource(ProfilerClockSource clock_source) { - gDefaultTraceClockSource = clock_source; +static uint32_t EncodeTraceMethodAndAction(const mirror::AbstractMethod* method, + TraceAction action) { + uint32_t tmid = reinterpret_cast<uint32_t>(method) | action; + DCHECK_EQ(method, DecodeTraceMethodId(tmid)); + return tmid; } -bool Trace::UseThreadCpuClock() { +void Trace::SetDefaultClockSource(ProfilerClockSource clock_source) { #if defined(HAVE_POSIX_CLOCKS) - return clock_source_ != kProfilerClockSourceWall; + default_clock_source_ = clock_source; #else - return false; + if (clock_source != kProfilerClockSourceWall) { + LOG(WARNING) << "Ignoring tracing request to use "; + } #endif } +static uint16_t GetTraceVersion(ProfilerClockSource clock_source) { + return (clock_source == kProfilerClockSourceDual) ? kTraceVersionDualClock + : kTraceVersionSingleClock; +} + +static uint16_t GetRecordSize(ProfilerClockSource clock_source) { + return (clock_source == kProfilerClockSourceDual) ? kTraceRecordSizeDualClock + : kTraceRecordSizeSingleClock; +} + +bool Trace::UseThreadCpuClock() { + return (clock_source_ == kProfilerClockSourceThreadCpu) || + (clock_source_ == kProfilerClockSourceDual); +} + bool Trace::UseWallClock() { -#if defined(HAVE_POSIX_CLOCKS) - return clock_source_ != kProfilerClockSourceThreadCpu; -#else - return true; -#endif + return (clock_source_ == kProfilerClockSourceWall) || + (clock_source_ == kProfilerClockSourceDual); } static void MeasureClockOverhead(Trace* trace) { @@ -165,109 +196,129 @@ static void Append8LE(uint8_t* buf, uint64_t val) { *buf++ = (uint8_t) (val >> 56); } -Trace::Trace(File* trace_file, int buffer_size, int flags) - : trace_file_(trace_file), buf_(new uint8_t[buffer_size]()), flags_(flags), - clock_source_(gDefaultTraceClockSource), overflow_(false), - buffer_size_(buffer_size), start_time_(0), trace_version_(0), record_size_(0), cur_offset_(0) { -} - -void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms) { - if (Runtime::Current()->IsMethodTracingActive()) { - LOG(INFO) << "Trace already in progress, ignoring this request"; - return; +void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, + bool direct_to_ddms) { + Thread* self = Thread::Current(); + { + MutexLock mu(self, *Locks::trace_lock_); + if (the_trace_ != NULL) { + LOG(ERROR) << "Trace already in progress, ignoring this request"; + return; + } } - - Runtime::Current()->GetThreadList()->SuspendAll(); + Runtime* runtime = Runtime::Current(); + runtime->GetThreadList()->SuspendAll(); // Open trace file if not going directly to ddms. - File* trace_file = NULL; + UniquePtr<File> trace_file; if (!direct_to_ddms) { if (trace_fd < 0) { - trace_file = OS::OpenFile(trace_filename, true); + trace_file.reset(OS::OpenFile(trace_filename, true)); } else { - trace_file = new File(trace_fd, "tracefile"); + trace_file.reset(new File(trace_fd, "tracefile")); trace_file->DisableAutoClose(); } - if (trace_file == NULL) { + if (trace_file.get() == NULL) { PLOG(ERROR) << "Unable to open trace file '" << trace_filename << "'"; - Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", - StringPrintf("Unable to open trace file '%s'", trace_filename).c_str()); - Runtime::Current()->GetThreadList()->ResumeAll(); + runtime->GetThreadList()->ResumeAll(); + ScopedObjectAccess soa(self); + ThrowRuntimeException("Unable to open trace file '%s'", trace_filename); return; } } // Create Trace object. - Trace* tracer(new Trace(trace_file, buffer_size, flags)); - - // Enable count of allocs if specified in the flags. - if ((flags && kTraceCountAllocs) != 0) { - Runtime::Current()->SetStatsEnabled(true); - } + { + MutexLock mu(self, *Locks::trace_lock_); + if(the_trace_ != NULL) { + LOG(ERROR) << "Trace already in progress, ignoring this request"; + } else { + the_trace_ = new Trace(trace_file.release(), buffer_size, flags); - Runtime::Current()->EnableMethodTracing(tracer); - tracer->BeginTracing(); + // Enable count of allocs if specified in the flags. + if ((flags && kTraceCountAllocs) != 0) { + runtime->SetStatsEnabled(true); + } - Runtime::Current()->GetThreadList()->ResumeAll(); + runtime->GetInstrumentation()->AddListener(the_trace_, + instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kMethodUnwind); + } + } + runtime->GetThreadList()->ResumeAll(); } void Trace::Stop() { - if (!Runtime::Current()->IsMethodTracingActive()) { - LOG(INFO) << "Trace stop requested, but no trace currently running"; - return; + Runtime* runtime = Runtime::Current(); + runtime->GetThreadList()->SuspendAll(); + Trace* the_trace = NULL; + { + MutexLock mu(Thread::Current(), *Locks::trace_lock_); + if (the_trace_ == NULL) { + LOG(ERROR) << "Trace stop requested, but no trace currently running"; + } else { + the_trace = the_trace_; + the_trace_ = NULL; + } } - - Runtime::Current()->GetThreadList()->SuspendAll(); - - Runtime::Current()->GetInstrumentation()->GetTrace()->FinishTracing(); - Runtime::Current()->DisableMethodTracing(); - - Runtime::Current()->GetThreadList()->ResumeAll(); + if (the_trace != NULL) { + the_trace->FinishTracing(); + runtime->GetInstrumentation()->RemoveListener(the_trace, + instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kMethodUnwind); + delete the_trace; + } + runtime->GetThreadList()->ResumeAll(); } void Trace::Shutdown() { - if (!Runtime::Current()->IsMethodTracingActive()) { - LOG(INFO) << "Trace shutdown requested, but no trace currently running"; - return; + if (IsMethodTracingActive()) { + Stop(); } - Runtime::Current()->GetInstrumentation()->GetTrace()->FinishTracing(); - Runtime::Current()->DisableMethodTracing(); } -void Trace::BeginTracing() { - // Set the start time of tracing. - start_time_ = MicroTime(); - - // Set trace version and record size. - if (UseThreadCpuClock() && UseWallClock()) { - trace_version_ = kTraceVersionDualClock; - record_size_ = kTraceRecordSizeDualClock; - } else { - trace_version_ = kTraceVersionSingleClock; - record_size_ = kTraceRecordSizeSingleClock; - } +bool Trace::IsMethodTracingActive() { + MutexLock mu(Thread::Current(), *Locks::trace_lock_); + return the_trace_ != NULL; +} +Trace::Trace(File* trace_file, int buffer_size, int flags) + : trace_file_(trace_file), buf_(new uint8_t[buffer_size]()), flags_(flags), + clock_source_(default_clock_source_), buffer_size_(buffer_size), start_time_(MicroTime()), + cur_offset_(0), overflow_(false) { // Set up the beginning of the trace. + uint16_t trace_version = GetTraceVersion(clock_source_); memset(buf_.get(), 0, kTraceHeaderLength); Append4LE(buf_.get(), kTraceMagicValue); - Append2LE(buf_.get() + 4, trace_version_); + Append2LE(buf_.get() + 4, trace_version); Append2LE(buf_.get() + 6, kTraceHeaderLength); Append8LE(buf_.get() + 8, start_time_); - if (trace_version_ >= kTraceVersionDualClock) { - Append2LE(buf_.get() + 16, record_size_); + if (trace_version >= kTraceVersionDualClock) { + uint16_t record_size = GetRecordSize(clock_source_); + Append2LE(buf_.get() + 16, record_size); } // Update current offset. cur_offset_ = kTraceHeaderLength; +} + +static void DumpBuf(uint8_t* buf, size_t buf_size, ProfilerClockSource clock_source) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint8_t* ptr = buf + kTraceHeaderLength; + uint8_t* end = buf + buf_size; - // Install all method tracing stubs. - Runtime::Current()->GetInstrumentation()->InstallStubs(); + while (ptr < end) { + uint32_t tmid = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16) | (ptr[5] << 24); + mirror::AbstractMethod* method = DecodeTraceMethodId(tmid); + TraceAction action = DecodeTraceAction(tmid); + LOG(INFO) << PrettyMethod(method) << " " << static_cast<int>(action); + ptr += GetRecordSize(clock_source); + } } void Trace::FinishTracing() { - // Uninstall all method tracing stubs. - Runtime::Current()->GetInstrumentation()->UninstallStubs(); - // Compute elapsed time. uint64_t elapsed = MicroTime() - start_time_; @@ -278,12 +329,13 @@ void Trace::FinishTracing() { Runtime::Current()->SetStatsEnabled(false); } - GetVisitedMethods(final_offset); + std::set<mirror::AbstractMethod*> visited_methods; + GetVisitedMethods(final_offset, &visited_methods); std::ostringstream os; os << StringPrintf("%cversion\n", kTraceTokenChar); - os << StringPrintf("%d\n", trace_version_); + os << StringPrintf("%d\n", GetTraceVersion(clock_source_)); os << StringPrintf("data-file-overflow=%s\n", overflow_ ? "true" : "false"); if (UseThreadCpuClock()) { if (UseWallClock()) { @@ -295,7 +347,8 @@ void Trace::FinishTracing() { os << StringPrintf("clock=wall\n"); } os << StringPrintf("elapsed-time-usec=%llu\n", elapsed); - os << StringPrintf("num-method-calls=%zd\n", (final_offset - kTraceHeaderLength) / record_size_); + size_t num_records = (final_offset - kTraceHeaderLength) / GetRecordSize(clock_source_); + os << StringPrintf("num-method-calls=%zd\n", num_records); os << StringPrintf("clock-call-overhead-nsec=%d\n", clock_overhead); os << StringPrintf("vm=art\n"); if ((flags_ & kTraceCountAllocs) != 0) { @@ -306,7 +359,7 @@ void Trace::FinishTracing() { os << StringPrintf("%cthreads\n", kTraceTokenChar); DumpThreadList(os); os << StringPrintf("%cmethods\n", kTraceTokenChar); - DumpMethodList(os); + DumpMethodList(os, visited_methods); os << StringPrintf("%cend\n", kTraceTokenChar); std::string header(os.str()); @@ -317,74 +370,128 @@ void Trace::FinishTracing() { iov[1].iov_base = buf_.get(); iov[1].iov_len = final_offset; Dbg::DdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2); + const bool kDumpTraceInfo = false; + if (kDumpTraceInfo) { + LOG(INFO) << "Trace sent:\n" << header; + DumpBuf(buf_.get(), final_offset, clock_source_); + } } else { if (!trace_file_->WriteFully(header.c_str(), header.length()) || !trace_file_->WriteFully(buf_.get(), final_offset)) { std::string detail(StringPrintf("Trace data write failed: %s", strerror(errno))); PLOG(ERROR) << detail; - Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", detail.c_str()); + ThrowRuntimeException("%s", detail.c_str()); } } } -void Trace::LogMethodTraceEvent(Thread* self, const mirror::AbstractMethod* method, - Trace::TraceEvent event) { - if (thread_clock_base_map_.find(self) == thread_clock_base_map_.end()) { - uint64_t time = ThreadCpuMicroTime(); - thread_clock_base_map_.Put(self, time); - } +void Trace::DexPcMoved(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t new_dex_pc) { + // We're not recorded to listen to this kind of event, so complain. + LOG(ERROR) << "Unexpected dex PC event in tracing " << PrettyMethod(method) << " " << new_dex_pc; +}; + +void Trace::MethodEntered(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) { + LogMethodTraceEvent(thread, method, instrumentation::Instrumentation::kMethodEntered); +} + +void Trace::MethodExited(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc, + const JValue& return_value) { + UNUSED(return_value); + LogMethodTraceEvent(thread, method, instrumentation::Instrumentation::kMethodExited); +} + +void Trace::MethodUnwind(Thread* thread, const mirror::AbstractMethod* method, uint32_t dex_pc) { + LogMethodTraceEvent(thread, method, instrumentation::Instrumentation::kMethodUnwind); +} + +void Trace::ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + LOG(ERROR) << "Unexpected exception caught event in tracing"; +} +void Trace::LogMethodTraceEvent(Thread* thread, const mirror::AbstractMethod* method, + instrumentation::Instrumentation::InstrumentationEvent event) { // Advance cur_offset_ atomically. int32_t new_offset; int32_t old_offset; do { old_offset = cur_offset_; - new_offset = old_offset + record_size_; + new_offset = old_offset + GetRecordSize(clock_source_); if (new_offset > buffer_size_) { overflow_ = true; return; } } while (android_atomic_release_cas(old_offset, new_offset, &cur_offset_) != 0); - uint32_t method_value = TraceMethodCombine(reinterpret_cast<uint32_t>(method), event); + TraceAction action = kTraceMethodEnter; + switch (event) { + case instrumentation::Instrumentation::kMethodEntered: + action = kTraceMethodEnter; + break; + case instrumentation::Instrumentation::kMethodExited: + action = kTraceMethodExit; + break; + case instrumentation::Instrumentation::kMethodUnwind: + action = kTraceUnroll; + break; + default: + UNIMPLEMENTED(FATAL) << "Unexpected event: " << event; + } + + uint32_t method_value = EncodeTraceMethodAndAction(method, action); // Write data uint8_t* ptr = buf_.get() + old_offset; - Append2LE(ptr, self->GetTid()); + Append2LE(ptr, thread->GetTid()); Append4LE(ptr + 2, method_value); ptr += 6; if (UseThreadCpuClock()) { - uint64_t thread_clock_base = thread_clock_base_map_.find(self)->second; - uint32_t thread_clock_diff = ThreadCpuMicroTime() - thread_clock_base; + // TODO: this isn't vaguely thread safe. + SafeMap<Thread*, uint64_t>::iterator it = thread_clock_base_map_.find(thread); + uint32_t thread_clock_diff = 0; + if (UNLIKELY(it == thread_clock_base_map_.end())) { + // First event, the diff is 0, record the base time in the map. + uint64_t time = ThreadCpuMicroTime(); + thread_clock_base_map_.Put(thread, time); + } else { + uint64_t thread_clock_base = it->second; + thread_clock_diff = ThreadCpuMicroTime() - thread_clock_base; + } Append4LE(ptr, thread_clock_diff); ptr += 4; } - if (UseWallClock()) { uint32_t wall_clock_diff = MicroTime() - start_time_; Append4LE(ptr, wall_clock_diff); } } -void Trace::GetVisitedMethods(size_t end_offset) { +void Trace::GetVisitedMethods(size_t buf_size, + std::set<mirror::AbstractMethod*>* visited_methods) { uint8_t* ptr = buf_.get() + kTraceHeaderLength; - uint8_t* end = buf_.get() + end_offset; + uint8_t* end = buf_.get() + buf_size; while (ptr < end) { - uint32_t method_value = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16) | (ptr[5] << 24); - mirror::AbstractMethod* method = - reinterpret_cast<mirror::AbstractMethod*>(TraceMethodId(method_value)); - visited_methods_.insert(method); - ptr += record_size_; + uint32_t tmid = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16) | (ptr[5] << 24); + mirror::AbstractMethod* method = DecodeTraceMethodId(tmid); + visited_methods->insert(method); + ptr += GetRecordSize(clock_source_); } } -void Trace::DumpMethodList(std::ostream& os) { - typedef std::set<const mirror::AbstractMethod*>::const_iterator It; // TODO: C++0x auto - for (It it = visited_methods_.begin(); it != visited_methods_.end(); ++it) { - const mirror::AbstractMethod* method = *it; - MethodHelper mh(method); +void Trace::DumpMethodList(std::ostream& os, + const std::set<mirror::AbstractMethod*>& visited_methods) { + typedef std::set<mirror::AbstractMethod*>::const_iterator It; // TODO: C++0x auto + MethodHelper mh; + for (It it = visited_methods.begin(); it != visited_methods.end(); ++it) { + mirror::AbstractMethod* method = *it; + mh.ChangeMethod(method); os << StringPrintf("%p\t%s\t%s\t%s\t%s\n", method, PrettyDescriptor(mh.GetDeclaringClassDescriptor()).c_str(), mh.GetName(), mh.GetSignature().c_str(), mh.GetDeclaringClassSourceFile()); diff --git a/src/trace.h b/src/trace.h index 1be1cc4..9432e71 100644 --- a/src/trace.h +++ b/src/trace.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "globals.h" +#include "instrumentation.h" #include "os.h" #include "safe_map.h" #include "UniquePtr.h" @@ -37,45 +38,65 @@ class Thread; enum ProfilerClockSource { kProfilerClockSourceThreadCpu, kProfilerClockSourceWall, - kProfilerClockSourceDual, + kProfilerClockSourceDual, // Both wall and thread CPU clocks. }; -class Trace { +class Trace : public instrumentation::InstrumentationListener { public: - enum TraceEvent { - kMethodTraceEnter = 0, - kMethodTraceExit = 1, - kMethodTraceUnwind = 2, - }; - enum TraceFlag { kTraceCountAllocs = 1, }; static void SetDefaultClockSource(ProfilerClockSource clock_source); - static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms); - static void Stop(); - static void Shutdown() NO_THREAD_SAFETY_ANALYSIS; // TODO: implement appropriate locking. + static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, + bool direct_to_ddms) + LOCKS_EXCLUDED(Locks::mutator_lock_, + Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_, + Locks::trace_lock_); + static void Stop() LOCKS_EXCLUDED(Locks::trace_lock_); + static void Shutdown() LOCKS_EXCLUDED(Locks::trace_lock_); + static bool IsMethodTracingActive() LOCKS_EXCLUDED(Locks::trace_lock_); bool UseWallClock(); bool UseThreadCpuClock(); - void LogMethodTraceEvent(Thread* self, const mirror::AbstractMethod* method, TraceEvent event); - + virtual void MethodEntered(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void MethodExited(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t dex_pc, + const JValue& return_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void MethodUnwind(Thread* thread, const mirror::AbstractMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, + const mirror::AbstractMethod* method, uint32_t new_dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::AbstractMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: explicit Trace(File* trace_file, int buffer_size, int flags); - void BeginTracing(); void FinishTracing() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void LogMethodTraceEvent(Thread* thread, const mirror::AbstractMethod* method, + instrumentation::Instrumentation::InstrumentationEvent event); + // Methods to output traced methods and threads. - void GetVisitedMethods(size_t end_offset); - void DumpMethodList(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void GetVisitedMethods(size_t end_offset, std::set<mirror::AbstractMethod*>* visited_methods); + void DumpMethodList(std::ostream& os, const std::set<mirror::AbstractMethod*>& visited_methods) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void DumpThreadList(std::ostream& os) LOCKS_EXCLUDED(Locks::thread_list_lock_); - // Set of methods visited by the profiler. - std::set<const mirror::AbstractMethod*> visited_methods_; + // Singleton instance of the Trace or NULL when no method tracing is active. + static Trace* the_trace_ GUARDED_BY(Locks::trace_lock_); + + // The default profiler clock source. + static ProfilerClockSource default_clock_source_; // Maps a thread to its clock base. SafeMap<Thread*, uint64_t> thread_clock_base_map_; @@ -87,18 +108,22 @@ class Trace { UniquePtr<uint8_t> buf_; // Flags enabling extra tracing of things such as alloc counts. - int flags_; + const int flags_; - ProfilerClockSource clock_source_; + const ProfilerClockSource clock_source_; - bool overflow_; - int buffer_size_; - uint64_t start_time_; - uint16_t trace_version_; - uint16_t record_size_; + // Size of buf_. + const int buffer_size_; + + // Time trace was created. + const uint64_t start_time_; + // Offset into buf_. volatile int32_t cur_offset_; + // Did we overflow the buffer recording traces? + bool overflow_; + DISALLOW_COPY_AND_ASSIGN(Trace); }; diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc index c7a1b4f..6228ba5 100644 --- a/src/verifier/reg_type_cache.cc +++ b/src/verifier/reg_type_cache.cc @@ -148,6 +148,10 @@ mirror::Class* RegTypeCache::ResolveClass(std::string descriptor, mirror::ClassL klass = class_linker->FindClass(descriptor.c_str(), loader); } else { klass = class_linker->LookupClass(descriptor.c_str(), loader); + if (klass != NULL && !klass->IsLoaded()) { + // We found the class but without it being loaded its not safe for use. + klass = NULL; + } } return klass; } @@ -244,12 +248,12 @@ const RegType& RegTypeCache::FromClass(mirror::Class* klass, bool precise) { RegTypeCache::~RegTypeCache() { CHECK_LE(primitive_count_, entries_.size()); // Delete only the non primitive types. - if (primitive_count_ == static_cast<uint16_t>(entries_.size())) { + if (entries_.size() == kNumPrimitives) { // All entries are primitive, nothing to delete. return; } std::vector<RegType*>::iterator non_primitive_begin = entries_.begin(); - std::advance(non_primitive_begin, primitive_count_); + std::advance(non_primitive_begin, kNumPrimitives); STLDeleteContainerPointers(non_primitive_begin, entries_.end()); } @@ -267,8 +271,8 @@ void RegTypeCache::ShutDown() { FloatType::GetInstance(); DoubleLoType::Destroy(); DoubleHiType::Destroy(); - RegTypeCache::primitive_initialized_ = false; - RegTypeCache::primitive_count_ = 0; + RegTypeCache::primitive_initialized_ = false; + RegTypeCache::primitive_count_ = 0; } } diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h index cacd517..41d3c69 100644 --- a/src/verifier/reg_type_cache.h +++ b/src/verifier/reg_type_cache.h @@ -35,6 +35,7 @@ namespace verifier { class RegType; +const size_t kNumPrimitives = 12; class RegTypeCache { public: explicit RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) { @@ -45,7 +46,7 @@ class RegTypeCache { if(!RegTypeCache::primitive_initialized_) { CHECK_EQ(RegTypeCache::primitive_count_, 0); CreatePrimitiveTypes(); - CHECK_EQ(RegTypeCache::primitive_count_, 12); + CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitives); RegTypeCache::primitive_initialized_ = true; } } diff --git a/test/003-omnibus-opcodes/expected.txt b/test/003-omnibus-opcodes/expected.txt index c5e67e5..a62c498 100644 --- a/test/003-omnibus-opcodes/expected.txt +++ b/test/003-omnibus-opcodes/expected.txt @@ -70,14 +70,6 @@ Throw.rethrow UnresTest1... UnresTest1... UnresTest2... -java.lang.NoClassDefFoundError: Failed resolution of: LUnresClass; - at UnresTest2.run(UnresTest2.java:33) - at Main.run(Main.java:64) - at Main.main(Main.java:26) -java.lang.NoClassDefFoundError: Failed resolution of: LUnresClassSubclass; - at UnresTest2.run(UnresTest2.java:41) - at Main.run(Main.java:64) - at Main.main(Main.java:26) UnresTest2 done InternedString.run Done! diff --git a/test/003-omnibus-opcodes/src/UnresTest2.java b/test/003-omnibus-opcodes/src/UnresTest2.java index c94f226..4135d73 100644 --- a/test/003-omnibus-opcodes/src/UnresTest2.java +++ b/test/003-omnibus-opcodes/src/UnresTest2.java @@ -33,22 +33,23 @@ class UnresTest2 { un = new UnresClass(); Main.assertTrue(false); } catch (NoClassDefFoundError ncdfe) { - ncdfe.printStackTrace(); + Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException); // good } try { - new UnresClassSubclass(); - Main.assertTrue(false); + new UnresClassSubclass(); + Main.assertTrue(false); } catch (NoClassDefFoundError ncdfe) { - ncdfe.printStackTrace(); - // good + Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException); + // good } try { UnresClass[] uar = new UnresClass[3]; Main.assertTrue(false); } catch (NoClassDefFoundError ncdfe) { + Main.assertTrue(ncdfe.getCause() instanceof ClassNotFoundException); // good } diff --git a/test/044-proxy/src/ReturnsAndArgPassing.java b/test/044-proxy/src/ReturnsAndArgPassing.java index 50eff77..a173410 100644 --- a/test/044-proxy/src/ReturnsAndArgPassing.java +++ b/test/044-proxy/src/ReturnsAndArgPassing.java @@ -51,6 +51,8 @@ public class ReturnsAndArgPassing { static int barInvocations = 0; static class MyInvocationHandler implements InvocationHandler { + boolean causeNpeOnReturn = false; + Class<?> returnType = null; public Object invoke(Object proxy, Method method, Object[] args) { check(proxy instanceof Proxy); check(method.getDeclaringClass() == MyInterface.class); @@ -62,30 +64,29 @@ public class ReturnsAndArgPassing { check(args == null); barInvocations++; } - if (name.equals("voidFoo")) { return null; } - else if (name.equals("voidBar")) { return null; } - else if (name.equals("booleanFoo")) { return true; } - else if (name.equals("booleanBar")) { return false; } - else if (name.equals("byteFoo")) { return Byte.MAX_VALUE; } - else if (name.equals("byteBar")) { return Byte.MIN_VALUE; } - else if (name.equals("charFoo")) { return Character.MAX_VALUE; } - else if (name.equals("charBar")) { return Character.MIN_VALUE; } - else if (name.equals("shortFoo")) { return Short.MAX_VALUE; } - else if (name.equals("shortBar")) { return Short.MIN_VALUE; } - else if (name.equals("intFoo")) { return Integer.MAX_VALUE; } - else if (name.equals("intBar")) { return Integer.MIN_VALUE; } - else if (name.equals("longFoo")) { return Long.MAX_VALUE; } - else if (name.equals("longBar")) { return Long.MIN_VALUE; } - else if (name.equals("floatFoo")) { return Float.MAX_VALUE; } - else if (name.equals("floatBar")) { return Float.MIN_VALUE; } - else if (name.equals("doubleFoo")) { return Double.MAX_VALUE; } - else if (name.equals("doubleBar")) { return Double.MIN_VALUE; } - else if (name.equals("selectArg")) { + if (causeNpeOnReturn) { + return null; + } else if (name.equals("voidFoo") || name.equals("voidBar")) { + return null; + } else if (name.equals("booleanFoo")) { + return true; + } else if (name.equals("booleanBar")) { + return false; + } else if (name.equals("selectArg")) { check(args.length == 6); int select = (Integer)args[0]; return args[select]; } else { - throw new AssertionError("Unexpect method " + method); + try { + if (name.endsWith("Foo")) { + return returnType.getField("MAX_VALUE").get(null); + } else { + check(name.endsWith("Bar")); + return returnType.getField("MIN_VALUE").get(null); + } + } catch (Exception e) { + throw new Error("return type = " + returnType, e); + } } } } @@ -106,6 +107,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 1); check(fooInvocations == 1); + myHandler.returnType = Boolean.class; check(proxyMyInterface.booleanFoo() == true); check(fooInvocations == 2); @@ -114,6 +116,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 2); check(fooInvocations == 2); + myHandler.returnType = Byte.class; check(proxyMyInterface.byteFoo() == Byte.MAX_VALUE); check(fooInvocations == 3); @@ -122,6 +125,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 3); check(fooInvocations == 3); + myHandler.returnType = Character.class; check(proxyMyInterface.charFoo() == Character.MAX_VALUE); check(fooInvocations == 4); @@ -130,6 +134,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 4); check(fooInvocations == 4); + myHandler.returnType = Short.class; check(proxyMyInterface.shortFoo() == Short.MAX_VALUE); check(fooInvocations == 5); @@ -138,6 +143,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 5); check(fooInvocations == 5); + myHandler.returnType = Integer.class; check(proxyMyInterface.intFoo() == Integer.MAX_VALUE); check(fooInvocations == 6); @@ -146,6 +152,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 6); check(fooInvocations == 6); + myHandler.returnType = Long.class; check(proxyMyInterface.longFoo() == Long.MAX_VALUE); check(fooInvocations == 7); @@ -154,6 +161,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 7); check(fooInvocations == 7); + myHandler.returnType = Float.class; check(proxyMyInterface.floatFoo() == Float.MAX_VALUE); check(fooInvocations == 8); @@ -162,6 +170,7 @@ public class ReturnsAndArgPassing { check(barInvocations == 8); check(fooInvocations == 8); + myHandler.returnType = Double.class; check(proxyMyInterface.doubleFoo() == Double.MAX_VALUE); check(fooInvocations == 9); @@ -169,6 +178,259 @@ public class ReturnsAndArgPassing { check(proxyMyInterface.doubleBar() == Double.MIN_VALUE); check(barInvocations == 9); + // Toggle flag to get return values to cause NPEs + myHandler.causeNpeOnReturn = true; + + check(fooInvocations == 9); + try { + proxyMyInterface.booleanFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 10); + + check(barInvocations == 9); + try { + proxyMyInterface.booleanBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 10); + + check(fooInvocations == 10); + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 11); + + check(barInvocations == 10); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 11); + + check(fooInvocations == 11); + try { + proxyMyInterface.charFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 12); + + check(barInvocations == 11); + try { + proxyMyInterface.charBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 12); + + check(fooInvocations == 12); + try { + proxyMyInterface.shortFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 13); + + check(barInvocations == 12); + try { + proxyMyInterface.shortBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 13); + + check(fooInvocations == 13); + try { + proxyMyInterface.intFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 14); + + check(barInvocations == 13); + try { + proxyMyInterface.intBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 14); + + check(fooInvocations == 14); + try { + proxyMyInterface.longFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 15); + + check(barInvocations == 14); + try { + proxyMyInterface.longBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 15); + + check(fooInvocations == 15); + try { + proxyMyInterface.floatFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 16); + + check(barInvocations == 15); + try { + proxyMyInterface.floatBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 16); + + check(fooInvocations == 16); + try { + proxyMyInterface.doubleFoo(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(fooInvocations == 17); + + check(barInvocations == 16); + try { + proxyMyInterface.doubleBar(); + throw new AssertionError("Expected NPE"); + } catch (NullPointerException e) { + } + check(barInvocations == 17); + + // Toggle flag to stop NPEs + myHandler.causeNpeOnReturn = false; + + check(fooInvocations == 17); + myHandler.returnType = Double.class; // Double -> byte == fail + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 18); + + check(barInvocations == 17); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 18); + + check(fooInvocations == 18); + myHandler.returnType = Float.class; // Float -> byte == fail + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 19); + + check(barInvocations == 18); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 19); + + check(fooInvocations == 19); + myHandler.returnType = Long.class; // Long -> byte == fail + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 20); + + check(barInvocations == 19); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 20); + + check(fooInvocations == 20); + myHandler.returnType = Integer.class; // Int -> byte == fail + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 21); + + check(barInvocations == 20); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 21); + + check(fooInvocations == 21); + myHandler.returnType = Short.class; // Short -> byte == fail + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 22); + + check(barInvocations == 21); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 22); + + check(fooInvocations == 22); + myHandler.returnType = Character.class; // Char -> byte == fail + try { + proxyMyInterface.byteFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 23); + + check(barInvocations == 22); + try { + proxyMyInterface.byteBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 23); + + check(fooInvocations == 23); + myHandler.returnType = Character.class; // Char -> short == fail + try { + proxyMyInterface.shortFoo(); + throw new AssertionError("Expected ClassCastException"); + } catch (ClassCastException e) { + } + check(fooInvocations == 24); + + check(barInvocations == 23); + try { + proxyMyInterface.shortBar(); + throw new AssertionError("Expected NPE"); + } catch (ClassCastException e) { + } + check(barInvocations == 24); + System.out.println(testName + ".testProxyReturns PASSED"); } diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 9417174..0c567d4 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -22,7 +22,7 @@ z 30 62 14 -java.lang.IllegalArgumentException: invalid primitive conversion from int to short +java.lang.IllegalArgumentException: Invalid primitive conversion from int to short at java.lang.reflect.Field.set(Native Method) at Main.testFieldReflection(Main.java:121) at Main.main(Main.java:269) diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java index 9b67db6..f8da644 100644 --- a/test/201-built-in-exception-detail-messages/src/Main.java +++ b/test/201-built-in-exception-detail-messages/src/Main.java @@ -393,7 +393,7 @@ public class Main { m.invoke("hello"); // Wrong number of arguments. fail(); } catch (IllegalArgumentException iae) { - assertEquals("wrong number of arguments; expected 1, got 0", iae.getMessage()); + assertEquals("Wrong number of arguments; expected 1, got 0", iae.getMessage()); } try { Method m = String.class.getMethod("charAt", int.class); @@ -414,14 +414,14 @@ public class Main { m.invoke(new Integer(5)); // Wrong type for 'this'. fail(); } catch (IllegalArgumentException iae) { - assertEquals("expected receiver of type java.lang.String, but got java.lang.Integer", iae.getMessage()); + assertEquals("Expected receiver of type java.lang.String, but got java.lang.Integer", iae.getMessage()); } try { Method m = String.class.getMethod("charAt", int.class); m.invoke(null); // Null for 'this'. fail(); } catch (NullPointerException npe) { - assertEquals("expected receiver of type java.lang.String, but got null", npe.getMessage()); + assertEquals("null receiver", npe.getMessage()); } } diff --git a/test/StackWalk/stack_walk_jni.cc b/test/StackWalk/stack_walk_jni.cc index 92cfa99..4b472da 100644 --- a/test/StackWalk/stack_walk_jni.cc +++ b/test/StackWalk/stack_walk_jni.cc @@ -21,6 +21,7 @@ #include "gc_map.h" #include "mirror/abstract_method.h" #include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "object_utils.h" |