summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--build/Android.common.mk2
-rw-r--r--src/base/mutex-inl.h5
-rw-r--r--src/check_jni.cc24
-rw-r--r--src/class_linker.cc221
-rw-r--r--src/class_linker_test.cc2
-rw-r--r--src/common_throws.cc392
-rw-r--r--src/common_throws.h98
-rw-r--r--src/compiler/dex/frontend.h1
-rw-r--r--src/compiler/dex/quick/gen_common.cc11
-rw-r--r--src/compiler/dex/quick/gen_invoke.cc3
-rw-r--r--src/compiler/dex/quick/mir_to_lir.h1
-rw-r--r--src/compiler/driver/compiler_driver.cc22
-rw-r--r--src/compiler/llvm/runtime_support_llvm.cc92
-rw-r--r--src/debugger.cc210
-rw-r--r--src/debugger.h13
-rw-r--r--src/dex_file.h5
-rw-r--r--src/gc/large_object_space.cc2
-rw-r--r--src/gc/mark_sweep.cc4
-rw-r--r--src/gc_map.h3
-rw-r--r--src/heap.cc66
-rw-r--r--src/hprof/hprof.cc11
-rw-r--r--src/instrumentation.cc547
-rw-r--r--src/instrumentation.h260
-rw-r--r--src/interpreter/interpreter.cc189
-rw-r--r--src/interpreter/interpreter.h8
-rw-r--r--src/invoke_arg_array_builder.h21
-rw-r--r--src/jdwp/jdwp_event.cc24
-rw-r--r--src/jdwp/jdwp_main.cc6
-rw-r--r--src/jni_internal.cc92
-rw-r--r--src/jni_internal.h3
-rw-r--r--src/jvalue.h2
-rw-r--r--src/locks.cc4
-rw-r--r--src/locks.h12
-rw-r--r--src/mirror/abstract_method-inl.h4
-rw-r--r--src/mirror/abstract_method.cc46
-rw-r--r--src/mirror/abstract_method.h5
-rw-r--r--src/mirror/array.cc19
-rw-r--r--src/mirror/class-inl.h2
-rw-r--r--src/mirror/class.cc31
-rw-r--r--src/mirror/object_test.cc10
-rw-r--r--src/mirror/string.cc5
-rw-r--r--src/mirror/throwable.cc4
-rw-r--r--src/monitor.cc14
-rw-r--r--src/monitor_android.cc2
-rw-r--r--src/native/dalvik_system_DexFile.cc15
-rw-r--r--src/native/dalvik_system_VMDebug.cc32
-rw-r--r--src/native/dalvik_system_VMRuntime.cc7
-rw-r--r--src/native/java_lang_Class.cc5
-rw-r--r--src/native/java_lang_String.cc6
-rw-r--r--src/native/java_lang_System.cc45
-rw-r--r--src/native/java_lang_Thread.cc3
-rw-r--r--src/native/java_lang_reflect_Array.cc3
-rw-r--r--src/native/java_lang_reflect_Constructor.cc9
-rw-r--r--src/native/java_lang_reflect_Field.cc30
-rw-r--r--src/nth_caller_visitor.h32
-rw-r--r--src/oat/runtime/arm/oat_support_entrypoints_arm.cc12
-rw-r--r--src/oat/runtime/arm/runtime_support_arm.S79
-rw-r--r--src/oat/runtime/mips/oat_support_entrypoints_mips.cc12
-rw-r--r--src/oat/runtime/mips/runtime_support_mips.S104
-rw-r--r--src/oat/runtime/oat_support_entrypoints.h4
-rw-r--r--src/oat/runtime/support_cast.cc20
-rw-r--r--src/oat/runtime/support_debug.cc44
-rw-r--r--src/oat/runtime/support_deoptimize.cc83
-rw-r--r--src/oat/runtime/support_field.cc18
-rw-r--r--src/oat/runtime/support_fillarray.cc11
-rw-r--r--src/oat/runtime/support_instrumentation.cc65
-rw-r--r--src/oat/runtime/support_jni.cc4
-rw-r--r--src/oat/runtime/support_stubs.cc13
-rw-r--r--src/oat/runtime/support_throw.cc47
-rw-r--r--src/oat/runtime/x86/oat_support_entrypoints_x86.cc12
-rw-r--r--src/oat/runtime/x86/runtime_support_x86.S140
-rw-r--r--src/reflection.cc112
-rw-r--r--src/reflection.h9
-rw-r--r--src/runtime.cc47
-rw-r--r--src/runtime.h19
-rw-r--r--src/runtime_support.cc80
-rw-r--r--src/runtime_support.h20
-rw-r--r--src/stack.cc81
-rw-r--r--src/stack.h10
-rw-r--r--src/thread.cc416
-rw-r--r--src/thread.h87
-rw-r--r--src/throw_location.cc41
-rw-r--r--src/throw_location.h78
-rw-r--r--src/trace.cc323
-rw-r--r--src/trace.h75
-rw-r--r--src/verifier/reg_type_cache.cc12
-rw-r--r--src/verifier/reg_type_cache.h3
-rw-r--r--test/003-omnibus-opcodes/expected.txt8
-rw-r--r--test/003-omnibus-opcodes/src/UnresTest2.java11
-rw-r--r--test/044-proxy/src/ReturnsAndArgPassing.java302
-rw-r--r--test/100-reflect2/expected.txt2
-rw-r--r--test/201-built-in-exception-detail-messages/src/Main.java6
-rw-r--r--test/StackWalk/stack_walk_jni.cc1
94 files changed, 3210 insertions, 1888 deletions
diff --git a/Android.mk b/Android.mk
index 145f8b0..27cce22 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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"