Merge "ART: Add object-readbarrier-inl.h"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index ed34a8d..11af1c0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -125,6 +125,7 @@
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
ART_GTEST_unstarted_runtime_test_DEX_DEPS := Nested
+ART_GTEST_heap_verification_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex
ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
@@ -655,6 +656,7 @@
ART_GTEST_stub_test_DEX_DEPS :=
ART_GTEST_transaction_test_DEX_DEPS :=
ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
+ART_GTEST_heap_verification_test_DEX_DEPS :=
ART_GTEST_verifier_deps_test_DEX_DEPS :=
ART_VALGRIND_DEPENDENCIES :=
ART_VALGRIND_TARGET_DEPENDENCIES :=
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 011ef65..0860b2e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -93,6 +93,7 @@
"gc/space/space.cc",
"gc/space/zygote_space.cc",
"gc/task_processor.cc",
+ "gc/verification.cc",
"hprof/hprof.cc",
"image.cc",
"indirect_reference_table.cc",
@@ -547,6 +548,7 @@
"gc/accounting/space_bitmap_test.cc",
"gc/collector/immune_spaces_test.cc",
"gc/heap_test.cc",
+ "gc/heap_verification_test.cc",
"gc/reference_queue_test.cc",
"gc/space/dlmalloc_space_static_test.cc",
"gc/space/dlmalloc_space_random_test.cc",
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 8a9b11d..dd449f9 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -96,7 +96,9 @@
}
template<bool kGrayImmuneObject, bool kFromGCThread>
-inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
+inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref,
+ mirror::Object* holder,
+ MemberOffset offset) {
if (from_ref == nullptr) {
return nullptr;
}
@@ -141,7 +143,7 @@
if (immune_spaces_.ContainsObject(from_ref)) {
return MarkImmuneSpace<kGrayImmuneObject>(from_ref);
} else {
- return MarkNonMoving(from_ref);
+ return MarkNonMoving(from_ref, holder, offset);
}
default:
UNREACHABLE();
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index a091fce..80b569a 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -29,6 +29,7 @@
#include "gc/reference_processor.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
+#include "gc/verification.h"
#include "image-inl.h"
#include "intern_table.h"
#include "mirror/class-inl.h"
@@ -2362,7 +2363,9 @@
return alloc_stack->Contains(ref);
}
-mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref) {
+mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref,
+ mirror::Object* holder,
+ MemberOffset offset) {
// ref is in a non-moving space (from_ref == to_ref).
DCHECK(!region_space_->HasAddress(ref)) << ref;
DCHECK(!immune_spaces_.ContainsObject(ref));
@@ -2408,6 +2411,11 @@
return ref;
}
}
+ if (is_los && !IsAligned<kPageSize>(ref)) {
+ // Ref is a large object that is not aligned, it must be heap corruption. Dump data before
+ // AtomicSetReadBarrierState since it will fault if the address is not valid.
+ heap_->GetVerification()->LogHeapCorruption(ref, offset, holder, /* fatal */ true);
+ }
// Not marked or on the allocation stack. Try to mark it.
// This may or may not succeed, which is ok.
bool cas_success = false;
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 398a7e2..c21520d 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -106,7 +106,9 @@
return IsMarked(ref) == ref;
}
template<bool kGrayImmuneObject = true, bool kFromGCThread = false>
- ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref)
+ ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref,
+ mirror::Object* holder = nullptr,
+ MemberOffset offset = MemberOffset(0))
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
ALWAYS_INLINE mirror::Object* MarkFromReadBarrier(mirror::Object* from_ref)
@@ -224,7 +226,10 @@
void DisableMarking() REQUIRES_SHARED(Locks::mutator_lock_);
void IssueDisableMarkingCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_);
void ExpandGcMarkStack() REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::Object* MarkNonMoving(mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_)
+ mirror::Object* MarkNonMoving(mirror::Object* from_ref,
+ mirror::Object* holder = nullptr,
+ MemberOffset offset = MemberOffset(0))
+ REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(mirror::Object* from_ref,
accounting::SpaceBitmap<kObjectAlignment>* bitmap)
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index df12d87..64dce5f 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -60,6 +60,7 @@
#include "gc/space/space-inl.h"
#include "gc/space/zygote_space.h"
#include "gc/task_processor.h"
+#include "gc/verification.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "gc_pause_listener.h"
#include "heap-inl.h"
@@ -287,6 +288,7 @@
CHECK_EQ(foreground_collector_type_, kCollectorTypeCC);
CHECK_EQ(background_collector_type_, kCollectorTypeCCBackground);
}
+ verification_.reset(new Verification(this));
CHECK_GE(large_object_threshold, kMinLargeObjectThreshold);
ScopedTrace trace(__FUNCTION__);
Runtime* const runtime = Runtime::Current();
@@ -4268,5 +4270,9 @@
return ret;
}
+const Verification* Heap::GetVerification() const {
+ return verification_.get();
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 241d84c..aa123d8 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -64,6 +64,7 @@
class GcPauseListener;
class ReferenceProcessor;
class TaskProcessor;
+class Verification;
namespace accounting {
class HeapBitmap;
@@ -821,6 +822,8 @@
// reasons, we assume it stays valid when we read it (so that we don't require a lock).
void RemoveGcPauseListener();
+ const Verification* GetVerification() const;
+
private:
class ConcurrentGCTask;
class CollectorTransitionTask;
@@ -1433,6 +1436,8 @@
// An installed GC Pause listener.
Atomic<GcPauseListener*> gc_pause_listener_;
+ std::unique_ptr<Verification> verification_;
+
friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
friend class collector::MarkCompact;
diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc
new file mode 100644
index 0000000..480ba2a
--- /dev/null
+++ b/runtime/gc/heap_verification_test.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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 "common_runtime_test.h"
+#include "verification.h"
+#include "mirror/string.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+namespace gc {
+
+class VerificationTest : public CommonRuntimeTest {
+ protected:
+ VerificationTest() {}
+
+ template <class T>
+ mirror::ObjectArray<T>* AllocObjectArray(Thread* self, size_t length)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ return mirror::ObjectArray<T>::Alloc(
+ self,
+ class_linker->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass),
+ length);
+ }
+};
+
+TEST_F(VerificationTest, IsValidHeapObjectAddress) {
+ ScopedObjectAccess soa(Thread::Current());
+ const Verification* const v = Runtime::Current()->GetHeap()->GetVerification();
+ EXPECT_FALSE(v->IsValidHeapObjectAddress(reinterpret_cast<const void*>(1)));
+ EXPECT_FALSE(v->IsValidHeapObjectAddress(reinterpret_cast<const void*>(4)));
+ EXPECT_FALSE(v->IsValidHeapObjectAddress(nullptr));
+ VariableSizedHandleScope hs(soa.Self());
+ Handle<mirror::String> string(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
+ EXPECT_TRUE(v->IsValidHeapObjectAddress(string.Get()));
+ EXPECT_TRUE(v->IsValidHeapObjectAddress(string->GetClass()));
+ const uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
+ // Not actually a valid object but the verification can't know that. Guaranteed to be inside a
+ // heap space.
+ EXPECT_TRUE(v->IsValidHeapObjectAddress(
+ reinterpret_cast<const void*>(uint_klass + kObjectAlignment)));
+ EXPECT_FALSE(v->IsValidHeapObjectAddress(
+ reinterpret_cast<const void*>(&uint_klass)));
+}
+
+TEST_F(VerificationTest, IsValidClass) {
+ ScopedObjectAccess soa(Thread::Current());
+ VariableSizedHandleScope hs(soa.Self());
+ Handle<mirror::String> string(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
+ const Verification* const v = Runtime::Current()->GetHeap()->GetVerification();
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(1)));
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(4)));
+ EXPECT_FALSE(v->IsValidClass(nullptr));
+ EXPECT_FALSE(v->IsValidClass(string.Get()));
+ EXPECT_TRUE(v->IsValidClass(string->GetClass()));
+ const uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(uint_klass - kObjectAlignment)));
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(&uint_klass)));
+}
+
+TEST_F(VerificationTest, DumpObjectInfo) {
+ ScopedLogSeverity sls(LogSeverity::INFO);
+ ScopedObjectAccess soa(Thread::Current());
+ Runtime* const runtime = Runtime::Current();
+ VariableSizedHandleScope hs(soa.Self());
+ Handle<mirror::String> string(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "obj")));
+ Handle<mirror::ObjectArray<mirror::Object>> arr(
+ hs.NewHandle(AllocObjectArray<mirror::Object>(soa.Self(), 256)));
+ const Verification* const v = runtime->GetHeap()->GetVerification();
+ LOG(INFO) << v->DumpObjectInfo(reinterpret_cast<const void*>(1), "obj");
+ LOG(INFO) << v->DumpObjectInfo(reinterpret_cast<const void*>(4), "obj");
+ LOG(INFO) << v->DumpObjectInfo(nullptr, "obj");
+ LOG(INFO) << v->DumpObjectInfo(string.Get(), "test");
+ LOG(INFO) << v->DumpObjectInfo(string->GetClass(), "obj");
+ const uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
+ LOG(INFO) << v->DumpObjectInfo(reinterpret_cast<const void*>(uint_klass - kObjectAlignment),
+ "obj");
+ LOG(INFO) << v->DumpObjectInfo(reinterpret_cast<const void*>(&uint_klass), "obj");
+ LOG(INFO) << v->DumpObjectInfo(arr.Get(), "arr");
+}
+
+TEST_F(VerificationTest, LogHeapCorruption) {
+ ScopedLogSeverity sls(LogSeverity::INFO);
+ ScopedObjectAccess soa(Thread::Current());
+ Runtime* const runtime = Runtime::Current();
+ VariableSizedHandleScope hs(soa.Self());
+ Handle<mirror::String> string(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "obj")));
+ using ObjArray = mirror::ObjectArray<mirror::Object>;
+ Handle<ObjArray> arr(
+ hs.NewHandle(AllocObjectArray<mirror::Object>(soa.Self(), 256)));
+ const Verification* const v = runtime->GetHeap()->GetVerification();
+ arr->Set(0, string.Get());
+ // Test normal cases.
+ v->LogHeapCorruption(arr.Get(), ObjArray::DataOffset(kHeapReferenceSize), string.Get(), false);
+ v->LogHeapCorruption(string.Get(), mirror::Object::ClassOffset(), string->GetClass(), false);
+ // Test null holder cases.
+ v->LogHeapCorruption(nullptr, MemberOffset(0), string.Get(), false);
+ v->LogHeapCorruption(nullptr, MemberOffset(0), arr.Get(), false);
+}
+
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
new file mode 100644
index 0000000..9e79cb4
--- /dev/null
+++ b/runtime/gc/verification.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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 "verification.h"
+
+#include <iomanip>
+#include <sstream>
+
+#include "mirror/class-inl.h"
+
+namespace art {
+namespace gc {
+
+std::string Verification::DumpObjectInfo(const void* addr, const char* tag) const {
+ std::ostringstream oss;
+ oss << tag << "=" << addr;
+ if (IsValidHeapObjectAddress(addr)) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<void*>(addr));
+ mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ oss << " klass=" << klass;
+ if (IsValidClass(klass)) {
+ oss << "(" << klass->PrettyClass() << ")";
+ if (klass->IsArrayClass<kVerifyNone, kWithoutReadBarrier>()) {
+ oss << " length=" << obj->AsArray<kVerifyNone, kWithoutReadBarrier>()->GetLength();
+ }
+ } else {
+ oss << " <invalid address>";
+ }
+ space::Space* const space = heap_->FindSpaceFromAddress(addr);
+ if (space != nullptr) {
+ oss << " space=" << *space;
+ }
+ accounting::CardTable* card_table = heap_->GetCardTable();
+ if (card_table->AddrIsInCardTable(addr)) {
+ oss << " card=" << static_cast<size_t>(
+ card_table->GetCard(reinterpret_cast<const mirror::Object*>(addr)));
+ }
+ // Dump adjacent RAM.
+ const uintptr_t uint_addr = reinterpret_cast<uintptr_t>(addr);
+ static constexpr size_t kBytesBeforeAfter = 2 * kObjectAlignment;
+ const uintptr_t dump_start = uint_addr - kBytesBeforeAfter;
+ const uintptr_t dump_end = uint_addr + kBytesBeforeAfter;
+ if (dump_start < dump_end &&
+ IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_start)) &&
+ IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_end - kObjectAlignment))) {
+ oss << " adjacent_ram=";
+ for (uintptr_t p = dump_start; p < dump_end; ++p) {
+ if (p == uint_addr) {
+ // Marker of where the object is.
+ oss << "|";
+ }
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(p);
+ oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr);
+ }
+ }
+ } else {
+ oss << " <invalid address>";
+ }
+ return oss.str();
+}
+
+void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder,
+ MemberOffset offset,
+ mirror::Object* ref,
+ bool fatal) const {
+ // Lowest priority logging first:
+ PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ // Buffer the output in the string stream since it is more important than the stack traces
+ // and we want it to have log priority. The stack traces are printed from Runtime::Abort
+ // which is called from LOG(FATAL) but before the abort message.
+ std::ostringstream oss;
+ oss << "GC tried to mark invalid reference " << ref << std::endl;
+ oss << DumpObjectInfo(ref, "ref") << "\n";
+ if (holder != nullptr) {
+ oss << DumpObjectInfo(holder.Ptr(), "holder");
+ mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (IsValidClass(holder_klass)) {
+ oss << "field_offset=" << offset.Uint32Value();
+ ArtField* field = holder->FindFieldByOffset(offset);
+ if (field != nullptr) {
+ oss << " name=" << field->GetName();
+ }
+ }
+ }
+
+ if (fatal) {
+ LOG(FATAL) << oss.str();
+ } else {
+ LOG(FATAL_WITHOUT_ABORT) << oss.str();
+ }
+}
+
+bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const {
+ if (!IsAligned<kObjectAlignment>(addr)) {
+ return false;
+ }
+ space::Space* const space = heap_->FindSpaceFromAddress(addr);
+ if (space != nullptr) {
+ if (out_space != nullptr) {
+ *out_space = space;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Verification::IsValidClass(const void* addr) const {
+ if (!IsValidHeapObjectAddress(addr)) {
+ return false;
+ }
+ mirror::Class* klass = reinterpret_cast<mirror::Class*>(const_cast<void*>(addr));
+ mirror::Class* k1 = klass->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (!IsValidHeapObjectAddress(k1)) {
+ return false;
+ }
+ // k should be class class, take the class again to verify.
+ // Note that this check may not be valid for the no image space since the class class might move
+ // around from moving GC.
+ mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (!IsValidHeapObjectAddress(k2)) {
+ return false;
+ }
+ return k1 == k2;
+}
+
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/verification.h b/runtime/gc/verification.h
new file mode 100644
index 0000000..3d95d93
--- /dev/null
+++ b/runtime/gc/verification.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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_RUNTIME_GC_VERIFICATION_H_
+#define ART_RUNTIME_GC_VERIFICATION_H_
+
+#include "obj_ptr.h"
+#include "offsets.h"
+
+namespace art {
+
+namespace mirror {
+class Class;
+class Object;
+} // namespace mirror
+
+namespace gc {
+
+namespace space {
+class Space;
+} // namespace space
+
+class Heap;
+
+class Verification {
+ public:
+ explicit Verification(gc::Heap* heap) : heap_(heap) {}
+
+ // Dump some reveant to debugging info about an object.
+ std::string DumpObjectInfo(const void* obj, const char* tag) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Don't use ObjPtr for things that might not be aligned like the invalid reference.
+ void LogHeapCorruption(ObjPtr<mirror::Object> holder,
+ MemberOffset offset,
+ mirror::Object* ref,
+ bool fatal) const REQUIRES_SHARED(Locks::mutator_lock_);
+
+
+ // Return true if the klass is likely to be a valid mirror::Class.
+ bool IsValidClass(const void* klass) const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Does not allow null.
+ bool IsValidHeapObjectAddress(const void* addr, space::Space** out_space = nullptr) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ gc::Heap* const heap_;
+};
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_VERIFICATION_H_
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 2318414..fb5f71b 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -4,7 +4,7 @@
From top
---------
getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
- print (Ljava/lang/Thread;II)V 0 36
+ print (Ljava/lang/Thread;II)V 0 38
printOrWait (IILart/ControlData;)V 6 41
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -22,10 +22,9 @@
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
doTest ()V 38 25
- run ()V 20 26
- main ([Ljava/lang/String;)V 0 19
+ run ()V 0 30
---------
- print (Ljava/lang/Thread;II)V 0 36
+ print (Ljava/lang/Thread;II)V 0 38
printOrWait (IILart/ControlData;)V 6 41
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -43,11 +42,10 @@
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
doTest ()V 42 26
- run ()V 20 26
- main ([Ljava/lang/String;)V 0 19
+ run ()V 0 30
---------
getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
- print (Ljava/lang/Thread;II)V 0 36
+ print (Ljava/lang/Thread;II)V 0 38
printOrWait (IILart/ControlData;)V 6 41
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -59,19 +57,19 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
From bottom
---------
- main ([Ljava/lang/String;)V 0 19
+ run ()V 0 30
---------
+ baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
doTest ()V 65 32
- run ()V 20 26
- main ([Ljava/lang/String;)V 0 19
+ run ()V 0 30
---------
+ bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
- doTest ()V 69 33
################################
### Other thread (suspended) ###
@@ -258,9 +256,12 @@
Signal Catcher
---------
-main
+Test911
---------
+main
+<not printed>
+---------
AllTraces Thread 0
wait ()V -1 -2
printOrWait (IILart/ControlData;)V 24 47
@@ -356,14 +357,16 @@
Signal Catcher
---------
-main
+Test911
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 75
doTest ()V 128 59
- run ()V 44 38
- main ([Ljava/lang/String;)V 0 19
+ run ()V 24 42
---------
+main
+<not printed>
+---------
AllTraces Thread 0
wait ()V -1 -2
printOrWait (IILart/ControlData;)V 24 47
@@ -589,18 +592,23 @@
Signal Catcher
---------
-main
+Test911
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 75
doTest ()V 133 61
- run ()V 44 38
- main ([Ljava/lang/String;)V 0 19
+ run ()V 24 42
+---------
+main
+<not printed>
########################################
### Other select threads (suspended) ###
########################################
---------
+Test911
+
+---------
ThreadListTraces Thread 0
---------
@@ -616,55 +624,58 @@
ThreadListTraces Thread 8
---------
-main
-
----------
-ThreadListTraces Thread 0
- wait ()V -1 -2
- printOrWait (IILart/ControlData;)V 24 47
- baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
- bar (IIILart/ControlData;)J 0 26
- foo (IIILart/ControlData;)I 0 21
-
----------
-ThreadListTraces Thread 2
- wait ()V -1 -2
- printOrWait (IILart/ControlData;)V 24 47
- baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
- bar (IIILart/ControlData;)J 0 26
- foo (IIILart/ControlData;)I 0 21
-
----------
-ThreadListTraces Thread 4
- wait ()V -1 -2
- printOrWait (IILart/ControlData;)V 24 47
- baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
- bar (IIILart/ControlData;)J 0 26
- foo (IIILart/ControlData;)I 0 21
-
----------
-ThreadListTraces Thread 6
- wait ()V -1 -2
- printOrWait (IILart/ControlData;)V 24 47
- baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
- bar (IIILart/ControlData;)J 0 26
- foo (IIILart/ControlData;)I 0 21
-
----------
-ThreadListTraces Thread 8
- wait ()V -1 -2
- printOrWait (IILart/ControlData;)V 24 47
- baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
- bar (IIILart/ControlData;)J 0 26
- foo (IIILart/ControlData;)I 0 21
-
----------
-main
+Test911
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 68
doTest ()V 116 54
- run ()V 52 42
- main ([Ljava/lang/String;)V 0 19
+ run ()V 32 46
+
+---------
+ThreadListTraces Thread 0
+ wait ()V -1 -2
+ printOrWait (IILart/ControlData;)V 24 47
+ baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
+ bar (IIILart/ControlData;)J 0 26
+ foo (IIILart/ControlData;)I 0 21
+
+---------
+ThreadListTraces Thread 2
+ wait ()V -1 -2
+ printOrWait (IILart/ControlData;)V 24 47
+ baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
+ bar (IIILart/ControlData;)J 0 26
+ foo (IIILart/ControlData;)I 0 21
+
+---------
+ThreadListTraces Thread 4
+ wait ()V -1 -2
+ printOrWait (IILart/ControlData;)V 24 47
+ baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
+ bar (IIILart/ControlData;)J 0 26
+ foo (IIILart/ControlData;)I 0 21
+
+---------
+ThreadListTraces Thread 6
+ wait ()V -1 -2
+ printOrWait (IILart/ControlData;)V 24 47
+ baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
+ bar (IIILart/ControlData;)J 0 26
+ foo (IIILart/ControlData;)I 0 21
+
+---------
+ThreadListTraces Thread 8
+ wait ()V -1 -2
+ printOrWait (IILart/ControlData;)V 24 47
+ baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
+ bar (IIILart/ControlData;)J 0 26
+ foo (IIILart/ControlData;)I 0 21
+
+---------
+Test911
+ getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
+ printList ([Ljava/lang/Thread;I)V 0 68
+ doTest ()V 121 56
+ run ()V 32 46
---------
ThreadListTraces Thread 0
@@ -771,25 +782,16 @@
foo (IIILart/ControlData;)I 0 21
run ()V 4 37
----------
-main
- getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
- printList ([Ljava/lang/Thread;I)V 0 68
- doTest ()V 121 56
- run ()V 52 42
- main ([Ljava/lang/String;)V 0 19
-
###################
### Same thread ###
###################
-5
+4
JVMTI_ERROR_ILLEGAL_ARGUMENT
[public static native java.lang.Object[] art.Frames.getFrameLocation(java.lang.Thread,int), ffffffff]
[public static void art.Frames.doTestSameThread(), 38]
[public static void art.Frames.doTest() throws java.lang.Exception, 0]
-[public static void art.Test911.run() throws java.lang.Exception, 3c]
-[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 0]
+[public void art.Test911$1.run(), 28]
JVMTI_ERROR_NO_MORE_FRAMES
################################
diff --git a/test/911-get-stack-trace/src/art/PrintThread.java b/test/911-get-stack-trace/src/art/PrintThread.java
index de1da9c..f50a66b 100644
--- a/test/911-get-stack-trace/src/art/PrintThread.java
+++ b/test/911-get-stack-trace/src/art/PrintThread.java
@@ -19,6 +19,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class PrintThread {
public static void print(String[][] stack) {
@@ -36,6 +38,20 @@
print(getStackTrace(t, start, max));
}
+ // We have to ignore some threads when printing all stack traces. These are threads that may or
+ // may not exist depending on the environment.
+ public final static String IGNORE_THREAD_NAME_REGEX =
+ "Binder:|RenderThread|hwuiTask|Jit thread pool worker|Instr:|JDWP|Profile Saver|main";
+ public final static Matcher IGNORE_THREADS =
+ Pattern.compile(IGNORE_THREAD_NAME_REGEX).matcher("");
+
+ // We have to skip the stack of some threads when printing all stack traces. These are threads
+ // that may have a different call stack (e.g., when run as an app), or may be in a
+ // non-deterministic state.
+ public final static String CUT_STACK_THREAD_NAME_REGEX = "Daemon|main";
+ public final static Matcher CUT_STACK_THREADS =
+ Pattern.compile(CUT_STACK_THREAD_NAME_REGEX).matcher("");
+
public static void printAll(Object[][] stacks) {
List<String> stringified = new ArrayList<String>(stacks.length);
@@ -43,11 +59,11 @@
Thread t = (Thread)stackInfo[0];
String name = (t != null) ? t.getName() : "null";
String stackSerialization;
- if (name.contains("Daemon")) {
+ if (CUT_STACK_THREADS.reset(name).find()) {
// Do not print daemon stacks, as they're non-deterministic.
stackSerialization = "<not printed>";
- } else if (name.startsWith("Jit thread pool worker")) {
- // Skip JIT thread pool. It may or may not be there depending on configuration.
+ } else if (IGNORE_THREADS.reset(name).find()) {
+ // Skip IGNORE_THREADS.
continue;
} else {
StringBuilder sb = new StringBuilder();
diff --git a/test/911-get-stack-trace/src/art/Test911.java b/test/911-get-stack-trace/src/art/Test911.java
index 71a5196..ee59368 100644
--- a/test/911-get-stack-trace/src/art/Test911.java
+++ b/test/911-get-stack-trace/src/art/Test911.java
@@ -23,27 +23,38 @@
Main.bindAgentJNIForClass(PrintThread.class);
Main.bindAgentJNIForClass(ThreadListTraces.class);
- SameThread.doTest();
+ Thread t = new Thread("Test911") {
+ @Override
+ public void run() {
+ try {
+ SameThread.doTest();
- System.out.println();
+ System.out.println();
- OtherThread.doTestOtherThreadWait();
+ OtherThread.doTestOtherThreadWait();
- System.out.println();
+ System.out.println();
- OtherThread.doTestOtherThreadBusyLoop();
+ OtherThread.doTestOtherThreadBusyLoop();
- System.out.println();
+ System.out.println();
- AllTraces.doTest();
+ AllTraces.doTest();
- System.out.println();
+ System.out.println();
- ThreadListTraces.doTest();
+ ThreadListTraces.doTest();
- System.out.println();
+ System.out.println();
- Frames.doTest();
+ Frames.doTest();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ t.start();
+ t.join();
System.out.println("Done");
}
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 0ab50af..b1274c9 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -62,7 +62,7 @@
print custom_env
os.environ.update(custom_env)
-if target.get('make'):
+if target.has_key('make'):
build_command = 'make'
build_command += ' -j' + str(n_threads)
build_command += ' -C ' + env.ANDROID_BUILD_TOP
@@ -74,7 +74,7 @@
if subprocess.call(build_command.split()):
sys.exit(1)
-if target.get('golem'):
+if target.has_key('golem'):
machine_type = target.get('golem')
# use art-opt-cc by default since it mimics the default preopt config.
default_golem_config = 'art-opt-cc'
@@ -92,7 +92,7 @@
if subprocess.call(cmd):
sys.exit(1)
-if target.get('run-test'):
+if target.has_key('run-test'):
run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
run_test_command += target.get('run-test', [])
diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt
index c775f98..086a856 100644
--- a/test/valgrind-suppressions.txt
+++ b/test/valgrind-suppressions.txt
@@ -69,3 +69,9 @@
fun:_ZN12BacktraceMap6CreateEib
}
+{
+ process_vm_readv
+ Memcheck:Param
+ process_vm_readv(lvec[...])
+ fun:process_vm_readv
+}
diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt
index 452a174..0d63a1c 100644
--- a/test/valgrind-target-suppressions.txt
+++ b/test/valgrind-target-suppressions.txt
@@ -67,3 +67,10 @@
fun:msync
fun:_ZN3art6MemMap11MapInternalEPvmiiilb
}
+
+{
+ process_vm_readv
+ Memcheck:Param
+ process_vm_readv(lvec[...])
+ fun:process_vm_readv
+}