summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--build/Android.common.mk2
-rw-r--r--build/Android.gtest.mk4
-rw-r--r--compiler/optimizing/nodes_vector.h5
-rw-r--r--oatdump/oatdump.cc5
-rw-r--r--profman/profman.cc1
-rw-r--r--runtime/Android.bp3
-rw-r--r--runtime/gc/space/region_space.cc67
-rw-r--r--runtime/gc/space/region_space.h5
-rw-r--r--runtime/monitor.cc4
-rw-r--r--test/682-double-catch-phi/expected.txt1
-rw-r--r--test/682-double-catch-phi/info.txt1
-rw-r--r--test/682-double-catch-phi/smali/DoubleCatchPhi.smali47
-rw-r--r--test/682-double-catch-phi/src/Main.java26
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");
+ }
+}