Remove blacklist
Removes the class initialization blacklist and use transaction to detect and
revert class initialization attempting to invoke native method. This only
concerns class initialization happening at compilation time when generating an
image (like boot.art for the system).
In transactional mode, we log every object's field assignment and array update.
Therefore we're able to abort a transaction to restore values of fields and
array as they were before the transaction starts. We also log changes to the
intern string table so we can restore its state prior to transaction start.
Since transactional mode only happens at compilation time, we don't need to log
all these changes at runtime. In order to reduce the overhead of testing if
transactional mode is on/off, we templatize interfaces of mirror::Object and
mirror::Array, respectively responsible for setting a field and setting an
array element.
For various reasons, we skip some specific fields from transaction:
- Object's class and array's length must remain unchanged so garbage collector
can compute object's size.
- Immutable fields only set during class loading: list of fields, method,
dex caches, vtables, ... as all classes have been loaded and verified before a
transaction occurs.
- Object's monitor for performance reason.
Before generating the image, we browse the heap to collect objects that need to
be written into it. Since the heap may still holds references to unreachable
objects due to aborted transactions, we trigger one collection at the end of
the class preinitialization phase.
Since the transaction is held by the runtime and all compilation threads share
the same runtime, we need to ensure only one compilation thread has exclusive
access to the runtime. To workaround this issue, we force class initialization
phase to run with only one thread. Note this is only done when generating image
so application compilation is not impacted. This issue will be addressed in a
separate CL.
Bug: 9676614
Change-Id: I221910a9183a5ba6c2b99a277f5a5a68bc69b5f9
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2392561..70291c1 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -40,7 +40,10 @@
// new_klass may be NULL prior to class linker initialization.
// We don't mark the card as this occurs as part of object allocation. Not all objects have
// backing cards, such as large objects.
- SetFieldObjectWithoutWriteBarrier(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false);
+ // We use non transactional version since we can't undo this write. We also disable checking as
+ // we may run in transaction mode here.
+ SetFieldObjectWithoutWriteBarrier<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, klass_),
+ new_klass, false, false);
}
inline LockWord Object::GetLockWord() {
@@ -48,12 +51,14 @@
}
inline void Object::SetLockWord(LockWord new_val) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
+ // Force use of non-transactional mode and do not check.
+ SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
}
inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) {
- return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
- new_val.GetValue());
+ // Force use of non-transactional mode and do not check.
+ return CasField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
+ new_val.GetValue());
}
inline uint32_t Object::GetLockOwnerThreadId() {
@@ -199,6 +204,18 @@
return down_cast<LongArray*>(this);
}
+inline FloatArray* Object::AsFloatArray() {
+ DCHECK(GetClass()->IsArrayClass());
+ DCHECK(GetClass()->GetComponentType()->IsPrimitiveFloat());
+ return down_cast<FloatArray*>(this);
+}
+
+inline DoubleArray* Object::AsDoubleArray() {
+ DCHECK(GetClass()->IsArrayClass());
+ DCHECK(GetClass()->GetComponentType()->IsPrimitiveDouble());
+ return down_cast<DoubleArray*>(this);
+}
+
inline String* Object::AsString() {
DCHECK(GetClass()->IsStringClass());
return down_cast<String*>(this);
@@ -253,8 +270,16 @@
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
bool this_is_valid) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, GetField32(field_offset, is_volatile),
+ is_volatile);
+ }
if (this_is_valid) {
VerifyObject(this);
}
@@ -269,7 +294,14 @@
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline bool Object::CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
+ }
VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
@@ -289,8 +321,16 @@
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
bool this_is_valid) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField64(this, field_offset, GetField64(field_offset, is_volatile),
+ is_volatile);
+ }
if (this_is_valid) {
VerifyObject(this);
}
@@ -309,7 +349,14 @@
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline bool Object::CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true);
+ }
VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(raw_addr);
@@ -331,8 +378,17 @@
return result;
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
bool is_volatile, bool this_is_valid) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset,
+ GetFieldObject<Object>(field_offset, is_volatile),
+ true);
+ }
if (this_is_valid) {
VerifyObject(this);
}
@@ -349,16 +405,26 @@
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
bool this_is_valid) {
- SetFieldObjectWithoutWriteBarrier(field_offset, new_value, is_volatile, this_is_valid);
+ SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(field_offset, new_value,
+ is_volatile,
+ this_is_valid);
if (new_value != nullptr) {
CheckFieldAssignment(field_offset, new_value);
Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
}
}
+template<bool kTransactionActive, bool kCheckTransaction>
inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+ }
VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);