diff options
author | Ian Rogers <irogers@google.com> | 2013-02-27 08:32:07 -0800 |
---|---|---|
committer | Ian Rogers <irogers@google.com> | 2013-04-08 14:24:13 -0700 |
commit | 62d6c772205b8859f0ebf7ad105402ec4c3e2e01 (patch) | |
tree | e2f2ba6d71ed5a39c9f6909e3f7c08e998053315 | |
parent | c9b17c7ee96cd04fac9048aab624ed554fe260bf (diff) | |
download | art-62d6c772205b8859f0ebf7ad105402ec4c3e2e01.zip art-62d6c772205b8859f0ebf7ad105402ec4c3e2e01.tar.gz art-62d6c772205b8859f0ebf7ad105402ec4c3e2e01.tar.bz2 |
Interpreter entries and instrumentation as a listener.
Make the instrumentation responsible for whether we want method entry/exit
stubs, and allow it to use interpreter entry stubs when instruction by
instruction instrumentation is required. Improve deoptimization so more JDWP
test cases are passing.
Refactor exception debug posting, in particular improve reporting in the
interpreter. Improve class linker exception throwing so that broken dex files
are more likely to be reported. Fixes the performance issue Bug: 8410519.
Fix some error reporting lock level errors for the large object space. Make
fast object verification faster.
Add some debug mode robustness to finding dex PCs in GC maps.
Add printf attributes to JniAbortF and fix errors.
Expand run-test 044 to test return behaviors and fix issues with not throwing
appropriate exceptions for proxies.
Ensure causes are reported with a class linker NoClassDefFoundError and JNI
NoSuchFieldError.
Remove unused debugMe and updateDebuggerFromCode.
There's a minor sizing tweak to the arg array builder, and an extra reference
array check in the interpreter.
Some clean-up of trace code.
Fix reg type cache destructor if it is called after the reg type cache is
shutdown (as is the case in oatdump).
Change-Id: I6519c7b35df77f978d011999354c864f4918e8ce
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" |