diff options
| -rw-r--r-- | build/Android.gtest.mk | 2 | ||||
| -rw-r--r-- | runtime/Android.bp | 2 | ||||
| -rw-r--r-- | runtime/gc/collector/concurrent_copying-inl.h | 6 | ||||
| -rw-r--r-- | runtime/gc/collector/concurrent_copying.cc | 10 | ||||
| -rw-r--r-- | runtime/gc/collector/concurrent_copying.h | 9 | ||||
| -rw-r--r-- | runtime/gc/heap.cc | 6 | ||||
| -rw-r--r-- | runtime/gc/heap.h | 5 | ||||
| -rw-r--r-- | runtime/gc/heap_verification_test.cc | 119 | ||||
| -rw-r--r-- | runtime/gc/verification.cc | 141 | ||||
| -rw-r--r-- | runtime/gc/verification.h | 67 | ||||
| -rw-r--r-- | test/911-get-stack-trace/expected.txt | 70 | ||||
| -rw-r--r-- | test/911-get-stack-trace/src/art/PrintThread.java | 22 | ||||
| -rw-r--r-- | test/911-get-stack-trace/src/art/Test911.java | 33 | ||||
| -rwxr-xr-x | test/testrunner/run_build_test_target.py | 6 | ||||
| -rw-r--r-- | test/valgrind-suppressions.txt | 6 | ||||
| -rw-r--r-- | test/valgrind-target-suppressions.txt | 7 |
16 files changed, 455 insertions, 56 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index ed34a8df5f..11af1c0ca8 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -125,6 +125,7 @@ ART_GTEST_stub_test_DEX_DEPS := AllFields 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_reflection_test_DEX_DEPS := 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 011ef65dac..0860b2e899 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -93,6 +93,7 @@ cc_defaults { "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 @@ art_cc_test { "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 8a9b11dc66..dd449f991b 100644 --- a/runtime/gc/collector/concurrent_copying-inl.h +++ b/runtime/gc/collector/concurrent_copying-inl.h @@ -96,7 +96,9 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { } 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 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) { 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 a091fcedd6..80b569add3 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 @@ bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) { 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 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref) { 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 398a7e2ae7..c21520d2fa 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -106,7 +106,9 @@ class ConcurrentCopying : public GarbageCollector { 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 @@ class ConcurrentCopying : public GarbageCollector { 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 df12d8784d..64dce5f4e7 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 @@ Heap::Heap(size_t initial_size, 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 @@ mirror::Object* Heap::AllocWithNewTLAB(Thread* self, 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 241d84ce22..aa123d8736 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -64,6 +64,7 @@ class AllocRecordObjectMap; class GcPauseListener; class ReferenceProcessor; class TaskProcessor; +class Verification; namespace accounting { class HeapBitmap; @@ -821,6 +822,8 @@ class Heap { // 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 @@ class Heap { // 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 0000000000..480ba2abb1 --- /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 0000000000..9e79cb4a5f --- /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 0000000000..3d95d93015 --- /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 2318414010..fb5f71bda2 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 @@ From top 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 @@ From top 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 @@ From top 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 @@ ReferenceQueueDaemon Signal Catcher --------- -main +Test911 --------- +main +<not printed> +--------- AllTraces Thread 0 wait ()V -1 -2 printOrWait (IILart/ControlData;)V 24 47 @@ -356,14 +357,16 @@ ReferenceQueueDaemon 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 @@ ReferenceQueueDaemon 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,7 +624,11 @@ ThreadListTraces Thread 6 ThreadListTraces Thread 8 --------- -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 32 46 --------- ThreadListTraces Thread 0 @@ -659,12 +671,11 @@ ThreadListTraces Thread 8 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 + doTest ()V 121 56 + run ()V 32 46 --------- ThreadListTraces Thread 0 @@ -771,25 +782,16 @@ ThreadListTraces Thread 8 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 de1da9c8a6..f50a66b963 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 @@ package art; 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 @@ public class PrintThread { 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 @@ public class PrintThread { 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 71a5196c22..ee5936823b 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 @@ public class Test911 { 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 0ab50afa40..b1274c901a 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -62,7 +62,7 @@ custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true' 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 target.get('make'): 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 target.get('golem'): 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 c775f98b70..086a856f51 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 452a17412a..0d63a1c7aa 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 +} |