Merge "Change gcstress runtest option"
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 46b7e5d..6c6d99f 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -284,7 +284,7 @@
endif
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
- # Vixl assembly support for ARM64 targets.
+ # VIXL assembly support for ARM64 targets.
ifeq ($$(art_ndebug_or_debug),debug)
ifeq ($$(art_static_or_shared), static)
LOCAL_WHOLESTATIC_LIBRARIES += libvixld-arm64
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2bf675d..4c4128c 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -432,11 +432,6 @@
(instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
- DCHECK(!(instruction_->IsArrayGet() &&
- instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
__ Bind(GetEntryLabel());
// No need to save live registers; it's taken care of by the
@@ -517,11 +512,6 @@
(instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
- DCHECK(!(instruction_->IsArrayGet() &&
- instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -4507,6 +4497,8 @@
Primitive::Type type = instruction->GetType();
HInstruction* array_instr = instruction->GetArray();
bool has_intermediate_address = array_instr->IsIntermediateAddress();
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
switch (type) {
case Primitive::kPrimBoolean:
@@ -4541,11 +4533,6 @@
}
case Primitive::kPrimNot: {
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
- DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
-
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
@@ -4688,6 +4675,8 @@
Location value_loc = locations->InAt(2);
HInstruction* array_instr = instruction->GetArray();
bool has_intermediate_address = array_instr->IsIntermediateAddress();
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -4952,6 +4941,8 @@
}
void LocationsBuilderARM::VisitIntermediateAddress(HIntermediateAddress* instruction) {
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!kEmitCompilerReadBarrier);
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -4966,6 +4957,9 @@
Location first = locations->InAt(0);
Location second = locations->InAt(1);
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!kEmitCompilerReadBarrier);
+
if (second.IsRegister()) {
__ add(out.AsRegister<Register>(),
first.AsRegister<Register>(),
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 3b8a521..d95e7df 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -598,11 +598,6 @@
(instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
- DCHECK(!(instruction_->IsArrayGet() &&
- instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
__ Bind(GetEntryLabel());
// No need to save live registers; it's taken care of by the
@@ -685,9 +680,7 @@
(instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
DCHECK(!(instruction_->IsArrayGet() &&
instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
@@ -1990,6 +1983,8 @@
}
void LocationsBuilderARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!kEmitCompilerReadBarrier);
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
@@ -1997,7 +1992,10 @@
locations->SetOut(Location::RequiresRegister());
}
-void InstructionCodeGeneratorARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
+void InstructionCodeGeneratorARM64::VisitIntermediateAddress(
+ HIntermediateAddress* instruction) {
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!kEmitCompilerReadBarrier);
__ Add(OutputRegister(instruction),
InputRegisterAt(instruction, 0),
Operand(InputOperandAt(instruction, 1)));
@@ -2093,15 +2091,11 @@
// Block pools between `Load` and `MaybeRecordImplicitNullCheck`.
BlockPoolsScope block_pools(masm);
- // The read barrier instrumentation of object ArrayGet instructions
- // does not support the HIntermediateAddress instruction.
- DCHECK(!((type == Primitive::kPrimNot) &&
- instruction->GetArray()->IsIntermediateAddress() &&
- kEmitCompilerReadBarrier));
-
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// Object ArrayGet with Baker's read barrier case.
Register temp = temps.AcquireW();
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!instruction->GetArray()->IsIntermediateAddress());
// Note that a potential implicit null check is handled in the
// CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call.
codegen_->GenerateArrayLoadWithBakerReadBarrier(
@@ -2115,6 +2109,9 @@
} else {
Register temp = temps.AcquireSameSizeAs(obj);
if (instruction->GetArray()->IsIntermediateAddress()) {
+ // The read barrier instrumentation does not support the
+ // HIntermediateAddress instruction yet.
+ DCHECK(!kEmitCompilerReadBarrier);
// We do not need to compute the intermediate address from the array: the
// input instruction has done it already. See the comment in
// `TryExtractArrayAccessAddress()`.
@@ -2204,6 +2201,9 @@
UseScratchRegisterScope temps(masm);
Register temp = temps.AcquireSameSizeAs(array);
if (instruction->GetArray()->IsIntermediateAddress()) {
+ // The read barrier instrumentation does not support the
+ // HIntermediateAddress instruction yet.
+ DCHECK(!kEmitCompilerReadBarrier);
// We do not need to compute the intermediate address from the array: the
// input instruction has done it already. See the comment in
// `TryExtractArrayAccessAddress()`.
@@ -2223,6 +2223,7 @@
codegen_->Store(value_type, value, destination);
codegen_->MaybeRecordImplicitNullCheck(instruction);
} else {
+ DCHECK(needs_write_barrier);
DCHECK(!instruction->GetArray()->IsIntermediateAddress());
vixl::aarch64::Label done;
SlowPathCodeARM64* slow_path = nullptr;
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 1b5fa85..921ce10 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -27,11 +27,11 @@
#include "utils/arm64/assembler_arm64.h"
#include "utils/type_reference.h"
-// TODO: make vixl clean wrt -Wshadow.
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
-#include "a64/disasm-a64.h"
-#include "a64/macro-assembler-a64.h"
+#include "aarch64/disasm-aarch64.h"
+#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index af0ee4e..cc949c5 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -22,8 +22,13 @@
#include "nodes.h"
#include "utils/arm64/assembler_arm64.h"
-#include "a64/disasm-a64.h"
-#include "a64/macro-assembler-a64.h"
+// TODO(VIXL): Make VIXL compile with -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "aarch64/disasm-aarch64.h"
+#include "aarch64/macro-assembler-aarch64.h"
+#include "aarch64/simulator-aarch64.h"
+#pragma GCC diagnostic pop
namespace art {
namespace arm64 {
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index 6632cd9..8f7778f 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -231,6 +231,15 @@
HInstruction* array,
HInstruction* index,
size_t data_offset) {
+ if (kEmitCompilerReadBarrier) {
+ // The read barrier instrumentation does not support the
+ // HIntermediateAddress instruction yet.
+ //
+ // TODO: Handle this case properly in the ARM64 and ARM code generator and
+ // re-enable this optimization; otherwise, remove this TODO.
+ // b/26601270
+ return false;
+ }
if (index->IsConstant() ||
(index->IsBoundsCheck() && index->AsBoundsCheck()->GetIndex()->IsConstant())) {
// When the index is a constant all the addressing can be fitted in the
@@ -242,13 +251,6 @@
// The access may require a runtime call or the original array pointer.
return false;
}
- if (kEmitCompilerReadBarrier &&
- access->IsArrayGet() &&
- access->AsArrayGet()->GetType() == Primitive::kPrimNot) {
- // For object arrays, the read barrier instrumentation requires
- // the original array pointer.
- return false;
- }
// Proceed to extract the base address computation.
HGraph* graph = access->GetBlock()->GetGraph();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index e233672..9cfe3ce 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -29,11 +29,11 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-// TODO: make vixl clean wrt -Wshadow.
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
-#include "a64/disasm-a64.h"
-#include "a64/macro-assembler-a64.h"
+#include "aarch64/disasm-aarch64.h"
+#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 22221e7..19450b3 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -36,7 +36,7 @@
}
size_t Arm64Assembler::CodeSize() const {
- return vixl_masm_.GetBufferCapacity() - vixl_masm_.GetRemainingBufferSpace();
+ return vixl_masm_.GetSizeOfCodeGenerated();
}
const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const {
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index 4e88e64..2847cb8 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -27,13 +27,11 @@
#include "utils/assembler.h"
#include "offsets.h"
-// TODO: make vixl clean wrt -Wshadow, -Wunknown-pragmas, -Wmissing-noreturn
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wshadow"
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-#include "a64/disasm-a64.h"
-#include "a64/macro-assembler-a64.h"
+#include "aarch64/disasm-aarch64.h"
+#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 79ee441..b9f6854 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -29,12 +29,10 @@
#include "utils/jni_macro_assembler.h"
#include "offsets.h"
-// TODO: make vixl clean wrt -Wshadow, -Wunknown-pragmas, -Wmissing-noreturn
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wshadow"
-#pragma GCC diagnostic ignored "-Wmissing-noreturn"
-#include "a64/macro-assembler-a64.h"
+#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index c64d8ea..7c64792 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -19,10 +19,11 @@
#include "disassembler.h"
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
-#include "a64/decoder-a64.h"
-#include "a64/disasm-a64.h"
+#include "aarch64/decoder-aarch64.h"
+#include "aarch64/disasm-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 0e2a672..492a12d 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -133,7 +133,7 @@
qpoints->pReadBarrierMarkReg09 = art_quick_read_barrier_mark_reg09;
qpoints->pReadBarrierMarkReg10 = art_quick_read_barrier_mark_reg10;
qpoints->pReadBarrierMarkReg11 = art_quick_read_barrier_mark_reg11;
- qpoints->pReadBarrierMarkReg12 = art_quick_read_barrier_mark_reg12;
+ qpoints->pReadBarrierMarkReg12 = nullptr; // Cannot use register 12 (IP) to pass arguments.
qpoints->pReadBarrierMarkReg13 = nullptr; // Cannot use register 13 (SP) to pass arguments.
qpoints->pReadBarrierMarkReg14 = nullptr; // Cannot use register 14 (LR) to pass arguments.
qpoints->pReadBarrierMarkReg15 = nullptr; // Cannot use register 15 (PC) to pass arguments.
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 3d0da80..c4ec726 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1246,9 +1246,15 @@
ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
// Read barrier for class load.
ldr r3, [r9, #THREAD_IS_GC_MARKING_OFFSET]
- cbnz r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
+ cbnz r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking
.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
+.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking:
+ cbz r2, .Lart_quick_alloc_object_region_tlab_slow_path // Null check for loading lock word.
+ // Check lock word for mark bit, if marked do the allocation.
+ ldr r3, [r2, MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ ands r3, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
+ bne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
// The read barrier slow path. Mark
// the class.
@@ -1817,6 +1823,39 @@
pop {pc}
END art_quick_l2f
+.macro CONDITIONAL_CBZ reg, reg_if, dest
+.ifc \reg, \reg_if
+ cbz \reg, \dest
+.endif
+.endm
+
+.macro CONDITIONAL_CMPBZ reg, reg_if, dest
+.ifc \reg, \reg_if
+ cmp \reg, #0
+ beq \dest
+.endif
+.endm
+
+// Use CBZ if the register is in {r0, r7} otherwise compare and branch.
+.macro SMART_CBZ reg, dest
+ CONDITIONAL_CBZ \reg, r0, \dest
+ CONDITIONAL_CBZ \reg, r1, \dest
+ CONDITIONAL_CBZ \reg, r2, \dest
+ CONDITIONAL_CBZ \reg, r3, \dest
+ CONDITIONAL_CBZ \reg, r4, \dest
+ CONDITIONAL_CBZ \reg, r5, \dest
+ CONDITIONAL_CBZ \reg, r6, \dest
+ CONDITIONAL_CBZ \reg, r7, \dest
+ CONDITIONAL_CMPBZ \reg, r8, \dest
+ CONDITIONAL_CMPBZ \reg, r9, \dest
+ CONDITIONAL_CMPBZ \reg, r10, \dest
+ CONDITIONAL_CMPBZ \reg, r11, \dest
+ CONDITIONAL_CMPBZ \reg, r12, \dest
+ CONDITIONAL_CMPBZ \reg, r13, \dest
+ CONDITIONAL_CMPBZ \reg, r14, \dest
+ CONDITIONAL_CMPBZ \reg, r15, \dest
+.endm
+
/*
* Create a function `name` calling the ReadBarrier::Mark routine,
* getting its argument and returning its result through register
@@ -1835,28 +1874,25 @@
.macro READ_BARRIER_MARK_REG name, reg
ENTRY \name
// Null check so that we can load the lock word.
- cmp \reg, #0
- beq .Lret_rb_\name
- // Check lock word for mark bit, if marked return.
- push {r0}
- ldr r0, [\reg, MIRROR_OBJECT_LOCK_WORD_OFFSET]
- and r0, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
- cbz r0, .Lslow_rb_\name
- // Restore LR and return.
- pop {r0}
- bx lr
+ SMART_CBZ \reg, .Lret_rb_\name
+ // Check lock word for mark bit, if marked return. Use IP for scratch since it is blocked.
+ ldr ip, [\reg, MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ ands ip, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
+ beq .Lslow_rb_\name
+ // Already marked, return right away.
+ bx lr
.Lslow_rb_\name:
- pop {r0}
- push {r0-r4, r9, r12, lr} @ save return address and core caller-save registers
+ push {r0-r5, r9, lr} @ save return address and core caller-save registers
+ @ also save callee save r5 for 16 byte alignment
.cfi_adjust_cfa_offset 32
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
.cfi_rel_offset r2, 8
.cfi_rel_offset r3, 12
.cfi_rel_offset r4, 16
- .cfi_rel_offset r9, 20
- .cfi_rel_offset r12, 24
+ .cfi_rel_offset r5, 20
+ .cfi_rel_offset r9, 24
.cfi_rel_offset lr, 28
vpush {s0-s15} @ save floating-point caller-save registers
.cfi_adjust_cfa_offset 64
@@ -1865,48 +1901,11 @@
mov r0, \reg @ pass arg1 - obj from `reg`
.endif
bl artReadBarrierMark @ r0 <- artReadBarrierMark(obj)
-
+ mov ip, r0 @ Save result in IP
vpop {s0-s15} @ restore floating-point registers
.cfi_adjust_cfa_offset -64
- @ If `reg` is a caller-save register, save the result to its
- @ corresponding stack slot; it will be restored by the "pop"
- @ instruction below. Otherwise, move result into `reg`.
- @
- @ (Note that saving `reg` to its stack slot will overwrite the value
- @ previously stored by the "push" instruction above. That is
- @ alright, as in that case we know that `reg` is not a live
- @ register, as it is used to pass the argument and return the result
- @ of this function.)
- .ifc \reg, r0
- PUSH_REG r0, 0 @ copy result to r0's stack location
- .else
- .ifc \reg, r1
- PUSH_REG r0, 4 @ copy result to r1's stack location
- .else
- .ifc \reg, r2
- PUSH_REG r0, 8 @ copy result to r2's stack location
- .else
- .ifc \reg, r3
- PUSH_REG r0, 12 @ copy result to r3's stack location
- .else
- .ifc \reg, r4
- PUSH_REG r0, 16 @ copy result to r4's stack location
- .else
- .ifc \reg, r9
- PUSH_REG r0, 20 @ copy result to r9's stack location
- .else
- .ifc \reg, r12
- PUSH_REG r0, 24 @ copy result to r12's stack location
- .else
- mov \reg, r0 @ return result into `reg`
- .endif
- .endif
- .endif
- .endif
- .endif
- .endif
- .endif
- pop {r0-r4, r9, r12, pc} @ restore caller-save registers and return
+ pop {r0-r5, r9, lr} @ restore caller-save registers
+ mov \reg, ip @ copy result to reg
.Lret_rb_\name:
bx lr
END \name
@@ -1924,4 +1923,3 @@
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11
-READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg12, r12
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index cc5bf29..55b09c3 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -149,7 +149,7 @@
qpoints->pReadBarrierMarkReg13 = art_quick_read_barrier_mark_reg13;
qpoints->pReadBarrierMarkReg14 = art_quick_read_barrier_mark_reg14;
qpoints->pReadBarrierMarkReg15 = art_quick_read_barrier_mark_reg15;
- qpoints->pReadBarrierMarkReg16 = art_quick_read_barrier_mark_reg16;
+ qpoints->pReadBarrierMarkReg16 = nullptr; // IP0 is used as a temp by the asm stub.
qpoints->pReadBarrierMarkReg17 = art_quick_read_barrier_mark_reg17;
qpoints->pReadBarrierMarkReg18 = art_quick_read_barrier_mark_reg18;
qpoints->pReadBarrierMarkReg19 = art_quick_read_barrier_mark_reg19;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 35f5c56..4289cab 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2751,7 +2751,7 @@
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg13, w13, x13
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg14, w14, x14
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg15, w15, x15
-READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg16, w16, x16
+// READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg16, w16, x16 ip0 is blocked
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg17, w17, x17
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg18, w18, x18
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg19, w19, x19
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 743fbb9..d717ec0 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -137,7 +137,20 @@
}
bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
- std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
+ std::string cache;
+ bool have_android_data;
+ bool dalvik_cache_exists;
+ bool is_global_cache;
+ GetDalvikCache(GetInstructionSetString(kRuntimeISA),
+ true,
+ &cache,
+ &have_android_data,
+ &dalvik_cache_exists,
+ &is_global_cache);
+ if (!dalvik_cache_exists) {
+ *error_msg = "Failed to create dalvik cache";
+ return false;
+ }
return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index b574c3b..aa3bf61 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -297,12 +297,13 @@
for (size_t index = 0; index < image_file_names.size(); ++index) {
std::string& image_name = image_file_names[index];
std::string error_msg;
- space::ImageSpace* boot_image_space = space::ImageSpace::CreateBootImage(
+ std::unique_ptr<space::ImageSpace> boot_image_space_uptr = space::ImageSpace::CreateBootImage(
image_name.c_str(),
image_instruction_set,
index > 0,
&error_msg);
- if (boot_image_space != nullptr) {
+ if (boot_image_space_uptr != nullptr) {
+ space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
AddSpace(boot_image_space);
added_image_spaces.push_back(boot_image_space);
// Oat files referenced by image files immediately follow them in memory, ensure alloc space
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 6fcad29..4505c24 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -79,7 +79,12 @@
return r;
}
-static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa,
+static int32_t ChooseRelocationOffsetDelta() {
+ return ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA, ART_BASE_ADDRESS_MAX_DELTA);
+}
+
+static bool GenerateImage(const std::string& image_filename,
+ InstructionSet image_isa,
std::string* error_msg) {
const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
std::vector<std::string> boot_class_path;
@@ -118,8 +123,7 @@
CHECK_EQ(image_isa, kRuntimeISA)
<< "We should always be generating an image for the current isa.";
- int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
- ART_BASE_ADDRESS_MAX_DELTA);
+ int32_t base_offset = ChooseRelocationOffsetDelta();
LOG(INFO) << "Using an offset of 0x" << std::hex << base_offset << " from default "
<< "art base address of 0x" << std::hex << ART_BASE_ADDRESS;
arg_vector.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset));
@@ -138,14 +142,17 @@
return Exec(arg_vector, error_msg);
}
-bool ImageSpace::FindImageFilename(const char* image_location,
- const InstructionSet image_isa,
- std::string* system_filename,
- bool* has_system,
- std::string* cache_filename,
- bool* dalvik_cache_exists,
- bool* has_cache,
- bool* is_global_cache) {
+static bool FindImageFilenameImpl(const char* image_location,
+ const InstructionSet image_isa,
+ bool* has_system,
+ std::string* system_filename,
+ bool* dalvik_cache_exists,
+ std::string* dalvik_cache,
+ bool* is_global_cache,
+ bool* has_cache,
+ std::string* cache_filename) {
+ DCHECK(dalvik_cache != nullptr);
+
*has_system = false;
*has_cache = false;
// image_location = /system/framework/boot.art
@@ -158,9 +165,12 @@
bool have_android_data = false;
*dalvik_cache_exists = false;
- std::string dalvik_cache;
- GetDalvikCache(GetInstructionSetString(image_isa), true, &dalvik_cache,
- &have_android_data, dalvik_cache_exists, is_global_cache);
+ GetDalvikCache(GetInstructionSetString(image_isa),
+ true,
+ dalvik_cache,
+ &have_android_data,
+ dalvik_cache_exists,
+ is_global_cache);
if (have_android_data && *dalvik_cache_exists) {
// Always set output location even if it does not exist,
@@ -169,7 +179,10 @@
// image_location = /system/framework/boot.art
// *image_filename = /data/dalvik-cache/<image_isa>/boot.art
std::string error_msg;
- if (!GetDalvikCacheFilename(image_location, dalvik_cache.c_str(), cache_filename, &error_msg)) {
+ if (!GetDalvikCacheFilename(image_location,
+ dalvik_cache->c_str(),
+ cache_filename,
+ &error_msg)) {
LOG(WARNING) << error_msg;
return *has_system;
}
@@ -178,6 +191,26 @@
return *has_system || *has_cache;
}
+bool ImageSpace::FindImageFilename(const char* image_location,
+ const InstructionSet image_isa,
+ std::string* system_filename,
+ bool* has_system,
+ std::string* cache_filename,
+ bool* dalvik_cache_exists,
+ bool* has_cache,
+ bool* is_global_cache) {
+ std::string dalvik_cache_unused;
+ return FindImageFilenameImpl(image_location,
+ image_isa,
+ has_system,
+ system_filename,
+ dalvik_cache_exists,
+ &dalvik_cache_unused,
+ is_global_cache,
+ has_cache,
+ cache_filename);
+}
+
static bool ReadSpecificImageHeader(const char* filename, ImageHeader* image_header) {
std::unique_ptr<File> image_file(OS::OpenFileForReading(filename));
if (image_file.get() == nullptr) {
@@ -191,8 +224,10 @@
}
// Relocate the image at image_location to dest_filename and relocate it by a random amount.
-static bool RelocateImage(const char* image_location, const char* dest_filename,
- InstructionSet isa, std::string* error_msg) {
+static bool RelocateImage(const char* image_location,
+ const char* dest_filename,
+ InstructionSet isa,
+ std::string* error_msg) {
// We should clean up so we are more likely to have room for the image.
if (Runtime::Current()->IsZygote()) {
LOG(INFO) << "Pruning dalvik-cache since we are relocating an image and will need to recompile";
@@ -211,8 +246,7 @@
instruction_set_arg += GetInstructionSetString(isa);
std::string base_offset_arg("--base-offset-delta=");
- StringAppendF(&base_offset_arg, "%d", ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
- ART_BASE_ADDRESS_MAX_DELTA));
+ StringAppendF(&base_offset_arg, "%d", ChooseRelocationOffsetDelta());
std::vector<std::string> argv;
argv.push_back(patchoat);
@@ -237,16 +271,6 @@
return hdr.release();
}
-ImageHeader* ImageSpace::ReadImageHeaderOrDie(const char* image_location,
- const InstructionSet image_isa) {
- std::string error_msg;
- ImageHeader* image_header = ReadImageHeader(image_location, image_isa, &error_msg);
- if (image_header == nullptr) {
- LOG(FATAL) << error_msg;
- }
- return image_header;
-}
-
ImageHeader* ImageSpace::ReadImageHeader(const char* image_location,
const InstructionSet image_isa,
std::string* error_msg) {
@@ -312,11 +336,31 @@
return nullptr;
}
-static bool ChecksumsMatch(const char* image_a, const char* image_b) {
+static bool ChecksumsMatch(const char* image_a, const char* image_b, std::string* error_msg) {
+ DCHECK(error_msg != nullptr);
+
ImageHeader hdr_a;
ImageHeader hdr_b;
- return ReadSpecificImageHeader(image_a, &hdr_a) && ReadSpecificImageHeader(image_b, &hdr_b)
- && hdr_a.GetOatChecksum() == hdr_b.GetOatChecksum();
+
+ if (!ReadSpecificImageHeader(image_a, &hdr_a)) {
+ *error_msg = StringPrintf("Cannot read header of %s", image_a);
+ return false;
+ }
+ if (!ReadSpecificImageHeader(image_b, &hdr_b)) {
+ *error_msg = StringPrintf("Cannot read header of %s", image_b);
+ return false;
+ }
+
+ if (hdr_a.GetOatChecksum() != hdr_b.GetOatChecksum()) {
+ *error_msg = StringPrintf("Checksum mismatch: %u(%s) vs %u(%s)",
+ hdr_a.GetOatChecksum(),
+ image_a,
+ hdr_b.GetOatChecksum(),
+ image_b);
+ return false;
+ }
+
+ return true;
}
static bool ImageCreationAllowed(bool is_global_cache, std::string* error_msg) {
@@ -334,256 +378,6 @@
return false;
}
-static constexpr uint64_t kLowSpaceValue = 50 * MB;
-static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
-
-// Read the free space of the cache partition and make a decision whether to keep the generated
-// image. This is to try to mitigate situations where the system might run out of space later.
-static bool CheckSpace(const std::string& cache_filename, std::string* error_msg) {
- // Using statvfs vs statvfs64 because of b/18207376, and it is enough for all practical purposes.
- struct statvfs buf;
-
- int res = TEMP_FAILURE_RETRY(statvfs(cache_filename.c_str(), &buf));
- if (res != 0) {
- // Could not stat. Conservatively tell the system to delete the image.
- *error_msg = "Could not stat the filesystem, assuming low-memory situation.";
- return false;
- }
-
- uint64_t fs_overall_size = buf.f_bsize * static_cast<uint64_t>(buf.f_blocks);
- // Zygote is privileged, but other things are not. Use bavail.
- uint64_t fs_free_size = buf.f_bsize * static_cast<uint64_t>(buf.f_bavail);
-
- // Take the overall size as an indicator for a tmpfs, which is being used for the decryption
- // environment. We do not want to fail quickening the boot image there, as it is beneficial
- // for time-to-UI.
- if (fs_overall_size > kTmpFsSentinelValue) {
- if (fs_free_size < kLowSpaceValue) {
- *error_msg = StringPrintf("Low-memory situation: only %4.2f megabytes available after image"
- " generation, need at least %" PRIu64 ".",
- static_cast<double>(fs_free_size) / MB,
- kLowSpaceValue / MB);
- return false;
- }
- }
- return true;
-}
-
-ImageSpace* ImageSpace::CreateBootImage(const char* image_location,
- const InstructionSet image_isa,
- bool secondary_image,
- std::string* error_msg) {
- ScopedTrace trace(__FUNCTION__);
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename;
- bool has_cache = false;
- bool dalvik_cache_exists = false;
- bool is_global_cache = true;
- bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
- &has_system, &cache_filename, &dalvik_cache_exists,
- &has_cache, &is_global_cache);
-
- const bool is_zygote = Runtime::Current()->IsZygote();
- if (is_zygote && !secondary_image) {
- MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
- }
-
- ImageSpace* space;
- bool relocate = Runtime::Current()->ShouldRelocate();
- bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
- if (found_image) {
- const std::string* image_filename;
- bool is_system = false;
- bool relocated_version_used = false;
- if (relocate) {
- if (!dalvik_cache_exists) {
- *error_msg = StringPrintf("Requiring relocation for image '%s' at '%s' but we do not have "
- "any dalvik_cache to find/place it in.",
- image_location, system_filename.c_str());
- return nullptr;
- }
- if (has_system) {
- if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
- // We already have a relocated version
- image_filename = &cache_filename;
- relocated_version_used = true;
- } else {
- // We cannot have a relocated version, Relocate the system one and use it.
-
- std::string reason;
- bool success;
-
- // Check whether we are allowed to relocate.
- if (!can_compile) {
- reason = "Image dex2oat disabled by -Xnoimage-dex2oat.";
- success = false;
- } else if (!ImageCreationAllowed(is_global_cache, &reason)) {
- // Whether we can write to the cache.
- success = false;
- } else if (secondary_image) {
- if (is_zygote) {
- // Secondary image is out of date. Clear cache and exit to let it retry from scratch.
- LOG(ERROR) << "Cannot patch secondary image '" << image_location
- << "', clearing dalvik_cache and restarting zygote.";
- PruneDalvikCache(image_isa);
- _exit(1);
- } else {
- reason = "Should not have to patch secondary image.";
- success = false;
- }
- } else {
- // Try to relocate.
- success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);
- }
-
- if (success) {
- relocated_version_used = true;
- image_filename = &cache_filename;
- } else {
- *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s",
- image_location, system_filename.c_str(),
- cache_filename.c_str(), reason.c_str());
- // We failed to create files, remove any possibly garbage output.
- // Since ImageCreationAllowed was true above, we are the zygote
- // and therefore the only process expected to generate these for
- // the device.
- PruneDalvikCache(image_isa);
- return nullptr;
- }
- }
- } else {
- CHECK(has_cache);
- // We can just use cache's since it should be fine. This might or might not be relocated.
- image_filename = &cache_filename;
- }
- } else {
- if (has_system && has_cache) {
- // Check they have the same cksum. If they do use the cache. Otherwise system.
- if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
- image_filename = &cache_filename;
- relocated_version_used = true;
- } else {
- image_filename = &system_filename;
- is_system = true;
- }
- } else if (has_system) {
- image_filename = &system_filename;
- is_system = true;
- } else {
- CHECK(has_cache);
- image_filename = &cache_filename;
- }
- }
- {
- // Note that we must not use the file descriptor associated with
- // ScopedFlock::GetFile to Init the image file. We want the file
- // descriptor (and the associated exclusive lock) to be released when
- // we leave Create.
- ScopedFlock image_lock;
- // Should this be a RDWR lock? This is only a defensive measure, as at
- // this point the image should exist.
- // However, only the zygote can write into the global dalvik-cache, so
- // restrict to zygote processes, or any process that isn't using
- // /data/dalvik-cache (which we assume to be allowed to write there).
- const bool rw_lock = is_zygote || !is_global_cache;
- image_lock.Init(image_filename->c_str(),
- rw_lock ? (O_CREAT | O_RDWR) : O_RDONLY /* flags */,
- true /* block */,
- error_msg);
- VLOG(startup) << "Using image file " << image_filename->c_str() << " for image location "
- << image_location;
- // If we are in /system we can assume the image is good. We can also
- // assume this if we are using a relocated image (i.e. image checksum
- // matches) since this is only different by the offset. We need this to
- // make sure that host tests continue to work.
- // Since we are the boot image, pass null since we load the oat file from the boot image oat
- // file name.
- space = ImageSpace::Init(image_filename->c_str(),
- image_location,
- !(is_system || relocated_version_used),
- /* oat_file */nullptr,
- error_msg);
- }
- if (space != nullptr) {
- // Check whether there is enough space left over in the data partition. Even if we can load
- // the image, we need to be conservative, as some parts of the platform are not very tolerant
- // of space constraints.
- // ImageSpace doesn't know about the data partition per se, it relies on the FindImageFilename
- // helper (which relies on GetDalvikCache). So for now, if we load an image out of /system,
- // ignore the check (as it would test for free space in /system instead).
- if (!is_system && !CheckSpace(*image_filename, error_msg)) {
- // No. Delete the generated image and try to run out of the dex files.
- PruneDalvikCache(image_isa);
- return nullptr;
- }
- return space;
- }
-
- if (relocated_version_used) {
- // Something is wrong with the relocated copy (even though checksums match). Cleanup.
- // This can happen if the .oat is corrupt, since the above only checks the .art checksums.
- // TODO: Check the oat file validity earlier.
- *error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s "
- "but image failed to load: %s",
- image_location, cache_filename.c_str(), system_filename.c_str(),
- error_msg->c_str());
- PruneDalvikCache(image_isa);
- return nullptr;
- } else if (is_system) {
- // If the /system file exists, it should be up-to-date, don't try to generate it.
- *error_msg = StringPrintf("Failed to load /system image '%s': %s",
- image_filename->c_str(), error_msg->c_str());
- return nullptr;
- } else {
- // Otherwise, log a warning and fall through to GenerateImage.
- LOG(WARNING) << *error_msg;
- }
- }
-
- if (!can_compile) {
- *error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";
- return nullptr;
- } else if (!dalvik_cache_exists) {
- *error_msg = StringPrintf("No place to put generated image.");
- return nullptr;
- } else if (!ImageCreationAllowed(is_global_cache, error_msg)) {
- return nullptr;
- } else if (secondary_image) {
- *error_msg = "Cannot compile a secondary image.";
- return nullptr;
- } else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
- *error_msg = StringPrintf("Failed to generate image '%s': %s",
- cache_filename.c_str(), error_msg->c_str());
- // We failed to create files, remove any possibly garbage output.
- // Since ImageCreationAllowed was true above, we are the zygote
- // and therefore the only process expected to generate these for
- // the device.
- PruneDalvikCache(image_isa);
- return nullptr;
- } else {
- // Check whether there is enough space left over after we have generated the image.
- if (!CheckSpace(cache_filename, error_msg)) {
- // No. Delete the generated image and try to run out of the dex files.
- PruneDalvikCache(image_isa);
- return nullptr;
- }
-
- // Note that we must not use the file descriptor associated with
- // ScopedFlock::GetFile to Init the image file. We want the file
- // descriptor (and the associated exclusive lock) to be released when
- // we leave Create.
- ScopedFlock image_lock;
- image_lock.Init(cache_filename.c_str(), error_msg);
- space = ImageSpace::Init(cache_filename.c_str(), image_location, true, nullptr, error_msg);
- if (space == nullptr) {
- *error_msg = StringPrintf("Failed to load generated image '%s': %s",
- cache_filename.c_str(), error_msg->c_str());
- }
- return space;
- }
-}
-
void ImageSpace::VerifyImageAllocations() {
uint8_t* current = Begin() + RoundUp(sizeof(ImageHeader), kObjectAlignment);
while (current < End()) {
@@ -652,865 +446,1168 @@
<< reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")";
}
-class FixupVisitor : public ValueObject {
+// Helper class encapsulating loading, so we can access private ImageSpace members (this is a
+// friend class), but not declare functions in the header.
+class ImageSpaceLoader {
public:
- FixupVisitor(const RelocationRange& boot_image,
- const RelocationRange& boot_oat,
- const RelocationRange& app_image,
- const RelocationRange& app_oat)
- : boot_image_(boot_image),
- boot_oat_(boot_oat),
- app_image_(app_image),
- app_oat_(app_oat) {}
-
- // Return the relocated address of a heap object.
- template <typename T>
- ALWAYS_INLINE T* ForwardObject(T* src) const {
- const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
- if (boot_image_.InSource(uint_src)) {
- return reinterpret_cast<T*>(boot_image_.ToDest(uint_src));
- }
- if (app_image_.InSource(uint_src)) {
- return reinterpret_cast<T*>(app_image_.ToDest(uint_src));
- }
- // Since we are fixing up the app image, there should only be pointers to the app image and
- // boot image.
- DCHECK(src == nullptr) << reinterpret_cast<const void*>(src);
- return src;
- }
-
- // Return the relocated address of a code pointer (contained by an oat file).
- ALWAYS_INLINE const void* ForwardCode(const void* src) const {
- const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
- if (boot_oat_.InSource(uint_src)) {
- return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
- }
- if (app_oat_.InSource(uint_src)) {
- return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src));
- }
- DCHECK(src == nullptr) << src;
- return src;
- }
-
- // Must be called on pointers that already have been relocated to the destination relocation.
- ALWAYS_INLINE bool IsInAppImage(mirror::Object* object) const {
- return app_image_.InDest(reinterpret_cast<uintptr_t>(object));
- }
-
- protected:
- // Source section.
- const RelocationRange boot_image_;
- const RelocationRange boot_oat_;
- const RelocationRange app_image_;
- const RelocationRange app_oat_;
-};
-
-// Adapt for mirror::Class::FixupNativePointers.
-class FixupObjectAdapter : public FixupVisitor {
- public:
- template<typename... Args>
- explicit FixupObjectAdapter(Args... args) : FixupVisitor(args...) {}
-
- template <typename T>
- T* operator()(T* obj) const {
- return ForwardObject(obj);
- }
-};
-
-class FixupRootVisitor : public FixupVisitor {
- public:
- template<typename... Args>
- explicit FixupRootVisitor(Args... args) : FixupVisitor(args...) {}
-
- ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ static std::unique_ptr<ImageSpace> Load(const char* image_location,
+ const std::string& image_filename,
+ bool is_zygote,
+ bool is_global_cache,
+ bool is_system,
+ bool relocated_version_used,
+ std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_) {
- if (!root->IsNull()) {
- VisitRoot(root);
- }
+ // Note that we must not use the file descriptor associated with
+ // ScopedFlock::GetFile to Init the image file. We want the file
+ // descriptor (and the associated exclusive lock) to be released when
+ // we leave Create.
+ ScopedFlock image_lock;
+ // Should this be a RDWR lock? This is only a defensive measure, as at
+ // this point the image should exist.
+ // However, only the zygote can write into the global dalvik-cache, so
+ // restrict to zygote processes, or any process that isn't using
+ // /data/dalvik-cache (which we assume to be allowed to write there).
+ const bool rw_lock = is_zygote || !is_global_cache;
+ image_lock.Init(image_filename.c_str(),
+ rw_lock ? (O_CREAT | O_RDWR) : O_RDONLY /* flags */,
+ true /* block */,
+ error_msg);
+ VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location "
+ << image_location;
+ // If we are in /system we can assume the image is good. We can also
+ // assume this if we are using a relocated image (i.e. image checksum
+ // matches) since this is only different by the offset. We need this to
+ // make sure that host tests continue to work.
+ // Since we are the boot image, pass null since we load the oat file from the boot image oat
+ // file name.
+ return Init(image_filename.c_str(),
+ image_location,
+ !(is_system || relocated_version_used),
+ /* oat_file */nullptr,
+ error_msg);
}
- ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ static std::unique_ptr<ImageSpace> Init(const char* image_filename,
+ const char* image_location,
+ bool validate_oat_file,
+ const OatFile* oat_file,
+ std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_) {
- mirror::Object* ref = root->AsMirrorPtr();
- mirror::Object* new_ref = ForwardObject(ref);
- if (ref != new_ref) {
- root->Assign(new_ref);
- }
- }
-};
+ CHECK(image_filename != nullptr);
+ CHECK(image_location != nullptr);
-class FixupObjectVisitor : public FixupVisitor {
- public:
- template<typename... Args>
- explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* visited,
- const PointerSize pointer_size,
- Args... args)
- : FixupVisitor(args...),
- pointer_size_(pointer_size),
- visited_(visited) {}
+ TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image));
+ VLOG(image) << "ImageSpace::Init entering image_filename=" << image_filename;
- // Fix up separately since we also need to fix up method entrypoints.
- ALWAYS_INLINE void VisitRootIfNonNull(
- mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
-
- ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
- const {}
-
- ALWAYS_INLINE void operator()(mirror::Object* obj,
- MemberOffset offset,
- bool is_static ATTRIBUTE_UNUSED) const
- NO_THREAD_SAFETY_ANALYSIS {
- // There could be overlap between ranges, we must avoid visiting the same reference twice.
- // Avoid the class field since we already fixed it up in FixupClassVisitor.
- if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
- // Space is not yet added to the heap, don't do a read barrier.
- mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
- offset);
- // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
- // image.
- obj->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(offset, ForwardObject(ref));
- }
- }
-
- // Visit a pointer array and forward corresponding native data. Ignores pointer arrays in the
- // boot image. Uses the bitmap to ensure the same array is not visited multiple times.
- template <typename Visitor>
- void UpdatePointerArrayContents(mirror::PointerArray* array, const Visitor& visitor) const
- NO_THREAD_SAFETY_ANALYSIS {
- DCHECK(array != nullptr);
- DCHECK(visitor.IsInAppImage(array));
- // The bit for the array contents is different than the bit for the array. Since we may have
- // already visited the array as a long / int array from walking the bitmap without knowing it
- // was a pointer array.
- static_assert(kObjectAlignment == 8u, "array bit may be in another object");
- mirror::Object* const contents_bit = reinterpret_cast<mirror::Object*>(
- reinterpret_cast<uintptr_t>(array) + kObjectAlignment);
- // If the bit is not set then the contents have not yet been updated.
- if (!visited_->Test(contents_bit)) {
- array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, pointer_size_, visitor);
- visited_->Set(contents_bit);
- }
- }
-
- // java.lang.ref.Reference visitor.
- void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
- mirror::Object* obj = ref->GetReferent<kWithoutReadBarrier>();
- ref->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
- mirror::Reference::ReferentOffset(),
- ForwardObject(obj));
- }
-
- void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
- if (visited_->Test(obj)) {
- // Already visited.
- return;
- }
- visited_->Set(obj);
-
- // Handle class specially first since we need it to be updated to properly visit the rest of
- // the instance fields.
+ std::unique_ptr<File> file;
{
- mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
- DCHECK(klass != nullptr) << "Null class in image";
- // No AsClass since our fields aren't quite fixed up yet.
- mirror::Class* new_klass = down_cast<mirror::Class*>(ForwardObject(klass));
- if (klass != new_klass) {
- obj->SetClass<kVerifyNone>(new_klass);
+ TimingLogger::ScopedTiming timing("OpenImageFile", &logger);
+ file.reset(OS::OpenFileForReading(image_filename));
+ if (file == nullptr) {
+ *error_msg = StringPrintf("Failed to open '%s'", image_filename);
+ return nullptr;
}
- if (new_klass != klass && IsInAppImage(new_klass)) {
- // Make sure the klass contents are fixed up since we depend on it to walk the fields.
- operator()(new_klass);
+ }
+ ImageHeader temp_image_header;
+ ImageHeader* image_header = &temp_image_header;
+ {
+ TimingLogger::ScopedTiming timing("ReadImageHeader", &logger);
+ bool success = file->ReadFully(image_header, sizeof(*image_header));
+ if (!success || !image_header->IsValid()) {
+ *error_msg = StringPrintf("Invalid image header in '%s'", image_filename);
+ return nullptr;
+ }
+ }
+ // Check that the file is larger or equal to the header size + data size.
+ const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
+ if (image_file_size < sizeof(ImageHeader) + image_header->GetDataSize()) {
+ *error_msg = StringPrintf("Image file truncated: %" PRIu64 " vs. %" PRIu64 ".",
+ image_file_size,
+ sizeof(ImageHeader) + image_header->GetDataSize());
+ return nullptr;
+ }
+
+ if (oat_file != nullptr) {
+ // If we have an oat file, check the oat file checksum. The oat file is only non-null for the
+ // app image case. Otherwise, we open the oat file after the image and check the checksum there.
+ const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
+ const uint32_t image_oat_checksum = image_header->GetOatChecksum();
+ if (oat_checksum != image_oat_checksum) {
+ *error_msg = StringPrintf("Oat checksum 0x%x does not match the image one 0x%x in image %s",
+ oat_checksum,
+ image_oat_checksum,
+ image_filename);
+ return nullptr;
}
}
- obj->VisitReferences</*visit native roots*/false, kVerifyNone, kWithoutReadBarrier>(
- *this,
- *this);
- // Note that this code relies on no circular dependencies.
- // We want to use our own class loader and not the one in the image.
- if (obj->IsClass<kVerifyNone, kWithoutReadBarrier>()) {
- mirror::Class* as_klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>();
- FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_);
- as_klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(as_klass,
- pointer_size_,
- visitor);
- // Deal with the pointer arrays. Use the helper function since multiple classes can reference
- // the same arrays.
- mirror::PointerArray* const vtable = as_klass->GetVTable<kVerifyNone, kWithoutReadBarrier>();
- if (vtable != nullptr && IsInAppImage(vtable)) {
- operator()(vtable);
- UpdatePointerArrayContents(vtable, visitor);
+ if (VLOG_IS_ON(startup)) {
+ LOG(INFO) << "Dumping image sections";
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const auto section_idx = static_cast<ImageHeader::ImageSections>(i);
+ auto& section = image_header->GetImageSection(section_idx);
+ LOG(INFO) << section_idx << " start="
+ << reinterpret_cast<void*>(image_header->GetImageBegin() + section.Offset()) << " "
+ << section;
}
- mirror::IfTable* iftable = as_klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
- // Ensure iftable arrays are fixed up since we need GetMethodArray to return the valid
- // contents.
- if (iftable != nullptr && IsInAppImage(iftable)) {
- operator()(iftable);
- for (int32_t i = 0, count = iftable->Count(); i < count; ++i) {
- if (iftable->GetMethodArrayCount<kVerifyNone, kWithoutReadBarrier>(i) > 0) {
- mirror::PointerArray* methods =
- iftable->GetMethodArray<kVerifyNone, kWithoutReadBarrier>(i);
- if (visitor.IsInAppImage(methods)) {
- operator()(methods);
- DCHECK(methods != nullptr);
- UpdatePointerArrayContents(methods, visitor);
+ }
+
+ const auto& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap);
+ // The location we want to map from is the first aligned page after the end of the stored
+ // (possibly compressed) data.
+ const size_t image_bitmap_offset = RoundUp(sizeof(ImageHeader) + image_header->GetDataSize(),
+ kPageSize);
+ const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size();
+ if (end_of_bitmap != image_file_size) {
+ *error_msg = StringPrintf(
+ "Image file size does not equal end of bitmap: size=%" PRIu64 " vs. %zu.", image_file_size,
+ end_of_bitmap);
+ return nullptr;
+ }
+
+ std::unique_ptr<MemMap> map;
+ // GetImageBegin is the preferred address to map the image. If we manage to map the
+ // image at the image begin, the amount of fixup work required is minimized.
+ map.reset(LoadImageFile(image_filename,
+ image_location,
+ *image_header,
+ image_header->GetImageBegin(),
+ file->Fd(),
+ logger,
+ error_msg));
+ // If the header specifies PIC mode, we can also map at a random low_4gb address since we can
+ // relocate in-place.
+ if (map == nullptr && image_header->IsPic()) {
+ map.reset(LoadImageFile(image_filename,
+ image_location,
+ *image_header,
+ /* address */ nullptr,
+ file->Fd(),
+ logger,
+ error_msg));
+ }
+ // Were we able to load something and continue?
+ if (map == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ DCHECK_EQ(0, memcmp(image_header, map->Begin(), sizeof(ImageHeader)));
+
+ std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr,
+ bitmap_section.Size(),
+ PROT_READ, MAP_PRIVATE,
+ file->Fd(),
+ image_bitmap_offset,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ image_filename,
+ error_msg));
+ if (image_bitmap_map == nullptr) {
+ *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
+ return nullptr;
+ }
+ // Loaded the map, use the image header from the file now in case we patch it with
+ // RelocateInPlace.
+ image_header = reinterpret_cast<ImageHeader*>(map->Begin());
+ const uint32_t bitmap_index = ImageSpace::bitmap_index_.FetchAndAddSequentiallyConsistent(1);
+ std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u",
+ image_filename,
+ bitmap_index));
+ // Bitmap only needs to cover until the end of the mirror objects section.
+ const ImageSection& image_objects = image_header->GetImageSection(ImageHeader::kSectionObjects);
+ // We only want the mirror object, not the ArtFields and ArtMethods.
+ uint8_t* const image_end = map->Begin() + image_objects.End();
+ std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap;
+ {
+ TimingLogger::ScopedTiming timing("CreateImageBitmap", &logger);
+ bitmap.reset(
+ accounting::ContinuousSpaceBitmap::CreateFromMemMap(
+ bitmap_name,
+ image_bitmap_map.release(),
+ reinterpret_cast<uint8_t*>(map->Begin()),
+ image_objects.End()));
+ if (bitmap == nullptr) {
+ *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
+ return nullptr;
+ }
+ }
+ {
+ TimingLogger::ScopedTiming timing("RelocateImage", &logger);
+ if (!RelocateInPlace(*image_header,
+ map->Begin(),
+ bitmap.get(),
+ oat_file,
+ error_msg)) {
+ return nullptr;
+ }
+ }
+ // We only want the mirror object, not the ArtFields and ArtMethods.
+ std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
+ image_location,
+ map.release(),
+ bitmap.release(),
+ image_end));
+
+ // VerifyImageAllocations() will be called later in Runtime::Init()
+ // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_
+ // and ArtField::java_lang_reflect_ArtField_, which are used from
+ // Object::SizeOf() which VerifyImageAllocations() calls, are not
+ // set yet at this point.
+ if (oat_file == nullptr) {
+ TimingLogger::ScopedTiming timing("OpenOatFile", &logger);
+ space->oat_file_ = OpenOatFile(*space, image_filename, error_msg);
+ if (space->oat_file_ == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ space->oat_file_non_owned_ = space->oat_file_.get();
+ } else {
+ space->oat_file_non_owned_ = oat_file;
+ }
+
+ if (validate_oat_file) {
+ TimingLogger::ScopedTiming timing("ValidateOatFile", &logger);
+ CHECK(space->oat_file_ != nullptr);
+ if (!ValidateOatFile(*space, *space->oat_file_, error_msg)) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ }
+
+ Runtime* runtime = Runtime::Current();
+
+ // If oat_file is null, then it is the boot image space. Use oat_file_non_owned_ from the space
+ // to set the runtime methods.
+ CHECK_EQ(oat_file != nullptr, image_header->IsAppImage());
+ if (image_header->IsAppImage()) {
+ CHECK_EQ(runtime->GetResolutionMethod(),
+ image_header->GetImageMethod(ImageHeader::kResolutionMethod));
+ CHECK_EQ(runtime->GetImtConflictMethod(),
+ image_header->GetImageMethod(ImageHeader::kImtConflictMethod));
+ CHECK_EQ(runtime->GetImtUnimplementedMethod(),
+ image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod));
+ CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveAllCalleeSaves),
+ image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod));
+ CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveRefsOnly),
+ image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod));
+ CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveRefsAndArgs),
+ image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod));
+ CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveEverything),
+ image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod));
+ } else if (!runtime->HasResolutionMethod()) {
+ runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet());
+ runtime->SetResolutionMethod(image_header->GetImageMethod(ImageHeader::kResolutionMethod));
+ runtime->SetImtConflictMethod(image_header->GetImageMethod(ImageHeader::kImtConflictMethod));
+ runtime->SetImtUnimplementedMethod(
+ image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod));
+ runtime->SetCalleeSaveMethod(
+ image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod),
+ Runtime::kSaveAllCalleeSaves);
+ runtime->SetCalleeSaveMethod(
+ image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod), Runtime::kSaveRefsOnly);
+ runtime->SetCalleeSaveMethod(
+ image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod),
+ Runtime::kSaveRefsAndArgs);
+ runtime->SetCalleeSaveMethod(
+ image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod), Runtime::kSaveEverything);
+ }
+
+ VLOG(image) << "ImageSpace::Init exiting " << *space.get();
+ if (VLOG_IS_ON(image)) {
+ logger.Dump(LOG(INFO));
+ }
+ return space;
+ }
+
+ private:
+ static MemMap* LoadImageFile(const char* image_filename,
+ const char* image_location,
+ const ImageHeader& image_header,
+ uint8_t* address,
+ int fd,
+ TimingLogger& logger,
+ std::string* error_msg) {
+ TimingLogger::ScopedTiming timing("MapImageFile", &logger);
+ const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode();
+ if (storage_mode == ImageHeader::kStorageModeUncompressed) {
+ return MemMap::MapFileAtAddress(address,
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ fd,
+ 0,
+ /*low_4gb*/true,
+ /*reuse*/false,
+ image_filename,
+ error_msg);
+ }
+
+ if (storage_mode != ImageHeader::kStorageModeLZ4 &&
+ storage_mode != ImageHeader::kStorageModeLZ4HC) {
+ *error_msg = StringPrintf("Invalid storage mode in image header %d",
+ static_cast<int>(storage_mode));
+ return nullptr;
+ }
+
+ // Reserve output and decompress into it.
+ std::unique_ptr<MemMap> map(MemMap::MapAnonymous(image_location,
+ address,
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/true,
+ /*reuse*/false,
+ error_msg));
+ if (map != nullptr) {
+ const size_t stored_size = image_header.GetDataSize();
+ const size_t decompress_offset = sizeof(ImageHeader); // Skip the header.
+ std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ /*offset*/0,
+ /*low_4gb*/false,
+ image_filename,
+ error_msg));
+ if (temp_map == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ memcpy(map->Begin(), &image_header, sizeof(ImageHeader));
+ const uint64_t start = NanoTime();
+ // LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
+ TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
+ const size_t decompressed_size = LZ4_decompress_safe(
+ reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
+ reinterpret_cast<char*>(map->Begin()) + decompress_offset,
+ stored_size,
+ map->Size() - decompress_offset);
+ VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
+ if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
+ *error_msg = StringPrintf(
+ "Decompressed size does not match expected image size %zu vs %zu",
+ decompressed_size + sizeof(ImageHeader),
+ image_header.GetImageSize());
+ return nullptr;
+ }
+ }
+
+ return map.release();
+ }
+
+ class FixupVisitor : public ValueObject {
+ public:
+ FixupVisitor(const RelocationRange& boot_image,
+ const RelocationRange& boot_oat,
+ const RelocationRange& app_image,
+ const RelocationRange& app_oat)
+ : boot_image_(boot_image),
+ boot_oat_(boot_oat),
+ app_image_(app_image),
+ app_oat_(app_oat) {}
+
+ // Return the relocated address of a heap object.
+ template <typename T>
+ ALWAYS_INLINE T* ForwardObject(T* src) const {
+ const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
+ if (boot_image_.InSource(uint_src)) {
+ return reinterpret_cast<T*>(boot_image_.ToDest(uint_src));
+ }
+ if (app_image_.InSource(uint_src)) {
+ return reinterpret_cast<T*>(app_image_.ToDest(uint_src));
+ }
+ // Since we are fixing up the app image, there should only be pointers to the app image and
+ // boot image.
+ DCHECK(src == nullptr) << reinterpret_cast<const void*>(src);
+ return src;
+ }
+
+ // Return the relocated address of a code pointer (contained by an oat file).
+ ALWAYS_INLINE const void* ForwardCode(const void* src) const {
+ const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
+ if (boot_oat_.InSource(uint_src)) {
+ return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
+ }
+ if (app_oat_.InSource(uint_src)) {
+ return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src));
+ }
+ DCHECK(src == nullptr) << src;
+ return src;
+ }
+
+ // Must be called on pointers that already have been relocated to the destination relocation.
+ ALWAYS_INLINE bool IsInAppImage(mirror::Object* object) const {
+ return app_image_.InDest(reinterpret_cast<uintptr_t>(object));
+ }
+
+ protected:
+ // Source section.
+ const RelocationRange boot_image_;
+ const RelocationRange boot_oat_;
+ const RelocationRange app_image_;
+ const RelocationRange app_oat_;
+ };
+
+ // Adapt for mirror::Class::FixupNativePointers.
+ class FixupObjectAdapter : public FixupVisitor {
+ public:
+ template<typename... Args>
+ explicit FixupObjectAdapter(Args... args) : FixupVisitor(args...) {}
+
+ template <typename T>
+ T* operator()(T* obj) const {
+ return ForwardObject(obj);
+ }
+ };
+
+ class FixupRootVisitor : public FixupVisitor {
+ public:
+ template<typename... Args>
+ explicit FixupRootVisitor(Args... args) : FixupVisitor(args...) {}
+
+ ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* ref = root->AsMirrorPtr();
+ mirror::Object* new_ref = ForwardObject(ref);
+ if (ref != new_ref) {
+ root->Assign(new_ref);
+ }
+ }
+ };
+
+ class FixupObjectVisitor : public FixupVisitor {
+ public:
+ template<typename... Args>
+ explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* visited,
+ const PointerSize pointer_size,
+ Args... args)
+ : FixupVisitor(args...),
+ pointer_size_(pointer_size),
+ visited_(visited) {}
+
+ // Fix up separately since we also need to fix up method entrypoints.
+ ALWAYS_INLINE void VisitRootIfNonNull(
+ mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
+ ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {}
+
+ ALWAYS_INLINE void operator()(mirror::Object* obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ NO_THREAD_SAFETY_ANALYSIS {
+ // There could be overlap between ranges, we must avoid visiting the same reference twice.
+ // Avoid the class field since we already fixed it up in FixupClassVisitor.
+ if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+ // Space is not yet added to the heap, don't do a read barrier.
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+ offset);
+ // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
+ // image.
+ obj->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(offset, ForwardObject(ref));
+ }
+ }
+
+ // Visit a pointer array and forward corresponding native data. Ignores pointer arrays in the
+ // boot image. Uses the bitmap to ensure the same array is not visited multiple times.
+ template <typename Visitor>
+ void UpdatePointerArrayContents(mirror::PointerArray* array, const Visitor& visitor) const
+ NO_THREAD_SAFETY_ANALYSIS {
+ DCHECK(array != nullptr);
+ DCHECK(visitor.IsInAppImage(array));
+ // The bit for the array contents is different than the bit for the array. Since we may have
+ // already visited the array as a long / int array from walking the bitmap without knowing it
+ // was a pointer array.
+ static_assert(kObjectAlignment == 8u, "array bit may be in another object");
+ mirror::Object* const contents_bit = reinterpret_cast<mirror::Object*>(
+ reinterpret_cast<uintptr_t>(array) + kObjectAlignment);
+ // If the bit is not set then the contents have not yet been updated.
+ if (!visited_->Test(contents_bit)) {
+ array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, pointer_size_, visitor);
+ visited_->Set(contents_bit);
+ }
+ }
+
+ // java.lang.ref.Reference visitor.
+ void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ mirror::Object* obj = ref->GetReferent<kWithoutReadBarrier>();
+ ref->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+ mirror::Reference::ReferentOffset(),
+ ForwardObject(obj));
+ }
+
+ void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
+ if (visited_->Test(obj)) {
+ // Already visited.
+ return;
+ }
+ visited_->Set(obj);
+
+ // Handle class specially first since we need it to be updated to properly visit the rest of
+ // the instance fields.
+ {
+ mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ DCHECK(klass != nullptr) << "Null class in image";
+ // No AsClass since our fields aren't quite fixed up yet.
+ mirror::Class* new_klass = down_cast<mirror::Class*>(ForwardObject(klass));
+ if (klass != new_klass) {
+ obj->SetClass<kVerifyNone>(new_klass);
+ }
+ if (new_klass != klass && IsInAppImage(new_klass)) {
+ // Make sure the klass contents are fixed up since we depend on it to walk the fields.
+ operator()(new_klass);
+ }
+ }
+
+ obj->VisitReferences</*visit native roots*/false, kVerifyNone, kWithoutReadBarrier>(
+ *this,
+ *this);
+ // Note that this code relies on no circular dependencies.
+ // We want to use our own class loader and not the one in the image.
+ if (obj->IsClass<kVerifyNone, kWithoutReadBarrier>()) {
+ mirror::Class* as_klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>();
+ FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_);
+ as_klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(as_klass,
+ pointer_size_,
+ visitor);
+ // Deal with the pointer arrays. Use the helper function since multiple classes can reference
+ // the same arrays.
+ mirror::PointerArray* const vtable = as_klass->GetVTable<kVerifyNone, kWithoutReadBarrier>();
+ if (vtable != nullptr && IsInAppImage(vtable)) {
+ operator()(vtable);
+ UpdatePointerArrayContents(vtable, visitor);
+ }
+ mirror::IfTable* iftable = as_klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
+ // Ensure iftable arrays are fixed up since we need GetMethodArray to return the valid
+ // contents.
+ if (iftable != nullptr && IsInAppImage(iftable)) {
+ operator()(iftable);
+ for (int32_t i = 0, count = iftable->Count(); i < count; ++i) {
+ if (iftable->GetMethodArrayCount<kVerifyNone, kWithoutReadBarrier>(i) > 0) {
+ mirror::PointerArray* methods =
+ iftable->GetMethodArray<kVerifyNone, kWithoutReadBarrier>(i);
+ if (visitor.IsInAppImage(methods)) {
+ operator()(methods);
+ DCHECK(methods != nullptr);
+ UpdatePointerArrayContents(methods, visitor);
+ }
}
}
}
}
}
- }
- private:
- const PointerSize pointer_size_;
- gc::accounting::ContinuousSpaceBitmap* const visited_;
-};
+ private:
+ const PointerSize pointer_size_;
+ gc::accounting::ContinuousSpaceBitmap* const visited_;
+ };
-class ForwardObjectAdapter {
- public:
- ALWAYS_INLINE explicit ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
+ class ForwardObjectAdapter {
+ public:
+ ALWAYS_INLINE explicit ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
- template <typename T>
- ALWAYS_INLINE T* operator()(T* src) const {
- return visitor_->ForwardObject(src);
- }
-
- private:
- const FixupVisitor* const visitor_;
-};
-
-class ForwardCodeAdapter {
- public:
- ALWAYS_INLINE explicit ForwardCodeAdapter(const FixupVisitor* visitor)
- : visitor_(visitor) {}
-
- template <typename T>
- ALWAYS_INLINE T* operator()(T* src) const {
- return visitor_->ForwardCode(src);
- }
-
- private:
- const FixupVisitor* const visitor_;
-};
-
-class FixupArtMethodVisitor : public FixupVisitor, public ArtMethodVisitor {
- public:
- template<typename... Args>
- explicit FixupArtMethodVisitor(bool fixup_heap_objects, PointerSize pointer_size, Args... args)
- : FixupVisitor(args...),
- fixup_heap_objects_(fixup_heap_objects),
- pointer_size_(pointer_size) {}
-
- virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
- // TODO: Separate visitor for runtime vs normal methods.
- if (UNLIKELY(method->IsRuntimeMethod())) {
- ImtConflictTable* table = method->GetImtConflictTable(pointer_size_);
- if (table != nullptr) {
- ImtConflictTable* new_table = ForwardObject(table);
- if (table != new_table) {
- method->SetImtConflictTable(new_table, pointer_size_);
- }
- }
- const void* old_code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
- const void* new_code = ForwardCode(old_code);
- if (old_code != new_code) {
- method->SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size_);
- }
- } else {
- if (fixup_heap_objects_) {
- method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
- }
- method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
+ template <typename T>
+ ALWAYS_INLINE T* operator()(T* src) const {
+ return visitor_->ForwardObject(src);
}
- }
- private:
- const bool fixup_heap_objects_;
- const PointerSize pointer_size_;
-};
+ private:
+ const FixupVisitor* const visitor_;
+ };
-class FixupArtFieldVisitor : public FixupVisitor, public ArtFieldVisitor {
- public:
- template<typename... Args>
- explicit FixupArtFieldVisitor(Args... args) : FixupVisitor(args...) {}
+ class ForwardCodeAdapter {
+ public:
+ ALWAYS_INLINE explicit ForwardCodeAdapter(const FixupVisitor* visitor)
+ : visitor_(visitor) {}
- virtual void Visit(ArtField* field) NO_THREAD_SAFETY_ANALYSIS {
- field->UpdateObjects(ForwardObjectAdapter(this));
- }
-};
+ template <typename T>
+ ALWAYS_INLINE T* operator()(T* src) const {
+ return visitor_->ForwardCode(src);
+ }
-// Relocate an image space mapped at target_base which possibly used to be at a different base
-// address. Only needs a single image space, not one for both source and destination.
-// In place means modifying a single ImageSpace in place rather than relocating from one ImageSpace
-// to another.
-static bool RelocateInPlace(ImageHeader& image_header,
- uint8_t* target_base,
- accounting::ContinuousSpaceBitmap* bitmap,
- const OatFile* app_oat_file,
- std::string* error_msg) {
- DCHECK(error_msg != nullptr);
- if (!image_header.IsPic()) {
- if (image_header.GetImageBegin() == target_base) {
+ private:
+ const FixupVisitor* const visitor_;
+ };
+
+ class FixupArtMethodVisitor : public FixupVisitor, public ArtMethodVisitor {
+ public:
+ template<typename... Args>
+ explicit FixupArtMethodVisitor(bool fixup_heap_objects, PointerSize pointer_size, Args... args)
+ : FixupVisitor(args...),
+ fixup_heap_objects_(fixup_heap_objects),
+ pointer_size_(pointer_size) {}
+
+ virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
+ // TODO: Separate visitor for runtime vs normal methods.
+ if (UNLIKELY(method->IsRuntimeMethod())) {
+ ImtConflictTable* table = method->GetImtConflictTable(pointer_size_);
+ if (table != nullptr) {
+ ImtConflictTable* new_table = ForwardObject(table);
+ if (table != new_table) {
+ method->SetImtConflictTable(new_table, pointer_size_);
+ }
+ }
+ const void* old_code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+ const void* new_code = ForwardCode(old_code);
+ if (old_code != new_code) {
+ method->SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size_);
+ }
+ } else {
+ if (fixup_heap_objects_) {
+ method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+ }
+ method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
+ }
+ }
+
+ private:
+ const bool fixup_heap_objects_;
+ const PointerSize pointer_size_;
+ };
+
+ class FixupArtFieldVisitor : public FixupVisitor, public ArtFieldVisitor {
+ public:
+ template<typename... Args>
+ explicit FixupArtFieldVisitor(Args... args) : FixupVisitor(args...) {}
+
+ virtual void Visit(ArtField* field) NO_THREAD_SAFETY_ANALYSIS {
+ field->UpdateObjects(ForwardObjectAdapter(this));
+ }
+ };
+
+ // Relocate an image space mapped at target_base which possibly used to be at a different base
+ // address. Only needs a single image space, not one for both source and destination.
+ // In place means modifying a single ImageSpace in place rather than relocating from one ImageSpace
+ // to another.
+ static bool RelocateInPlace(ImageHeader& image_header,
+ uint8_t* target_base,
+ accounting::ContinuousSpaceBitmap* bitmap,
+ const OatFile* app_oat_file,
+ std::string* error_msg) {
+ DCHECK(error_msg != nullptr);
+ if (!image_header.IsPic()) {
+ if (image_header.GetImageBegin() == target_base) {
+ return true;
+ }
+ *error_msg = StringPrintf("Cannot relocate non-pic image for oat file %s",
+ (app_oat_file != nullptr) ? app_oat_file->GetLocation().c_str() : "");
+ return false;
+ }
+ // Set up sections.
+ uint32_t boot_image_begin = 0;
+ uint32_t boot_image_end = 0;
+ uint32_t boot_oat_begin = 0;
+ uint32_t boot_oat_end = 0;
+ const PointerSize pointer_size = image_header.GetPointerSize();
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end);
+ if (boot_image_begin == boot_image_end) {
+ *error_msg = "Can not relocate app image without boot image space";
+ return false;
+ }
+ if (boot_oat_begin == boot_oat_end) {
+ *error_msg = "Can not relocate app image without boot oat file";
+ return false;
+ }
+ const uint32_t boot_image_size = boot_image_end - boot_image_begin;
+ const uint32_t boot_oat_size = boot_oat_end - boot_oat_begin;
+ const uint32_t image_header_boot_image_size = image_header.GetBootImageSize();
+ const uint32_t image_header_boot_oat_size = image_header.GetBootOatSize();
+ if (boot_image_size != image_header_boot_image_size) {
+ *error_msg = StringPrintf("Boot image size %" PRIu64 " does not match expected size %"
+ PRIu64,
+ static_cast<uint64_t>(boot_image_size),
+ static_cast<uint64_t>(image_header_boot_image_size));
+ return false;
+ }
+ if (boot_oat_size != image_header_boot_oat_size) {
+ *error_msg = StringPrintf("Boot oat size %" PRIu64 " does not match expected size %"
+ PRIu64,
+ static_cast<uint64_t>(boot_oat_size),
+ static_cast<uint64_t>(image_header_boot_oat_size));
+ return false;
+ }
+ TimingLogger logger(__FUNCTION__, true, false);
+ RelocationRange boot_image(image_header.GetBootImageBegin(),
+ boot_image_begin,
+ boot_image_size);
+ RelocationRange boot_oat(image_header.GetBootOatBegin(),
+ boot_oat_begin,
+ boot_oat_size);
+ RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()),
+ reinterpret_cast<uintptr_t>(target_base),
+ image_header.GetImageSize());
+ // Use the oat data section since this is where the OatFile::Begin is.
+ RelocationRange app_oat(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
+ // Not necessarily in low 4GB.
+ reinterpret_cast<uintptr_t>(app_oat_file->Begin()),
+ image_header.GetOatDataEnd() - image_header.GetOatDataBegin());
+ VLOG(image) << "App image " << app_image;
+ VLOG(image) << "App oat " << app_oat;
+ VLOG(image) << "Boot image " << boot_image;
+ VLOG(image) << "Boot oat " << boot_oat;
+ // True if we need to fixup any heap pointers, otherwise only code pointers.
+ const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0;
+ const bool fixup_code = boot_oat.Delta() != 0 || app_oat.Delta() != 0;
+ if (!fixup_image && !fixup_code) {
+ // Nothing to fix up.
return true;
}
- *error_msg = StringPrintf("Cannot relocate non-pic image for oat file %s",
- (app_oat_file != nullptr) ? app_oat_file->GetLocation().c_str() : "");
- return false;
- }
- // Set up sections.
- uint32_t boot_image_begin = 0;
- uint32_t boot_image_end = 0;
- uint32_t boot_oat_begin = 0;
- uint32_t boot_oat_end = 0;
- const PointerSize pointer_size = image_header.GetPointerSize();
- gc::Heap* const heap = Runtime::Current()->GetHeap();
- heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end);
- if (boot_image_begin == boot_image_end) {
- *error_msg = "Can not relocate app image without boot image space";
- return false;
- }
- if (boot_oat_begin == boot_oat_end) {
- *error_msg = "Can not relocate app image without boot oat file";
- return false;
- }
- const uint32_t boot_image_size = boot_image_end - boot_image_begin;
- const uint32_t boot_oat_size = boot_oat_end - boot_oat_begin;
- const uint32_t image_header_boot_image_size = image_header.GetBootImageSize();
- const uint32_t image_header_boot_oat_size = image_header.GetBootOatSize();
- if (boot_image_size != image_header_boot_image_size) {
- *error_msg = StringPrintf("Boot image size %" PRIu64 " does not match expected size %"
- PRIu64,
- static_cast<uint64_t>(boot_image_size),
- static_cast<uint64_t>(image_header_boot_image_size));
- return false;
- }
- if (boot_oat_size != image_header_boot_oat_size) {
- *error_msg = StringPrintf("Boot oat size %" PRIu64 " does not match expected size %"
- PRIu64,
- static_cast<uint64_t>(boot_oat_size),
- static_cast<uint64_t>(image_header_boot_oat_size));
- return false;
- }
- TimingLogger logger(__FUNCTION__, true, false);
- RelocationRange boot_image(image_header.GetBootImageBegin(),
- boot_image_begin,
- boot_image_size);
- RelocationRange boot_oat(image_header.GetBootOatBegin(),
- boot_oat_begin,
- boot_oat_size);
- RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()),
- reinterpret_cast<uintptr_t>(target_base),
- image_header.GetImageSize());
- // Use the oat data section since this is where the OatFile::Begin is.
- RelocationRange app_oat(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
- // Not necessarily in low 4GB.
- reinterpret_cast<uintptr_t>(app_oat_file->Begin()),
- image_header.GetOatDataEnd() - image_header.GetOatDataBegin());
- VLOG(image) << "App image " << app_image;
- VLOG(image) << "App oat " << app_oat;
- VLOG(image) << "Boot image " << boot_image;
- VLOG(image) << "Boot oat " << boot_oat;
- // True if we need to fixup any heap pointers, otherwise only code pointers.
- const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0;
- const bool fixup_code = boot_oat.Delta() != 0 || app_oat.Delta() != 0;
- if (!fixup_image && !fixup_code) {
- // Nothing to fix up.
- return true;
- }
- ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
- // Need to update the image to be at the target base.
- const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects);
- uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
- uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
- FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
- if (fixup_image) {
- // Two pass approach, fix up all classes first, then fix up non class-objects.
- // The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
- std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> visited_bitmap(
- gc::accounting::ContinuousSpaceBitmap::Create("Relocate bitmap",
- target_base,
- image_header.GetImageSize()));
- FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(),
- pointer_size,
- boot_image,
- boot_oat,
- app_image,
- app_oat);
- TimingLogger::ScopedTiming timing("Fixup classes", &logger);
- // Fixup objects may read fields in the boot image, use the mutator lock here for sanity. Though
- // its probably not required.
- ScopedObjectAccess soa(Thread::Current());
- timing.NewTiming("Fixup objects");
- bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
- // Fixup image roots.
- CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
- image_header.GetImageRoots<kWithoutReadBarrier>())));
- image_header.RelocateImageObjects(app_image.Delta());
- CHECK_EQ(image_header.GetImageBegin(), target_base);
- // Fix up dex cache DexFile pointers.
- auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
- AsObjectArray<mirror::DexCache, kVerifyNone, kWithoutReadBarrier>();
- for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get<kVerifyNone, kWithoutReadBarrier>(i);
- // Fix up dex cache pointers.
- GcRoot<mirror::String>* strings = dex_cache->GetStrings();
- if (strings != nullptr) {
- GcRoot<mirror::String>* new_strings = fixup_adapter.ForwardObject(strings);
- if (strings != new_strings) {
- dex_cache->SetStrings(new_strings);
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
+ // Need to update the image to be at the target base.
+ const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects);
+ uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+ uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+ FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
+ if (fixup_image) {
+ // Two pass approach, fix up all classes first, then fix up non class-objects.
+ // The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
+ std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> visited_bitmap(
+ gc::accounting::ContinuousSpaceBitmap::Create("Relocate bitmap",
+ target_base,
+ image_header.GetImageSize()));
+ FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(),
+ pointer_size,
+ boot_image,
+ boot_oat,
+ app_image,
+ app_oat);
+ TimingLogger::ScopedTiming timing("Fixup classes", &logger);
+ // Fixup objects may read fields in the boot image, use the mutator lock here for sanity. Though
+ // its probably not required.
+ ScopedObjectAccess soa(Thread::Current());
+ timing.NewTiming("Fixup objects");
+ bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
+ // Fixup image roots.
+ CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
+ image_header.GetImageRoots<kWithoutReadBarrier>())));
+ image_header.RelocateImageObjects(app_image.Delta());
+ CHECK_EQ(image_header.GetImageBegin(), target_base);
+ // Fix up dex cache DexFile pointers.
+ auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
+ AsObjectArray<mirror::DexCache, kVerifyNone, kWithoutReadBarrier>();
+ for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get<kVerifyNone, kWithoutReadBarrier>(i);
+ // Fix up dex cache pointers.
+ GcRoot<mirror::String>* strings = dex_cache->GetStrings();
+ if (strings != nullptr) {
+ GcRoot<mirror::String>* new_strings = fixup_adapter.ForwardObject(strings);
+ if (strings != new_strings) {
+ dex_cache->SetStrings(new_strings);
+ }
+ dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
}
- dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
- }
- GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
- if (types != nullptr) {
- GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
- if (types != new_types) {
- dex_cache->SetResolvedTypes(new_types);
+ GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+ if (types != nullptr) {
+ GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
+ if (types != new_types) {
+ dex_cache->SetResolvedTypes(new_types);
+ }
+ dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter);
}
- dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter);
- }
- ArtMethod** methods = dex_cache->GetResolvedMethods();
- if (methods != nullptr) {
- ArtMethod** new_methods = fixup_adapter.ForwardObject(methods);
- if (methods != new_methods) {
- dex_cache->SetResolvedMethods(new_methods);
- }
- for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
- ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size);
- ArtMethod* copy = fixup_adapter.ForwardObject(orig);
- if (orig != copy) {
- mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size);
+ ArtMethod** methods = dex_cache->GetResolvedMethods();
+ if (methods != nullptr) {
+ ArtMethod** new_methods = fixup_adapter.ForwardObject(methods);
+ if (methods != new_methods) {
+ dex_cache->SetResolvedMethods(new_methods);
+ }
+ for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
+ ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size);
+ ArtMethod* copy = fixup_adapter.ForwardObject(orig);
+ if (orig != copy) {
+ mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size);
+ }
}
}
- }
- ArtField** fields = dex_cache->GetResolvedFields();
- if (fields != nullptr) {
- ArtField** new_fields = fixup_adapter.ForwardObject(fields);
- if (fields != new_fields) {
- dex_cache->SetResolvedFields(new_fields);
- }
- for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
- ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, pointer_size);
- ArtField* copy = fixup_adapter.ForwardObject(orig);
- if (orig != copy) {
- mirror::DexCache::SetElementPtrSize(new_fields, j, copy, pointer_size);
+ ArtField** fields = dex_cache->GetResolvedFields();
+ if (fields != nullptr) {
+ ArtField** new_fields = fixup_adapter.ForwardObject(fields);
+ if (fields != new_fields) {
+ dex_cache->SetResolvedFields(new_fields);
+ }
+ for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
+ ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, pointer_size);
+ ArtField* copy = fixup_adapter.ForwardObject(orig);
+ if (orig != copy) {
+ mirror::DexCache::SetElementPtrSize(new_fields, j, copy, pointer_size);
+ }
}
}
}
}
- }
- {
- // Only touches objects in the app image, no need for mutator lock.
- TimingLogger::ScopedTiming timing("Fixup methods", &logger);
- FixupArtMethodVisitor method_visitor(fixup_image,
- pointer_size,
- boot_image,
- boot_oat,
- app_image,
- app_oat);
- image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size);
- }
- if (fixup_image) {
{
// Only touches objects in the app image, no need for mutator lock.
- TimingLogger::ScopedTiming timing("Fixup fields", &logger);
- FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat);
- image_header.VisitPackedArtFields(&field_visitor, target_base);
+ TimingLogger::ScopedTiming timing("Fixup methods", &logger);
+ FixupArtMethodVisitor method_visitor(fixup_image,
+ pointer_size,
+ boot_image,
+ boot_oat,
+ app_image,
+ app_oat);
+ image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size);
}
- {
- TimingLogger::ScopedTiming timing("Fixup imt", &logger);
- image_header.VisitPackedImTables(fixup_adapter, target_base, pointer_size);
+ if (fixup_image) {
+ {
+ // Only touches objects in the app image, no need for mutator lock.
+ TimingLogger::ScopedTiming timing("Fixup fields", &logger);
+ FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat);
+ image_header.VisitPackedArtFields(&field_visitor, target_base);
+ }
+ {
+ TimingLogger::ScopedTiming timing("Fixup imt", &logger);
+ image_header.VisitPackedImTables(fixup_adapter, target_base, pointer_size);
+ }
+ {
+ TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
+ image_header.VisitPackedImtConflictTables(fixup_adapter, target_base, pointer_size);
+ }
+ // In the app image case, the image methods are actually in the boot image.
+ image_header.RelocateImageMethods(boot_image.Delta());
+ const auto& class_table_section = image_header.GetImageSection(ImageHeader::kSectionClassTable);
+ if (class_table_section.Size() > 0u) {
+ // Note that we require that ReadFromMemory does not make an internal copy of the elements.
+ // This also relies on visit roots not doing any verification which could fail after we update
+ // the roots to be the image addresses.
+ ScopedObjectAccess soa(Thread::Current());
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ClassTable temp_table;
+ temp_table.ReadFromMemory(target_base + class_table_section.Offset());
+ FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat);
+ temp_table.VisitRoots(root_visitor);
+ }
}
- {
- TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
- image_header.VisitPackedImtConflictTables(fixup_adapter, target_base, pointer_size);
+ if (VLOG_IS_ON(image)) {
+ logger.Dump(LOG(INFO));
}
- // In the app image case, the image methods are actually in the boot image.
- image_header.RelocateImageMethods(boot_image.Delta());
- const auto& class_table_section = image_header.GetImageSection(ImageHeader::kSectionClassTable);
- if (class_table_section.Size() > 0u) {
- // Note that we require that ReadFromMemory does not make an internal copy of the elements.
- // This also relies on visit roots not doing any verification which could fail after we update
- // the roots to be the image addresses.
- ScopedObjectAccess soa(Thread::Current());
- WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- ClassTable temp_table;
- temp_table.ReadFromMemory(target_base + class_table_section.Offset());
- FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat);
- temp_table.VisitRoots(root_visitor);
- }
- }
- if (VLOG_IS_ON(image)) {
- logger.Dump(LOG(INFO));
- }
- return true;
-}
-
-static MemMap* LoadImageFile(const char* image_filename,
- const char* image_location,
- const ImageHeader& image_header,
- uint8_t* address,
- int fd,
- TimingLogger& logger,
- std::string* error_msg) {
- TimingLogger::ScopedTiming timing("MapImageFile", &logger);
- const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode();
- if (storage_mode == ImageHeader::kStorageModeUncompressed) {
- return MemMap::MapFileAtAddress(address,
- image_header.GetImageSize(),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- fd,
- 0,
- /*low_4gb*/true,
- /*reuse*/false,
- image_filename,
- error_msg);
+ return true;
}
- if (storage_mode != ImageHeader::kStorageModeLZ4 &&
- storage_mode != ImageHeader::kStorageModeLZ4HC) {
- *error_msg = StringPrintf("Invalid storage mode in image header %d",
- static_cast<int>(storage_mode));
- return nullptr;
- }
+ static std::unique_ptr<OatFile> OpenOatFile(const ImageSpace& image,
+ const char* image_path,
+ std::string* error_msg) {
+ const ImageHeader& image_header = image.GetImageHeader();
+ std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
- // Reserve output and decompress into it.
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous(image_location,
- address,
- image_header.GetImageSize(),
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- /*reuse*/false,
- error_msg));
- if (map != nullptr) {
- const size_t stored_size = image_header.GetDataSize();
- const size_t decompress_offset = sizeof(ImageHeader); // Skip the header.
- std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
- PROT_READ,
- MAP_PRIVATE,
- fd,
- /*offset*/0,
- /*low_4gb*/false,
- image_filename,
- error_msg));
- if (temp_map == nullptr) {
- DCHECK(!error_msg->empty());
+ CHECK(image_header.GetOatDataBegin() != nullptr);
+
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+ oat_filename,
+ image_header.GetOatDataBegin(),
+ image_header.GetOatFileBegin(),
+ !Runtime::Current()->IsAotCompiler(),
+ /*low_4gb*/false,
+ nullptr,
+ error_msg));
+ if (oat_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
+ oat_filename.c_str(),
+ image.GetName(),
+ error_msg->c_str());
return nullptr;
}
- memcpy(map->Begin(), &image_header, sizeof(ImageHeader));
- const uint64_t start = NanoTime();
- // LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
- TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
- const size_t decompressed_size = LZ4_decompress_safe(
- reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
- reinterpret_cast<char*>(map->Begin()) + decompress_offset,
- stored_size,
- map->Size() - decompress_offset);
- VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
- if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
- *error_msg = StringPrintf(
- "Decompressed size does not match expected image size %zu vs %zu",
- decompressed_size + sizeof(ImageHeader),
- image_header.GetImageSize());
- return nullptr;
- }
- }
-
- return map.release();
-}
-
-ImageSpace* ImageSpace::Init(const char* image_filename,
- const char* image_location,
- bool validate_oat_file,
- const OatFile* oat_file,
- std::string* error_msg) {
- CHECK(image_filename != nullptr);
- CHECK(image_location != nullptr);
-
- TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image));
- VLOG(image) << "ImageSpace::Init entering image_filename=" << image_filename;
-
- std::unique_ptr<File> file;
- {
- TimingLogger::ScopedTiming timing("OpenImageFile", &logger);
- file.reset(OS::OpenFileForReading(image_filename));
- if (file == nullptr) {
- *error_msg = StringPrintf("Failed to open '%s'", image_filename);
- return nullptr;
- }
- }
- ImageHeader temp_image_header;
- ImageHeader* image_header = &temp_image_header;
- {
- TimingLogger::ScopedTiming timing("ReadImageHeader", &logger);
- bool success = file->ReadFully(image_header, sizeof(*image_header));
- if (!success || !image_header->IsValid()) {
- *error_msg = StringPrintf("Invalid image header in '%s'", image_filename);
- return nullptr;
- }
- }
- // Check that the file is larger or equal to the header size + data size.
- const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
- if (image_file_size < sizeof(ImageHeader) + image_header->GetDataSize()) {
- *error_msg = StringPrintf("Image file truncated: %" PRIu64 " vs. %" PRIu64 ".",
- image_file_size,
- sizeof(ImageHeader) + image_header->GetDataSize());
- return nullptr;
- }
-
- if (oat_file != nullptr) {
- // If we have an oat file, check the oat file checksum. The oat file is only non-null for the
- // app image case. Otherwise, we open the oat file after the image and check the checksum there.
- const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
- const uint32_t image_oat_checksum = image_header->GetOatChecksum();
+ uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
+ uint32_t image_oat_checksum = image_header.GetOatChecksum();
if (oat_checksum != image_oat_checksum) {
- *error_msg = StringPrintf("Oat checksum 0x%x does not match the image one 0x%x in image %s",
+ *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x"
+ " in image %s",
oat_checksum,
image_oat_checksum,
- image_filename);
+ image.GetName());
return nullptr;
}
- }
-
- if (VLOG_IS_ON(startup)) {
- LOG(INFO) << "Dumping image sections";
- for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
- const auto section_idx = static_cast<ImageHeader::ImageSections>(i);
- auto& section = image_header->GetImageSection(section_idx);
- LOG(INFO) << section_idx << " start="
- << reinterpret_cast<void*>(image_header->GetImageBegin() + section.Offset()) << " "
- << section;
- }
- }
-
- const auto& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap);
- // The location we want to map from is the first aligned page after the end of the stored
- // (possibly compressed) data.
- const size_t image_bitmap_offset = RoundUp(sizeof(ImageHeader) + image_header->GetDataSize(),
- kPageSize);
- const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size();
- if (end_of_bitmap != image_file_size) {
- *error_msg = StringPrintf(
- "Image file size does not equal end of bitmap: size=%" PRIu64 " vs. %zu.", image_file_size,
- end_of_bitmap);
- return nullptr;
- }
-
- std::unique_ptr<MemMap> map;
- // GetImageBegin is the preferred address to map the image. If we manage to map the
- // image at the image begin, the amount of fixup work required is minimized.
- map.reset(LoadImageFile(image_filename,
- image_location,
- *image_header,
- image_header->GetImageBegin(),
- file->Fd(),
- logger,
- error_msg));
- // If the header specifies PIC mode, we can also map at a random low_4gb address since we can
- // relocate in-place.
- if (map == nullptr && image_header->IsPic()) {
- map.reset(LoadImageFile(image_filename,
- image_location,
- *image_header,
- /* address */ nullptr,
- file->Fd(),
- logger,
- error_msg));
- }
- // Were we able to load something and continue?
- if (map == nullptr) {
- DCHECK(!error_msg->empty());
- return nullptr;
- }
- DCHECK_EQ(0, memcmp(image_header, map->Begin(), sizeof(ImageHeader)));
-
- std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr,
- bitmap_section.Size(),
- PROT_READ, MAP_PRIVATE,
- file->Fd(),
- image_bitmap_offset,
- /*low_4gb*/false,
- /*reuse*/false,
- image_filename,
- error_msg));
- if (image_bitmap_map == nullptr) {
- *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
- return nullptr;
- }
- // Loaded the map, use the image header from the file now in case we patch it with
- // RelocateInPlace.
- image_header = reinterpret_cast<ImageHeader*>(map->Begin());
- const uint32_t bitmap_index = bitmap_index_.FetchAndAddSequentiallyConsistent(1);
- std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u",
- image_filename,
- bitmap_index));
- // Bitmap only needs to cover until the end of the mirror objects section.
- const ImageSection& image_objects = image_header->GetImageSection(ImageHeader::kSectionObjects);
- // We only want the mirror object, not the ArtFields and ArtMethods.
- uint8_t* const image_end = map->Begin() + image_objects.End();
- std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap;
- {
- TimingLogger::ScopedTiming timing("CreateImageBitmap", &logger);
- bitmap.reset(
- accounting::ContinuousSpaceBitmap::CreateFromMemMap(
- bitmap_name,
- image_bitmap_map.release(),
- reinterpret_cast<uint8_t*>(map->Begin()),
- image_objects.End()));
- if (bitmap == nullptr) {
- *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
+ int32_t image_patch_delta = image_header.GetPatchDelta();
+ int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+ if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
+ // We should have already relocated by this point. Bail out.
+ *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
+ "in image %s",
+ oat_patch_delta,
+ image_patch_delta,
+ image.GetName());
return nullptr;
}
+
+ return oat_file;
}
- {
- TimingLogger::ScopedTiming timing("RelocateImage", &logger);
- if (!RelocateInPlace(*image_header,
- map->Begin(),
- bitmap.get(),
- oat_file,
- error_msg)) {
- return nullptr;
+
+ static bool ValidateOatFile(const ImageSpace& space,
+ const OatFile& oat_file,
+ std::string* error_msg) {
+ for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
+ const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
+ uint32_t dex_file_location_checksum;
+ if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
+ *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
+ "%s",
+ dex_file_location.c_str(),
+ space.GetName(),
+ error_msg->c_str());
+ return false;
+ }
+ if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
+ "dex file '%s' (0x%x != 0x%x)",
+ oat_file.GetLocation().c_str(),
+ dex_file_location.c_str(),
+ oat_dex_file->GetDexFileLocationChecksum(),
+ dex_file_location_checksum);
+ return false;
+ }
}
+ return true;
}
- // We only want the mirror object, not the ArtFields and ArtMethods.
- std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
- image_location,
- map.release(),
- bitmap.release(),
- image_end));
+};
- // VerifyImageAllocations() will be called later in Runtime::Init()
- // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_
- // and ArtField::java_lang_reflect_ArtField_, which are used from
- // Object::SizeOf() which VerifyImageAllocations() calls, are not
- // set yet at this point.
- if (oat_file == nullptr) {
- TimingLogger::ScopedTiming timing("OpenOatFile", &logger);
- space->oat_file_.reset(space->OpenOatFile(image_filename, error_msg));
- if (space->oat_file_ == nullptr) {
- DCHECK(!error_msg->empty());
- return nullptr;
- }
- space->oat_file_non_owned_ = space->oat_file_.get();
- } else {
- space->oat_file_non_owned_ = oat_file;
+static constexpr uint64_t kLowSpaceValue = 50 * MB;
+static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
+
+// Read the free space of the cache partition and make a decision whether to keep the generated
+// image. This is to try to mitigate situations where the system might run out of space later.
+static bool CheckSpace(const std::string& cache_filename, std::string* error_msg) {
+ // Using statvfs vs statvfs64 because of b/18207376, and it is enough for all practical purposes.
+ struct statvfs buf;
+
+ int res = TEMP_FAILURE_RETRY(statvfs(cache_filename.c_str(), &buf));
+ if (res != 0) {
+ // Could not stat. Conservatively tell the system to delete the image.
+ *error_msg = "Could not stat the filesystem, assuming low-memory situation.";
+ return false;
}
- if (validate_oat_file) {
- TimingLogger::ScopedTiming timing("ValidateOatFile", &logger);
- if (!space->ValidateOatFile(error_msg)) {
- DCHECK(!error_msg->empty());
- return nullptr;
- }
- }
+ uint64_t fs_overall_size = buf.f_bsize * static_cast<uint64_t>(buf.f_blocks);
+ // Zygote is privileged, but other things are not. Use bavail.
+ uint64_t fs_free_size = buf.f_bsize * static_cast<uint64_t>(buf.f_bavail);
- Runtime* runtime = Runtime::Current();
-
- // If oat_file is null, then it is the boot image space. Use oat_file_non_owned_ from the space
- // to set the runtime methods.
- CHECK_EQ(oat_file != nullptr, image_header->IsAppImage());
- if (image_header->IsAppImage()) {
- CHECK_EQ(runtime->GetResolutionMethod(),
- image_header->GetImageMethod(ImageHeader::kResolutionMethod));
- CHECK_EQ(runtime->GetImtConflictMethod(),
- image_header->GetImageMethod(ImageHeader::kImtConflictMethod));
- CHECK_EQ(runtime->GetImtUnimplementedMethod(),
- image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod));
- CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveAllCalleeSaves),
- image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod));
- CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveRefsOnly),
- image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod));
- CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveRefsAndArgs),
- image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod));
- CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveEverything),
- image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod));
- } else if (!runtime->HasResolutionMethod()) {
- runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet());
- runtime->SetResolutionMethod(image_header->GetImageMethod(ImageHeader::kResolutionMethod));
- runtime->SetImtConflictMethod(image_header->GetImageMethod(ImageHeader::kImtConflictMethod));
- runtime->SetImtUnimplementedMethod(
- image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod));
- runtime->SetCalleeSaveMethod(
- image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod),
- Runtime::kSaveAllCalleeSaves);
- runtime->SetCalleeSaveMethod(
- image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod), Runtime::kSaveRefsOnly);
- runtime->SetCalleeSaveMethod(
- image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod),
- Runtime::kSaveRefsAndArgs);
- runtime->SetCalleeSaveMethod(
- image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod), Runtime::kSaveEverything);
- }
-
- VLOG(image) << "ImageSpace::Init exiting " << *space.get();
- if (VLOG_IS_ON(image)) {
- logger.Dump(LOG(INFO));
- }
- return space.release();
-}
-
-OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg) const {
- const ImageHeader& image_header = GetImageHeader();
- std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
-
- CHECK(image_header.GetOatDataBegin() != nullptr);
-
- OatFile* oat_file = OatFile::Open(oat_filename,
- oat_filename,
- image_header.GetOatDataBegin(),
- image_header.GetOatFileBegin(),
- !Runtime::Current()->IsAotCompiler(),
- /*low_4gb*/false,
- nullptr,
- error_msg);
- if (oat_file == nullptr) {
- *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
- oat_filename.c_str(), GetName(), error_msg->c_str());
- return nullptr;
- }
- uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
- uint32_t image_oat_checksum = image_header.GetOatChecksum();
- if (oat_checksum != image_oat_checksum) {
- *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x"
- " in image %s", oat_checksum, image_oat_checksum, GetName());
- return nullptr;
- }
- int32_t image_patch_delta = image_header.GetPatchDelta();
- int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
- if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
- // We should have already relocated by this point. Bail out.
- *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
- "in image %s", oat_patch_delta, image_patch_delta, GetName());
- return nullptr;
- }
-
- return oat_file;
-}
-
-bool ImageSpace::ValidateOatFile(std::string* error_msg) const {
- CHECK(oat_file_.get() != nullptr);
- for (const OatFile::OatDexFile* oat_dex_file : oat_file_->GetOatDexFiles()) {
- const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
- uint32_t dex_file_location_checksum;
- if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
- *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
- "%s", dex_file_location.c_str(), GetName(), error_msg->c_str());
- return false;
- }
- if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
- "dex file '%s' (0x%x != 0x%x)",
- oat_file_->GetLocation().c_str(), dex_file_location.c_str(),
- oat_dex_file->GetDexFileLocationChecksum(),
- dex_file_location_checksum);
+ // Take the overall size as an indicator for a tmpfs, which is being used for the decryption
+ // environment. We do not want to fail quickening the boot image there, as it is beneficial
+ // for time-to-UI.
+ if (fs_overall_size > kTmpFsSentinelValue) {
+ if (fs_free_size < kLowSpaceValue) {
+ *error_msg = StringPrintf("Low-memory situation: only %4.2f megabytes available, need at "
+ "least %" PRIu64 ".",
+ static_cast<double>(fs_free_size) / MB,
+ kLowSpaceValue / MB);
return false;
}
}
return true;
}
+std::unique_ptr<ImageSpace> ImageSpace::CreateBootImage(const char* image_location,
+ const InstructionSet image_isa,
+ bool secondary_image,
+ std::string* error_msg) {
+ ScopedTrace trace(__FUNCTION__);
+
+ // Step 0: Extra zygote work.
+
+ // Step 0.a: If we're the zygote, mark boot.
+ const bool is_zygote = Runtime::Current()->IsZygote();
+ if (is_zygote && !secondary_image) {
+ MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
+ }
+
+ // Step 0.b: If we're the zygote, check for free space, and prune the cache preemptively,
+ // if necessary. While the runtime may be fine (it is pretty tolerant to
+ // out-of-disk-space situations), other parts of the platform are not.
+ //
+ // The advantage of doing this proactively is that the later steps are simplified,
+ // i.e., we do not need to code retries.
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ bool is_global_cache = true;
+ std::string dalvik_cache;
+ bool found_image = FindImageFilenameImpl(image_location,
+ image_isa,
+ &has_system,
+ &system_filename,
+ &dalvik_cache_exists,
+ &dalvik_cache,
+ &is_global_cache,
+ &has_cache,
+ &cache_filename);
+
+ if (is_zygote && dalvik_cache_exists) {
+ DCHECK(!dalvik_cache.empty());
+ std::string local_error_msg;
+ if (!CheckSpace(dalvik_cache, &local_error_msg)) {
+ LOG(WARNING) << local_error_msg << " Preemptively pruning the dalvik cache.";
+ PruneDalvikCache(image_isa);
+
+ // Re-evaluate the image.
+ found_image = FindImageFilenameImpl(image_location,
+ image_isa,
+ &has_system,
+ &system_filename,
+ &dalvik_cache_exists,
+ &dalvik_cache,
+ &is_global_cache,
+ &has_cache,
+ &cache_filename);
+ }
+ }
+
+ // Collect all the errors.
+ std::vector<std::string> error_msgs;
+
+ // Step 1: Check if we have an existing and relocated image.
+
+ // Step 1.a: Have files in system and cache. Then they need to match.
+ if (found_image && has_system && has_cache) {
+ std::string local_error_msg;
+ // Check that the files are matching.
+ if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str(), &local_error_msg)) {
+ std::unique_ptr<ImageSpace> relocated_space =
+ ImageSpaceLoader::Load(image_location,
+ cache_filename,
+ is_zygote,
+ is_global_cache,
+ /* is_system */ false,
+ /* relocated_version_used */ true,
+ &local_error_msg);
+ if (relocated_space != nullptr) {
+ return relocated_space;
+ }
+ }
+ error_msgs.push_back(local_error_msg);
+ }
+
+ // Step 1.b: Only have a cache file.
+ if (found_image && !has_system && has_cache) {
+ std::string local_error_msg;
+ std::unique_ptr<ImageSpace> cache_space =
+ ImageSpaceLoader::Load(image_location,
+ cache_filename,
+ is_zygote,
+ is_global_cache,
+ /* is_system */ false,
+ /* relocated_version_used */ true,
+ &local_error_msg);
+ if (cache_space != nullptr) {
+ return cache_space;
+ }
+ error_msgs.push_back(local_error_msg);
+ }
+
+ // Step 2: We have an existing image in /system.
+
+ // Step 2.a: We are not required to relocate it. Then we can use it directly.
+ bool relocate = Runtime::Current()->ShouldRelocate();
+
+ if (found_image && has_system && !relocate) {
+ std::string local_error_msg;
+ std::unique_ptr<ImageSpace> system_space =
+ ImageSpaceLoader::Load(image_location,
+ system_filename,
+ is_zygote,
+ is_global_cache,
+ /* is_system */ true,
+ /* relocated_version_used */ false,
+ &local_error_msg);
+ if (system_space != nullptr) {
+ return system_space;
+ }
+ error_msgs.push_back(local_error_msg);
+ }
+
+ // Step 2.b: We require a relocated image. Then we must patch it. This step fails if this is a
+ // secondary image.
+ if (found_image && has_system && relocate) {
+ std::string local_error_msg;
+ if (!Runtime::Current()->IsImageDex2OatEnabled()) {
+ local_error_msg = "Patching disabled.";
+ } else if (secondary_image) {
+ local_error_msg = "Cannot patch a secondary image.";
+ } else if (ImageCreationAllowed(is_global_cache, &local_error_msg)) {
+ bool patch_success =
+ RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg);
+ if (patch_success) {
+ std::unique_ptr<ImageSpace> patched_space =
+ ImageSpaceLoader::Load(image_location,
+ cache_filename,
+ is_zygote,
+ is_global_cache,
+ /* is_system */ false,
+ /* relocated_version_used */ true,
+ &local_error_msg);
+ if (patched_space != nullptr) {
+ return patched_space;
+ }
+ }
+ }
+ error_msgs.push_back(StringPrintf("Cannot relocate image %s to %s: %s",
+ image_location,
+ cache_filename.c_str(),
+ local_error_msg.c_str()));
+ }
+
+ // Step 3: We do not have an existing image in /system, so generate an image into the dalvik
+ // cache. This step fails if this is a secondary image.
+ if (!has_system) {
+ std::string local_error_msg;
+ if (!Runtime::Current()->IsImageDex2OatEnabled()) {
+ local_error_msg = "Image compilation disabled.";
+ } else if (secondary_image) {
+ local_error_msg = "Cannot compile a secondary image.";
+ } else if (ImageCreationAllowed(is_global_cache, &local_error_msg)) {
+ bool compilation_success = GenerateImage(cache_filename, image_isa, &local_error_msg);
+ if (compilation_success) {
+ std::unique_ptr<ImageSpace> compiled_space =
+ ImageSpaceLoader::Load(image_location,
+ cache_filename,
+ is_zygote,
+ is_global_cache,
+ /* is_system */ false,
+ /* relocated_version_used */ true,
+ &local_error_msg);
+ if (compiled_space != nullptr) {
+ return compiled_space;
+ }
+ }
+ }
+ error_msgs.push_back(StringPrintf("Cannot compile image to %s: %s",
+ cache_filename.c_str(),
+ local_error_msg.c_str()));
+ }
+
+ // We failed. Prune the cache the free up space, create a compound error message and return no
+ // image.
+ PruneDalvikCache(image_isa);
+
+ std::ostringstream oss;
+ bool first = true;
+ for (auto msg : error_msgs) {
+ if (!first) {
+ oss << "\n ";
+ }
+ oss << msg;
+ }
+ *error_msg = oss.str();
+
+ return nullptr;
+}
+
+std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
+ const OatFile* oat_file,
+ std::string* error_msg) {
+ return ImageSpaceLoader::Init(image,
+ image,
+ /*validate_oat_file*/false,
+ oat_file,
+ /*out*/error_msg);
+}
+
const OatFile* ImageSpace::GetOatFile() const {
return oat_file_non_owned_;
}
@@ -1570,16 +1667,6 @@
}
}
-ImageSpace* ImageSpace::CreateFromAppImage(const char* image,
- const OatFile* oat_file,
- std::string* error_msg) {
- return gc::space::ImageSpace::Init(image,
- image,
- /*validate_oat_file*/false,
- oat_file,
- /*out*/error_msg);
-}
-
void ImageSpace::DumpSections(std::ostream& os) const {
const uint8_t* base = Begin();
const ImageHeader& header = GetImageHeader();
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index c9741d0..534232d 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -43,24 +43,19 @@
// creation of the alloc space. The ReleaseOatFile will later be
// used to transfer ownership of the OatFile to the ClassLinker when
// it is initialized.
- static ImageSpace* CreateBootImage(const char* image,
+ static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
InstructionSet image_isa,
bool secondary_image,
std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_);
// Try to open an existing app image space.
- static ImageSpace* CreateFromAppImage(const char* image,
- const OatFile* oat_file,
- std::string* error_msg)
+ static std::unique_ptr<ImageSpace> CreateFromAppImage(const char* image,
+ const OatFile* oat_file,
+ std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_);
// Reads the image header from the specified image location for the
- // instruction set image_isa or dies trying.
- static ImageHeader* ReadImageHeaderOrDie(const char* image_location,
- InstructionSet image_isa);
-
- // Reads the image header from the specified image location for the
// instruction set image_isa. Returns null on failure, with
// reason in error_msg.
static ImageHeader* ReadImageHeader(const char* image_location,
@@ -158,21 +153,13 @@
// relative to its DexFile inputs. Otherwise (for /data), validate the inputs and generate the
// OatFile in /data/dalvik-cache if necessary. If the oat_file is null, it uses the oat file from
// the image.
- static ImageSpace* Init(const char* image_filename,
- const char* image_location,
- bool validate_oat_file,
- const OatFile* oat_file,
- std::string* error_msg)
+ static std::unique_ptr<ImageSpace> Init(const char* image_filename,
+ const char* image_location,
+ bool validate_oat_file,
+ const OatFile* oat_file,
+ std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_);
- OatFile* OpenOatFile(const char* image, std::string* error_msg) const
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- bool ValidateOatFile(std::string* error_msg) const
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- friend class Space;
-
static Atomic<uint32_t> bitmap_index_;
std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap_;
@@ -194,6 +181,9 @@
const std::string image_location_;
+ friend class ImageSpaceLoader;
+ friend class Space;
+
private:
DISALLOW_COPY_AND_ASSIGN(ImageSpace);
};
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index fc9a3cf..fa941c0 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -89,9 +89,11 @@
static void PruneDalvikCache(InstructionSet isa) {
CHECK_NE(isa, kNone);
// Prune the base /data/dalvik-cache.
- impl::DeleteDirectoryContents(GetDalvikCacheOrDie(".", false), false);
+ // Note: GetDalvikCache may return the empty string if the directory doesn't
+ // exist. It is safe to pass "" to DeleteDirectoryContents, so this is okay.
+ impl::DeleteDirectoryContents(GetDalvikCache("."), false);
// Prune /data/dalvik-cache/<isa>.
- impl::DeleteDirectoryContents(GetDalvikCacheOrDie(GetInstructionSetString(isa), false), false);
+ impl::DeleteDirectoryContents(GetDalvikCache(GetInstructionSetString(isa)), false);
// Be defensive. There should be a runtime created here, but this may be called in a test.
if (Runtime::Current() != nullptr) {
@@ -104,7 +106,8 @@
// present, it usually means the boot didn't complete. We wipe the entire dalvik
// cache if that's the case.
static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
- const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
+ const std::string isa_subdir = GetDalvikCache(GetInstructionSetString(isa));
+ CHECK(!isa_subdir.empty()) << "Dalvik cache not found";
const std::string boot_marker = isa_subdir + "/.booting";
const char* file_name = boot_marker.c_str();
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 2c2a2b8..fe6332d 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -713,21 +713,15 @@
CHECK(oat_filename != nullptr);
CHECK(error_msg != nullptr);
- // TODO: The work done in GetDalvikCache is overkill for what we need.
- // Ideally a new API for getting the DalvikCacheDirectory the way we want
- // (without existence testing, creation, or death) is provided with the rest
- // of the GetDalvikCache family of functions. Until such an API is in place,
- // we use GetDalvikCache to avoid duplicating the logic for determining the
- // dalvik cache directory.
- std::string dalvik_cache_dir;
- bool ignored;
- GetDalvikCache("", false, &dalvik_cache_dir, &ignored, &ignored, &ignored);
+ std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa));
+ if (cache_dir.empty()) {
+ *error_msg = "Dalvik cache directory does not exist";
+ return false;
+ }
// TODO: The oat file assistant should be the definitive place for
// determining the oat file name from the dex location, not
// GetDalvikCacheFilename.
- std::string cache_dir = StringPrintf("%s%s",
- dalvik_cache_dir.c_str(), GetInstructionSetString(isa));
return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg);
}
@@ -786,8 +780,12 @@
image_header.GetOatDataBegin());
cached_image_info_.patch_delta = image_header.GetPatchDelta();
} else {
+ std::string error_msg;
std::unique_ptr<ImageHeader> image_header(
- gc::space::ImageSpace::ReadImageHeaderOrDie(cached_image_info_.location.c_str(), isa_));
+ gc::space::ImageSpace::ReadImageHeader(cached_image_info_.location.c_str(),
+ isa_,
+ &error_msg));
+ CHECK(image_header != nullptr) << error_msg;
cached_image_info_.oat_checksum = image_header->GetOatChecksum();
cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(
image_header->GetOatDataBegin());
@@ -813,8 +811,10 @@
} else {
for (gc::space::ImageSpace* image_space : image_spaces) {
std::string location = image_space->GetImageLocation();
+ std::string error_msg;
std::unique_ptr<ImageHeader> image_header(
- gc::space::ImageSpace::ReadImageHeaderOrDie(location.c_str(), isa));
+ gc::space::ImageSpace::ReadImageHeader(location.c_str(), isa, &error_msg));
+ CHECK(image_header != nullptr) << error_msg;
checksum ^= image_header->GetOatChecksum();
}
}
@@ -828,7 +828,7 @@
return combined_image_checksum_;
}
-gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
+std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
DCHECK(oat_file != nullptr);
std::string art_file = ArtFileName(oat_file);
if (art_file.empty()) {
@@ -836,9 +836,8 @@
}
std::string error_msg;
ScopedObjectAccess soa(Thread::Current());
- gc::space::ImageSpace* ret = gc::space::ImageSpace::CreateFromAppImage(art_file.c_str(),
- oat_file,
- &error_msg);
+ std::unique_ptr<gc::space::ImageSpace> ret =
+ gc::space::ImageSpace::CreateFromAppImage(art_file.c_str(), oat_file, &error_msg);
if (ret == nullptr && (VLOG_IS_ON(image) || OS::FileExists(art_file.c_str()))) {
LOG(INFO) << "Failed to open app image " << art_file.c_str() << " " << error_msg;
}
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index effeabb..3f018dc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -180,7 +180,7 @@
std::unique_ptr<OatFile> GetBestOatFile();
// Open and returns an image space associated with the oat file.
- static gc::space::ImageSpace* OpenImageSpace(const OatFile* oat_file);
+ static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
// Loads the dex files in the given oat file for the given dex location.
// The oat file should be up to date for the given dex location.
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 7680517..2e67ffe 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -632,8 +632,8 @@
if (source_oat_file != nullptr) {
bool added_image_space = false;
if (source_oat_file->IsExecutable()) {
- std::unique_ptr<gc::space::ImageSpace> image_space(
- kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr);
+ std::unique_ptr<gc::space::ImageSpace> image_space =
+ kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;
if (image_space != nullptr) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
diff --git a/runtime/simulator/code_simulator_arm64.h b/runtime/simulator/code_simulator_arm64.h
index 69388b1..59ea34f 100644
--- a/runtime/simulator/code_simulator_arm64.h
+++ b/runtime/simulator/code_simulator_arm64.h
@@ -20,10 +20,10 @@
#include "memory"
#include "simulator/code_simulator.h"
-// TODO: make vixl clean wrt -Wshadow.
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
-#include "a64/simulator-a64.h"
+#include "aarch64/simulator-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 515ba9f..8ab8cd2 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1087,58 +1087,18 @@
}
}
-static std::string GetDalvikCacheImpl(const char* subdir,
- const bool create_if_absent,
- const bool abort_on_error) {
+std::string GetDalvikCache(const char* subdir) {
CHECK(subdir != nullptr);
const char* android_data = GetAndroidData();
const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
const std::string dalvik_cache = dalvik_cache_root + subdir;
if (!OS::DirectoryExists(dalvik_cache.c_str())) {
- if (!create_if_absent) {
- // TODO: Check callers. Traditional behavior is to not to abort, even when abort_on_error.
- return "";
- }
-
- // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
- if (strcmp(android_data, "/data") == 0) {
- if (abort_on_error) {
- LOG(FATAL) << "Failed to find dalvik-cache directory " << dalvik_cache
- << ", cannot create /data dalvik-cache.";
- UNREACHABLE();
- }
- return "";
- }
-
- int result = mkdir(dalvik_cache_root.c_str(), 0700);
- if (result != 0 && errno != EEXIST) {
- if (abort_on_error) {
- PLOG(FATAL) << "Failed to create dalvik-cache root directory " << dalvik_cache_root;
- UNREACHABLE();
- }
- return "";
- }
-
- result = mkdir(dalvik_cache.c_str(), 0700);
- if (result != 0) {
- if (abort_on_error) {
- PLOG(FATAL) << "Failed to create dalvik-cache directory " << dalvik_cache;
- UNREACHABLE();
- }
- return "";
- }
+ // TODO: Check callers. Traditional behavior is to not abort.
+ return "";
}
return dalvik_cache;
}
-std::string GetDalvikCache(const char* subdir, const bool create_if_absent) {
- return GetDalvikCacheImpl(subdir, create_if_absent, false);
-}
-
-std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) {
- return GetDalvikCacheImpl(subdir, create_if_absent, true);
-}
-
bool GetDalvikCacheFilename(const char* location, const char* cache_location,
std::string* filename, std::string* error_msg) {
if (location[0] != '/') {
diff --git a/runtime/utils.h b/runtime/utils.h
index 699b732..fd1ba23 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -251,11 +251,8 @@
const char* GetAndroidDataSafe(std::string* error_msg);
// Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
-// could not be found (or created).
-std::string GetDalvikCache(const char* subdir, bool create_if_absent = true);
-// Returns the dalvik-cache location, or dies trying. subdir will be
-// appended to the cache location.
-std::string GetDalvikCacheOrDie(const char* subdir, bool create_if_absent = true);
+// could not be found.
+std::string GetDalvikCache(const char* subdir);
// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
// have_android_data will be set to true if we have an ANDROID_DATA that exists,
// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 55b6e01..d2100d1 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -337,11 +337,9 @@
}
TEST_F(UtilsTest, GetDalvikCache) {
- EXPECT_STREQ("", GetDalvikCache("should-not-exist123", false).c_str());
+ EXPECT_STREQ("", GetDalvikCache("should-not-exist123").c_str());
- EXPECT_STREQ((android_data_ + "/dalvik-cache/.").c_str(), GetDalvikCache(".", false).c_str());
- EXPECT_STREQ((android_data_ + "/dalvik-cache/should-not-be-there").c_str(),
- GetDalvikCache("should-not-be-there", true).c_str());
+ EXPECT_STREQ((android_data_ + "/dalvik-cache/.").c_str(), GetDalvikCache(".").c_str());
}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 05ac5f4..b87e142 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -555,10 +555,13 @@
# Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT).
# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
# more parallel moves on x86, thus some Checker assertions may fail.
+# 527: On ARM64 and ARM, the read barrier instrumentation does not support the HIntermediateAddress
+# instruction yet (b/26601270).
# 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are
# not yet handled in the read barrier configuration.
TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
484-checker-register-hints \
+ 527-checker-array-access-split \
537-checker-arraycopy
# Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).