diff options
| -rw-r--r-- | Android.mk | 2 | ||||
| -rw-r--r-- | build/Android.common.mk | 2 | ||||
| -rw-r--r-- | build/Android.gtest.mk | 4 | ||||
| -rw-r--r-- | compiler/optimizing/nodes_vector.h | 5 | ||||
| -rw-r--r-- | oatdump/oatdump.cc | 5 | ||||
| -rw-r--r-- | profman/profman.cc | 1 | ||||
| -rw-r--r-- | runtime/Android.bp | 3 | ||||
| -rw-r--r-- | runtime/gc/space/region_space.cc | 67 | ||||
| -rw-r--r-- | runtime/gc/space/region_space.h | 5 | ||||
| -rw-r--r-- | runtime/monitor.cc | 4 | ||||
| -rw-r--r-- | test/682-double-catch-phi/expected.txt | 1 | ||||
| -rw-r--r-- | test/682-double-catch-phi/info.txt | 1 | ||||
| -rw-r--r-- | test/682-double-catch-phi/smali/DoubleCatchPhi.smali | 47 | ||||
| -rw-r--r-- | test/682-double-catch-phi/src/Main.java | 26 |
14 files changed, 162 insertions, 11 deletions
diff --git a/Android.mk b/Android.mk index e4f4e74cb2..47bcaac343 100644 --- a/Android.mk +++ b/Android.mk @@ -298,7 +298,7 @@ test-art-target-jit$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-jit $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) # Secondary target architecture variants: -ifdef TARGET_2ND_ARCH +ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX .PHONY: test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \ test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) diff --git a/build/Android.common.mk b/build/Android.common.mk index b0fa124e48..a6a9f0fc47 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -59,8 +59,6 @@ ifdef TARGET_2ND_ARCH ART_PHONY_TEST_TARGET_SUFFIX := 64 2ND_ART_PHONY_TEST_TARGET_SUFFIX := 32 else - # TODO: ??? - $(warning Do not know what to do with this multi-target configuration!) ART_PHONY_TEST_TARGET_SUFFIX := 32 2ND_ART_PHONY_TEST_TARGET_SUFFIX := endif diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3b2a5bef26..a4a72f4ccc 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -696,7 +696,7 @@ endef # define-art-gtest-host-both ifeq ($(ART_BUILD_TARGET),true) $(foreach file,$(ART_TARGET_GTEST_FILES), $(eval $(call define-art-gtest-target,$(file),))) - ifdef TARGET_2ND_ARCH + ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX $(foreach file,$(2ND_ART_TARGET_GTEST_FILES), $(eval $(call define-art-gtest-target,$(file),2ND_))) endif # Rules to run the different architecture versions of the gtest. @@ -756,7 +756,7 @@ $(eval $(call define-test-art-gtest-combination,target,TARGET,,)) $(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,)) $(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX))) $(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX))) -ifdef TARGET_2ND_ARCH +ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX $(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) $(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) endif diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 9b114eb1f7..1a484e1944 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -171,9 +171,12 @@ class HVecOperation : public HVariableInputSizeInstruction { if (instruction->IsVecOperation()) { return !instruction->IsVecExtractScalar(); // only scalar returning vec op } else if (instruction->IsPhi()) { + // Vectorizer only uses Phis in reductions, so checking for a 2-way phi + // with a direct vector operand as second argument suffices. return instruction->GetType() == kSIMDType && - instruction->InputAt(1)->IsVecOperation(); // vectorizer does not go deeper + instruction->InputCount() == 2 && + instruction->InputAt(1)->IsVecOperation(); } return false; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7530a9c5a8..6a68b55fad 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1714,7 +1714,10 @@ class OatDumper { CHECK(dex_cache != nullptr); ArtMethod* method = runtime->GetClassLinker()->ResolveMethodWithoutInvokeType( dex_method_idx, dex_cache, *options_.class_loader_); - CHECK(method != nullptr); + if (method == nullptr) { + soa.Self()->ClearException(); + return nullptr; + } return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, class_def, code_item, method, method_access_flags); diff --git a/profman/profman.cc b/profman/profman.cc index 4e1ea23e08..f2cec47da3 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -19,7 +19,6 @@ #include <stdlib.h> #include <sys/file.h> #include <sys/param.h> -#include <sys/stat.h> #include <unistd.h> #include <fstream> diff --git a/runtime/Android.bp b/runtime/Android.bp index 9512d2b367..6b432058d9 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -241,6 +241,9 @@ cc_defaults { "entrypoints/quick/quick_trampoline_entrypoints.cc", ], + // b/77976998, clang lld does not recognize the --keep-unique flag. + use_clang_lld: false, + arch: { arm: { clang_asflags: ["-no-integrated-as"], diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index 6a01c88c5d..74abe1c2ab 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -29,10 +29,19 @@ namespace space { // value of the region size, evaculate the region. static constexpr uint kEvacuateLivePercentThreshold = 75U; -// If we protect the cleared regions. +// Whether we protect the cleared regions. // Only protect for target builds to prevent flaky test failures (b/63131961). static constexpr bool kProtectClearedRegions = kIsTargetBuild; +// Wether we poison memory areas occupied by dead objects in unevacuated regions. +static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = kIsDebugBuild; + +// Special 32-bit value used to poison memory areas occupied by dead +// objects in unevacuated regions. Dereferencing this value is expected +// to trigger a memory protection fault, as it is unlikely that it +// points to a valid, non-protected memory area. +static constexpr uint32_t kPoisonDeadObject = 0xBADDB01D; // "BADDROID" + MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin) { CHECK_ALIGNED(capacity, kRegionSize); @@ -370,6 +379,13 @@ void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, // as they are unevac regions that are live. // Subtract one for the for-loop. i += regions_to_clear_bitmap - 1; + } else { + // Only some allocated bytes are live in this unevac region. + // This should only happen for an allocated non-large region. + DCHECK(r->IsAllocated()) << r->State(); + if (kPoisonDeadObjectsInUnevacuatedRegions) { + PoisonDeadObjectsInUnevacuatedRegion(r); + } } } // Note r != last_checked_region if r->IsInUnevacFromSpace() was true above. @@ -388,6 +404,55 @@ void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, num_evac_regions_ = 0; } +// Poison the memory area in range [`begin`, `end`) with value `kPoisonDeadObject`. +static void PoisonUnevacuatedRange(uint8_t* begin, uint8_t* end) { + static constexpr size_t kPoisonDeadObjectSize = sizeof(kPoisonDeadObject); + static_assert(IsPowerOfTwo(kPoisonDeadObjectSize) && + IsPowerOfTwo(RegionSpace::kAlignment) && + (kPoisonDeadObjectSize < RegionSpace::kAlignment), + "RegionSpace::kAlignment should be a multiple of kPoisonDeadObjectSize" + " and both should be powers of 2"); + DCHECK_ALIGNED(begin, kPoisonDeadObjectSize); + DCHECK_ALIGNED(end, kPoisonDeadObjectSize); + uint32_t* begin_addr = reinterpret_cast<uint32_t*>(begin); + uint32_t* end_addr = reinterpret_cast<uint32_t*>(end); + std::fill(begin_addr, end_addr, kPoisonDeadObject); +} + +void RegionSpace::PoisonDeadObjectsInUnevacuatedRegion(Region* r) { + // The live byte count of `r` should be different from -1, as this + // region should neither be a newly allocated region nor an + // evacuated region. + DCHECK_NE(r->LiveBytes(), static_cast<size_t>(-1)); + + // Past-the-end address of the previously visited (live) object (or + // the beginning of the region, if `maybe_poison` has not run yet). + uint8_t* prev_obj_end = reinterpret_cast<uint8_t*>(r->Begin()); + + // Functor poisoning the space between `obj` and the previously + // visited (live) object (or the beginng of the region), if any. + auto maybe_poison = [this, &prev_obj_end](mirror::Object* obj) REQUIRES(Locks::mutator_lock_) { + DCHECK_ALIGNED(obj, kAlignment); + uint8_t* cur_obj_begin = reinterpret_cast<uint8_t*>(obj); + if (cur_obj_begin != prev_obj_end) { + // There is a gap (dead object(s)) between the previously + // visited (live) object (or the beginning of the region) and + // `obj`; poison that space. + PoisonUnevacuatedRange(prev_obj_end, cur_obj_begin); + } + prev_obj_end = reinterpret_cast<uint8_t*>(GetNextObject(obj)); + }; + + // Visit live objects in `r` and poison gaps (dead objects) between them. + GetLiveBitmap()->VisitMarkedRange(reinterpret_cast<uintptr_t>(r->Begin()), + reinterpret_cast<uintptr_t>(r->Top()), + maybe_poison); + // Poison memory between the last live object and the end of the region, if any. + if (prev_obj_end < r->Top()) { + PoisonUnevacuatedRange(prev_obj_end, r->Top()); + } +} + void RegionSpace::LogFragmentationAllocFailure(std::ostream& os, size_t /* failed_alloc_bytes */) { size_t max_contiguous_allocation = 0; diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index c7e18885db..ab18b1bcb9 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -595,6 +595,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { /* out */ size_t* bytes_tl_bulk_allocated, /* out */ size_t* next_region = nullptr) REQUIRES(region_lock_); + // Poison memory areas used by dead objects within unevacuated + // region `r`. This is meant to detect dangling references to dead + // objects earlier in debug mode. + void PoisonDeadObjectsInUnevacuatedRegion(Region* r); + Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; uint32_t time_; // The time as the number of collections since the startup. diff --git a/runtime/monitor.cc b/runtime/monitor.cc index f246d8b1c0..2c38de5dae 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -59,8 +59,8 @@ static constexpr uint64_t kLongWaitMs = 100 * kDebugThresholdFudgeFactor; * though, because we have a full 32 bits to work with. * * The two states of an Object's lock are referred to as "thin" and "fat". A lock may transition - * from the "thin" state to the "fat" state and this transition is referred to as inflation. Once - * a lock has been inflated it remains in the "fat" state indefinitely. + * from the "thin" state to the "fat" state and this transition is referred to as inflation. We + * deflate locks from time to time as part of heap trimming. * * The lock value itself is stored in mirror::Object::monitor_ and the representation is described * in the LockWord value type. diff --git a/test/682-double-catch-phi/expected.txt b/test/682-double-catch-phi/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/682-double-catch-phi/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/682-double-catch-phi/info.txt b/test/682-double-catch-phi/info.txt new file mode 100644 index 0000000000..0ef2e59e9b --- /dev/null +++ b/test/682-double-catch-phi/info.txt @@ -0,0 +1 @@ +Regression test on double-typed catch phi diff --git a/test/682-double-catch-phi/smali/DoubleCatchPhi.smali b/test/682-double-catch-phi/smali/DoubleCatchPhi.smali new file mode 100644 index 0000000000..1d3f927bb2 --- /dev/null +++ b/test/682-double-catch-phi/smali/DoubleCatchPhi.smali @@ -0,0 +1,47 @@ +# Copyright (C) 2018 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. + +.class public LDoubleCatchPhi; + +.super Ljava/lang/Object; + +.field public mValue:D + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public strangeMethod(F)V +.registers 6 + move-object v2, v4 + monitor-enter v4 +:try_start1 + float-to-double v0, v5 + iput-wide v0, v4, LDoubleCatchPhi;->mValue:D + monitor-exit v2 +:try_end1 + goto :end_catch +:catch +:try_start2 + move-exception v3 + monitor-exit v2 +:try_end2 + throw v3 +:end_catch + return-void +.catchall {:try_start1 .. :try_end1} :catch +.catchall {:try_start2 .. :try_end2} :catch +.end method diff --git a/test/682-double-catch-phi/src/Main.java b/test/682-double-catch-phi/src/Main.java new file mode 100644 index 0000000000..e65bf0d4d4 --- /dev/null +++ b/test/682-double-catch-phi/src/Main.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 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. + */ + +import java.lang.reflect.Method; + +public class Main { + public static void main(String[] args) throws Exception { + if (System.getProperty("java.vm.name").equals("Dalvik")) { + Class<?> c = Class.forName("DoubleCatchPhi"); + } + System.out.println("passed"); + } +} |